Added SSD1306 I2C
This commit is contained in:
parent
451490c885
commit
3936f2dab6
|
|
@ -12,7 +12,67 @@ const uint8_t PROGMEM lcdBootProgram[] = {
|
||||||
//
|
//
|
||||||
// Further reading: https://www.adafruit.com/datasheets/SSD1306.pdf
|
// Further reading: https://www.adafruit.com/datasheets/SSD1306.pdf
|
||||||
|
|
||||||
#ifdef OLED_SH1106
|
#ifdef OLED_SSD1306_I2C
|
||||||
|
// Sets all registers to sane defaults since i2c
|
||||||
|
// displays usually havn't a reset input
|
||||||
|
|
||||||
|
// Display Off
|
||||||
|
0xAE,
|
||||||
|
|
||||||
|
// Set Display Clock Divisor v = 0xF0
|
||||||
|
// default is 0x80
|
||||||
|
0xD5, 0xF0,
|
||||||
|
|
||||||
|
// Set Multiplex Ratio v = 0x3F
|
||||||
|
0xA8, 0x3F,
|
||||||
|
|
||||||
|
// Set Display Offset v = 0
|
||||||
|
0xD3, 0x00,
|
||||||
|
|
||||||
|
// Set Start Line (0)
|
||||||
|
0x40,
|
||||||
|
|
||||||
|
// Charge Pump Setting v = enable (0x14)
|
||||||
|
// default is disabled
|
||||||
|
0x8D, 0x14,
|
||||||
|
|
||||||
|
// Set Segment Re-map (A0) | (b0001)
|
||||||
|
// default is (b0000)
|
||||||
|
0xA1,
|
||||||
|
|
||||||
|
// Set COM Output Scan Direction
|
||||||
|
0xC8,
|
||||||
|
|
||||||
|
// Set COM Pins v
|
||||||
|
0xDA, 0x12,
|
||||||
|
|
||||||
|
// Set Contrast v = 0xCF
|
||||||
|
0x81, 0xCF,
|
||||||
|
|
||||||
|
// Set Precharge = 0xF1
|
||||||
|
0xD9, 0xF1,
|
||||||
|
|
||||||
|
// Set VCom Detect
|
||||||
|
0xDB, 0x40,
|
||||||
|
|
||||||
|
// Entire Display ON
|
||||||
|
0xA4,
|
||||||
|
|
||||||
|
// Set normal/inverse display
|
||||||
|
0xA6,
|
||||||
|
|
||||||
|
// Display On
|
||||||
|
0xAF,
|
||||||
|
|
||||||
|
// set display mode = horizontal addressing mode (0x00)
|
||||||
|
0x20, 0x00,
|
||||||
|
|
||||||
|
// set col address range
|
||||||
|
0x21, 0x00, WIDTH-1,
|
||||||
|
|
||||||
|
// set page address range
|
||||||
|
0x22, 0x00, 0x07,
|
||||||
|
#elif defined(OLED_SH1106)
|
||||||
0x8D, 0x14, // Charge Pump Setting v = enable (0x14)
|
0x8D, 0x14, // Charge Pump Setting v = enable (0x14)
|
||||||
0xA1, // Set Segment Re-map
|
0xA1, // Set Segment Re-map
|
||||||
0xC8, // Set COM Output Scan Direction
|
0xC8, // Set COM Output Scan Direction
|
||||||
|
|
@ -292,8 +352,43 @@ void Arduboy2Core::bootPins()
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef OLED_SSD1306_I2C
|
||||||
|
#define I2CADDR 0x3c
|
||||||
|
|
||||||
|
void i2c_send_byte(uint8_t data) {
|
||||||
|
TWDR = data;
|
||||||
|
TWCR = _BV(TWINT) | _BV(TWEN);
|
||||||
|
while( !(TWCR & _BV(TWINT)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void i2c_start() {
|
||||||
|
TWCR = _BV(TWINT) | _BV(TWSTA) | _BV(TWEN);
|
||||||
|
while( !(TWCR & _BV(TWINT)));
|
||||||
|
i2c_send_byte(I2CADDR<<1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void i2c_stop(void) {
|
||||||
|
TWCR = _BV(TWINT) | _BV(TWEN) | _BV(TWSTO);
|
||||||
|
while( (TWCR & _BV(TWSTO)));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void Arduboy2Core::bootOLED()
|
void Arduboy2Core::bootOLED()
|
||||||
{
|
{
|
||||||
|
#ifdef OLED_SSD1306_I2C
|
||||||
|
TWSR = 0;
|
||||||
|
TWBR = F_CPU/(2*100000)-8;
|
||||||
|
|
||||||
|
i2c_start();
|
||||||
|
i2c_send_byte(0x00);
|
||||||
|
for (uint8_t i = 0; i < sizeof(lcdBootProgram); i++)
|
||||||
|
i2c_send_byte(pgm_read_byte(lcdBootProgram + i));
|
||||||
|
i2c_stop();
|
||||||
|
|
||||||
|
// TWBR = F_CPU/(2*400000)-8;
|
||||||
|
TWBR = 1; // 12 = 400kHz
|
||||||
|
#else
|
||||||
// reset the display
|
// reset the display
|
||||||
uint8_t cmd;
|
uint8_t cmd;
|
||||||
const void* ptr = lcdBootProgram;
|
const void* ptr = lcdBootProgram;
|
||||||
|
|
@ -350,9 +445,11 @@ void Arduboy2Core::LCDCommandMode()
|
||||||
// Initialize the SPI interface for the display
|
// Initialize the SPI interface for the display
|
||||||
void Arduboy2Core::bootSPI()
|
void Arduboy2Core::bootSPI()
|
||||||
{
|
{
|
||||||
|
#ifndef OLED_SSD1306_I2C
|
||||||
// master, mode 0, MSB first, CPU clock / 2 (8MHz)
|
// master, mode 0, MSB first, CPU clock / 2 (8MHz)
|
||||||
SPCR = _BV(SPE) | _BV(MSTR);
|
SPCR = _BV(SPE) | _BV(MSTR);
|
||||||
SPSR = _BV(SPI2X);
|
SPSR = _BV(SPI2X);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write to the SPI bus (MOSI pin)
|
// Write to the SPI bus (MOSI pin)
|
||||||
|
|
@ -398,12 +495,16 @@ void Arduboy2Core::idle()
|
||||||
|
|
||||||
void Arduboy2Core::bootPowerSaving()
|
void Arduboy2Core::bootPowerSaving()
|
||||||
{
|
{
|
||||||
|
#ifdef OLED_SSD1306_I2C
|
||||||
|
// FIXME
|
||||||
|
#else
|
||||||
// disable Two Wire Interface (I2C) and the ADC
|
// disable Two Wire Interface (I2C) and the ADC
|
||||||
// All other bits will be written with 0 so will be enabled
|
// All other bits will be written with 0 so will be enabled
|
||||||
PRR0 = _BV(PRTWI) | _BV(PRADC);
|
PRR0 = _BV(PRTWI) | _BV(PRADC);
|
||||||
// disable USART1
|
// disable USART1
|
||||||
PRR1 = _BV(PRUSART1);
|
PRR1 = _BV(PRUSART1);
|
||||||
// All other bits will be written with 0 so will be enabled
|
// All other bits will be written with 0 so will be enabled
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// Shut down the display
|
// Shut down the display
|
||||||
|
|
@ -432,12 +533,32 @@ uint8_t Arduboy2Core::height() { return HEIGHT; }
|
||||||
|
|
||||||
void Arduboy2Core::paint8Pixels(uint8_t pixels)
|
void Arduboy2Core::paint8Pixels(uint8_t pixels)
|
||||||
{
|
{
|
||||||
|
#ifdef OLED_SSD1306_I2C
|
||||||
|
i2c_start();
|
||||||
|
i2c_send_byte(0x40);
|
||||||
|
i2c_send_byte(pixels);
|
||||||
|
i2c_stop();
|
||||||
|
#else
|
||||||
SPItransfer(pixels);
|
SPItransfer(pixels);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void Arduboy2Core::paintScreen(const uint8_t *image)
|
void Arduboy2Core::paintScreen(const uint8_t *image)
|
||||||
{
|
{
|
||||||
#if defined(OLED_SH1106) || defined(LCD_ST7565)
|
#ifdef OLED_SSD1306_I2C
|
||||||
|
// I2C
|
||||||
|
for (uint8_t i=0; i<(WIDTH*HEIGHT/(16*8));) {
|
||||||
|
// send a bunch of data in one xmission
|
||||||
|
i2c_start();
|
||||||
|
i2c_send_byte(0x40);
|
||||||
|
for(uint8_t x=0;x<16;x++,i++) {
|
||||||
|
TWDR = pgm_read_byte(image+i);
|
||||||
|
TWCR = _BV(TWINT) | _BV(TWEN);
|
||||||
|
while( !(TWCR & _BV(TWINT)));
|
||||||
|
}
|
||||||
|
i2c_stop();
|
||||||
|
}
|
||||||
|
#elif defined(OLED_SH1106) || defined(LCD_ST7565)
|
||||||
for (uint8_t i = 0; i < HEIGHT / 8; i++)
|
for (uint8_t i = 0; i < HEIGHT / 8; i++)
|
||||||
{
|
{
|
||||||
LCDCommandMode();
|
LCDCommandMode();
|
||||||
|
|
@ -513,7 +634,22 @@ void Arduboy2Core::paintScreen(const uint8_t *image)
|
||||||
// will be used by any buffer based subclass
|
// will be used by any buffer based subclass
|
||||||
void Arduboy2Core::paintScreen(uint8_t image[], bool clear)
|
void Arduboy2Core::paintScreen(uint8_t image[], bool clear)
|
||||||
{
|
{
|
||||||
#if defined(OLED_SH1106) || defined(LCD_ST7565)
|
#ifdef OLED_SSD1306_I2C
|
||||||
|
// I2C
|
||||||
|
uint16_t i;
|
||||||
|
i2c_start();
|
||||||
|
TWDR = 0x40;
|
||||||
|
TWCR = _BV(TWINT) | _BV(TWEN);
|
||||||
|
for (uint16_t i=0; i<(WIDTH*HEIGHT/8);i++) {
|
||||||
|
while( !(TWCR & _BV(TWINT)));
|
||||||
|
TWDR = image[i];
|
||||||
|
TWCR = _BV(TWINT) | _BV(TWEN);
|
||||||
|
if(clear) image[i] = 0;
|
||||||
|
}
|
||||||
|
while( !(TWCR & _BV(TWINT)));
|
||||||
|
i2c_stop();
|
||||||
|
|
||||||
|
#elif defined(OLED_SH1106) || defined(LCD_ST7565)
|
||||||
//Assembly optimized page mode display code with clear support.
|
//Assembly optimized page mode display code with clear support.
|
||||||
//Each byte transfer takes 18 cycles
|
//Each byte transfer takes 18 cycles
|
||||||
asm volatile (
|
asm volatile (
|
||||||
|
|
@ -685,21 +821,43 @@ void Arduboy2Core::paintScreen(uint8_t image[], bool clear)
|
||||||
|
|
||||||
void Arduboy2Core::blank()
|
void Arduboy2Core::blank()
|
||||||
{
|
{
|
||||||
#ifdef OLED_SH1106
|
#ifdef OLED_SSD1306_I2C
|
||||||
|
TWSR = 0;
|
||||||
|
TWBR = F_CPU/(2*100000)-8;
|
||||||
|
|
||||||
|
i2c_start();
|
||||||
|
i2c_send_byte(0x00);
|
||||||
|
for (uint8_t i = 0; i < sizeof(lcdBootProgram); i++)
|
||||||
|
i2c_send_byte(pgm_read_byte(lcdBootProgram + i));
|
||||||
|
i2c_stop();
|
||||||
|
|
||||||
|
// TWBR = F_CPU/(2*400000)-8;
|
||||||
|
TWBR = 1; // 12 = 400kHz
|
||||||
|
#elif defined(OLED_SH1106)
|
||||||
for (int i = 0; i < (HEIGHT * 132) / 8; i++)
|
for (int i = 0; i < (HEIGHT * 132) / 8; i++)
|
||||||
#elif defined(OLED_96X96) || defined(OLED_128X96) || defined(OLED_128X128)|| defined(OLED_128X64_ON_96X96) || defined(OLED_128X64_ON_128X96) || defined(OLED_128X64_ON_128X128)|| defined(OLED_128X96_ON_128X128) || defined(OLED_96X96_ON_128X128) || defined(OLED_64X128_ON_128X128)
|
#elif defined(OLED_96X96) || defined(OLED_128X96) || defined(OLED_128X128)|| defined(OLED_128X64_ON_96X96) || defined(OLED_128X64_ON_128X96) || defined(OLED_128X64_ON_128X128)|| defined(OLED_128X96_ON_128X128) || defined(OLED_96X96_ON_128X128) || defined(OLED_64X128_ON_128X128)
|
||||||
for (int i = 0; i < (HEIGHT * WIDTH) / 2; i++)
|
for (int i = 0; i < (HEIGHT * WIDTH) / 2; i++)
|
||||||
#else //OLED SSD1306 and compatibles
|
#else //OLED SSD1306 and compatibles
|
||||||
for (int i = 0; i < (HEIGHT * WIDTH) / 8; i++)
|
for (int i = 0; i < (HEIGHT * WIDTH) / 8; i++)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef OLED_SSD1306_I2C
|
||||||
SPItransfer(0x00);
|
SPItransfer(0x00);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void Arduboy2Core::sendLCDCommand(uint8_t command)
|
void Arduboy2Core::sendLCDCommand(uint8_t command)
|
||||||
{
|
{
|
||||||
|
#ifdef OLED_SSD1306_I2C
|
||||||
|
i2c_start();
|
||||||
|
i2c_send_byte(0x00);
|
||||||
|
i2c_send_byte(command);
|
||||||
|
i2c_stop();
|
||||||
|
#else
|
||||||
LCDCommandMode();
|
LCDCommandMode();
|
||||||
SPItransfer(command);
|
SPItransfer(command);
|
||||||
LCDDataMode();
|
LCDDataMode();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// invert the display or set to normal
|
// invert the display or set to normal
|
||||||
|
|
|
||||||
|
|
@ -44,24 +44,27 @@ void Sprites::draw(int16_t x, int16_t y,
|
||||||
if (bitmap == NULL)
|
if (bitmap == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// uint8_t width = pgm_read_byte(bitmap);
|
#ifdef OLED_SSD1306_I2C
|
||||||
// uint8_t height = pgm_read_byte(++bitmap);
|
uint8_t width = pgm_read_byte(bitmap);
|
||||||
// bitmap++;
|
uint8_t height = pgm_read_byte(++bitmap);
|
||||||
// if (frame > 0 || sprite_frame > 0) {
|
bitmap++;
|
||||||
// frame_offset = (width * ( height / 8 + ( height % 8 == 0 ? 0 : 1)));
|
if (frame > 0 || sprite_frame > 0) {
|
||||||
// // sprite plus mask uses twice as much space for each frame
|
frame_offset = (width * ( height / 8 + ( height % 8 == 0 ? 0 : 1)));
|
||||||
// if (drawMode == SPRITE_PLUS_MASK) {
|
// sprite plus mask uses twice as much space for each frame
|
||||||
// frame_offset *= 2;
|
if (drawMode == SPRITE_PLUS_MASK) {
|
||||||
// } else if (mask != NULL) {
|
frame_offset *= 2;
|
||||||
// mask += sprite_frame * frame_offset;
|
} else if (mask != NULL) {
|
||||||
// }
|
mask += sprite_frame * frame_offset;
|
||||||
// bitmap += frame * frame_offset;
|
}
|
||||||
// }
|
bitmap += frame * frame_offset;
|
||||||
// // if we're detecting the draw mode then base it on whether a mask
|
}
|
||||||
// // was passed as a separate object
|
|
||||||
// if (drawMode == SPRITE_AUTO_MODE) {
|
// if we're detecting the draw mode then base it on whether a mask
|
||||||
// drawMode = mask == NULL ? SPRITE_UNMASKED : SPRITE_MASKED;
|
// was passed as a separate object
|
||||||
// }
|
if (drawMode == SPRITE_AUTO_MODE) {
|
||||||
|
drawMode = mask == NULL ? SPRITE_UNMASKED : SPRITE_MASKED;
|
||||||
|
}
|
||||||
|
#else
|
||||||
// assembly optimisation of above code saving 20(+) bytes
|
// assembly optimisation of above code saving 20(+) bytes
|
||||||
uint8_t width;
|
uint8_t width;
|
||||||
uint8_t height;
|
uint8_t height;
|
||||||
|
|
@ -121,6 +124,9 @@ void Sprites::draw(int16_t x, int16_t y,
|
||||||
[sprite_masked] "M" (SPRITE_MASKED)
|
[sprite_masked] "M" (SPRITE_MASKED)
|
||||||
: "r20", "r21"
|
: "r20", "r21"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
drawBitmap(x, y, bitmap, mask, width, height, drawMode);
|
drawBitmap(x, y, bitmap, mask, width, height, drawMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue