/*********************************************************************
This is a library for our Monochrome OLEDs based on SSD1306 drivers
Pick one up today in the adafruit shop!
------> http://www.adafruit.com/category/63_98
These displays use SPI to communicate, 4 or 5 pins are required to
interface
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
Written by Limor Fried/Ladyada for Adafruit Industries.
BSD license, check license.txt for more information
All text above, and the splash screen below must be included in any redistribution
This code is taken from the ADAfruit library - it is used for playing with an OLED screen
*********************************************************************/
#include <stdint.h>
#include <string.h>
#include "libSSD1306/SSD1306.h"
#include "libSSD1306/spiInterface.h"
#define swap(x,y) { typeof(x)t = x; x=y; y=t; }
#define abs(x) ((x)>0?(x):-(x))
static uint8_t rotation = 0;
const uint16_t WIDTH = SSD1306_LCDWIDTH;
const uint16_t HEIGHT = SSD1306_LCDHEIGHT;
// the memory buffer for the LCD
// pointer to the current display - affects buffer used and also chipselect
uint8_t display_buffer[SSD1306_LCDHEIGHT * SSD1306_LCDWIDTH / 8];
inline uint8_t*
display_address (void)
{
return (uint8_t*) (&display_buffer[0]);
}
inline uint8_t
getRotation (void)
{
return rotation;
}
inline int16_t
width (void)
{
switch (rotation)
{
case 0:
return WIDTH;
break;
case 1:
return WIDTH;
break;
case 2:
return HEIGHT;
break;
case 3:
return -WIDTH;
break;
}
return 0;
}
inline int16_t
height (void)
{
switch (rotation)
{
case 0:
return HEIGHT;
break;
case 1:
return HEIGHT;
break;
case 2:
return WIDTH;
break;
case 3:
return -HEIGHT;
break;
}
return 0;
}
// the most basic function, set a single pixel
inline void
drawPixel (int16_t x, int16_t y, uint16_t color)
{
if ((x < 0) || (x >= width ()) || (y < 0) || (y >= height ()))
return;
// check rotation, move pixel around if necessary
switch (getRotation ())
{
case 1:
swap(x, y)
;
x = WIDTH - x - 1;
break;
case 2:
x = WIDTH - x - 1;
y = HEIGHT - y - 1;
break;
case 3:
swap(x, y)
;
y = HEIGHT - y - 1;
break;
}
// x is which column
switch (color)
{
case BLACK:
display_buffer[x + (y / 8) * SSD1306_LCDWIDTH] &= ~(1 << (y & 7));
break;
default:
case WHITE:
display_buffer[x + (y / 8) * SSD1306_LCDWIDTH] |= (1 << (y & 7));
break;
case INVERT:
display_buffer[x + (y / 8) * SSD1306_LCDWIDTH] ^= (1 << (y & 7));
break;
}
}
void
ssd1306_begin (uint8_t vccstate, uint8_t i2caddr)
{
(void) i2caddr;
ssd1306resetDisplay ();
// Init sequence for 128x32 or 128x64 OLED module
ssd1306commandSPIwrite (SSD1306_DISPLAYOFF); // 0xAE
ssd1306commandSPIwrite (SSD1306_SETDISPLAYCLOCKDIV); // 0xD5
ssd1306commandSPIwrite (0x80); // the suggested ratio 0x80
ssd1306commandSPIwrite (SSD1306_SETMULTIPLEX); // 0xA8
ssd1306commandSPIwrite (SSD1306_LCDHEIGHT - 1);
ssd1306commandSPIwrite (SSD1306_SETDISPLAYOFFSET); // 0xD3
ssd1306commandSPIwrite (0x0); // no offset
ssd1306commandSPIwrite (SSD1306_SETSTARTLINE | 0x0); // line #0
ssd1306commandSPIwrite (SSD1306_CHARGEPUMP); // 0x8D
if (vccstate == SSD1306_EXTERNALVCC)
{
ssd1306commandSPIwrite (0x10);
}
else
{
ssd1306commandSPIwrite (0x14);
}
ssd1306commandSPIwrite (SSD1306_MEMORYMODE); // 0x20
ssd1306commandSPIwrite (0x00); // 0x0 act like ks0108
ssd1306commandSPIwrite (SSD1306_SEGREMAP | 0x1);
ssd1306commandSPIwrite (SSD1306_COMSCANDEC);
ssd1306commandSPIwrite (SSD1306_SETCOMPINS); // 0xDA
ssd1306commandSPIwrite (SSD1306_LCDHEIGHT == 32 ? 0x02 : 0x12);
ssd1306commandSPIwrite (SSD1306_SETCONTRAST); // 0x81
if (vccstate == SSD1306_EXTERNALVCC)
{
ssd1306commandSPIwrite (0x9F);
}
else
{
ssd1306commandSPIwrite (0xCF);
}
ssd1306commandSPIwrite (SSD1306_SETPRECHARGE); // 0xd9
if (vccstate == SSD1306_EXTERNALVCC)
{
ssd1306commandSPIwrite (0x22);
}
else
{
ssd1306commandSPIwrite (0xF1);
}
ssd1306commandSPIwrite (SSD1306_SETVCOMDETECT); // 0xDB
ssd1306commandSPIwrite (0x40);
ssd1306commandSPIwrite (SSD1306_DISPLAYALLON_RESUME); // 0xA4
ssd1306commandSPIwrite (SSD1306_NORMALDISPLAY); // 0xA6
ssd1306commandSPIwrite (SSD1306_DISPLAYON); //--turn on oled panel
}
void
invertDisplay (uint8_t i)
{
if (i)
{
ssd1306commandSPIwrite (SSD1306_INVERTDISPLAY);
}
else
{
ssd1306commandSPIwrite (SSD1306_NORMALDISPLAY);
}
}
// startscrollright
// Activate a right handed scroll for rows start through stop
// Hint, the display is 16 rows tall. To scroll the whole display, run:
// display.scrollright(0x00, 0x0F)
void
startscrollright (uint8_t start, uint8_t stop)
{
ssd1306commandSPIwrite (SSD1306_RIGHT_HORIZONTAL_SCROLL);
ssd1306commandSPIwrite (0X00);
ssd1306commandSPIwrite (start);
ssd1306commandSPIwrite (0X00);
ssd1306commandSPIwrite (stop);
ssd1306commandSPIwrite (0X00);
ssd1306commandSPIwrite (0XFF);
ssd1306commandSPIwrite (SSD1306_ACTIVATE_SCROLL);
}
// startscrollleft
// Activate a right handed scroll for rows start through stop
// Hint, the display is 16 rows tall. To scroll the whole display, run:
// display.scrollright(0x00, 0x0F)
void
startscrollleft (uint8_t start, uint8_t stop)
{
ssd1306commandSPIwrite (SSD1306_LEFT_HORIZONTAL_SCROLL);
ssd1306commandSPIwrite (0X00);
ssd1306commandSPIwrite (start);
ssd1306commandSPIwrite (0X00);
ssd1306commandSPIwrite (stop);
ssd1306commandSPIwrite (0X00);
ssd1306commandSPIwrite (0XFF);
ssd1306commandSPIwrite (SSD1306_ACTIVATE_SCROLL);
}
// startscrolldiagright
// Activate a diagonal scroll for rows start through stop
// Hint, the display is 16 rows tall. To scroll the whole display, run:
// display.scrollright(0x00, 0x0F)
void
startscrolldiagright (uint8_t start, uint8_t stop)
{
ssd1306commandSPIwrite (SSD1306_SET_VERTICAL_SCROLL_AREA);
ssd1306commandSPIwrite (0X00);
ssd1306commandSPIwrite (SSD1306_LCDHEIGHT);
ssd1306commandSPIwrite (SSD1306_VERTICAL_AND_RIGHT_HORIZONTAL_SCROLL);
ssd1306commandSPIwrite (0X00);
ssd1306commandSPIwrite (start);
ssd1306commandSPIwrite (0X00);
ssd1306commandSPIwrite (stop);
ssd1306commandSPIwrite (0X01);
ssd1306commandSPIwrite (SSD1306_ACTIVATE_SCROLL);
}
// startscrolldiagleft
// Activate a diagonal scroll for rows start through stop
// Hint, the display is 16 rows tall. To scroll the whole display, run:
// display.scrollright(0x00, 0x0F)
void
startscrolldiagleft (uint8_t start, uint8_t stop)
{
ssd1306commandSPIwrite (SSD1306_SET_VERTICAL_SCROLL_AREA);
ssd1306commandSPIwrite (0X00);
ssd1306commandSPIwrite (SSD1306_LCDHEIGHT);
ssd1306commandSPIwrite (SSD1306_VERTICAL_AND_LEFT_HORIZONTAL_SCROLL);
ssd1306commandSPIwrite (0X00);
ssd1306commandSPIwrite (start);
ssd1306commandSPIwrite (0X00);
ssd1306commandSPIwrite (stop);
ssd1306commandSPIwrite (0X01);
ssd1306commandSPIwrite (SSD1306_ACTIVATE_SCROLL);
}
void
stopscroll (void)
{
ssd1306commandSPIwrite (SSD1306_DEACTIVATE_SCROLL);
}
// Dim the display
// dim = true: display is dimmed
// dim = false: display is normal
void
dim (uint8_t contrast)
{
// the range of contrast to too small to be really useful
// it is useful to dim the display
ssd1306commandSPIwrite (SSD1306_SETCONTRAST);
ssd1306commandSPIwrite (contrast);
}
void
display (void)
{
// select entire display as window to write into
ssd1306commandSPIwrite (SSD1306_COLUMNADDR);
ssd1306commandSPIwrite (0); // Column start address (0 = reset)
ssd1306commandSPIwrite (SSD1306_RAMWIDTH - 1); // Column end address (127 = reset)
ssd1306commandSPIwrite (SSD1306_PAGEADDR);
ssd1306commandSPIwrite (0); // Page start address (0 = reset)
ssd1306commandSPIwrite ((SSD1306_LCDHEIGHT == 64) ? 7 : 3); // Page end address
int row;
int col = SSD1306_RAMWIDTH == 132 ? 2 : 0;
for (row = 0; row < SSD1306_LCDHEIGHT / 8; row++)
{
// set the cursor to
ssd1306commandSPIwrite (0xB0 + row); //set page address
ssd1306commandSPIwrite (col & 0xf); //set lower column address
ssd1306commandSPIwrite (0x10 | (col >> 4)); //set higher column address
ssd1306SendDisplay (
(uint8_t*) (&display_buffer[0]) + row * SSD1306_LCDWIDTH,
SSD1306_LCDWIDTH);
}
}
// clear everything
void
clearDisplay (void)
{
memset (&display_buffer
, 0, (SSD1306_LCDWIDTH
* SSD1306_LCDHEIGHT
/ 8));
}
/* using Bresenham draw algorithm */
void
drawLine (int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint8_t color)
{
int16_t x, y, dx, dy, //deltas
dx2, dy2, //scaled deltas
ix, iy, //increase rate on the x and y axis
err; //the error term
uint16_t i; //looping variable
// identify the first pixel
x = x1;
y = y1;
// difference between starting and ending points
dx = x2 - x1;
dy = y2 - y1;
// calculate direction of the vector and store in ix and iy
if (dx >= 0)
ix = 1;
if (dx < 0)
{
ix = -1;
}
if (dy >= 0)
iy = 1;
if (dy < 0)
{
iy = -1;
}
// scale deltas and store in dx2 and dy2
dx2 = dx * 2;
dy2 = dy * 2;
// all variables are set and it's time to enter the main loop.
if (dx > dy) // dx is the major axis
{
// initialize the error term
err = dy2 - dx;
for (i = 0; i <= dx; i++)
{
drawPixel (x, y, color);
if (err >= 0)
{
err -= dx2;
y += iy;
}
err += dy2;
x += ix;
}
}
else // dy is the major axis
{
// initialize the error term
err = dx2 - dy;
for (i = 0; i <= dy; i++)
{
drawPixel (x, y, color);
if (err >= 0)
{
err -= dy2;
x += ix;
}
err += dx2;
y += iy;
}
}
}