172 lines
5.5 KiB
C++
172 lines
5.5 KiB
C++
|
#include <Wire.h>
|
||
|
|
||
|
#define SSD1306_MEMORYMODE 0x20
|
||
|
#define SSD1306_COLUMNADDR 0x21
|
||
|
#define SSD1306_PAGEADDR 0x22
|
||
|
#define SSD1306_SETCONTRAST 0x81
|
||
|
#define SSD1306_CHARGEPUMP 0x8D
|
||
|
#define SSD1306_SEGREMAP 0xA0
|
||
|
#define SSD1306_DISPLAYALLON_RESUME 0xA4
|
||
|
#define SSD1306_DISPLAYALLON 0xA5
|
||
|
#define SSD1306_NORMALDISPLAY 0xA6
|
||
|
#define SSD1306_INVERTDISPLAY 0xA7
|
||
|
#define SSD1306_SETMULTIPLEX 0xA8
|
||
|
#define SSD1306_DISPLAYOFF 0xAE
|
||
|
#define SSD1306_DISPLAYON 0xAF
|
||
|
#define SSD1306_COMSCANINC 0xC0
|
||
|
#define SSD1306_COMSCANDEC 0xC8
|
||
|
#define SSD1306_SETDISPLAYOFFSET 0xD3
|
||
|
#define SSD1306_SETDISPLAYCLOCKDIV 0xD5
|
||
|
#define SSD1306_SETPRECHARGE 0xD9
|
||
|
#define SSD1306_SETCOMPINS 0xDA
|
||
|
#define SSD1306_SETVCOMDETECT 0xDB
|
||
|
|
||
|
#define SSD1306_SETLOWCOLUMN 0x00
|
||
|
#define SSD1306_SETHIGHCOLUMN 0x10
|
||
|
#define SSD1306_SETSTARTLINE 0x40
|
||
|
|
||
|
// TODO: remove
|
||
|
#define SSD1306_EXTERNALVCC 0x01 // External display voltage source
|
||
|
#define SSD1306_SWITCHCAPVCC 0x02 // Gen. display voltage from 3.3V
|
||
|
|
||
|
#define SSD1306_RIGHT_HORIZONTAL_SCROLL 0x26 // Init rt scroll
|
||
|
#define SSD1306_LEFT_HORIZONTAL_SCROLL 0x27 // Init left scroll
|
||
|
#define SSD1306_VERTICAL_AND_RIGHT_HORIZONTAL_SCROLL 0x29 // Init diag scroll
|
||
|
#define SSD1306_VERTICAL_AND_LEFT_HORIZONTAL_SCROLL 0x2A // Init diag scroll
|
||
|
#define SSD1306_DEACTIVATE_SCROLL 0x2E // Stop scroll
|
||
|
#define SSD1306_ACTIVATE_SCROLL 0x2F // Start scroll
|
||
|
#define SSD1306_SET_VERTICAL_SCROLL_AREA 0xA3 // Set scroll range
|
||
|
|
||
|
#define WIDTH 85
|
||
|
#define HEIGHT 64
|
||
|
#define BUFFERSIZE (WIDTH * HEIGHT / 8)
|
||
|
#define I2C_ADDR 0x3c
|
||
|
#define OLED_RESET 4
|
||
|
|
||
|
uint8_t framebuffer[BUFFERSIZE] = {};
|
||
|
|
||
|
void pixel_set(int x, int y, bool value) {
|
||
|
uint8_t bitmask = (1 << (y & 0x7));
|
||
|
uint8_t* offset = &buffer[x + (y / 8) * WIDTH];
|
||
|
if (value) *offset |= bitmask;
|
||
|
else *offset &= ~bitmask;
|
||
|
}
|
||
|
|
||
|
void pixel_flip(int x, int y) {
|
||
|
uint8_t bitmask = (1 << (y & 0x7));
|
||
|
uint8_t* offset = &buffer[x + (y / 8) * WIDTH];
|
||
|
*offset ^= bitmask;
|
||
|
}
|
||
|
|
||
|
uint8_t* decode_frame(uint8_t* data) {
|
||
|
int pixel_x = 0;
|
||
|
int pixel_y = 0;
|
||
|
while (pixel_x != WIDHT-1 && pixel_y != HEIGHT-1) {
|
||
|
uint8_t byte = *data++;
|
||
|
|
||
|
int do_flip = byte & 0x80;
|
||
|
int n_affected = byte & 0x3f;
|
||
|
if (byte & 0x40) {
|
||
|
n_affected = (n_affected << 8) | *(data++);
|
||
|
}
|
||
|
|
||
|
// idea: traverse along y instead, to optimize this logic
|
||
|
while (n_affected--) {
|
||
|
if (do_flip) pixel_flip(pixel_x, pixel_y);
|
||
|
if (pixel_x == WIDTH-1) {
|
||
|
pixel_x = 0;
|
||
|
pixel_y++;
|
||
|
} else {
|
||
|
pixel_x++;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return data; // returns the next frame offset
|
||
|
}
|
||
|
|
||
|
void ssd1306_command(uint8_t c) {
|
||
|
Wire->beginTransmission(I2C_ADDR);
|
||
|
Wire.write(0x00); // Co = 0, D/C = 0
|
||
|
Wire.write(c);
|
||
|
Wire->endTransmission();
|
||
|
}
|
||
|
|
||
|
void init_display(int reset_pin, bool vcc_is_5v_external) {
|
||
|
Wire.begin();
|
||
|
pinMode(OLED_RESET, OUTPUT);
|
||
|
|
||
|
// reset
|
||
|
digitalWrite(OLED_RESET, HIGH);
|
||
|
delay(1);
|
||
|
digitalWrite(OLED_RESET, LOW);
|
||
|
delay(10);
|
||
|
digitalWrite(OLED_RESET, HIGH);
|
||
|
|
||
|
// init sequence
|
||
|
ssd1306_command(SSD1306_DISPLAYOFF);
|
||
|
ssd1306_command(SSD1306_SETDISPLAYCLOCKDIV);
|
||
|
ssd1306_command(0x80); // the suggested ratio
|
||
|
ssd1306_command(SSD1306_SETMULTIPLEX);
|
||
|
ssd1306_command(HEIGHT - 1);
|
||
|
|
||
|
ssd1306_command(SSD1306_SETDISPLAYOFFSET);
|
||
|
ssd1306_command(0x00); // no offset
|
||
|
ssd1306_command(SSD1306_SETSTARTLINE | 0x0); // (line #0)
|
||
|
ssd1306_command(SSD1306_CHARGEPUMP);
|
||
|
|
||
|
ssd1306_command((vcc_is_5v_external) ? 0x10 : 0x14);
|
||
|
|
||
|
ssd1306_command(SSD1306_MEMORYMODE); // 0x20
|
||
|
ssd1306_command(0x00); // 0x0 act like ks0108
|
||
|
ssd1306_command(SSD1306_SEGREMAP | 0x1);
|
||
|
ssd1306_command(SSD1306_COMSCANDEC);
|
||
|
|
||
|
uint8_t comPins, contrast;
|
||
|
if ((WIDTH == 128) && (HEIGHT == 64)) { comPins = 0x12; contrast = (vcc_is_5v_external) ? 0x9F : 0xCF; }
|
||
|
//else if ((WIDTH == 128) && (HEIGHT == 32)) { comPins = 0x02; contrast = 0x8F; }
|
||
|
//else if ((WIDTH == 96) && (HEIGHT == 16)) { comPins = 0x02; contrast = (vcc_is_5v_external) ? 0x10 : 0xAF; }
|
||
|
//else { /* Other screen varieties -- TBD */ }
|
||
|
|
||
|
ssd1306_command(SSD1306_SETCOMPINS);
|
||
|
ssd1306_command(comPins);
|
||
|
ssd1306_command(SSD1306_SETCONTRAST);
|
||
|
ssd1306_command(contrast);
|
||
|
|
||
|
ssd1306_command(SSD1306_SETPRECHARGE); // 0xd9
|
||
|
ssd1306_command((vcc_is_5v_external) ? 0x22 : 0xF1);
|
||
|
|
||
|
ssd1306_command(SSD1306_SETVCOMDETECT);
|
||
|
ssd1306_command(0x40);
|
||
|
ssd1306_command(SSD1306_DISPLAYALLON_RESUME);
|
||
|
ssd1306_command(SSD1306_NORMALDISPLAY);
|
||
|
ssd1306_command(SSD1306_DEACTIVATE_SCROLL);
|
||
|
ssd1306_command(SSD1306_DISPLAYON); // Main screen turn on
|
||
|
|
||
|
ssd1306_command();
|
||
|
}
|
||
|
|
||
|
void send_framebuffer() {
|
||
|
int buffersize = 32;
|
||
|
Wire.beginTransmission(I2C_ADDR);
|
||
|
Wire.write(0x40);
|
||
|
for (int i = 0; i < BUFFERSIZE; i++) {
|
||
|
if (!--buffersize) {
|
||
|
Wire.endTransmission();
|
||
|
Wire.beginTransmission(I2C_ADDR);
|
||
|
buffersize = 32;
|
||
|
}
|
||
|
Wire.write(framebuffer[i]);
|
||
|
}
|
||
|
Wire.endTransmission();
|
||
|
}
|
||
|
|
||
|
|
||
|
void Setup() {
|
||
|
init_display();
|
||
|
send_framebuffer();
|
||
|
}
|
||
|
|
||
|
Void Loop() {
|
||
|
|
||
|
}
|