#include <Wire.h>
//~ DEFINES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Some defines for the SSD1306 controller driving a 128x64 resolution OLED display
// PART - http://www.simplelabs.co.in/content/96-blue-i2c-oled-module
// DATASHEET - https://www.adafruit.com/datasheets/SSD1306.pdf
// The Slave Address (SLA) of the OLED controller - SSD1306 - is 0x3C
// The LSB is supposed to be the mode. Since we are only going to WRITE to the OLED, the LSB is going to be 0
// SLA (0x3C) + WRITE_MODE (0x00) = 0x78 (0b01111000)
#define OLED_I2C_ADDRESS 0x3C
// The SSD1306 datasheet (pg.20) says that a control byte has to be sent before sending a command
// Control byte consists of
// bit 7 : Co : Continuation bit - If 0, then it assumes all the next bytes are data (no more control bytes).
// : You can send a stream of data, ie: gRAM dump - if Co=0
// : For Command, you'd prolly wanna set this - one at a time. Hence, Co=1 for commands
// : For Data stream, Co=0 :)
// bit 6 : D/C# : Data/Command Selection bit, Data=1/Command=0
// bit [5-0] : lower 6 bits have to be 0
#define OLED_CONTROL_BYTE_CMD_SINGLE 0x80
#define OLED_CONTROL_BYTE_CMD_STREAM 0x00
#define OLED_CONTROL_BYTE_DATA_STREAM 0x40
// Fundamental commands (pg.28)
#define OLED_CMD_SET_CONTRAST 0x81 // follow with 0x7F
#define OLED_CMD_DISPLAY_RAM 0xA4
#define OLED_CMD_DISPLAY_ALLON 0xA5
#define OLED_CMD_DISPLAY_NORMAL 0xA6
#define OLED_CMD_DISPLAY_INVERTED 0xA7
#define OLED_CMD_DISPLAY_OFF 0xAE
#define OLED_CMD_DISPLAY_ON 0xAF
// Addressing Command Table (pg.30)
#define OLED_CMD_SET_MEMORY_ADDR_MODE 0x20 // follow with 0x00 = HORZ mode = Behave like a KS108 graphic LCD
#define OLED_CMD_SET_COLUMN_RANGE 0x21 // can be used only in HORZ/VERT mode - follow with 0x00 + 0x7F = COL127
#define OLED_CMD_SET_PAGE_RANGE 0x22 // can be used only in HORZ/VERT mode - follow with 0x00 + 0x07 = PAGE7
// Hardware Config (pg.31)
#define OLED_CMD_SET_DISPLAY_START_LINE 0x40
#define OLED_CMD_SET_SEGMENT_REMAP 0xA1
#define OLED_CMD_SET_MUX_RATIO 0xA8 // follow with 0x3F = 64 MUX
#define OLED_CMD_SET_COM_SCAN_MODE 0xC8
#define OLED_CMD_SET_DISPLAY_OFFSET 0xD3 // follow with 0x00
#define OLED_CMD_SET_COM_PIN_MAP 0xDA // follow with 0x12
#define OLED_CMD_NOP 0xE3 // NOP
// Timing and Driving Scheme (pg.32)
#define OLED_CMD_SET_DISPLAY_CLK_DIV 0xD5 // follow with 0x80
#define OLED_CMD_SET_PRECHARGE 0xD9 // follow with 0x22
#define OLED_CMD_SET_VCOMH_DESELCT 0xDB // follow with 0x30
// Charge Pump (pg.62)
#define OLED_CMD_SET_CHARGE_PUMP 0x8D // follow with 0x14
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Used LCD Assistant to generate this starting splash screen for the buffer
// 128x64 = 8192 bits = 1024 bytes
static unsigned char buffer [1024] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xE0, 0xE0, 0x18, 0x18, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x18,
0x18, 0xE0, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xFF, 0xFF, 0xFE, 0xFE, 0x06, 0x06, 0x06, 0x06, 0x1E, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0x06,
0x06, 0x06, 0x06, 0x06, 0x06, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0x1E, 0x06, 0x06, 0x06, 0x06, 0xFE,
0xFE, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x7F, 0x7F, 0x87, 0x87, 0x00, 0x00, 0x00, 0x00, 0x60, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x60, 0x00, 0x00, 0x00, 0x00, 0x87,
0x87, 0x7F, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xF0, 0x0C, 0x0C, 0x0C,
0x0C, 0x00, 0x00, 0xFC, 0xFC, 0xC0, 0xC0, 0x00, 0x00, 0x00, 0x00, 0xCC, 0xCC, 0x00, 0x00, 0xC0,
0xC0, 0xC0, 0xC0, 0x00, 0x00, 0x00, 0x30, 0x30, 0x48, 0x86, 0x86, 0x48, 0x30, 0x30, 0x00, 0xC0,
0xC0, 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x00, 0xCC, 0xCC, 0x00, 0x00, 0xC0, 0xC0, 0xC0, 0xC0, 0x00,
0x00, 0x00, 0x00, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8,
0xF8, 0x18, 0x18, 0x19, 0x19, 0x1E, 0x1E, 0x18, 0x18, 0x18, 0x18, 0x60, 0x60, 0x60, 0x60, 0x60,
0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x18, 0x18, 0x18, 0x18, 0x1E, 0x1E, 0x19,
0x19, 0x18, 0x18, 0xF8, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x0C, 0x0C, 0x0C,
0x0C, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x3F,
0x3F, 0x0C, 0x0C, 0x03, 0x03, 0x00, 0x0C, 0x0C, 0x12, 0x61, 0x61, 0x12, 0x0C, 0x0C, 0x00, 0x03,
0x03, 0x0C, 0x0C, 0x0F, 0x0F, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x0F,
0x0F, 0x00, 0x00, 0x0F, 0x0F, 0x0C, 0x0C, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xE0, 0x60, 0x60, 0x7F,
0x7F, 0x60, 0x60, 0x60, 0x60, 0xE0, 0xE0, 0xF8, 0xF8, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xF8, 0xF8, 0xE0, 0xE0, 0x60,
0x60, 0x60, 0x60, 0x7F, 0x7F, 0x60, 0x60, 0xE0, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x7F, 0x60, 0x60, 0x60,
0x60, 0x60, 0x60, 0x60, 0x60, 0xFF, 0xFF, 0x7F, 0x7F, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0xE1,
0xE1, 0x61, 0x61, 0xE1, 0xE1, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x7F, 0x7F, 0xFF, 0xFF, 0x60,
0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x7F, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x7F, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x7F,
0x7F, 0x00, 0x00, 0x7F, 0x7F, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x7F, 0x7F, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
unsigned long start_time, end_time;
unsigned int frame=-1;
unsigned int frame_cntr=0;
void setup() {
Serial.begin(19200);
Serial.println("RDY");
// Init the OLED controller
OLED_init();
// Customary delay
delay(250);
}
void loop() {
// if(frame==-1){
// frame=millis();
// }
start_time = micros();
Wire.beginTransmission(OLED_I2C_ADDRESS);
Wire.write(OLED_CONTROL_BYTE_CMD_STREAM);
Wire.write(OLED_CMD_SET_COLUMN_RANGE);
Wire.write(0x00);
Wire.write(0x7F);
Wire.write(OLED_CMD_SET_PAGE_RANGE);
Wire.write(0x00);
Wire.write(0x07);
Wire.endTransmission();
for(uint16_t i=0;i<1024;i++){
Wire.beginTransmission(OLED_I2C_ADDRESS);
Wire.write(OLED_CONTROL_BYTE_DATA_STREAM);
for (uint8_t x=0; x<16; x++) {
Wire.write(buffer[i]);
i++;
}
i--;
Wire.endTransmission();
}
end_time = micros();
Serial.println("Frame Time: ");
Serial.println(end_time-start_time);
while(1){};
// Frame Guard
// while((millis()-frame)<40){ };
// frame_cntr++;
// if((millis()-frame)>=1000){
// Serial.println("Frame Rate: ");
// Serial.println(frame_cntr);
// // while(1){};
// frame_cntr=0;
// frame=millis();
// };
}
void OLED_init() {
// Init the I2C interface (pins A4 and A5 on the Arduino Uno board) in Master Mode.
Wire.begin();
// Set the I2C to HS mode - 400KHz!
// TWBR = (CPU_CLK / I2C_CLK) -16 /2
// TWBR = ((16,000,000 / 400,000) - 16) / 2 = 12
TWBR=12;
// keywords:
// SEG = COL = segment = column byte data on a page
// Page = 8 pixel tall row. Has 128 SEGs and 8 COMs
// COM = row
// Begin the I2C comm with SSD1306's address (SLA+Write)
Wire.beginTransmission(OLED_I2C_ADDRESS);
// Tell the SSD1306 that a command stream is incoming
Wire.write(OLED_CONTROL_BYTE_CMD_STREAM);
// Follow instructions on pg.64 of the dataSheet for software configuration of the SSD1306
// Turn the Display OFF
Wire.write(OLED_CMD_DISPLAY_OFF);
// Set mux ration tp select max number of rows - 64
Wire.write(OLED_CMD_SET_MUX_RATIO);
Wire.write(0x3F);
// Set the display offset to 0
Wire.write(OLED_CMD_SET_DISPLAY_OFFSET);
Wire.write(0x00);
// Display start line to 0
Wire.write(OLED_CMD_SET_DISPLAY_START_LINE);
// Mirror the x-axis. In case you set it up such that the pins are north.
// Wire.write(0xA0); - in case pins are south - default
Wire.write(OLED_CMD_SET_SEGMENT_REMAP);
// Mirror the y-axis. In case you set it up such that the pins are north.
// Wire.write(0xC0); - in case pins are south - default
Wire.write(OLED_CMD_SET_COM_SCAN_MODE);
// Default - alternate COM pin map
Wire.write(OLED_CMD_SET_COM_PIN_MAP);
Wire.write(0x12);
// set contrast
Wire.write(OLED_CMD_SET_CONTRAST);
Wire.write(0x7F);
// Set display to enable rendering from GDDRAM (Graphic Display Data RAM)
Wire.write(OLED_CMD_DISPLAY_RAM);
// Normal mode!
Wire.write(OLED_CMD_DISPLAY_NORMAL);
// Default oscillator clock
Wire.write(OLED_CMD_SET_DISPLAY_CLK_DIV);
Wire.write(0x80);
// Enable the charge pump
Wire.write(OLED_CMD_SET_CHARGE_PUMP);
Wire.write(0x14);
// Set precharge cycles to high cap type
Wire.write(OLED_CMD_SET_PRECHARGE);
Wire.write(0x22);
// Set the V_COMH deselect volatage to max
Wire.write(OLED_CMD_SET_VCOMH_DESELCT);
Wire.write(0x30);
// Horizonatal addressing mode - same as the KS108 GLCD
Wire.write(OLED_CMD_SET_MEMORY_ADDR_MODE);
Wire.write(0x00);
// Turn the Display ON
Wire.write(OLED_CMD_DISPLAY_ON);
// End the I2C comm with the SSD1306
Wire.endTransmission();
}