Raspberry Pi 3 + OLED 128x64 display
Example code of connecting Raspberry Pi with the Adafruit OLED 128x64 pixels. I am simulating the SPI communication in software.
Video
Please click thumbnail image to start the video


Photo






Sketch
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Raspberry Pi 3 with OLED 128x64 display (software SPI) | |
// Copyright (C) 2021 https://www.roboticboat.uk | |
// cac93a5b-3ce1-4477-a224-0395d2a6f3a8 | |
// | |
// This program is free software: you can redistribute it and/or modify | |
// it under the terms of the GNU General Public License as published by | |
// the Free Software Foundation, either version 3 of the License, or | |
// (at your option) any later version. | |
// | |
// This program is distributed in the hope that it will be useful, | |
// but WITHOUT ANY WARRANTY; without even the implied warranty of | |
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
// GNU General Public License for more details. | |
// | |
// You should have received a copy of the GNU General Public License | |
// along with this program. If not, see <https://www.gnu.org/licenses/>. | |
// These Terms shall be governed and construed in accordance with the laws of | |
// England and Wales, without regard to its conflict of law provisions. | |
#include <stdio.h> | |
#include <string.h> | |
#include <errno.h> | |
#include <stdbool.h> // bool variable | |
#include <stdlib.h> // atoi function | |
#include <unistd.h> // usleep | |
#define pgm_read_byte(addr) (*(const unsigned char *)(addr)) | |
#define BYTE_SIZE 8 | |
#define OLED_WIDTH 128 | |
#define OLED_HEIGHT 64 | |
#define OLED_BYTE_ROWS 8 // OLED_HEIGHT/BYTE_SIZE | |
#define FONT_WIDTH 5 | |
#define FONT_HEIGHT 8 | |
#define HIGH true | |
#define LOW false | |
#define INPUT true | |
#define OUTPUT false | |
// Allocate memory to store the screen pixels | |
// On the OLED the screenbuffer appears as a series of 1 bit wide, 8 bit high, vertical bars. | |
// Starting in the top left, the bars are displayed left to right, then the next row | |
// I call the 8 bits high row as a ByteRow | |
static unsigned char screenbuffer[OLED_WIDTH * OLED_BYTE_ROWS] = {0}; | |
static const unsigned char fonttable[] = { | |
0x00, 0x00, 0x00, 0x00, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x00, 0x00, 0x00, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x1C, 0x22, 0x41, 0x00, // ( | |
0x00, 0x41, 0x22, 0x1C, 0x00, // ) | |
0x2A, 0x1C, 0x7F, 0x1C, 0x2A, // * | |
0x08, 0x08, 0x3E, 0x08, 0x08, // + | |
0x00, 0x80, 0x70, 0x30, 0x00, // , | |
0x08, 0x08, 0x08, 0x08, 0x08, // - | |
0x00, 0x00, 0x60, 0x60, 0x00, // . | |
0x20, 0x10, 0x08, 0x04, 0x02, // / | |
0x3E, 0x41, 0x41, 0x41, 0x3E, // 0 | |
0x00, 0x00, 0x7F, 0x00, 0x00, // 1 | |
0x72, 0x49, 0x49, 0x49, 0x46, // 2 | |
0x22, 0x41, 0x49, 0x49, 0x36, // 3 | |
0x18, 0x14, 0x12, 0x7F, 0x10, // 4 | |
0x4F, 0x49, 0x49, 0x49, 0x31, // 5 | |
0x3E, 0x49, 0x49, 0x49, 0x32, // 6 | |
0x01, 0x71, 0x09, 0x09, 0x07, // 7 | |
0x36, 0x49, 0x49, 0x49, 0x36, // 8 | |
0x26, 0x49, 0x49, 0x49, 0x3E, // 9 | |
0x00, 0x00, 0x14, 0x00, 0x00, // : | |
0x00, 0x40, 0x34, 0x00, 0x00, // ; | |
0x00, 0x08, 0x14, 0x22, 0x41, // < | |
0x14, 0x14, 0x14, 0x14, 0x14, // = | |
0x00, 0x41, 0x22, 0x14, 0x08, // > | |
0x02, 0x01, 0x59, 0x09, 0x06, // ? | |
0x3E, 0x41, 0x5D, 0x59, 0x4E, // @ | |
0x7E, 0x11, 0x11, 0x11, 0x7E, // A | |
0x7F, 0x49, 0x49, 0x49, 0x36, // B | |
0x3E, 0x41, 0x41, 0x41, 0x22, // C | |
0x7F, 0x41, 0x41, 0x41, 0x3E, // D | |
0x7F, 0x49, 0x49, 0x49, 0x41, // E | |
0x7F, 0x09, 0x09, 0x09, 0x01, // F | |
0x3E, 0x41, 0x41, 0x49, 0x3A, // G | |
0x7F, 0x08, 0x08, 0x08, 0x7F, // H | |
0x00, 0x41, 0x7F, 0x41, 0x00, // I | |
0x20, 0x40, 0x41, 0x3F, 0x01, // J | |
0x7F, 0x08, 0x14, 0x22, 0x41, // K | |
0x7F, 0x40, 0x40, 0x40, 0x40, // L | |
0x7F, 0x02, 0x0C, 0x02, 0x7F, // M | |
0x7F, 0x04, 0x08, 0x10, 0x7F, // N | |
0x3E, 0x41, 0x41, 0x41, 0x3E, // O | |
0x7F, 0x09, 0x09, 0x09, 0x06, // P | |
0x3E, 0x41, 0x51, 0x21, 0x5E, // Q | |
0x7F, 0x09, 0x19, 0x29, 0x46, // R | |
0x26, 0x49, 0x49, 0x49, 0x32, // S | |
0x01, 0x01, 0x7F, 0x01, 0x01, // T | |
0x3F, 0x40, 0x40, 0x40, 0x3F, // U | |
0x1F, 0x20, 0x40, 0x20, 0x1F, // V | |
0x3F, 0x40, 0x38, 0x40, 0x3F, // W | |
0x63, 0x14, 0x08, 0x14, 0x63, // X | |
0x03, 0x04, 0x78, 0x04, 0x03, // Y | |
0x61, 0x51, 0x49, 0x45, 0x43, // Z | |
0x00, 0x7F, 0x41, 0x41, 0x41, // [ | |
0x02, 0x04, 0x08, 0x10, 0x20, // | |
0x00, 0x41, 0x41, 0x41, 0x7F, // ] | |
0x04, 0x02, 0x01, 0x02, 0x04, // ^ | |
0x40, 0x40, 0x40, 0x40, 0x40, // _ | |
0x00, 0x03, 0x07, 0x08, 0x00, // ` | |
0x20, 0x54, 0x54, 0x78, 0x40, // a | |
0x7F, 0x28, 0x44, 0x44, 0x38, // b | |
0x38, 0x44, 0x44, 0x44, 0x28, // c | |
0x38, 0x44, 0x44, 0x28, 0x7F, // d | |
0x38, 0x54, 0x54, 0x54, 0x18, // e | |
0x00, 0x08, 0x7E, 0x09, 0x02, // f | |
0x18, 0xA4, 0xA4, 0x9C, 0x78, // g | |
0x7F, 0x08, 0x04, 0x04, 0x78, // h | |
0x00, 0x44, 0x7D, 0x40, 0x00, // i | |
0x20, 0x40, 0x40, 0x3D, 0x00, // j | |
0x7F, 0x10, 0x28, 0x44, 0x00, // k | |
0x00, 0x41, 0x7F, 0x40, 0x00, // l | |
0x7C, 0x04, 0x78, 0x04, 0x78, // m | |
0x7C, 0x08, 0x04, 0x04, 0x78, // n | |
0x38, 0x44, 0x44, 0x44, 0x38, // o | |
0xFC, 0x18, 0x24, 0x24, 0x18, // p | |
0x18, 0x24, 0x24, 0x18, 0xFC, // q | |
0x7C, 0x08, 0x04, 0x04, 0x08, // r | |
0x48, 0x54, 0x54, 0x54, 0x24, // s | |
0x04, 0x04, 0x3F, 0x44, 0x24, // t | |
0x3C, 0x40, 0x40, 0x20, 0x7C, // u | |
0x1C, 0x20, 0x40, 0x20, 0x1C, // v | |
0x3C, 0x40, 0x30, 0x40, 0x3C, // w | |
0x44, 0x28, 0x10, 0x28, 0x44, // x | |
0x4C, 0x90, 0x90, 0x90, 0x7C, // y | |
0x44, 0x64, 0x54, 0x4C, 0x44, // z | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x14, 0x08, 0x14, 0x00, | |
0x00, 0x00, 0x00, 0x00, 0x00 | |
}; | |
int8_t _i2caddr; | |
int16_t _positionx; | |
int16_t _positiony; | |
unsigned char _textsize = 1; | |
FILE* sid; | |
FILE* sclk; | |
FILE* dc; | |
FILE* rst; | |
FILE* cs; | |
// Define function signatures | |
void spi_command(unsigned char); | |
void fastSPIwrite(unsigned char); | |
void digitalWrite(FILE*, bool); | |
FILE* pinMode(int, bool); | |
void setPosition(int16_t, int16_t); | |
void updateDisplay(void); | |
void updatePixel(int16_t, int16_t); | |
void clearDisplay(void); | |
void println(const char c[]); | |
void setTextSize(unsigned char s); | |
void drawChar(int16_t x, int16_t y, unsigned char c, unsigned char size); | |
size_t writeOLED(unsigned char c); | |
void begin() ; | |
int main(){ | |
// Initialize with the SPI (for the 128x64) | |
// Please note this is software driven SPI | |
// White wire = GPIO18 = MOSI (note MISO not used) | |
// Blue wire = GPIO22 = Clock | |
// Green wire = GPIO27 = Data/Command flag | |
// Yellow wire = GPIO17 = Reset | |
// Purple wire = GPIO04 = Chip Select | |
// Orange wire = 3.3v | |
// Black wire = GND | |
begin(); | |
while(1){ | |
clearDisplay(); | |
setPosition(0,0); | |
setTextSize(4); | |
println("Hello"); | |
println("Size4"); | |
updateDisplay(); | |
usleep(1000000); | |
clearDisplay(); | |
setPosition(0,0); | |
setTextSize(3); | |
println("Hello"); | |
println("Size 3"); | |
updateDisplay(); | |
usleep(1000000); | |
clearDisplay(); | |
setPosition(0,0); | |
setTextSize(2); | |
println("HelloWorld"); | |
println("0123456789"); | |
println("Size 2"); | |
updateDisplay(); | |
usleep(1000000); | |
clearDisplay(); | |
setPosition(0,0); | |
setTextSize(1); | |
println("HelloWorld"); | |
println("0123456789"); | |
println("ABCDEFGHIJKLMNOPQRSTU"); | |
println("VXWXY"); | |
println("abcdefghijklmnopqrstu"); | |
println("vwxyz"); | |
println("seventh row"); | |
println("Size 1"); | |
updateDisplay(); | |
usleep(1000000); | |
} | |
return 0; | |
} | |
void spi_command(unsigned char cmd) | |
{ | |
//startTransmission | |
digitalWrite(cs, HIGH); | |
digitalWrite(dc, LOW); | |
digitalWrite(cs, LOW); | |
//Send the instructions | |
fastSPIwrite(cmd); | |
//endTransmission | |
digitalWrite(cs, HIGH); | |
} | |
void fastSPIwrite(unsigned char d) { | |
for(unsigned char bit = 0x80; bit; bit >>= 1) | |
{ | |
digitalWrite(sclk, LOW); | |
if(d & bit){ | |
digitalWrite(sid, HIGH); | |
} | |
else{ | |
digitalWrite(sid, LOW); | |
} | |
digitalWrite(sclk, HIGH); | |
} | |
} | |
void begin() { | |
_positionx = 0; | |
_positiony = 0; | |
// Setup the output pins | |
cs = pinMode(4, OUTPUT); // Purple | |
rst = pinMode(17, OUTPUT); // Yellow | |
sid = pinMode(18, OUTPUT); // White - MOSI | |
sclk = pinMode(22, OUTPUT); // Blue - Clock | |
dc = pinMode(27, OUTPUT); // Green | |
// Set reset high | |
digitalWrite(rst, HIGH); | |
// VDD (3.3V) goes high at start | |
usleep(100000); | |
// Set reset low | |
digitalWrite(rst, LOW); | |
// wait 100ms | |
usleep(100000); | |
// Set reset high | |
digitalWrite(rst, HIGH); | |
// Initialization procedure | |
// Set Display Off | |
spi_command(0xAE); | |
// Set Display Clock Ratio | |
spi_command(0xD5); | |
// Set Display Clock Oscillator Frequency | |
spi_command(0x80); | |
// Set Multiplex Ratio | |
spi_command(0xA8); | |
spi_command(OLED_HEIGHT - 1); | |
// Set Display Offset | |
spi_command(0xD3); | |
spi_command(0x00); | |
// Set Display Start Line | |
spi_command(0x40); | |
// Set Charge Pump Regulator | |
spi_command(0x8D); | |
spi_command(0x14); | |
// Set Low Power Display Mode | |
spi_command(0xD8); | |
spi_command(0x05); | |
// There are 3 different memory addressing mode in SSD1306: | |
// page, horizontal and vertical addressing mode | |
spi_command(0x20); | |
spi_command(0x00); | |
// Set Segment Re-Map | |
spi_command(0xA1); | |
// Set COM Output Scan Direction | |
spi_command(0xC8); | |
// Set COM Pins Hardware Configuration | |
spi_command(0xDA); | |
// 128x32 display set = 0x02 | |
// 128x64 display set = 0x12. Otherwise alternate blank rows | |
spi_command(0x12); | |
// Set Contract Control | |
spi_command(0x81); | |
spi_command(0x8F); | |
// Set Pre-Charge Period | |
spi_command(0xD9); | |
spi_command(0xF1); | |
// Set VCOMH Deselect Level | |
spi_command(0xDB); | |
spi_command(0x40); | |
// Set Entire Display On & Resume | |
spi_command(0xA4); | |
// Set Normal/Inverse Display | |
spi_command(0xA6); | |
// Set Display On | |
spi_command(0xAF); | |
usleep(100000); | |
// Set Column Address | |
// This triple byte command specifies column start address and | |
// end address of the display data RAM | |
spi_command(0x21); | |
spi_command(0); | |
spi_command(OLED_WIDTH-1); | |
// Set Page Address | |
// This triple byte command specifies page start address and | |
// end address of the display data RAM. | |
spi_command(0x22); | |
spi_command(0); | |
spi_command(OLED_HEIGHT/FONT_HEIGHT - 1); | |
} | |
void setPosition(int16_t x, int16_t y) { | |
_positionx = x; | |
_positiony = y; | |
} | |
void updateDisplay(void) | |
{ | |
//startTransmission | |
digitalWrite(cs, HIGH); | |
digitalWrite(dc, HIGH); | |
digitalWrite(cs, LOW); | |
for (int16_t i=0; i<(OLED_WIDTH * OLED_BYTE_ROWS); i++) | |
{ | |
fastSPIwrite(screenbuffer[i]); | |
} | |
//endTransmission | |
digitalWrite(cs, HIGH); | |
} | |
size_t writeOLED(unsigned char c) { | |
if (c == '\r') return 1; | |
// Newline? | |
if(c == '\n') | |
{ | |
// Set to the start of the row | |
_positionx = 0; | |
// Move to the next row | |
_positiony = _positiony + _textsize * FONT_HEIGHT; | |
} | |
else | |
{ | |
// Too far to the right? | |
if ((_positionx + _textsize * (FONT_WIDTH +1)) > OLED_WIDTH) | |
{ | |
// Set to the start of the row | |
_positionx = 0; | |
// Move to the next row | |
_positiony = _positiony + _textsize * FONT_HEIGHT; | |
} | |
// Print the character to the screen | |
drawChar(_positionx, _positiony, c, _textsize); | |
// Advance 1 character position | |
_positionx = _positionx + _textsize * (FONT_WIDTH +1); | |
} | |
return 1; | |
} | |
void drawChar(int16_t x, int16_t y, unsigned char c, unsigned char size) { | |
// Check pixel is within the screen area | |
if ((x >= OLED_WIDTH) || (y >= OLED_HEIGHT) || ((x + size*(FONT_WIDTH+1)-1) < 0) || ((y + size*FONT_HEIGHT-1) < 0)) return; | |
// Characters are 5 bits wide and have a separating blank bit | |
for(int8_t i=0; i < FONT_WIDTH; i++ ) | |
{ | |
unsigned char mybyte = pgm_read_byte(&fonttable[c * FONT_WIDTH + i]); | |
// Loop over the height of the font | |
for(int8_t j=0; j<FONT_HEIGHT; j++) | |
{ | |
// Bitwise operator AND | |
// 1100110x mybyte say. | |
// 00000001 (e.g. 1) picks up the last digit 'x' | |
if (mybyte & 1) | |
{ | |
if (size > 0) | |
{ | |
// Update one pixel in memory | |
// x | |
updatePixel(x + i*size, y + j*size); | |
} | |
if (size > 1) | |
{ | |
// Increase square to be 1 bit wider and 1 bit higher | |
// x X | |
// X X | |
updatePixel(x + i*size+1, y + j*size); | |
// Bottom row | |
updatePixel(x + i*size, y + j*size+1); | |
updatePixel(x + i*size+1, y + j*size+1); | |
} | |
if (size > 2) | |
{ | |
// Increase square to be 1 bit wider and 1 bit higher | |
// x x X | |
// x x X | |
// X X X | |
updatePixel(x + i*size+2, y + j*size); | |
updatePixel(x + i*size+2, y + j*size+1); | |
// Bottom row | |
updatePixel(x + i*size, y + j*size+2); | |
updatePixel(x + i*size+1, y + j*size+2); | |
updatePixel(x + i*size+2, y + j*size+2); | |
} | |
if (size >3) | |
{ | |
// Increase square to be 1 bit wider and 1 bit higher | |
// x x x X | |
// x x x X | |
// x x x X | |
// X X X X | |
updatePixel(x + i*size+3, y + j*size); | |
updatePixel(x + i*size+3, y + j*size+1); | |
updatePixel(x + i*size+3, y + j*size+2); | |
// Bottom row | |
updatePixel(x + i*size, y + j*size+3); | |
updatePixel(x + i*size+1, y + j*size+3); | |
updatePixel(x + i*size+2, y + j*size+3); | |
updatePixel(x + i*size+3, y + j*size+3); | |
} | |
if (size >4) | |
{ | |
// Increase square to be 1 bit wider and 1 bit higher | |
// x x x x X | |
// x x x x X | |
// x x x x X | |
// x x x x X | |
// X X X X X | |
updatePixel(x + i*size+4, y + j*size); | |
updatePixel(x + i*size+4, y + j*size+1); | |
updatePixel(x + i*size+4, y + j*size+2); | |
updatePixel(x + i*size+4, y + j*size+3); | |
// Bottom row | |
updatePixel(x + i*size, y + j*size+4); | |
updatePixel(x + i*size+1, y + j*size+4); | |
updatePixel(x + i*size+2, y + j*size+4); | |
updatePixel(x + i*size+3, y + j*size+4); | |
updatePixel(x + i*size+4, y + j*size+4); | |
} | |
} | |
// Bitwise Right Shift operator | |
// [variable] >> [number of places] | |
mybyte >>= 1; | |
} | |
} | |
} | |
void updatePixel(int16_t x, int16_t y) { | |
// Pixel is within the screen area | |
if ((x < 0) || (x >= OLED_WIDTH) || (y < 0) || (y >= OLED_HEIGHT)) return; | |
// Consider the display as a series of 8 bit high rows. A ByteRow | |
// Perform integer arithmetic to find the correct ByteRow the pixel is on | |
int8_t ByteRow = y / BYTE_SIZE; | |
// The trick here is to find the remainder bits = MOD(y,8) in Excel | |
// In the 8 bit high bar want to find the position | |
// Using binary arithmetic interested only in the last 3 bits | |
// If y=146, binary=10010010, last 3 bits=010, in decimal=2 | |
// Bitwise operator AND | |
// 11001xyz mybyte say. | |
// 00000111 (e.g. 7) picks up the last digits 'xyz' | |
int8_t correctposition = y & (BYTE_SIZE-1); | |
// Bitwise Left Shift operator | |
// [variable] << [number of places] | |
// Shift the on pixel to the correct position within the 8 bit high bar | |
// Then perform Bitwise | OR (to add the pixel) | |
screenbuffer[ x + ByteRow * OLED_WIDTH] |= (1 << correctposition); | |
} | |
// Clear the screen | |
void clearDisplay(void) | |
{ | |
memset(screenbuffer, 0, (OLED_WIDTH * OLED_BYTE_ROWS)); | |
} | |
void println(const char c[]) | |
{ | |
size_t i; | |
for (i=0; i< strlen(c); i++){ | |
writeOLED(c[i]); | |
} | |
writeOLED('\r'); | |
writeOLED('\n'); | |
} | |
void setTextSize(unsigned char s) { | |
_textsize = (s > 0) ? s : 1; | |
} | |
//================== PINs ======================== | |
FILE* pinMode(int pinNumber, bool setInputOutput) | |
{ | |
// Allocate memory | |
char buf[40] = {0}; | |
// Create file pointer to the pin | |
// Ensure exclusive use of the pin | |
FILE* file_pin = fopen("/sys/class/gpio/export","w"); | |
fprintf(file_pin,"%d", pinNumber); | |
fclose(file_pin); | |
// Define if the pin is an INPUT or OUTPUT | |
sprintf(buf,"/sys/class/gpio/gpio%d/direction", pinNumber); | |
file_pin = fopen(buf,"w"); | |
if (setInputOutput) | |
{ | |
fprintf(file_pin,"in"); | |
} | |
else | |
{ | |
fprintf(file_pin,"out"); | |
} | |
// Close the file | |
fclose(file_pin); | |
// Set the value of the pin. | |
// Notice that this file is left open to change value in the loop | |
sprintf(buf,"/sys/class/gpio/gpio%d/value", pinNumber); | |
if (setInputOutput) | |
{ | |
// Input so read | |
file_pin = fopen(buf,"r"); | |
} | |
else | |
{ | |
// Output so write | |
file_pin = fopen(buf,"w"); | |
} | |
return file_pin; | |
} | |
void digitalWrite(FILE* file_pin, bool setHigh) | |
{ | |
// The function assumes the file is already open | |
// This is done to save time | |
if (setHigh) | |
{ | |
// Turn-on LED | |
fprintf(file_pin,"1"); | |
} | |
else | |
{ | |
// Turn-off LED | |
fprintf(file_pin,"0"); | |
} | |
// Update the file (without closing it) | |
fflush(file_pin); | |
} |