211 lines
5.9 KiB
C
211 lines
5.9 KiB
C
#include "ssd1306.h"
|
|
|
|
|
|
// Screenbuffer
|
|
static uint8_t SSD1306_Buffer[SSD1306_WIDTH * SSD1306_HEIGHT / 8];
|
|
|
|
// Screen object
|
|
static SSD1306_t SSD1306;
|
|
|
|
|
|
//
|
|
// Send a byte to the command register
|
|
//
|
|
static uint8_t ssd1306_WriteCommand(I2C_HandleTypeDef *hi2c, uint8_t command)
|
|
{
|
|
return HAL_I2C_Mem_Write(hi2c, SSD1306_I2C_ADDR, 0x00, 1, &command, 1, 10);
|
|
}
|
|
|
|
|
|
//
|
|
// Initialize the oled screen
|
|
//
|
|
uint8_t ssd1306_Init(I2C_HandleTypeDef *hi2c)
|
|
{
|
|
// Wait for the screen to boot
|
|
HAL_Delay(100);
|
|
int status = 0;
|
|
|
|
// Init LCD
|
|
status += ssd1306_WriteCommand(hi2c, 0xAE); // Display off
|
|
status += ssd1306_WriteCommand(hi2c, 0x20); // Set Memory Addressing Mode
|
|
status += ssd1306_WriteCommand(hi2c, 0x10); // 00,Horizontal Addressing Mode;01,Vertical Addressing Mode;10,Page Addressing Mode (RESET);11,Invalid
|
|
status += ssd1306_WriteCommand(hi2c, 0xB0); // Set Page Start Address for Page Addressing Mode,0-7
|
|
status += ssd1306_WriteCommand(hi2c, 0xC8); // Set COM Output Scan Direction
|
|
status += ssd1306_WriteCommand(hi2c, 0x00); // Set low column address
|
|
status += ssd1306_WriteCommand(hi2c, 0x10); // Set high column address
|
|
status += ssd1306_WriteCommand(hi2c, 0x40); // Set start line address
|
|
status += ssd1306_WriteCommand(hi2c, 0x81); // set contrast control register
|
|
status += ssd1306_WriteCommand(hi2c, 0xFF);
|
|
status += ssd1306_WriteCommand(hi2c, 0xA1); // Set segment re-map 0 to 127
|
|
status += ssd1306_WriteCommand(hi2c, 0xA6); // Set normal display
|
|
status += ssd1306_WriteCommand(hi2c, 0xA8); // Set multiplex ratio(1 to 64)
|
|
status += ssd1306_WriteCommand(hi2c, 0x3F);
|
|
status += ssd1306_WriteCommand(hi2c, 0xA4); // 0xa4,Output follows RAM content;0xa5,Output ignores RAM content
|
|
status += ssd1306_WriteCommand(hi2c, 0xD3); // Set display offset
|
|
status += ssd1306_WriteCommand(hi2c, 0x00); // No offset
|
|
status += ssd1306_WriteCommand(hi2c, 0xD5); // Set display clock divide ratio/oscillator frequency
|
|
status += ssd1306_WriteCommand(hi2c, 0xF0); // Set divide ratio
|
|
status += ssd1306_WriteCommand(hi2c, 0xD9); // Set pre-charge period
|
|
status += ssd1306_WriteCommand(hi2c, 0x22);
|
|
status += ssd1306_WriteCommand(hi2c, 0xDA); // Set com pins hardware configuration
|
|
status += ssd1306_WriteCommand(hi2c, 0x12);
|
|
status += ssd1306_WriteCommand(hi2c, 0xDB); // Set vcomh
|
|
status += ssd1306_WriteCommand(hi2c, 0x20); // 0x20,0.77xVcc
|
|
status += ssd1306_WriteCommand(hi2c, 0x8D); // Set DC-DC enable
|
|
status += ssd1306_WriteCommand(hi2c, 0x14); //
|
|
status += ssd1306_WriteCommand(hi2c, 0xAF); // Turn on SSD1306 panel
|
|
|
|
if (status != 0) {
|
|
return 1;
|
|
}
|
|
|
|
// Clear screen
|
|
ssd1306_Fill(Black);
|
|
|
|
// Flush buffer to screen
|
|
ssd1306_UpdateScreen(hi2c);
|
|
|
|
// Set default values for screen object
|
|
SSD1306.CurrentX = 0;
|
|
SSD1306.CurrentY = 0;
|
|
|
|
SSD1306.Initialized = 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// Fill the whole screen with the given color
|
|
//
|
|
void ssd1306_Fill(SSD1306_COLOR color)
|
|
{
|
|
// Fill screenbuffer with a constant value (color)
|
|
uint32_t i;
|
|
|
|
for(i = 0; i < sizeof(SSD1306_Buffer); i++)
|
|
{
|
|
SSD1306_Buffer[i] = (color == Black) ? 0x00 : 0xFF;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Write the screenbuffer with changed to the screen
|
|
//
|
|
void ssd1306_UpdateScreen(I2C_HandleTypeDef *hi2c)
|
|
{
|
|
uint8_t i;
|
|
|
|
for (i = 0; i < 8; i++) {
|
|
ssd1306_WriteCommand(hi2c, 0xB0 + i);
|
|
ssd1306_WriteCommand(hi2c, 0x00);
|
|
ssd1306_WriteCommand(hi2c, 0x10);
|
|
|
|
HAL_I2C_Mem_Write(hi2c, SSD1306_I2C_ADDR, 0x40, 1, &SSD1306_Buffer[SSD1306_WIDTH * i], SSD1306_WIDTH, 100);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Draw one pixel in the screenbuffer
|
|
// X => X Coordinate
|
|
// Y => Y Coordinate
|
|
// color => Pixel color
|
|
//
|
|
void ssd1306_DrawPixel(uint8_t x, uint8_t y, SSD1306_COLOR color)
|
|
{
|
|
if (x >= SSD1306_WIDTH || y >= SSD1306_HEIGHT)
|
|
{
|
|
// Don't write outside the buffer
|
|
return;
|
|
}
|
|
|
|
// Check if pixel should be inverted
|
|
if (SSD1306.Inverted)
|
|
{
|
|
color = (SSD1306_COLOR)!color;
|
|
}
|
|
|
|
// Draw in the correct color
|
|
if (color == White)
|
|
{
|
|
SSD1306_Buffer[x + (y / 8) * SSD1306_WIDTH] |= 1 << (y % 8);
|
|
}
|
|
else
|
|
{
|
|
SSD1306_Buffer[x + (y / 8) * SSD1306_WIDTH] &= ~(1 << (y % 8));
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Draw 1 char to the screen buffer
|
|
// ch => Character to write
|
|
// Font => Font to use
|
|
// color => Black or White
|
|
//
|
|
char ssd1306_WriteChar(char ch, FontDef Font, SSD1306_COLOR color)
|
|
{
|
|
uint32_t i, b, j;
|
|
|
|
// Check remaining space on current line
|
|
if (SSD1306_WIDTH <= (SSD1306.CurrentX + Font.FontWidth) ||
|
|
SSD1306_HEIGHT <= (SSD1306.CurrentY + Font.FontHeight))
|
|
{
|
|
// Not enough space on current line
|
|
return 0;
|
|
}
|
|
|
|
// Translate font to screenbuffer
|
|
for (i = 0; i < Font.FontHeight; i++)
|
|
{
|
|
b = Font.data[(ch - 32) * Font.FontHeight + i];
|
|
for (j = 0; j < Font.FontWidth; j++)
|
|
{
|
|
if ((b << j) & 0x8000)
|
|
{
|
|
ssd1306_DrawPixel(SSD1306.CurrentX + j, (SSD1306.CurrentY + i), (SSD1306_COLOR) color);
|
|
}
|
|
else
|
|
{
|
|
ssd1306_DrawPixel(SSD1306.CurrentX + j, (SSD1306.CurrentY + i), (SSD1306_COLOR)!color);
|
|
}
|
|
}
|
|
}
|
|
|
|
// The current space is now taken
|
|
SSD1306.CurrentX += Font.FontWidth;
|
|
|
|
// Return written char for validation
|
|
return ch;
|
|
}
|
|
|
|
//
|
|
// Write full string to screenbuffer
|
|
//
|
|
char ssd1306_WriteString(char* str, FontDef Font, SSD1306_COLOR color)
|
|
{
|
|
// Write until null-byte
|
|
while (*str)
|
|
{
|
|
if (ssd1306_WriteChar(*str, Font, color) != *str)
|
|
{
|
|
// Char could not be written
|
|
return *str;
|
|
}
|
|
|
|
// Next char
|
|
str++;
|
|
}
|
|
|
|
// Everything ok
|
|
return *str;
|
|
}
|
|
|
|
//
|
|
// Set cursor position
|
|
//
|
|
void ssd1306_SetCursor(uint8_t x, uint8_t y)
|
|
{
|
|
SSD1306.CurrentX = x;
|
|
SSD1306.CurrentY = y;
|
|
} |