Not all uses of SSD1306 OLED requires lots of resources as can be seen in this Conway example:
// Conway's Game Of Life 256x256
// PaulRB http://stm32duino.com/viewtopic.php?f=19&t=137&hilit=conway
// Jun 2015
/*
Compiled with ArduinoIDE 1.7.3 on Linux Mint 17.3 Cinnamon tested 20160201
Sketch uses 14,932 bytes (12%) of program storage space. Maximum is 122,880 bytes.
Global variables use 10,968 bytes of dynamic memory.
*/
#include <SPI.h>
//Pins controlling SSD1306 Graphic OLED
#define OLED_DC 1
#define OLED_CS 0
#define OLED_RESET 2
#define OLED_COLS 128
#define OLED_ROWS 8
#define MATRIX_COLS 256
#define MATRIX_ROWS 32
union MatrixData {
unsigned long long l[MATRIX_ROWS/8];
byte b[MATRIX_ROWS];
};
#define SW_U 17
#define SW_D 20
#define SW_L 18
#define SW_R 16
#define SW_C 19
int scrollHorz = 0;
int scrollVert = 0;
MatrixData Matrix[MATRIX_COLS+1]; // Cell data in ram
void setup() {
pinMode(OLED_DC, OUTPUT);
pinMode(OLED_CS, OUTPUT);
pinMode(OLED_RESET, OUTPUT);
SPI.begin();
SPI.setBitOrder(MSBFIRST); // Set the SPI bit order
SPI.setDataMode(SPI_MODE0); //Set the SPI data mode 0
SPI.setClockDivider(SPI_CLOCK_DIV2); // 2.25 MHz
digitalWrite(OLED_RESET, HIGH);
delay(1);
digitalWrite(OLED_RESET, LOW);
delay(10);
digitalWrite(OLED_RESET, HIGH);
digitalWrite(OLED_DC, LOW);
digitalWrite(OLED_CS, LOW);
SPI.transfer(0xAE); // Display off
SPI.transfer(0xD5); // Set display clock divider
SPI.transfer(0x80);
SPI.transfer(0xA8); // Set multiplex
SPI.transfer(0x3F);
SPI.transfer(0xD3); // Set display offset
SPI.transfer(0x00);
SPI.transfer(0x40); // Set start line to zero
SPI.transfer(0x8D); // Set charge pump
SPI.transfer(0x14);
SPI.transfer(0x20); // Set memory mode
SPI.transfer(0x00);
SPI.transfer(0xA0 | 0x1); // Set segment remapping
SPI.transfer(0xC8); // Set command Scan decode
SPI.transfer(0xDA); // Set Comm pins
SPI.transfer(0x12);
SPI.transfer(0x81); // Set contrast
SPI.transfer(0xCF);
SPI.transfer(0xd9); // Set precharge
SPI.transfer(0xF1);
SPI.transfer(0xDB); // Set Vcom detect
SPI.transfer(0x40);
SPI.transfer(0xA4); // Allow display resume
SPI.transfer(0xA6); // Set normal display
SPI.transfer(0xAF); // Display On
digitalWrite(OLED_CS, HIGH);
//R-pentomino
//Matrix[64].l[4] = 0b0000010;
//Matrix[65].l[4] = 0b0000111;
//Matrix[66].l[4] = 0b0000100;
//Gosper's Glider Gun
//Matrix[64].l[4] = 0b00000000000000000000000000010000000000000000000000;
//Matrix[65].l[4] = 0b00000000000000000000000001010000000000000000000000;
//Matrix[66].l[4] = 0b00000000000000011000000110000000000001100000000000;
//Matrix[67].l[4] = 0b00000000000000100010000110000000000001100000000000;
//Matrix[68].l[4] = 0b00011000000001000001000110000000000000000000000000;
//Matrix[69].l[4] = 0b00011000000001000101100001010000000000000000000000;
//Matrix[70].l[4] = 0b00000000000001000001000000010000000000000000000000;
//Matrix[71].l[4] = 0b00000000000000100010000000000000000000000000000000;
//Matrix[72].l[4] = 0b00000000000000011000000000000000000000000000000000;
randomiseMatrix();
outputMatrix();
Serial.begin(115200);
pinMode(SW_U, INPUT_PULLUP);
pinMode(SW_D, INPUT_PULLUP);
pinMode(SW_L, INPUT_PULLUP);
pinMode(SW_R, INPUT_PULLUP);
pinMode(SW_C, INPUT_PULLUP);
}
void loop() {
unsigned long start = millis();
for (int i=0; i<1000; i++) {
generateMatrix();
outputMatrix();
}
//Serial.print("Gens/s:"); Serial.println(1000000UL/(millis() - start));
}
void outputMatrix() {
digitalWrite(OLED_DC, LOW); //Command mode
digitalWrite(OLED_CS, LOW); //Enable display on SPI bus
SPI.transfer(0x21); // Set column address
SPI.transfer(0);
SPI.transfer(OLED_COLS-1);
SPI.transfer(0x22); // Set page address
SPI.transfer(0);
SPI.transfer(OLED_ROWS-1);
digitalWrite(OLED_CS, HIGH); //Disable display on SPI bus
digitalWrite(OLED_DC, HIGH); // Data mode
digitalWrite(OLED_CS, LOW); //Enable display on SPI bus
if (digitalRead(SW_L) == LOW && scrollHorz < (MATRIX_COLS - OLED_COLS)) scrollHorz++;
if (digitalRead(SW_R) == LOW && scrollHorz > 0) scrollHorz--;
if (digitalRead(SW_U) == LOW && scrollVert < ((MATRIX_ROWS - OLED_ROWS)<<3)) scrollVert++;
if (digitalRead(SW_D) == LOW && scrollVert > 0) scrollVert--;
//Send matrix data for display on OLED
for (int col = 0; col < OLED_ROWS; col++) {
int colScrolled = col + (scrollVert>>3);
for (int row = 0; row < OLED_COLS; row++) {
SPI.transfer(Matrix[row+scrollHorz].b[colScrolled]);
}
}
digitalWrite(OLED_CS, HIGH);
}
void randomiseMatrix() {
//Set up initial cells in matrix
randomSeed(analogRead(0));
for (int row = 0; row < MATRIX_COLS; row++) {
for (int col = 0; col < MATRIX_ROWS; col++) {
Matrix[row].b[col] = random(0xff);
}
}
}
void injectGlider() {
int col = random(MATRIX_COLS);
int row = random(MATRIX_ROWS);
Matrix[col++].b[row] |= 0b0000111;
Matrix[col++].b[row] |= 0b0000001;
Matrix[col++].b[row] |= 0b0000010;
}
void rotateLeft(unsigned long long x[]) {
unsigned long long c = 0;
for (int i=0; i<4; i++) {
unsigned long long c2 = x[i] >> 63;
x[i] = x[i] << 1 | c;
c = c2;
}
x[0] |= c;
}
void rotateRight(unsigned long long x[]) {
unsigned long long c = 0;
for (int i=3; i>=0; i--) {
unsigned long long c2 = x[i] << 63;
x[i] = x[i] >> 1 | c;
c = c2;
}
x[3] |= c;
}
int generateMatrix() {
//Variables holding data on neighbouring cells
MatrixData NeighbourN, NeighbourNW, NeighbourNE, CurrCells, NeighbourW, NeighbourE, NeighbourS, NeighbourSW, NeighbourSE;
//Variables used in calculating new cells
unsigned long long tot1, carry, tot2, tot4, NewCells;
int changes = 0; // counts the changes in the matrix
static int prevChanges[4]; // counts the changes in the matrix on prev 4 generations
static int staleCount = 0; // counts the consecutive occurrances of the same number of changes in the matrix
//set up N, NW, NE, W & E neighbour data
NeighbourN = Matrix[MATRIX_COLS-1];
CurrCells = Matrix[0];
Matrix[MATRIX_COLS] = CurrCells; // copy row 0 to location after last row to remove need for wrap-around code in the loop
NeighbourNW = NeighbourN;
rotateLeft(NeighbourNW.l);
NeighbourNE = NeighbourN;
rotateRight(NeighbourNE.l);
NeighbourW = CurrCells;
rotateLeft(NeighbourW.l);
NeighbourE = CurrCells;
rotateRight(NeighbourE.l);
//Process each row of the matrix
for (int row = 0; row < MATRIX_COLS; row++) {
//Pick up new S, SW & SE neighbours
NeighbourS = Matrix[row + 1];
NeighbourSW = NeighbourS;
rotateLeft(NeighbourSW.l);
NeighbourSE = NeighbourS;
rotateRight(NeighbourSE.l);
for (int i=0; i<MATRIX_ROWS/8; i++) {
//Count the live neighbours (in parallel) for the current row of cells
//However, if total goes over 3, we don't care (see below), so counting stops at 4
tot1 = NeighbourN.l[i];
tot2 = tot1 & NeighbourNW.l[i]; tot1 = tot1 ^ NeighbourNW.l[i];
carry = tot1 & NeighbourNE.l[i]; tot1 = tot1 ^ NeighbourNE.l[i]; tot4 = tot2 & carry; tot2 = tot2 ^ carry;
carry = tot1 & NeighbourW.l[i]; tot1 = tot1 ^ NeighbourW.l[i]; tot4 = tot2 & carry | tot4; tot2 = tot2 ^ carry;
carry = tot1 & NeighbourE.l[i]; tot1 = tot1 ^ NeighbourE.l[i]; tot4 = tot2 & carry | tot4; tot2 = tot2 ^ carry;
carry = tot1 & NeighbourS.l[i]; tot1 = tot1 ^ NeighbourS.l[i]; tot4 = tot2 & carry | tot4; tot2 = tot2 ^ carry;
carry = tot1 & NeighbourSW.l[i]; tot1 = tot1 ^ NeighbourSW.l[i]; tot4 = tot2 & carry | tot4; tot2 = tot2 ^ carry;
carry = tot1 & NeighbourSE.l[i]; tot1 = tot1 ^ NeighbourSE.l[i]; tot4 = tot2 & carry | tot4; tot2 = tot2 ^ carry;
//Calculate the updated cells:
// <2 or >3 neighbours, cell dies
// =2 neighbours, cell continues to live
// =3 neighbours, new cell born
NewCells = (CurrCells.l[i] | tot1) & tot2 & ~ tot4;
//Have any cells changed?
if (NewCells != CurrCells.l[i]) {
//Count the change for "stale" test
changes++;
Matrix[row].l[i] = NewCells;
}
}
//Current cells (before update), E , W, SE, SW and S neighbours become
//new N, NW, NE, E, W neighbours and current cells for next loop
NeighbourN = CurrCells;
NeighbourNW = NeighbourW;
NeighbourNE = NeighbourE;
NeighbourE = NeighbourSE;
NeighbourW = NeighbourSW;
CurrCells = NeighbourS;
}
if (changes != prevChanges[0] && changes != prevChanges[1] && changes != prevChanges[2] && changes != prevChanges[3]) {
staleCount = 0;
}
else {
staleCount++; //Detect "stale" matrix
}
if (staleCount > 64) injectGlider(); //Inject a glider
//Serial.println(changes);
for (int i=3; i>0; i--) {
prevChanges[i] = prevChanges[i-1];
}
prevChanges[0] = changes;
}
Take-A-Way:
-
a MCU should match the target needs which implies that some effort has been applied to document the overall requirements!
-
Not all OLED projects require a heavy hitting graphics library such as the Adafruit lib.
EXAMPLE
/*
------------------------------------------------------------------------------------
Credits to Mike Rankin for OLED routines...
------------------------------------------------------------------------------------
*/
static void reset_display(void)
{
displayOff();
clear_display();
displayOn();
}
/*
------------------------------------------------------------------------------------
Simple sub routine added to get OLED up and running
------------------------------------------------------------------------------------
*/
void StartUp_OLED()
{
init_OLED();
reset_display();
displayOff();
setXY(0,0);
clear_display();
displayOn();
}
//==========================================================//
// Turns display on.
void displayOn(void)
{
sendcommand(0xaf); //display on
}
//==========================================================//
// Turns display off.
void displayOff(void)
{
sendcommand(0xae); //display off
}
//==========================================================//
// Clears the display by sendind 0 to all the screen map.
static void clear_display(void)
{
unsigned char i,k;
for(k=0;k<8;k++)
{
setXY(k,0);
{
for(i=0;i<128;i++) //clear all COL
{
SendChar(0); //clear all COL
}
}
}
}
//==========================================================//
// Actually this sends a byte, not a char to draw in the display.
// Display's chars uses 8 byte font the small ones and 96 bytes
// for the big number font.
static void SendChar(unsigned char data)
{
Wire.beginTransmission(OLED_address); // begin transmitting
Wire.write(0x40);//data mode
Wire.write(data);
Wire.endTransmission(); // stop transmitting
}
//==========================================================//
// Prints a display char (not just a byte) in coordinates X Y,
// being multiples of 8. This means we have 16 COLS (0-15)
// and 8 ROWS (0-7).
static void sendCharXY(unsigned char data, int X, int Y)
{
//if (interrupt && !doing_menu) return; // Stop printing only if interrupt is call but not in button functions
setXY(X, Y);
Wire.beginTransmission(OLED_address); // begin transmitting
Wire.write(0x40);//data mode
for(int i=0;i<8;i++)
Wire.write(pgm_read_byte(myFont[data-0x20]+i));
Wire.endTransmission(); // stop transmitting
}
//==========================================================//
// Used to send commands to the display.
static void sendcommand(unsigned char com)
{
Wire.beginTransmission(OLED_address); //begin transmitting
Wire.write(0x80); //command mode
Wire.write(com);
Wire.endTransmission(); // stop transmitting
}
//==========================================================//
// Set the cursor position in a 16 COL * 8 ROW map.
static void setXY(unsigned char row,unsigned char col)
{
sendcommand(0xb0+row); //set page address
sendcommand(0x00+(8*col&0x0f)); //set low col address
sendcommand(0x10+((8*col>>4)&0x0f)); //set high col address
}
//==========================================================//
// Prints a string regardless the cursor position.
static void sendStr(unsigned char *string)
{
unsigned char i=0;
while(*string)
{
for(i=0;i<8;i++)
{
SendChar(pgm_read_byte(myFont[*string-0x20]+i));
}
*string++;
}
}
//==========================================================//
// Prints a string in coordinates X Y, being multiples of 8.
// This means we have 16 COLS (0-15) and 8 ROWS (0-7).
static void sendStrXY( char *string, int X, int Y)
{
setXY(X,Y);
unsigned char i=0;
while(*string)
{
for(i=0;i<8;i++)
{
SendChar(pgm_read_byte(myFont[*string-0x20]+i));
}
*string++;
}
}
//==========================================================//
// Inits oled and draws logo at startup
static void init_OLED(void)
{
sendcommand(0xae); //display off
sendcommand(0xa6); //Set Normal Display (default)
// Adafruit Init sequence for 128x64 OLED module
sendcommand(0xAE); //DISPLAYOFF
sendcommand(0xD5); //SETDISPLAYCLOCKDIV
sendcommand(0x80); // the suggested ratio 0x80
sendcommand(0xA8); //SSD1306_SETMULTIPLEX
sendcommand(0x3F);
sendcommand(0xD3); //SETDISPLAYOFFSET
sendcommand(0x0); //no offset
sendcommand(0x40 | 0x0); //SETSTARTLINE
sendcommand(0x8D); //CHARGEPUMP
sendcommand(0x14);
sendcommand(0x20); //MEMORYMODE
sendcommand(0x00); //0x0 act like ks0108
//sendcommand(0xA0 | 0x1); //SEGREMAP //Rotate screen 180 deg
sendcommand(0xA0);
//sendcommand(0xC8); //COMSCANDEC Rotate screen 180 Deg
sendcommand(0xC0);
sendcommand(0xDA); //0xDA
sendcommand(0x12); //COMSCANDEC
sendcommand(0x81); //SETCONTRAS
sendcommand(0xCF); //
sendcommand(0xd9); //SETPRECHARGE
sendcommand(0xF1);
sendcommand(0xDB); //SETVCOMDETECT
sendcommand(0x40);
sendcommand(0xA4); //DISPLAYALLON_RESUME
sendcommand(0xA6); //NORMALDISPLAY
clear_display();
sendcommand(0x2e); // stop scroll
//----------------------------REVERSE comments----------------------------//
// sendcommand(0xa0); //seg re-map 0->127(default)
// sendcommand(0xa1); //seg re-map 127->0
// sendcommand(0xc8);
// delay(1000);
//----------------------------REVERSE comments----------------------------//
// sendcommand(0xa7); //Set Inverse Display
// sendcommand(0xae); //display off
sendcommand(0x20); //Set Memory Addressing Mode
sendcommand(0x00); //Set Memory Addressing Mode ab Horizontal addressing mode
// sendcommand(0x02); // Set Memory Addressing Mode ab Page addressing mode(RESET)
// setXY(0,0);
// Display Logo here :)
// for(int i=0;i<128*8;i++) // show 128* 64 Logo
// {
// SendChar(pgm_read_byte(logo+i));
// }
// sendcommand(0xaf); //display on
// delay(5000);
}
/*
------------------------------------------------------------------------------------
Added to Mikes routine Draw routines
------------------------------------------------------------------------------------
*/
void Draw_WIFI()
{
clear_display();
setXY(0,0);
// Display Logo here :)
for(int i=0;i<128*8;i++) // show 128* 64 Logo
{
SendChar(pgm_read_byte(WIFI1+i));
}
sendcommand(0xaf); //display on
}
void Draw_WAVES()
{
clear_display();
setXY(0,0);
// Display Logo here :)
for(int i=0;i<128*8;i++) // show 128* 64 Logo
{
SendChar(pgm_read_byte(rfwaves+i));
}
sendcommand(0xaf); //display on
}
void Draw_LOCKED()
{
clear_display();
setXY(0,0);
// Display Logo here :)
for(int i=0;i<128*8;i++) // show 128* 64 Logo
{
SendChar(pgm_read_byte(locked+i));
}
sendcommand(0xaf); //display on
}