SSD1306/lib/ssd1306.c

827 lines
17 KiB
C
Raw Normal View History

2020-10-13 04:45:09 -04:00
/**
* ---------------------------------------------------------------+
* @desc SSD1306 OLED Driver
* ---------------------------------------------------------------+
* Copyright (C) 2020 Marian Hrinko.
* Written by Marian Hrinko (mato.hrinko@gmail.com)
*
* @author Marian Hrinko
* @datum 06.10.2020
2021-04-29 02:47:08 -04:00
* @file ssd1306.c
* @tested AVR Atmega16, ATmega8, Atmega328
2020-10-13 04:48:15 -04:00
*
* @depend font.h, twi.h
2020-10-13 04:45:09 -04:00
* ---------------------------------------------------------------+
2021-04-29 02:47:08 -04:00
* @usage Basic Setup for OLED
2020-10-13 04:45:09 -04:00
*/
2021-04-29 02:47:08 -04:00
2020-10-13 04:45:09 -04:00
#include <avr/pgmspace.h>
#include "font.h"
#include "twi.h"
#include "ssd1306.h"
// +---------------------------+
// | Set MUX Ratio |
// +---------------------------+
// | 0xA8, 0x3F |
// +---------------------------+
// |
// +---------------------------+
// | Set Display Offset |
// +---------------------------+
// | 0xD3, 0x00 |
// +---------------------------+
// |
// +---------------------------+
// | Set Display Start Line |
// +---------------------------+
// | 0x40 |
// +---------------------------+
// |
// +---------------------------+
// | Set Segment Remap |
// +---------------------------+
// | 0xA0 / 0xA1 |
// +---------------------------+
// |
// +---------------------------+
// | Set COM Output Scan |
// | Direction |
// +---------------------------+
// | 0xC0 / 0xC8 |
// +---------------------------+
// |
// +---------------------------+
// | Set COM Pins hardware |
// | configuration |
// +---------------------------+
// | 0xDA, 0x02 |
// +---------------------------+
// |
// +---------------------------+
// | Set Contrast Control |
// +---------------------------+
// | 0x81, 0x7F |
// +---------------------------+
// |
// +---------------------------+
// | Disable Entire Display On |
// +---------------------------+
// | 0xA4 |
// +---------------------------+
// |
// +---------------------------+
// | Set Normal Display |
// +---------------------------+
// | 0xA6 |
// +---------------------------+
// |
// +---------------------------+
// | Set Osc Frequency |
// +---------------------------+
// | 0xD5, 0x80 |
// +---------------------------+
// |
// +---------------------------+
// | Enable charge pump |
// | regulator |
// +---------------------------+
// | 0x8D, 0x14 |
// +---------------------------+
// |
// +---------------------------+
// | Display On |
// +---------------------------+
// | 0xAF |
// +---------------------------+
/** @array Init command */
const uint8_t INIT_SSD1306[] PROGMEM = {
// number of initializers
2020-11-02 07:33:31 -05:00
18,
2020-10-13 04:45:09 -04:00
// 0xAE = Set Display OFF
// -----------------------------------
0, SSD1306_DISPLAY_OFF,
2020-10-27 08:05:53 -04:00
// 0xA8
1, SSD1306_SET_MUX_RATIO, 0x3F,
2020-10-13 04:45:09 -04:00
// 0x20 = Set Memory Addressing Mode
// -----------------------------------
// 0x00 - Horizontal Addressing Mode
// 0x01 - Vertical Addressing Mode
// 0x02 - Page Addressing Mode (RESET)
1, SSD1306_MEMORY_ADDR_MODE, 0x00,
// 0x21 = Set Column Address
// -----------------------------------
// 0x00 - Start Column Address
// 0xFF - End Column Address
2, SSD1306_SET_COLUMN_ADDR, START_COLUMN_ADDR, END_COLUMN_ADDR,
// 0x22 = Set Page Address
// -----------------------------------
// 0x00 - Start Column Address
// 0x07 - End Column Address
2, SSD1306_SET_PAGE_ADDR, START_PAGE_ADDR, END_PAGE_ADDR,
// 0x40
0, SSD1306_SET_START_LINE,
2020-10-27 08:05:53 -04:00
// 0xD3
1, SSD1306_DISPLAY_OFFSET, 0x00,
// 0xA0 / remap 0xA1
0, SSD1306_SEG_REMAP_OP,
// 0xC0 / remap 0xC8
0, SSD1306_COM_SCAN_DIR_OP,
// 0xDA
1, SSD1306_COM_PIN_CONF, 0x12,
// 0x81
1, SSD1306_SET_CONTRAST, 0x7F,
2020-10-13 04:45:09 -04:00
// 0xA4
0, SSD1306_DIS_ENT_DISP_ON,
// 0xA6
0, SSD1306_DIS_NORMAL,
2020-10-27 08:05:53 -04:00
// 0xD5
2020-10-13 04:45:09 -04:00
1, SSD1306_SET_OSC_FREQ, 0x80,
2020-10-27 08:05:53 -04:00
// 0xD9, 1st Period = higher value less blinking
1, SSD1306_SET_PRECHARGE, 0xc2,
// Set V COMH Deselect, reset value 0x22 = 0,77xUcc
1, SSD1306_VCOM_DESELECT, 0x20,
// 0x8D
2020-10-13 04:45:09 -04:00
1, SSD1306_SET_CHAR_REG, 0x14,
// 0xAF = Set Display ON
// -----------------------------------
2020-11-02 07:33:31 -05:00
0, SSD1306_DISPLAY_ON
2020-10-13 04:45:09 -04:00
};
2020-10-27 08:05:53 -04:00
// @var global - set area
unsigned int set_area = (END_PAGE_ADDR - START_PAGE_ADDR + 1) * (END_COLUMN_ADDR - START_COLUMN_ADDR + 1);
// @var global - cache index column
unsigned short int indexCol = START_COLUMN_ADDR;
// @var global - cache index page
unsigned short int indexPage = START_PAGE_ADDR;
2020-10-13 04:45:09 -04:00
/**
* @desc SSD1306 Init
*
* @param void
*
* @return char
*/
2021-04-29 02:47:08 -04:00
char SSD1306_Init (void)
2020-10-13 04:45:09 -04:00
{
// variables
const uint8_t *commands = INIT_SSD1306;
// number of commands
unsigned short int no_of_commands = pgm_read_byte(commands++);
// argument
char no_of_arguments;
// command
char command;
// init status
char status = INIT_STATUS;
// TWI: Init
// -------------------------
TWI_Init();
// TWI: start & SLAW
// -------------------------
status = SSD1306_Send_StartAndSLAW();
// request - start TWI
if (SSD1306_SUCCESS != status) {
// error
return status;
}
// loop throuh commands
while (no_of_commands) {
// number of arguments
no_of_arguments = pgm_read_byte(commands++);
// command
command = pgm_read_byte(commands++);
// send command
// -------------------------
status = SSD1306_Send_Command(command);
// request - start TWI
if (SSD1306_SUCCESS != status) {
// error
return status;
}
// send arguments
// -------------------------
while (no_of_arguments--) {
// send command
status = SSD1306_Send_Command(pgm_read_byte(commands++));
// request - start TWI
if (SSD1306_SUCCESS != status) {
// error
return status;
}
}
// decrement
no_of_commands--;
}
// TWI: Stop
// -------------------------
TWI_Stop();
// success
return SSD1306_SUCCESS;
}
/**
* @desc SSD1306 Send Start and SLAW request
*
* @param void
*
* @return char
*/
2021-04-29 02:47:08 -04:00
char SSD1306_Send_StartAndSLAW (void)
2020-10-13 04:45:09 -04:00
{
// init status
char status = INIT_STATUS;
// TWI: start
// -------------------------
status = TWI_MT_Start();
// request - start TWI
if (SSD1306_SUCCESS != status) {
// error
return status;
}
// TWI: send SLAW
// -------------------------
status = TWI_MT_Send_SLAW(SSD1306_ADDRESS);
// request - send SLAW
if (SSD1306_SUCCESS != status) {
// error
return status;
}
// success
return SSD1306_SUCCESS;
}
/**
* @desc SSD1306 Send command
*
* @param char
*
* @return char
*/
2021-04-29 02:47:08 -04:00
char SSD1306_Send_Command (char command)
2020-10-13 04:45:09 -04:00
{
// init status
char status = INIT_STATUS;
// send control byte
// -------------------------
status = TWI_MT_Send_Data(SSD1306_COMMAND);
// request - start TWI
if (SSD1306_SUCCESS != status) {
// error
return status;
}
// send command
// -------------------------
status = TWI_MT_Send_Data(command);
// request - start TWI
if (SSD1306_SUCCESS != status) {
// error
return status;
}
// success
return SSD1306_SUCCESS;
}
2020-10-27 08:05:53 -04:00
/**
* @desc SSD1306 Normal Colors
*
* @param void
*
* @return char
*/
2021-04-29 02:47:08 -04:00
char SSD1306_NormalScreen (void)
2020-10-27 08:05:53 -04:00
{
// init status
char status = INIT_STATUS;
// TWI: start & SLAW
// -------------------------
status = SSD1306_Send_StartAndSLAW();
// request - start TWI
if (SSD1306_SUCCESS != status) {
// error
return status;
}
// send command
// -------------------------
status = SSD1306_Send_Command(SSD1306_DIS_NORMAL);
// request - start TWI
if (SSD1306_SUCCESS != status) {
// error
return status;
}
// success
return SSD1306_SUCCESS;
}
/**
* @desc SSD1306 Inverse Colors
*
* @param void
*
* @return char
*/
2021-04-29 02:47:08 -04:00
char SSD1306_InverseScreen (void)
2020-10-27 08:05:53 -04:00
{
// init status
char status = INIT_STATUS;
// TWI: start & SLAW
// -------------------------
status = SSD1306_Send_StartAndSLAW();
// request - start TWI
if (SSD1306_SUCCESS != status) {
// error
return status;
}
// send command
// -------------------------
status = SSD1306_Send_Command(SSD1306_DIS_INVERSE);
// request - start TWI
if (SSD1306_SUCCESS != status) {
// error
return status;
}
// success
return SSD1306_SUCCESS;
}
/**
2020-11-02 07:33:31 -05:00
* @desc SSD1306 Update Screen On
2020-10-27 08:05:53 -04:00
*
* @param void
*
* @return char
*/
2021-04-29 02:47:08 -04:00
char SSD1306_UpdateScreen (void)
2020-10-27 08:05:53 -04:00
{
// init status
char status = INIT_STATUS;
// TWI: start & SLAW
// -------------------------
status = SSD1306_Send_StartAndSLAW();
// request - start TWI
if (SSD1306_SUCCESS != status) {
// error
return status;
}
// send command
// -------------------------
status = SSD1306_Send_Command(SSD1306_DISPLAY_ON);
// request - start TWI
if (SSD1306_SUCCESS != status) {
// error
return status;
}
// success
return SSD1306_SUCCESS;
}
2020-10-13 04:45:09 -04:00
/**
* @desc SSD1306 Send Command
*
2020-11-02 07:33:31 -05:00
* @param void
2020-10-13 04:45:09 -04:00
*
* @return char
*/
2021-04-29 02:47:08 -04:00
char SSD1306_ClearScreen (void)
2020-10-13 04:45:09 -04:00
{
// init status
char status = INIT_STATUS;
2020-11-02 07:33:31 -05:00
short int i = 0;
// update - null col, increment page
status = SSD1306_SetPosition(0, 0);
// request
if (SSD1306_SUCCESS != status) {
// error
return status;
}
2020-10-13 04:45:09 -04:00
// TWI: start & SLAW
// -------------------------
status = SSD1306_Send_StartAndSLAW();
// request - start TWI
if (SSD1306_SUCCESS != status) {
// error
return status;
}
// control byte data stream
// -------------------------
status = TWI_MT_Send_Data(SSD1306_DATA_STREAM);
// request - start TWI
if (SSD1306_SUCCESS != status) {
// error
return status;
}
// erase whole area
// -------------------------
2020-11-02 07:33:31 -05:00
while (i < set_area) {
2020-10-13 04:45:09 -04:00
// send null data
2020-11-02 07:33:31 -05:00
status = TWI_MT_Send_Data(0x00);
2020-10-13 04:45:09 -04:00
// request - start TWI
if (SSD1306_SUCCESS != status) {
// error
return status;
}
2020-11-02 07:33:31 -05:00
// increment
i++;
2020-10-13 04:45:09 -04:00
}
2020-11-02 07:33:31 -05:00
2020-10-13 04:45:09 -04:00
// stop TWI
TWI_Stop();
// success
return SSD1306_SUCCESS;
}
/**
* @desc SSD1306 Set Poisition
*
* @param char
* @param char
*
* @return char
*/
2021-04-29 02:47:08 -04:00
char SSD1306_SetPosition (char x, char y)
2020-10-13 04:45:09 -04:00
{
// variables
char status = INIT_STATUS;
// TWI: start & SLAW
// -------------------------
status = SSD1306_Send_StartAndSLAW();
// request - start TWI
if (SSD1306_SUCCESS != status) {
// error
return status;
}
// SET COLUMN address
// ***************************************************
// set column addr
// -------------------------
status = SSD1306_Send_Command(SSD1306_SET_COLUMN_ADDR);
// request - start TWI
if (SSD1306_SUCCESS != status) {
// error
return status;
}
// start COLUMN
// -------------------------
status = SSD1306_Send_Command(x);
// request - start TWI
if (SSD1306_SUCCESS != status) {
// error
return status;
}
// end COLUMN
// -------------------------
status = SSD1306_Send_Command(END_COLUMN_ADDR);
// request - start TWI
if (SSD1306_SUCCESS != status) {
// error
return status;
}
2020-10-27 08:05:53 -04:00
// update column index
indexCol = x;
2020-10-13 04:45:09 -04:00
// SET PAGE address
// ***************************************************
// set page addr
// -------------------------
status = SSD1306_Send_Command(SSD1306_SET_PAGE_ADDR);
// request - start TWI
if (SSD1306_SUCCESS != status) {
// error
return status;
}
// start PAGE
// -------------------------
status = SSD1306_Send_Command(y);
// request - start TWI
if (SSD1306_SUCCESS != status) {
// error
return status;
}
// end PAGE
// -------------------------
status = SSD1306_Send_Command(END_PAGE_ADDR);
// request - start TWI
if (SSD1306_SUCCESS != status) {
// error
return status;
}
2020-10-27 08:05:53 -04:00
// update column index
indexPage = y;
2020-10-13 04:45:09 -04:00
// stop TWI
TWI_Stop();
// success
return SSD1306_SUCCESS;
}
2020-10-27 08:05:53 -04:00
/**
* @desc SSD1306 Check Text Poisition
*
* @param char
* @param char
*
* @return char
*/
2021-04-29 02:47:08 -04:00
char SSD1306_UpdTxtPosition (void)
2020-10-27 08:05:53 -04:00
{
// init status
char status = INIT_STATUS;
// check end column position
unsigned short int x = indexCol+CHARS_COLS_LENGTH+1;
// check position
if ((x > END_COLUMN_ADDR) && (indexPage > (END_PAGE_ADDR-1))) {
// return out of range
return SSD1306_ERROR;
// if x out reach end but page in range
} else if ((x > END_COLUMN_ADDR) && (indexPage < END_PAGE_ADDR)) {
// update - column
indexCol = 0;
// update - page
indexPage = indexPage+1;
// update - null col, increment page
status = SSD1306_SetPosition(indexCol, indexPage);
// request
if (SSD1306_SUCCESS != status) {
// error
return status;
}
}
// success
return SSD1306_SUCCESS;
}
2020-10-13 04:45:09 -04:00
/**
* @desc Draw character
*
* @param char
*
* @return void
*/
2021-04-29 02:47:08 -04:00
char SSD1306_DrawChar (char character)
2020-10-13 04:45:09 -04:00
{
// variables
2020-11-02 07:33:31 -05:00
uint8_t idxCol = 0;
2020-10-13 04:45:09 -04:00
// init status
char status = INIT_STATUS;
2020-10-27 08:05:53 -04:00
// update text position
if (SSD1306_UpdTxtPosition() != SSD1306_SUCCESS) {
// error
return SSD1306_ERROR;
}
2020-10-13 04:45:09 -04:00
// TWI: start & SLAW
// -------------------------
status = SSD1306_Send_StartAndSLAW();
// request - start TWI
if (SSD1306_SUCCESS != status) {
// error
return status;
}
// control byte data stream
// -------------------------
status = TWI_MT_Send_Data(SSD1306_DATA_STREAM);
// request - start TWI
if (SSD1306_SUCCESS != status) {
// error
return status;
}
// loop through 5 bits
while (idxCol < CHARS_COLS_LENGTH) {
// send control byte data
// -------------------------
status = TWI_MT_Send_Data(pgm_read_byte(&FONTS[character-32][idxCol]));
// request - start TWI
if (SSD1306_SUCCESS != status) {
// error
return status;
}
2020-10-27 08:05:53 -04:00
// increment
2020-10-13 04:45:09 -04:00
idxCol++;
}
2020-10-13 04:48:15 -04:00
// empty column
2020-10-13 04:45:09 -04:00
// -------------------------
status = TWI_MT_Send_Data(CLEAR_COLOR);
// request - start TWI
if (SSD1306_SUCCESS != status) {
// error
return status;
}
2020-10-27 08:05:53 -04:00
// increment global index col
indexCol = indexCol + CHARS_COLS_LENGTH + 1;
2020-10-13 04:45:09 -04:00
// stop TWI
TWI_Stop();
// success
2020-10-27 08:05:53 -04:00
return SSD1306_SUCCESS;
}
/**
* @desc Send 1 Byte of data
*
* @param char
*
* @return void
*/
2021-04-29 02:47:08 -04:00
char SSD1306_SendByte (char data)
2020-10-27 08:05:53 -04:00
{
// init status
char status = INIT_STATUS;
// TWI: start & SLAW
// -------------------------
status = SSD1306_Send_StartAndSLAW();
// request - start TWI
if (SSD1306_SUCCESS != status) {
// error
return status;
}
// control byte data stream
// -------------------------
status = TWI_MT_Send_Data(SSD1306_DATA_STREAM);
// request - start TWI
if (SSD1306_SUCCESS != status) {
// error
return status;
}
// send byte of data
// -------------------------
status = TWI_MT_Send_Data(data);
// request - start TWI
if (SSD1306_SUCCESS != status) {
// error
return status;
}
// increment global index col
indexCol = indexCol + 1;
// stop TWI
TWI_Stop();
// success
return SSD1306_SUCCESS;
}
/**
2020-11-02 07:33:31 -05:00
* @desc Send Same Bytes
2020-10-27 08:05:53 -04:00
*
* @param char
* @param char
*
* @return void
*/
2021-04-29 02:47:08 -04:00
char SSD1306_SendBytes (char data, char length)
2020-10-27 08:05:53 -04:00
{
// index
unsigned short int i = 0;
// init status
char status = INIT_STATUS;
// TWI: start & SLAW
// -------------------------
status = SSD1306_Send_StartAndSLAW();
// request - start TWI
if (SSD1306_SUCCESS != status) {
// error
return status;
}
// control byte data stream
// -------------------------
status = TWI_MT_Send_Data(SSD1306_DATA_STREAM);
// request - start TWI
if (SSD1306_SUCCESS != status) {
// error
return status;
}
// loop through data
while (i++ < length) {
// draw line only within 1 page
if (indexCol < MAX_X) {
// send byte of data
// -------------------------
status = TWI_MT_Send_Data(data);
// request - start TWI
if (SSD1306_SUCCESS != status) {
// error
return status;
}
// increment global index col
indexCol = indexCol + 1;
} else {
// end loop
break;
}
}
// stop TWI
TWI_Stop();
// success
return SSD1306_SUCCESS;
2020-10-13 04:45:09 -04:00
}
/**
* @desc SSD1306 Draw String
*
* @param char *
*
* @return void
*/
2021-04-29 02:47:08 -04:00
char SSD1306_DrawString (char *str)
2020-10-13 04:45:09 -04:00
{
// init status
char status = INIT_STATUS;
// init
int i = 0;
// loop through character of string
while (str[i] != '\0') {
// draw string
status = SSD1306_DrawChar(str[i++]);
// request - start TWI
if (SSD1306_SUCCESS != status) {
// error
return status;
}
}
// success
return SSD1306_SUCCESS;
}
2020-10-27 08:05:53 -04:00
/**
2020-11-02 07:33:31 -05:00
* @desc Draw horizontal line
2020-10-27 08:05:53 -04:00
*
* @param char
* @param char
2020-11-02 07:33:31 -05:00
* @param char
2020-10-27 08:05:53 -04:00
*
* @return void
*/
2021-04-29 02:47:08 -04:00
char SSD1306_DrawLineHorizontal (char x, char y, char len)
2020-10-27 08:05:53 -04:00
{
char page = 0;
char pixel = 0;
2020-11-02 07:33:31 -05:00
// if out of range
2020-10-27 08:05:53 -04:00
if ((x > MAX_X) && (y > MAX_Y)) {
// out of range
return SSD1306_ERROR;
}
2020-11-02 07:33:31 -05:00
// find page
2020-10-27 08:05:53 -04:00
page = y / 8;
// which pixel
pixel |= 1 << (y % 8);
// send position
SSD1306_SetPosition(x, page);
// draw pixel
SSD1306_SendBytes(pixel, len);
// success
return SSD1306_SUCCESS;
}