I found the technical PDF for the display; attached.
The initialisation code I use (In C, but obvious and linear) - ssd1309_write_command(xx) does exactly what you'd expect and sends the byte to the display via SPI
Code:
void ssd1309_init (void)
{
// initialise the display for further use
ssd1309_CS_HI // deselect oled
ssd1309_RES_LO // reset
HAL_Delay (10); // hold
ssd1309_RES_HI
HAL_Delay (100); // wait for screen to boot
ssd1309_write_command (0xae); // display off
ssd1309_write_command (0x20); // Set Memory Addressing Mode
ssd1309_write_command (0x02); // 00b,Horizontal Addressing Mode; 01b,Vertical Addressing Mode;
// 10b,Page Addressing Mode (RESET); 11b,Invalid
ssd1309_write_command (0xB0); // Set Page Start Address for Page Addressing Mode,0-7
ssd1309_write_command (0xC8); // Set COM Output Scan Direction
//ssd1309_write_command (0x2e); // disable horizontal scroll
ssd1309_write_command (0xa0);
ssd1309_write_command (0x02); // ---set low column address
ssd1309_write_command (0x10); // ---set high column address (low nibbles so 0x00)
ssd1309_write_command (0x40); // --set start line address - CHECK
ssd1309_write_command (0x81);
ssd1309_write_command (0xff); // contrast max
ssd1309_write_command (0xA1); //--set segment re-map 0 to 127 - CHECK
ssd1309_write_command (0xA6); //--set normal color
ssd1309_write_command (0xA8); //--set multiplex ratio(1 to 64) - CHECK
ssd1309_write_command (0x3F); //
ssd1309_write_command (0xA4); // 0xa4,Output follows RAM content;0xa5,Output ignores RAM content
ssd1309_write_command (0xD3); // -set display offset - CHECK
ssd1309_write_command (0x00); // -not offset
ssd1309_write_command (0xD5); // --set display clock divide ratio/oscillator frequency
ssd1309_write_command (0xF0); // --set divide ratio
ssd1309_write_command (0xD9); // --set pre-charge period
ssd1309_write_command (0x22); //
ssd1309_write_command (0xDA); // --set com pins hardware configuration - CHECK
ssd1309_write_command (0x12);
ssd1309_write_command (0xDB); // --set vcomh
ssd1309_write_command (0x20); // 0x20,0.77xVcc
ssd1309_write_command (0x8D); // --set DC-DC enable
ssd1309_write_command (0x14); //
ssd1309_write_command (0xaf); // and turn display on
}
And a font using an 5 by 12 character in a 6 by 16 cell. Individual memory cells in the 1309 are vertical eight bits, so these font data match that. I use it to fill an area of memory to match the memory in the 1309 and then dump the lot in one hit to avoid video artifacts.
Code:
// each character occupies six wide by 16 deep cell, including space around it
// there are expected to be 20 characters across (the original display device
// was 120 dots wide; this display is 128 so we inset 3 bytes for some elbow
// room). Note that characters 0x00-0x1f are not included
const uint8_t fonts[960] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ' '
0x00, 0x00, 0x00, 0x00, 0x0B, 0xF8, 0x00, 0x00, 0x00, 0x00, // '!'
0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, // '"'
0x02, 0x20, 0x0F, 0xF8, 0x02, 0x20, 0x0F, 0xF8, 0x02, 0x20, // '#'
0x04, 0x60, 0x08, 0x90, 0x1F, 0xF8, 0x09, 0x10, 0x06, 0x20, // '$'
0x06, 0x10, 0x01, 0x28, 0x04, 0x90, 0x0A, 0x40, 0x04, 0x30, // '%'
0x07, 0x30, 0x08, 0xC8, 0x09, 0x30, 0x06, 0x00, 0x09, 0x00, // '&'
0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x00, // '''
0x00, 0x00, 0x00, 0x00, 0x0F, 0xF8, 0x10, 0x04, 0x00, 0x00, // '('
0x00, 0x00, 0x00, 0x00, 0x10, 0x04, 0x0F, 0xF8, 0x00, 0x00, // ')'
0x01, 0x10, 0x00, 0xA0, 0x03, 0xF8, 0x00, 0xA0, 0x01, 0x10, // '*'
0x01, 0x00, 0x01, 0x00, 0x07, 0xC0, 0x01, 0x00, 0x01, 0x00, // '+'
0x00, 0x00, 0x2C, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, // ','
0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, // '-'
0x0C, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // '.'
0x0C, 0x00, 0x03, 0x00, 0x00, 0xC0, 0x00, 0x38, 0x00, 0x00, // '/'
0x07, 0xF0, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x07, 0xF0, // '0'
0x00, 0x00, 0x00, 0x10, 0x00, 0x10, 0x0F, 0xF8, 0x00, 0x00, // '1'
0x0C, 0x10, 0x0A, 0x08, 0x09, 0x08, 0x08, 0x88, 0x08, 0x70, // '2'
0x04, 0x10, 0x08, 0x08, 0x08, 0x88, 0x08, 0x88, 0x07, 0x70, // '3'
0x03, 0x00, 0x02, 0xC0, 0x02, 0x30, 0x0F, 0xF8, 0x02, 0x00, // '4'
0x04, 0xF8, 0x08, 0x48, 0x08, 0x48, 0x08, 0x48, 0x07, 0x88, // '5'
0x07, 0xF0, 0x08, 0x88, 0x08, 0x88, 0x08, 0x88, 0x07, 0x10, // '6'
0x00, 0x08, 0x0E, 0x08, 0x01, 0x88, 0x00, 0x68, 0x00, 0x18, // '7'
0x07, 0x70, 0x08, 0x88, 0x08, 0x88, 0x08, 0x88, 0x07, 0x70, // '8'
0x04, 0x70, 0x08, 0x88, 0x08, 0x88, 0x08, 0x88, 0x07, 0xF0, // '9'
0x0C, 0xC0, 0x0C, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ':'
0x00, 0x00, 0x2C, 0xC0, 0x1C, 0xC0, 0x00, 0x00, 0x00, 0x00, // ';'
0x01, 0x00, 0x02, 0x80, 0x04, 0x40, 0x08, 0x20, 0x00, 0x00, // '<'
0x02, 0x80, 0x02, 0x80, 0x02, 0x80, 0x02, 0x80, 0x02, 0x80, // '='
0x08, 0x20, 0x04, 0x40, 0x02, 0x80, 0x01, 0x00, 0x00, 0x00, // '>'
0x00, 0x10, 0x00, 0x08, 0x0B, 0x08, 0x00, 0x88, 0x00, 0x70, // '?'
0x01, 0xE0, 0x02, 0x10, 0x04, 0xC8, 0x05, 0x28, 0x02, 0xF0, // '@'
0x0F, 0x00, 0x02, 0xE0, 0x02, 0x18, 0x02, 0xE0, 0x0F, 0x00, // 'A'
0x0F, 0xF8, 0x08, 0x88, 0x08, 0x88, 0x08, 0x88, 0x07, 0x70, // 'B'
0x07, 0xF0, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x04, 0x10, // 'C'
0x0F, 0xF8, 0x08, 0x08, 0x08, 0x08, 0x04, 0x10, 0x03, 0xE0, // 'D'
0x0F, 0xF8, 0x08, 0x88, 0x08, 0x88, 0x08, 0x08, 0x00, 0x00, // 'E'
0x0F, 0xF8, 0x00, 0x88, 0x00, 0x88, 0x00, 0x08, 0x00, 0x00, // 'F'
0x07, 0xF0, 0x08, 0x08, 0x08, 0x88, 0x04, 0x88, 0x0F, 0x90, // 'G'
0x0F, 0xF8, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x0F, 0xF8, // 'H'
0x00, 0x00, 0x0F, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 'I'
0x06, 0x00, 0x08, 0x00, 0x08, 0x00, 0x07, 0xF8, 0x00, 0x00, // 'J'
0x0F, 0xF8, 0x00, 0xC0, 0x01, 0x20, 0x02, 0x10, 0x0C, 0x08, // 'K'
0x0F, 0xF8, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, // 'L'
0x0F, 0xF8, 0x00, 0x70, 0x01, 0x80, 0x00, 0x70, 0x0F, 0xF8, // 'M'
0x0F, 0xF8, 0x00, 0x30, 0x00, 0xC0, 0x03, 0x00, 0x0F, 0xF8, // 'N'
0x07, 0xF0, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x07, 0xF0, // 'O'
0x0F, 0xF8, 0x00, 0x88, 0x00, 0x88, 0x00, 0x88, 0x00, 0x70, // 'P'
0x07, 0xF0, 0x08, 0x08, 0x0A, 0x08, 0x0C, 0x08, 0x17, 0xF0, // 'Q'
0x0F, 0xF8, 0x00, 0x88, 0x00, 0x88, 0x00, 0x88, 0x0F, 0x70, // 'R'
0x04, 0x70, 0x08, 0x88, 0x08, 0x88, 0x08, 0x88, 0x07, 0x10, // 'S'
0x00, 0x08, 0x00, 0x08, 0x0F, 0xF8, 0x00, 0x08, 0x00, 0x08, // 'T'
0x07, 0xF8, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x07, 0xF8, // 'U'
0x00, 0x38, 0x03, 0xC0, 0x0C, 0x00, 0x03, 0xC0, 0x00, 0x38, // 'V'
0x00, 0xF8, 0x0F, 0x00, 0x00, 0xE0, 0x0F, 0x00, 0x00, 0xF8, // 'W'
0x0E, 0x38, 0x01, 0x40, 0x00, 0x80, 0x01, 0x40, 0x0E, 0x38, // 'X'
0x00, 0x38, 0x00, 0x40, 0x0F, 0x80, 0x00, 0x40, 0x00, 0x38, // 'Y'
0x0C, 0x08, 0x0B, 0x08, 0x08, 0x88, 0x08, 0x68, 0x08, 0x18, // 'Z'
0x00, 0x00, 0x1F, 0xFC, 0x10, 0x04, 0x00, 0x00, 0x00, 0x00, // '['
0x00, 0x00, 0x00, 0x38, 0x00, 0xC0, 0x03, 0x00, 0x0C, 0x00, // '\'
0x00, 0x00, 0x10, 0x04, 0x1F, 0xFC, 0x00, 0x00, 0x00, 0x00, // ']'
0x00, 0x10, 0x00, 0x08, 0x00, 0x04, 0x00, 0x08, 0x00, 0x10, // '^'
0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, // '_'
0x00, 0x08, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // '`'
0x06, 0x00, 0x09, 0x40, 0x09, 0x40, 0x09, 0x40, 0x0F, 0x80, // 'a'
0x0F, 0xF8, 0x08, 0x40, 0x08, 0x40, 0x08, 0x40, 0x07, 0x80, // 'b'
0x07, 0x80, 0x08, 0x40, 0x08, 0x40, 0x08, 0x40, 0x04, 0x80, // 'c'
0x07, 0x80, 0x08, 0x40, 0x08, 0x40, 0x08, 0x40, 0x0F, 0xF8, // 'd'
0x07, 0x80, 0x09, 0x40, 0x09, 0x40, 0x09, 0x40, 0x05, 0x80, // 'e'
0x0F, 0xF0, 0x00, 0x48, 0x00, 0x48, 0x00, 0x48, 0x00, 0x10, // 'f'
0x27, 0x80, 0x28, 0x40, 0x28, 0x40, 0x28, 0x40, 0x1F, 0xC0, // 'g'
0x0F, 0xF8, 0x00, 0x80, 0x00, 0x40, 0x00, 0x40, 0x0F, 0x80, // 'h'
0x00, 0x00, 0x00, 0x00, 0x0F, 0xC8, 0x00, 0x00, 0x00, 0x00, // 'i'
0x20, 0x00, 0x20, 0x00, 0x1F, 0xC8, 0x00, 0x00, 0x00, 0x00, // 'j'
0x0F, 0xF8, 0x01, 0x00, 0x02, 0x80, 0x04, 0x40, 0x08, 0x00, // 'k'
0x00, 0x00, 0x00, 0x00, 0x0F, 0xF8, 0x00, 0x00, 0x00, 0x00, // 'l'
0x0F, 0xC0, 0x00, 0x40, 0x0F, 0x80, 0x00, 0x40, 0x0F, 0x80, // 'm'
0x0F, 0xC0, 0x00, 0x80, 0x00, 0x40, 0x00, 0x40, 0x0F, 0x80, // 'n'
0x07, 0x80, 0x08, 0x40, 0x08, 0x40, 0x08, 0x40, 0x07, 0x80, // 'o'
0x3F, 0xC0, 0x08, 0x40, 0x08, 0x40, 0x08, 0x40, 0x07, 0x80, // 'p'
0x07, 0x80, 0x08, 0x40, 0x08, 0x40, 0x08, 0x40, 0x3F, 0xC0, // 'q'
0x0F, 0xC0, 0x00, 0x80, 0x00, 0x40, 0x00, 0x40, 0x00, 0x00, // 'r'
0x04, 0x80, 0x09, 0x40, 0x0A, 0x40, 0x04, 0x80, 0x00, 0x00, // 's'
0x07, 0xF0, 0x08, 0x40, 0x08, 0x40, 0x00, 0x00, 0x00, 0x00, // 't'
0x07, 0xC0, 0x08, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0F, 0xC0, // 'u'
0x00, 0xC0, 0x03, 0x00, 0x0C, 0x00, 0x03, 0x00, 0x00, 0xC0, // 'v'
0x01, 0xC0, 0x0E, 0x00, 0x01, 0xC0, 0x0E, 0x00, 0x01, 0xC0, // 'w'
0x0C, 0xC0, 0x03, 0x00, 0x03, 0x00, 0x0C, 0xC0, 0x00, 0x00, // 'x'
0x20, 0x00, 0x23, 0xC0, 0x1C, 0x00, 0x04, 0x00, 0x03, 0xC0, // 'y'
0x0C, 0x40, 0x0A, 0x40, 0x09, 0x40, 0x08, 0xC0, 0x00, 0x00, // 'z'
0x00, 0x00, 0x00, 0x80, 0x0F, 0x78, 0x10, 0x04, 0x00, 0x00, // '{'
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xF8, 0x00, 0x00, // '|'
0x00, 0x00, 0x10, 0x04, 0x0F, 0x78, 0x00, 0x80, 0x00, 0x00, // '}'
0x00, 0x10, 0x00, 0x08, 0x00, 0x04, 0x00, 0x08, 0x00, 0x10, // '~'
0x00, 0x38, 0x00, 0x44, 0x00, 0x44, 0x00, 0x44, 0x00, 0x38, // 'degree'
};
Neil