//
// SSD1327 - 128x128x16 grayscale OLED library
// Copyright 2019 (C) BitBank Software, Inc.
// Project started 6/10/2019
// Written by Larry Bank (bitbank@pobox.com)
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
//
#ifdef _LINUX_
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define PROGMEM
#define memcpy_P memcpy
static int file_i2c = 0;
#define USE_BACKBUFFER
#else // Arduino
#include
#ifdef __AVR__
#include
#else
// On systems with enough RAM (not AVR), we allocate a static 8K back buffer
#define USE_BACKBUFFER
#endif
#include
#ifndef __AVR_ATtiny85__
#include
#include
#endif
#endif // _LINUX_
#include
#ifdef USE_BACKBUFFER
static uint8_t ucBackbuffer[8192];
#endif
void ssd1327Power(unsigned char bOn);
// small (8x8) font
const unsigned char ucFont[]PROGMEM = {
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x5f,0x5f,0x06,0x00,0x00,
0x00,0x07,0x07,0x00,0x07,0x07,0x00,0x00,0x14,0x7f,0x7f,0x14,0x7f,0x7f,0x14,0x00,
0x24,0x2e,0x2a,0x6b,0x6b,0x3a,0x12,0x00,0x46,0x66,0x30,0x18,0x0c,0x66,0x62,0x00,
0x30,0x7a,0x4f,0x5d,0x37,0x7a,0x48,0x00,0x00,0x04,0x07,0x03,0x00,0x00,0x00,0x00,
0x00,0x1c,0x3e,0x63,0x41,0x00,0x00,0x00,0x00,0x41,0x63,0x3e,0x1c,0x00,0x00,0x00,
0x08,0x2a,0x3e,0x1c,0x1c,0x3e,0x2a,0x08,0x00,0x08,0x08,0x3e,0x3e,0x08,0x08,0x00,
0x00,0x00,0x80,0xe0,0x60,0x00,0x00,0x00,0x00,0x08,0x08,0x08,0x08,0x08,0x08,0x00,
0x00,0x00,0x00,0x60,0x60,0x00,0x00,0x00,0x60,0x30,0x18,0x0c,0x06,0x03,0x01,0x00,
0x3e,0x7f,0x59,0x4d,0x47,0x7f,0x3e,0x00,0x40,0x42,0x7f,0x7f,0x40,0x40,0x00,0x00,
0x62,0x73,0x59,0x49,0x6f,0x66,0x00,0x00,0x22,0x63,0x49,0x49,0x7f,0x36,0x00,0x00,
0x18,0x1c,0x16,0x53,0x7f,0x7f,0x50,0x00,0x27,0x67,0x45,0x45,0x7d,0x39,0x00,0x00,
0x3c,0x7e,0x4b,0x49,0x79,0x30,0x00,0x00,0x03,0x03,0x71,0x79,0x0f,0x07,0x00,0x00,
0x36,0x7f,0x49,0x49,0x7f,0x36,0x00,0x00,0x06,0x4f,0x49,0x69,0x3f,0x1e,0x00,0x00,
0x00,0x00,0x00,0x66,0x66,0x00,0x00,0x00,0x00,0x00,0x80,0xe6,0x66,0x00,0x00,0x00,
0x08,0x1c,0x36,0x63,0x41,0x00,0x00,0x00,0x00,0x14,0x14,0x14,0x14,0x14,0x14,0x00,
0x00,0x41,0x63,0x36,0x1c,0x08,0x00,0x00,0x00,0x02,0x03,0x59,0x5d,0x07,0x02,0x00,
0x3e,0x7f,0x41,0x5d,0x5d,0x5f,0x0e,0x00,0x7c,0x7e,0x13,0x13,0x7e,0x7c,0x00,0x00,
0x41,0x7f,0x7f,0x49,0x49,0x7f,0x36,0x00,0x1c,0x3e,0x63,0x41,0x41,0x63,0x22,0x00,
0x41,0x7f,0x7f,0x41,0x63,0x3e,0x1c,0x00,0x41,0x7f,0x7f,0x49,0x5d,0x41,0x63,0x00,
0x41,0x7f,0x7f,0x49,0x1d,0x01,0x03,0x00,0x1c,0x3e,0x63,0x41,0x51,0x33,0x72,0x00,
0x7f,0x7f,0x08,0x08,0x7f,0x7f,0x00,0x00,0x00,0x41,0x7f,0x7f,0x41,0x00,0x00,0x00,
0x30,0x70,0x40,0x41,0x7f,0x3f,0x01,0x00,0x41,0x7f,0x7f,0x08,0x1c,0x77,0x63,0x00,
0x41,0x7f,0x7f,0x41,0x40,0x60,0x70,0x00,0x7f,0x7f,0x0e,0x1c,0x0e,0x7f,0x7f,0x00,
0x7f,0x7f,0x06,0x0c,0x18,0x7f,0x7f,0x00,0x1c,0x3e,0x63,0x41,0x63,0x3e,0x1c,0x00,
0x41,0x7f,0x7f,0x49,0x09,0x0f,0x06,0x00,0x1e,0x3f,0x21,0x31,0x61,0x7f,0x5e,0x00,
0x41,0x7f,0x7f,0x09,0x19,0x7f,0x66,0x00,0x26,0x6f,0x4d,0x49,0x59,0x73,0x32,0x00,
0x03,0x41,0x7f,0x7f,0x41,0x03,0x00,0x00,0x7f,0x7f,0x40,0x40,0x7f,0x7f,0x00,0x00,
0x1f,0x3f,0x60,0x60,0x3f,0x1f,0x00,0x00,0x3f,0x7f,0x60,0x30,0x60,0x7f,0x3f,0x00,
0x63,0x77,0x1c,0x08,0x1c,0x77,0x63,0x00,0x07,0x4f,0x78,0x78,0x4f,0x07,0x00,0x00,
0x47,0x63,0x71,0x59,0x4d,0x67,0x73,0x00,0x00,0x7f,0x7f,0x41,0x41,0x00,0x00,0x00,
0x01,0x03,0x06,0x0c,0x18,0x30,0x60,0x00,0x00,0x41,0x41,0x7f,0x7f,0x00,0x00,0x00,
0x08,0x0c,0x06,0x03,0x06,0x0c,0x08,0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,
0x00,0x00,0x03,0x07,0x04,0x00,0x00,0x00,0x20,0x74,0x54,0x54,0x3c,0x78,0x40,0x00,
0x41,0x7f,0x3f,0x48,0x48,0x78,0x30,0x00,0x38,0x7c,0x44,0x44,0x6c,0x28,0x00,0x00,
0x30,0x78,0x48,0x49,0x3f,0x7f,0x40,0x00,0x38,0x7c,0x54,0x54,0x5c,0x18,0x00,0x00,
0x48,0x7e,0x7f,0x49,0x03,0x06,0x00,0x00,0x98,0xbc,0xa4,0xa4,0xf8,0x7c,0x04,0x00,
0x41,0x7f,0x7f,0x08,0x04,0x7c,0x78,0x00,0x00,0x44,0x7d,0x7d,0x40,0x00,0x00,0x00,
0x60,0xe0,0x80,0x84,0xfd,0x7d,0x00,0x00,0x41,0x7f,0x7f,0x10,0x38,0x6c,0x44,0x00,
0x00,0x41,0x7f,0x7f,0x40,0x00,0x00,0x00,0x7c,0x7c,0x18,0x78,0x1c,0x7c,0x78,0x00,
0x7c,0x78,0x04,0x04,0x7c,0x78,0x00,0x00,0x38,0x7c,0x44,0x44,0x7c,0x38,0x00,0x00,
0x84,0xfc,0xf8,0xa4,0x24,0x3c,0x18,0x00,0x18,0x3c,0x24,0xa4,0xf8,0xfc,0x84,0x00,
0x44,0x7c,0x78,0x4c,0x04,0x0c,0x18,0x00,0x48,0x5c,0x54,0x74,0x64,0x24,0x00,0x00,
0x04,0x04,0x3e,0x7f,0x44,0x24,0x00,0x00,0x3c,0x7c,0x40,0x40,0x3c,0x7c,0x40,0x00,
0x1c,0x3c,0x60,0x60,0x3c,0x1c,0x00,0x00,0x3c,0x7c,0x60,0x30,0x60,0x7c,0x3c,0x00,
0x44,0x6c,0x38,0x10,0x38,0x6c,0x44,0x00,0x9c,0xbc,0xa0,0xa0,0xfc,0x7c,0x00,0x00,
0x4c,0x64,0x74,0x5c,0x4c,0x64,0x00,0x00,0x08,0x08,0x3e,0x77,0x41,0x41,0x00,0x00,
0x00,0x00,0x00,0x77,0x77,0x00,0x00,0x00,0x41,0x41,0x77,0x3e,0x08,0x08,0x00,0x00,
0x02,0x03,0x01,0x03,0x02,0x03,0x01,0x00,0x70,0x78,0x4c,0x46,0x4c,0x78,0x70,0x00};
// 5x7 font (in 6x8 cell)
const unsigned char ucSmallFont[]PROGMEM = {
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x5f,0x06,0x00,0x00,0x07,0x03,0x00,
0x07,0x03,0x00,0x24,0x7e,0x24,0x7e,0x24,0x00,0x24,0x2b,0x6a,0x12,0x00,0x00,0x63,
0x13,0x08,0x64,0x63,0x00,0x36,0x49,0x56,0x20,0x50,0x00,0x00,0x07,0x03,0x00,0x00,
0x00,0x00,0x3e,0x41,0x00,0x00,0x00,0x00,0x41,0x3e,0x00,0x00,0x00,0x08,0x3e,0x1c,
0x3e,0x08,0x00,0x08,0x08,0x3e,0x08,0x08,0x00,0x00,0xe0,0x60,0x00,0x00,0x00,0x08,
0x08,0x08,0x08,0x08,0x00,0x00,0x60,0x60,0x00,0x00,0x00,0x20,0x10,0x08,0x04,0x02,
0x00,0x3e,0x51,0x49,0x45,0x3e,0x00,0x00,0x42,0x7f,0x40,0x00,0x00,0x62,0x51,0x49,
0x49,0x46,0x00,0x22,0x49,0x49,0x49,0x36,0x00,0x18,0x14,0x12,0x7f,0x10,0x00,0x2f,
0x49,0x49,0x49,0x31,0x00,0x3c,0x4a,0x49,0x49,0x30,0x00,0x01,0x71,0x09,0x05,0x03,
0x00,0x36,0x49,0x49,0x49,0x36,0x00,0x06,0x49,0x49,0x29,0x1e,0x00,0x00,0x6c,0x6c,
0x00,0x00,0x00,0x00,0xec,0x6c,0x00,0x00,0x00,0x08,0x14,0x22,0x41,0x00,0x00,0x24,
0x24,0x24,0x24,0x24,0x00,0x00,0x41,0x22,0x14,0x08,0x00,0x02,0x01,0x59,0x09,0x06,
0x00,0x3e,0x41,0x5d,0x55,0x1e,0x00,0x7e,0x11,0x11,0x11,0x7e,0x00,0x7f,0x49,0x49,
0x49,0x36,0x00,0x3e,0x41,0x41,0x41,0x22,0x00,0x7f,0x41,0x41,0x41,0x3e,0x00,0x7f,
0x49,0x49,0x49,0x41,0x00,0x7f,0x09,0x09,0x09,0x01,0x00,0x3e,0x41,0x49,0x49,0x7a,
0x00,0x7f,0x08,0x08,0x08,0x7f,0x00,0x00,0x41,0x7f,0x41,0x00,0x00,0x30,0x40,0x40,
0x40,0x3f,0x00,0x7f,0x08,0x14,0x22,0x41,0x00,0x7f,0x40,0x40,0x40,0x40,0x00,0x7f,
0x02,0x04,0x02,0x7f,0x00,0x7f,0x02,0x04,0x08,0x7f,0x00,0x3e,0x41,0x41,0x41,0x3e,
0x00,0x7f,0x09,0x09,0x09,0x06,0x00,0x3e,0x41,0x51,0x21,0x5e,0x00,0x7f,0x09,0x09,
0x19,0x66,0x00,0x26,0x49,0x49,0x49,0x32,0x00,0x01,0x01,0x7f,0x01,0x01,0x00,0x3f,
0x40,0x40,0x40,0x3f,0x00,0x1f,0x20,0x40,0x20,0x1f,0x00,0x3f,0x40,0x3c,0x40,0x3f,
0x00,0x63,0x14,0x08,0x14,0x63,0x00,0x07,0x08,0x70,0x08,0x07,0x00,0x71,0x49,0x45,
0x43,0x00,0x00,0x00,0x7f,0x41,0x41,0x00,0x00,0x02,0x04,0x08,0x10,0x20,0x00,0x00,
0x41,0x41,0x7f,0x00,0x00,0x04,0x02,0x01,0x02,0x04,0x00,0x80,0x80,0x80,0x80,0x80,
0x00,0x00,0x03,0x07,0x00,0x00,0x00,0x20,0x54,0x54,0x54,0x78,0x00,0x7f,0x44,0x44,
0x44,0x38,0x00,0x38,0x44,0x44,0x44,0x28,0x00,0x38,0x44,0x44,0x44,0x7f,0x00,0x38,
0x54,0x54,0x54,0x08,0x00,0x08,0x7e,0x09,0x09,0x00,0x00,0x18,0xa4,0xa4,0xa4,0x7c,
0x00,0x7f,0x04,0x04,0x78,0x00,0x00,0x00,0x00,0x7d,0x40,0x00,0x00,0x40,0x80,0x84,
0x7d,0x00,0x00,0x7f,0x10,0x28,0x44,0x00,0x00,0x00,0x00,0x7f,0x40,0x00,0x00,0x7c,
0x04,0x18,0x04,0x78,0x00,0x7c,0x04,0x04,0x78,0x00,0x00,0x38,0x44,0x44,0x44,0x38,
0x00,0xfc,0x44,0x44,0x44,0x38,0x00,0x38,0x44,0x44,0x44,0xfc,0x00,0x44,0x78,0x44,
0x04,0x08,0x00,0x08,0x54,0x54,0x54,0x20,0x00,0x04,0x3e,0x44,0x24,0x00,0x00,0x3c,
0x40,0x20,0x7c,0x00,0x00,0x1c,0x20,0x40,0x20,0x1c,0x00,0x3c,0x60,0x30,0x60,0x3c,
0x00,0x6c,0x10,0x10,0x6c,0x00,0x00,0x9c,0xa0,0x60,0x3c,0x00,0x00,0x64,0x54,0x54,
0x4c,0x00,0x00,0x08,0x3e,0x41,0x41,0x00,0x00,0x00,0x00,0x77,0x00,0x00,0x00,0x00,
0x41,0x41,0x3e,0x08,0x00,0x02,0x01,0x02,0x01,0x00,0x00,0x3c,0x26,0x23,0x26,0x3c};
// some globals
static int iCSPin, iDCPin, iResetPin;
static int iMaxX, iMaxY, iPitch;
static int oled_type, oled_flip, oled_addr;
static int iSDAPin, iSCLPin;
static void ssd1327WriteCommand(unsigned char c);
// wrapper/adapter functions to make the code work on Linux
#ifdef _LINUX_
static uint8_t pgm_read_byte(uint8_t *ptr)
{
return *ptr;
}
static int16_t pgm_read_word(uint8_t *ptr)
{
return ptr[0] + (ptr[1]<<8);
}
int I2CReadRegister(uint8_t addr, uint8_t reg, uint8_t *pBuf, int iLen)
{
int rc;
rc = write(file_i2c, ®, 1);
rc = read(file_i2c, pBuf, iLen);
return (rc > 0);
}
int I2CInit(int iSDAPin, int iSCLPin, int32_t iSpeed)
{
char filename[32];
sprintf(filename, "/dev/i2c-%d", iSDAPin); // I2C bus number passed in SDA pin
if ((file_i2c = open(filename, O_RDWR)) < 0)
return 1;
if (ioctl(file_i2c, I2C_SLAVE, iSCLPin) < 0) // set slave address
{
close(file_i2c);
file_i2c = 0;
return 1;
}
return 0;
}
static void I2CWrite(unsigned char ucAddr, unsigned char *pData, int iLen)
{
write(file_i2c, pData, iLen);
}
#endif // _LINUX_
// use only the bitbang version on ATtiny85 to avoid linking wire library
#if defined( __AVR_ATtiny85__ ) || defined(_LINUX_)
static void oledWrite(unsigned char *pData, int iLen)
{
I2CWrite(oled_addr, pData, iLen);
} /* oledWrite() */
#else
// Wrapper function to write I2C data on Arduino
static void oledWrite(unsigned char *pData, int iLen)
{
if (iCSPin != -1) // we're writing to SPI, treat it differently
{
digitalWrite(iDCPin, (pData[0] == 0) ? LOW : HIGH); // data versus command
digitalWrite(iCSPin, LOW);
#ifdef HAL_ESP32_HAL_H_
{
uint8_t ucTemp[1024];
SPI.transferBytes(&pData[1], ucTemp, iLen-1);
}
#else
SPI.transfer(&pData[1], iLen-1);
#endif
digitalWrite(iCSPin, HIGH);
}
else // must be I2C
{
if (iSDAPin != -1 && iSCLPin != -1)
{
I2CWrite(oled_addr, pData, iLen);
}
else
{
Wire.beginTransmission(oled_addr);
Wire.write(pData, iLen);
Wire.endTransmission();
}
} // I2C
} /* oledWrite() */
#endif // !__AVR_ATtiny85__
//
// Turn off the display
//
void ssd1327Shutdown(void)
{
ssd1327Power(0); // power off the display
#ifdef _LINUX_
close(file_i2c);
file_i2c = 0;
#endif
} /* ssd1327Shutdown() */
//
// ssd1322
//
static uint8_t ssd1322_init_table[] = {
2, 0xfd, 0x12, // unlock the controller
1, 0xa4, // set display off
// 1, 0xb9, // default grayscale mode
2, 0xb3, 0x91, // set clock divider
2, 0xca, 0x3f, // set COMS multiplex ratio 1/64
2, 0xa2, 0x00, // set display offset
2, 0xa1, 0x00, // set display start line
3, 0xa0, 0x14, 0x11, // set display remap
2, 0xb5, 0x00, // disable GPIO
2, 0xab, 0x01, // select external VDD regulator (none)
3, 0xb4, 0xa0, 0xfd, // external VSL display enhancement
2, 0xb1, 0xe2, // set phase length
3, 0xd1, 0x82, 0x20, // display enahancement B
1, 0xa6, // set normal display mode
0
};
void ssd1322Init(int bFlip)
{
uint8_t *s = (uint8_t *)ssd1322_init_table;
uint8_t ucTemp[8], iCount;
iCount = 1;
if (bFlip)
ssd1322_init_table[19] = 0x06;
else
ssd1322_init_table[19] = 0x14; // segment remap
ucTemp[0] = 0x40; // pretend it's I2C data
while (iCount)
{
iCount = *s++;
if (iCount != 0)
{
ssd1327WriteCommand(*s++);
memcpy(&ucTemp[1], s, iCount-1);
oledWrite(ucTemp, iCount);
s += iCount-1;
}
}
} /* ssd1322Init() */
#if !defined( __AVR_ATtiny85__ ) && !defined(_LINUX_)
//
// Initialize the OLED controller for SPI mode
//
void ssd1327SPIInit(int iType, int iDC, int iCS, int iReset, int bFlip, int bInvert, int32_t iSpeed)
{
uint8_t uc[32];
iDCPin = iDC;
iCSPin = iCS;
oled_type = iType;
iResetPin = iReset;
oled_flip = bFlip;
pinMode(iDCPin, OUTPUT);
pinMode(iCSPin, OUTPUT);
digitalWrite(iCSPin, HIGH);
// Reset it
if (iResetPin != -1)
{
pinMode(iResetPin, OUTPUT);
digitalWrite(iResetPin, HIGH);
delay(50);
digitalWrite(iResetPin, LOW);
delay(50);
digitalWrite(iResetPin, HIGH);
delay(10);
}
// Initialize SPI
SPI.begin();
SPI.beginTransaction(SPISettings(iSpeed, MSBFIRST, SPI_MODE0));
// SPI.setClockDivider(16);
// SPI.setBitOrder(MSBFIRST);
// SPI.setDataMode(SPI_MODE0);
if (oled_type == OLED_256x64)
{
iMaxX = 256;
iMaxY = 64;
iPitch = 128;
ssd1322Init(bFlip);
}
else
{
iPitch = 64;
iMaxX = 128;
iMaxY = 128;
}
ssd1327Power(1);
if (bInvert)
{
uc[0] = 0; // command
uc[1] = 0xa7; // invert command
oledWrite(uc, 2);
}
if (bFlip && oled_type == OLED_128x128) // rotate display 180
{
uc[0] = 0; // command
uc[1] = 0xa0;
oledWrite(uc, 2);
uc[0] = 0;
uc[1] = 0xc0;
oledWrite(uc, 2);
}
} /* ssd1327SPIInit() */
#endif
//
// Initializes the OLED controller
//
void ssd1327Init(int iType, int iAddr, int bFlip, int bInvert, int sda, int scl, int32_t iSpeed)
{
unsigned char uc[4];
oled_addr = iAddr;
oled_flip = bFlip;
oled_type = iType;
iSDAPin = sda;
iSCLPin = scl;
// Disable SPI mode code
iCSPin = iDCPin = iResetPin = -1;
#ifdef _LINUX_
I2CInit(sda, iAddr, iSpeed); // bus number and address for Linux
#else
if (sda != -1 && scl != -1)
{
I2CInit(sda, scl, iSpeed);
}
#ifndef __AVR_ATtiny85__
else
{
Wire.begin(); // Initiate the Wire library
Wire.setClock(iSpeed); // use high speed I2C mode (default is 100Khz)
}
#endif
#endif // _LINUX_
if (oled_type == OLED_256x64)
{
iMaxX = 256;
iMaxY = 64;
iPitch = 128;
ssd1322Init(bFlip);
}
else
{
iPitch = 64;
iMaxX = 128;
iMaxY = 128;
}
ssd1327Power(0); // turn off the power
if (oled_type == OLED_128x128)
{
uc[0] = 0x00; // command
uc[1] = 0xa0; // GDDRAM mapping
if (bFlip)
uc[2] = 0x42;
else
uc[2] = 0x51; // default (top to bottom, left to right mapping)
oledWrite(uc, 3);
}
ssd1327Power(1); // turn on the power
uc[0] = 0; // command
uc[1] = (bInvert) ? 0xa7:0xa4; // invert command / normal display
oledWrite(uc, 2);
} /* ssd1327Init() */
//
// Sends a command to turn on or off the OLED display
//
void ssd1327Power(unsigned char bOn)
{
if (bOn)
ssd1327WriteCommand(0xaf); // turn on OLED
else
ssd1327WriteCommand(0xae); // turn off OLED
} /* oledPower() */
// Send a single byte command to the OLED controller
static void ssd1327WriteCommand(unsigned char c)
{
unsigned char buf[2];
buf[0] = 0x00; // command introducer
buf[1] = c;
oledWrite(buf, 2);
} /* ssd1327WriteCommand() */
static void ssd1327WriteCommand2(unsigned char c, unsigned char d)
{
unsigned char buf[3];
buf[0] = 0x00;
buf[1] = c;
buf[2] = d;
oledWrite(buf, 3);
} /* ssd1327WriteCommand2() */
//
// Send commands to position the "cursor" (aka memory write address)
// to the given row and column as well as the ending col/row
//
static void ssd1327SetPosition(int x, int y, int cx, int cy)
{
unsigned char buf[8];
buf[0] = 0x00; // command introducer
buf[1] = 0x15; // column start/end
if (oled_type == OLED_256x64)
{
oledWrite(buf, 2);
buf[0] = 0x40; // data
buf[1] = 28 + (x/4); // strange SSD1322 mapping
buf[2] = 28 + (((x+cx)/4)-1);
oledWrite(buf, 3); // need to write this part as data
buf[0] = 0x00; // command
buf[1] = 0x75; // set row
oledWrite(buf, 2);
buf[0] = 0x40; // data
buf[1] = y;
buf[2] = y+cy-1;
oledWrite(buf, 3);
buf[0] = 0x00; // command
buf[1] = 0x5c; // enable RAM write
oledWrite(buf, 2);
}
else
{
buf[2] = x/2; // start address
buf[3] = (uint8_t)(((x+cx)/2)-1); // end address
buf[4] = 0x75; // row start/end
buf[5] = y; // start row
buf[6] = y+cy-1; // end row
oledWrite(buf, 7);
}
} /* ssd1327SetPosition() */
//
// Write a block of pixel data to the OLED
// Length can be anything from 1 to 8192 (whole display)
//
static void ssd1327WriteDataBlock(unsigned char *ucBuf, int iLen)
{
unsigned char ucTemp[129];
ucTemp[0] = 0x40; // data command
// Copying the data has the benefit in SPI mode of not letting
// the original data get overwritten by the SPI.transfer() function
memcpy(&ucTemp[1], ucBuf, iLen);
oledWrite(ucTemp, iLen+1);
} /* ssd1327WriteDataBlock() */
#ifdef FUTURE
//
// Load a 128x64 1-bpp Windows bitmap
// Pass the pointer to the beginning of the BMP file
// First pass version assumes a full screen bitmap
//
int oledLoadBMP(unsigned char *pBMP)
{
int16_t i16;
int iOffBits, q, y, j; // offset to bitmap data
int iPitch;
unsigned char x, z, b, *s;
unsigned char dst_mask;
unsigned char ucTemp[16]; // process 16 bytes at a time
unsigned char bFlipped = false;
i16 = pgm_read_word(pBMP);
if (i16 != 0x4d42) // must start with 'BM'
return -1; // not a BMP file
i16 = pgm_read_word(pBMP + 18);
if (i16 != 128) // must be 128 pixels wide
return -1;
i16 = pgm_read_word(pBMP + 22);
if (i16 != 64 && i16 != -64) // must be 64 pixels tall
return -1;
if (i16 == 64) // BMP is flipped vertically (typical)
bFlipped = true;
i16 = pgm_read_word(pBMP + 28);
if (i16 != 1) // must be 1 bit per pixel
return -1;
iOffBits = pgm_read_word(pBMP + 10);
iPitch = 16;
if (bFlipped)
{
iPitch = -16;
iOffBits += (63 * 16); // start from bottom
}
// rotate the data and send it to the display
for (y=0; y<8; y++) // 8 lines of 8 pixels
{
ssd1327SetPosition(0, y);
for (j=0; j<8; j++) // do 8 sections of 16 columns
{
s = &pBMP[iOffBits + (j*2) + (y * iPitch*8)]; // source line
memset(ucTemp, 0, 16); // start with all black
for (x=0; x<16; x+=8) // do each block of 16x8 pixels
{
dst_mask = 1;
for (q=0; q<8; q++) // gather 8 rows
{
b = pgm_read_byte(s + (q * iPitch));
for (z=0; z<8; z++) // gather up the 8 bits of this column
{
if (b & 0x80)
ucTemp[x+z] |= dst_mask;
b <<= 1;
} // for z
dst_mask <<= 1;
} // for q
s++; // next source byte
} // for x
ssd1327WriteDataBlock(ucTemp, 16);
} // for j
} // for y
return 0;
} /* oledLoadBMP() */
#endif // FUTURE
//
// Draw a string of normal (8x8), small (6x8) or large (16x32) characters
// At the given col+row
// For AVR systems (very little RAM), the text gets drawn directly on the display
// For non-AVR systems, the text is written to the back buffer and transparent text
// can be enabled by setting the background color to -1
//
void ssd1327WriteString(uint8_t x, uint8_t y, char *szMsg, uint8_t iSize, int ucFG, int ucBG)
{
int i, iFontOff;
int tx, ty;
unsigned char uc, ucMask;
unsigned char c, *s, *d, ucTemp2[8];
#ifndef USE_BACKBUFFER
unsigned char ucTemp[40];
#endif
int iBG;
#ifdef __AVR__
if (ucBG == -1) ucBG = 0; // no transparent text allowed
#endif
if (iSize == FONT_NORMAL || iSize == FONT_SMALL) // 8x8 and 6x8 font
{
uint8_t cx = (iSize == FONT_NORMAL) ? 8:6;
uint8_t *pFont = (iSize == FONT_NORMAL) ? (uint8_t*)ucFont:(uint8_t*)ucSmallFont;
i = 0;
if (y > iMaxY-7) return; // will write past the bottom
while (x < iMaxX-7 && szMsg[i] != 0)
{
ssd1327SetPosition(x, y, cx, 8);
c = (unsigned char)szMsg[i];
iFontOff = (int)(c-32) * cx;
// we can't directly use the pointer to FLASH memory, so copy to a local buffer
memcpy_P(ucTemp2, &pFont[iFontOff], cx);
s = ucTemp2;
#ifdef USE_BACKBUFFER
d = &ucBackbuffer[(y*iPitch) + (x/2)];
#else
d = &ucTemp[0];
*d++ = 0x40; // data introducer
#endif
ucMask = 1;
for (ty=0; ty<8; ty++)
{
if (ucBG == -1) // transparent text
{
for (tx=0; tx iMaxY-15) return; // will write past the bottom
i = 0;
while (x < iMaxX-15 && szMsg[i] != 0)
{
// stretch the 'normal' font instead of using the big font
int tx, ty;
c = szMsg[i] - 32;
unsigned char ucMask;
s = (unsigned char *)&ucFont[(int)c*8];
memcpy_P(ucTemp2, s, 8);
iBG = ucBG;
ucFG |= (ucFG << 4); // 2 pixels at a time
ucBG |= (ucBG << 4);
// Stretch the font to double width + double height
ucMask = 1;
#ifdef USE_BACKBUFFER
d = &ucBackbuffer[(y*iPitch)+(x/2)];
#else
ssd1327SetPosition(x, y, 16, 16);
ucTemp[0] = 0x40; // start of data (write one row at a time)
#endif
for (ty=0; ty<8; ty++)
{
for (tx=0; tx<8; tx++)
{
#ifdef USE_BACKBUFFER
if (ucTemp2[tx] & ucMask) // pixel set
d[tx] = d[tx+iPitch] = ucFG;
else if (ucBG != -1)
d[tx] = d[tx+iPitch] = ucBG;
#else
if (ucTemp2[tx] & ucMask) // pixel set
c = ucFG;
else
c = ucBG;
ucTemp[1+tx] = ucTemp[1+tx+8] = c; // double it vertically
#endif
}
#ifdef USE_BACKBUFFER
d += iPitch*2; // move to next line
#else
oledWrite(ucTemp, 17); // write 2 rows of the character
#endif
ucMask <<= 1;
}
x += 16;
i++;
}
}
} /* ssd1327WriteString() */
#ifdef USE_BACKBUFFER
//
// For drawing ellipses, a circle is drawn and the x and y pixels are scaled by a 16-bit integer fraction
// This function draws a single pixel and scales its position based on the x/y fraction of the ellipse
//
void DrawScaledPixel(int32_t iCX, int32_t iCY, int32_t x, int32_t y, int32_t iXFrac, int32_t iYFrac, uint8_t ucColor)
{
uint8_t *d;
if (iXFrac != 0x10000) x = (x * iXFrac) >> 16;
if (iYFrac != 0x10000) y = (y * iYFrac) >> 16;
x += iCX; y += iCY;
if (x < 0 || x >= iMaxX || y < 0 || y >= iMaxY)
return; // off the screen
d = &ucBackbuffer[(y * iPitch) + x/2];
if (x & 1)
{
d[0] &= 0xf0;
d[0] |= ucColor;
}
else
{
d[0] &= 0xf;
d[0] |= (ucColor << 4);
}
} /* DrawScaledPixel() */
void DrawScaledLine(int32_t iCX, int32_t iCY, int32_t x, int32_t y, int32_t iXFrac, int32_t iYFrac, uint8_t ucColor)
{
int32_t iLen, x2;
uint8_t *d;
if (iXFrac != 0x10000) x = (x * iXFrac) >> 16;
if (iYFrac != 0x10000) y = (y * iYFrac) >> 16;
iLen = x * 2;
x = iCX - x; y += iCY;
x2 = x + iLen;
if (y < 0 || y >= iMaxY)
return; // completely off the screen
if (x < 0) x = 0;
if (x2 >= iMaxX) x2 = iMaxX-1;
iLen = x2 - x + 1; // new length
d = &ucBackbuffer[(y * iPitch) + x/2];
uint8_t c = ucColor | (ucColor << 4);
if (x & 1) // starting on odd pixel
{
d[0] &= 0xf0;
d[0] |= ucColor;
d++;
iLen--;
}
while (iLen >= 2) // middle part
{
*d++ = c;
iLen -= 2;
}
if (iLen) // last odd pixel
{
d[0] &= 0xf;
d[0] |= (ucColor << 4);
}
} /* DrawScaledLine() */
//
// Draw the 8 pixels around the Bresenham circle
// (scaled to make an ellipse)
//
void BresenhamCircle(int32_t iCX, int32_t iCY, int32_t x, int32_t y, int32_t iXFrac, int32_t iYFrac, uint8_t iColor, int bFill)
{
if (bFill) // draw a filled ellipse
{
// for a filled ellipse, draw 4 lines instead of 8 pixels
DrawScaledLine(iCX, iCY, x, y, iXFrac, iYFrac, iColor);
DrawScaledLine(iCX, iCY, x, -y, iXFrac, iYFrac, iColor);
DrawScaledLine(iCX, iCY, y, x, iXFrac, iYFrac, iColor);
DrawScaledLine(iCX, iCY, y, -x, iXFrac, iYFrac, iColor);
}
else // draw 8 pixels around the edges
{
DrawScaledPixel(iCX, iCY, x, y, iXFrac, iYFrac, iColor);
DrawScaledPixel(iCX, iCY, -x, y, iXFrac, iYFrac, iColor);
DrawScaledPixel(iCX, iCY, x, -y, iXFrac, iYFrac, iColor);
DrawScaledPixel(iCX, iCY, -x, -y, iXFrac, iYFrac, iColor);
DrawScaledPixel(iCX, iCY, y, x, iXFrac, iYFrac, iColor);
DrawScaledPixel(iCX, iCY, -y, x, iXFrac, iYFrac, iColor);
DrawScaledPixel(iCX, iCY, y, -x, iXFrac, iYFrac, iColor);
DrawScaledPixel(iCX, iCY, -y, -x, iXFrac, iYFrac, iColor);
}
} /* BresenhamCircle() */
void ssd1327Ellipse(int32_t iCenterX, int32_t iCenterY, int32_t iRadiusX, int32_t iRadiusY, uint8_t ucColor, int bFilled)
{
int32_t iRadius, iXFrac, iYFrac;
int32_t iDelta, x, y;
if (iRadiusX > iRadiusY) // use X as the primary radius
{
iRadius = iRadiusX;
iXFrac = 65536;
iYFrac = (iRadiusY * 65536) / iRadiusX;
}
else
{
iRadius = iRadiusY;
iXFrac = (iRadiusX * 65536) / iRadiusY;
iYFrac = 65536;
}
// set up a buffer with the widest possible run of pixels to dump in 1 shot
iDelta = 3 - (2 * iRadius);
x = 0; y = iRadius;
while (x <= y)
{
BresenhamCircle(iCenterX, iCenterY, x, y, iXFrac, iYFrac, ucColor, bFilled);
x++;
if (iDelta < 0)
{
iDelta += (4*x) + 6;
}
else
{
iDelta += 4 * (x-y) + 10;
y--;
}
}
} /* ssd1327Ellipse() */
//
// Draw a 1-bpp pattern into the backbuffer with the given color
// 1 bits are drawn as color, 0 are transparent
//
void ssd1327DrawPattern(uint8_t *pPattern, int iSrcPitch, int iDestX, int iDestY, int iCX, int iCY, uint8_t ucColor)
{
int x, y;
uint8_t *s, uc, ucPixel, ucMask;
uint8_t *d;
if (iDestX + iCX > iMaxX || iDestY + iCY > iMaxY || iDestX < 0 || iDestY < 0 || iCX < 0 || iCY < 0)
return; // invalid parameter
if (iDestX+iCX > iMaxX) // trim to fit on display
iCX = (iMaxX - iDestX);
if (iDestY+iCY > iMaxY)
iCY = (iMaxY - iDestY);
if (pPattern == NULL || iDestX < 0 || iDestY < 0 || iCX <=0 || iCY <= 0)
return;
for (y=0; y>= 1;
ucPixel = d[0];
if (ucMask == 0)
{
ucMask = 0x80;
uc = *s++;
}
if (uc & ucMask) // active pixel
{
ucPixel &= 0xf;
ucPixel |= (ucColor << 4);
}
ucMask >>= 1;
if (uc & ucMask) // odd pixel
{
ucPixel &= 0xf0;
ucPixel |= ucColor;
}
*d++ = ucPixel; // write modified pixel pair
} // for x
} // for y
} /* ssd1327DrawPattern() */
//
// Display part or whole of the backbuffer or a custom bitmap to the visible display
// Pass a NULL pointer to display the backbuffer
//
void ssd1327ShowBitmap(uint8_t *pBuffer, int iLocalPitch, int x, int y, int w, int h)
{
int ty;
uint8_t *s;
if (x < 0 || y < 0 || x >= iMaxX || y >= iMaxY || (x+w) > iMaxX || (y+h) > iMaxY)
return; // invalid coordinates
if (pBuffer == NULL)
{
pBuffer = &ucBackbuffer[(y*iPitch)+(x/2)]; // starting point also
iLocalPitch = iPitch;
}
ssd1327SetPosition(x, y, w, h);
for (ty=0; ty= iMaxX || x+w > iMaxX)
return; // out of bounds
if (y < 0 || y >= iMaxY || y+h > iMaxY)
return;
if (bFill)
{
for (i=0; i 359 || iCenterX < 0 || iCenterX >= iWidth || iCenterY < 0 || iCenterY >= iHeight || (iBpp != 1 && iBpp != 4))
return;
// since we're rotating from dest back to source, reverse the angle
iAngle = 360 - iAngle;
if (iAngle == 360) // just copy src to dest
{
memcpy(pDest, pSrc, iHeight * iPitch);
return;
}
// Create a quicker lookup table for sin/cos pre-multiplied at the given angle
sa = (int32_t)i16SineTable[iAngle]; // sine of given angle
ca = (int32_t)i16SineTable[iAngle+90]; // cosine of given angle
for (i=-256; i<256; i++) // create the pre-calc tables
{
pre_sin[i+256] = (sa * i) >> 15; // sin * x
pre_cos[i+256] = (ca * i) >> 15;
}
pSin = &pre_sin[256]; pCos = &pre_cos[256]; // point to 0 points in tables
for (y=0; y 0 && ty > 0 && tx < iWidth && ty < iHeight) // check source pixel
{
s = &pSrc[(ty*iPitch)+(tx>>3)];
if (s[0] & (0x80 >> (tx & 7)))
uc |= ucMask; // set destination pixel
}
ucMask >>= 1;
if (ucMask == 0) // write the byte into the destination bitmap
{
ucMask = 0x80;
*d++ = uc;
uc = 0;
}
}
else // 4-bpp
{
if (tx > 0 && ty > 0 && tx < iWidth && ty < iHeight) // check source pixel
{
uc = pSrc[(ty*iPitch)+(tx/2)];
if (tx & 1)
uc >>= 4;
else
uc &= 0xf; // isolate the pixel in the lower 4 bits
ucPixel |= (uc << iShift);
iShift = 4-iShift;
if (iShift == 0) // time to store the pair
{
*d++ = ucPixel;
ucPixel = 0;
}
}
}
}
if (iBpp == 1 && ucMask != 0x80) // store partial byte
*d++ = uc;
if (iBpp == 4 && iShift == 4) // store partial byte
*d++ = ucPixel;
} // for y
} /* ssd1327RotateBitmap() */
void ssd1327DrawLine(int x1, int y1, int x2, int y2, uint8_t ucColor)
{
int temp;
int dx = x2 - x1;
int dy = y2 - y1;
int error;
uint8_t *p;
int xinc, yinc, shift;
int y, x;
if (x1 < 0 || x2 < 0 || y1 < 0 || y2 < 0 || x1 >= iMaxX || x2 >= iMaxX || y1 >= iMaxY || y2 >= iMaxY)
return;
if(abs(dx) > abs(dy)) {
// X major case
if(x2 < x1) {
dx = -dx;
temp = x1;
x1 = x2;
x2 = temp;
temp = y1;
y1 = y2;
y2 = temp;
}
y = y1;
dy = (y2 - y1);
error = dx >> 1;
yinc = 1;
if (dy < 0)
{
dy = -dy;
yinc = -1;
}
p = &ucBackbuffer[(x1/2) + (y1 * iPitch)]; // point to current spot in back buffer
shift = (x1 & 1) ? 0:4; // current bit offset
for(x=x1; x1 <= x2; x1++) {
*p &= (0xf0 >> shift);
*p |= (ucColor << shift);
shift = 4-shift;
if (shift == 4) // time to increment pointer
p++;
error -= dy;
if (error < 0)
{
error += dx;
if (yinc > 0)
p += iPitch;
else
p -= iPitch;
y += yinc;
}
} // for x1
}
else {
// Y major case
if(y1 > y2) {
dy = -dy;
temp = x1;
x1 = x2;
x2 = temp;
temp = y1;
y1 = y2;
y2 = temp;
}
p = &ucBackbuffer[(x1/2) + (y1 * iPitch)]; // point to current spot in back buffer
shift = (x1 & 1) ? 0:4; // current bit offset
dx = (x2 - x1);
error = dy >> 1;
xinc = 1;
if (dx < 0)
{
dx = -dx;
xinc = -1;
}
for(x = x1; y1 <= y2; y1++) {
*p &= (0xf0 >> shift); // set the pixel
*p |= (ucColor << shift);
error -= dx;
p += iPitch; // y1++
if (error < 0)
{
error += dy;
x += xinc;
shift = 4-shift;
if (xinc == 1)
{
if (shift == 4) // time to increment pointer
p++;
}
else
{
if (shift == 0)
p--;
}
}
} // for y
} // y major case
} /* ssd1327DrawLine() */
#endif // USE_BACKBUFFER