bad-apple-oled/decode.cpp

172 lines
5.5 KiB
C++
Raw Permalink Normal View History

2023-07-08 01:49:20 +02:00
#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() {
}