From bdd2237074718afc542295f1d5741594cbbc2cb9 Mon Sep 17 00:00:00 2001 From: IOsetting Date: Tue, 4 Jan 2022 20:15:35 +0800 Subject: [PATCH] feat: i2c ssd1306 demo --- demo/i2c/ssd1306/ssd1306.c | 335 +++++++++++++++++++++++++++++ demo/i2c/ssd1306/ssd1306.h | 117 ++++++++++ demo/i2c/ssd1306/ssd1306_stc8h3k.c | 101 +++++++++ 3 files changed, 553 insertions(+) create mode 100755 demo/i2c/ssd1306/ssd1306.c create mode 100755 demo/i2c/ssd1306/ssd1306.h create mode 100644 demo/i2c/ssd1306/ssd1306_stc8h3k.c diff --git a/demo/i2c/ssd1306/ssd1306.c b/demo/i2c/ssd1306/ssd1306.c new file mode 100755 index 0000000..e0cdf3d --- /dev/null +++ b/demo/i2c/ssd1306/ssd1306.c @@ -0,0 +1,335 @@ +// Copyright 2021 IOsetting +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "ssd1306.h" + +/* Absolute value */ +#define ABS(x) ((x) > 0 ? (x) : -(x)) + +/* SSD1306 data buffer */ +static __XDATA uint8_t SSD1306_Buffer_all[SSD1306_WIDTH * SSD1306_HEIGHT / 8]; + +/* Private SSD1306 structure */ +typedef struct { + uint16_t CurrentX; + uint16_t CurrentY; + uint8_t Inverted; + uint8_t Initialized; +} SSD1306_t; + +/* Private variable */ +static SSD1306_t SSD1306; + +void SSD1306_WriteCommand(uint8_t command) +{ + I2C_Write(SSD1306_I2C_ADDR, 0x00, &command, 1); +} + +void SSD1306_WriteData(uint8_t dat) +{ + I2C_Write(SSD1306_I2C_ADDR, 0x40, &dat, 1); +} + +void SSD1306_Init(void) +{ + SYS_Delay(100); + + /* Init LCD */ + SSD1306_WriteCommand(0xAE); //display off + /** + * Set the lower start column address of pointer by command 00h~0Fh. + * Set the upper start column address of pointer by command 10h~1Fh. + */ + SSD1306_WriteCommand(0x00); //---set low column address + SSD1306_WriteCommand(0x10); //---set high column address + + /** set contrast control register, 2 bytes, 0x00 - 0xFF */ + SSD1306_WriteCommand(0x81); + SSD1306_WriteCommand(0x7F); + /** 0xA4,Output follows RAM content + * 0xA5,Output ignores RAM content */ + SSD1306_WriteCommand(0xA4); + /** 0xA6, Normal display (RESET) + * 0xA7, Inverse display */ + SSD1306_WriteCommand(0xA6); + /* 0x20,Set Memory Addressing Mode, 2 bytes, + * 0x00,Horizontal Addressing Mode (slide horizontally and goto next page) + * 0x01,Vertical Addressing Mode (slide vertically and goto next column) + * 0x02,Page Addressing Mode (RESET) (slide horizontally and remain in the same page) + * 0x03,Invalid + */ + SSD1306_WriteCommand(0x20); + SSD1306_WriteCommand(0x00); + /** + * Set the page start address of the target display location by command B0h to B7h + * For Page Addressing Mode only + */ + SSD1306_WriteCommand(0xB0); + /** + * Set Page Address, 3 bytes + * For Horizontal and Vertical Addressing Mode only + */ + SSD1306_WriteCommand(0x22); + SSD1306_WriteCommand(0x00); // From Page 0 + SSD1306_WriteCommand(0x07); // To Page 7 + + /** + * COM Output Scan Direction + * 0xC0: normal mode (RESET) Scan from COM0 to COM[N –1] + * 0xC8: remapped mode. Scan from COM[N-1] to COM0 */ + SSD1306_WriteCommand(0xC8); //Set COM Output Scan Direction + /** + * Set display RAM display start line register from 0-63 */ + SSD1306_WriteCommand(0x40); + /** + * Segment Re-map + * 0xA0: column address 0 is mapped to SEG0 (RESET), + * 0xA1: column address 127 is mapped to SEG0 */ + SSD1306_WriteCommand(0xA1); + /** + * Set MUX ratio to N+1 MUX + * N=A[5:0]: from 16MUX to 64MUX, RESET=111111b (i.e. 63d, 64MUX) + * A[5:0] from 0 to 14 are invalid entry.*/ + SSD1306_WriteCommand(0xA8); + SSD1306_WriteCommand(0x3F); + /** + * Set Display Offset, Set vertical shift by COM from 0d~63d + * The value is reset to 00h after RESET */ + SSD1306_WriteCommand(0xD3); + SSD1306_WriteCommand(0x00); // offset in vertical + /** + * Set COM Pins Hardware Configuration + * A[4]=0b, Sequential COM pin configuration + * A[4]=1b(RESET), Alternative COM pin configuration + * A[5]=0b(RESET), Disable COM Left/Right remap + * A[5]=1b, Enable COM Left/Right remap */ + SSD1306_WriteCommand(0xDA); + SSD1306_WriteCommand(0x12); // A[4]=0, A[5]=1 + /** + * Set Display Divide Ratio/Oscillator Frequency + * */ + SSD1306_WriteCommand(0xD5); + SSD1306_WriteCommand(0xF0); // divide ratio + /** + * Set Pre-charge Period */ + SSD1306_WriteCommand(0xD9); + SSD1306_WriteCommand(0x22); + /** + * Set V COMH Deselect Level + * 0x00: 0.65 * Vcc + * 0x10: 0.77 * Vcc (RESET) + * 0x11: 0.83 * Vcc + * */ + SSD1306_WriteCommand(0xDB); + SSD1306_WriteCommand(0x10); + + /** charge pump setting + * 0x10: Disable charge pump(RESET) + * 0x14: Enable charge pump during display on + */ + SSD1306_WriteCommand(0x8D); + SSD1306_WriteCommand(0x14); + + /** 0xAE, Display OFF (sleep mode), + * 0xAF, Display ON in normal mode */ + SSD1306_WriteCommand(0xAF); + + /* Clear screen */ + SSD1306_Fill(SSD1306_COLOR_BLACK); + + /* Update screen */ + SSD1306_UpdateScreen(); + + /* Set default values */ + SSD1306.CurrentX = 0; + SSD1306.CurrentY = 0; + + /* Initialized OK */ + SSD1306.Initialized = 1; +} + +void SSD1306_UpdateScreen(void) +{ + I2C_Write(SSD1306_I2C_ADDR, 0x40, SSD1306_Buffer_all, SSD1306_WIDTH * SSD1306_HEIGHT / 8); +} + +void SSD1306_ToggleInvert(void) +{ + uint16_t i; + + /* Toggle invert */ + SSD1306.Inverted = !SSD1306.Inverted; + + /* Do memory toggle */ + for (i = 0; i < sizeof(SSD1306_Buffer_all); i++) + { + SSD1306_Buffer_all[i] = ~SSD1306_Buffer_all[i]; + } +} + +void SSD1306_Fill(uint8_t color) +{ + if (SSD1306.Inverted) + { + color = (uint8_t)!color; + } + /* Set memory */ + memset(SSD1306_Buffer_all, (color == SSD1306_COLOR_BLACK) ? 0x00 : 0xFF, SSD1306_WIDTH * SSD1306_HEIGHT / 8); +} + +void SSD1306_DrawPixel(uint16_t x, uint16_t y, uint8_t color) +{ + if (x >= SSD1306_WIDTH || y >= SSD1306_HEIGHT) + { + /* Error */ + return; + } + + /* Check if pixels are inverted */ + if (SSD1306.Inverted) + { + color = (uint8_t)!color; + } + + /* Set color */ + if (color == SSD1306_COLOR_WHITE) + { + SSD1306_Buffer_all[x + (y / 8) * SSD1306_WIDTH] |= 1 << (y % 8); + } + else + { + SSD1306_Buffer_all[x + (y / 8) * SSD1306_WIDTH] &= ~(1 << (y % 8)); + } +} + +void SSD1306_GotoXY(uint16_t x, uint16_t y) +{ + /* Set write pointers */ + SSD1306.CurrentX = x; + SSD1306.CurrentY = y; +} + +void SSD1306_DrawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint8_t c) +{ + int16_t dx, dy, sx, sy, err, e2, i, tmp; + + /* Check for overflow */ + if (x0 >= SSD1306_WIDTH) + { + x0 = SSD1306_WIDTH - 1; + } + if (x1 >= SSD1306_WIDTH) + { + x1 = SSD1306_WIDTH - 1; + } + if (y0 >= SSD1306_HEIGHT) + { + y0 = SSD1306_HEIGHT - 1; + } + if (y1 >= SSD1306_HEIGHT) + { + y1 = SSD1306_HEIGHT - 1; + } + + dx = (x0 < x1) ? (x1 - x0) : (x0 - x1); + dy = (y0 < y1) ? (y1 - y0) : (y0 - y1); + sx = (x0 < x1) ? 1 : -1; + sy = (y0 < y1) ? 1 : -1; + err = ((dx > dy) ? dx : -dy) / 2; + + if (dx == 0) + { + if (y1 < y0) + { + tmp = y1; + y1 = y0; + y0 = tmp; + } + + if (x1 < x0) + { + tmp = x1; + x1 = x0; + x0 = tmp; + } + + /* Vertical line */ + for (i = y0; i <= y1; i++) + { + SSD1306_DrawPixel(x0, i, c); + } + + /* Return from function */ + return; + } + + if (dy == 0) + { + if (y1 < y0) + { + tmp = y1; + y1 = y0; + y0 = tmp; + } + + if (x1 < x0) + { + tmp = x1; + x1 = x0; + x0 = tmp; + } + + /* Horizontal line */ + for (i = x0; i <= x1; i++) + { + SSD1306_DrawPixel(i, y0, c); + } + + /* Return from function */ + return; + } + + while (1) + { + SSD1306_DrawPixel(x0, y0, c); + if (x0 == x1 && y0 == y1) + { + break; + } + e2 = err; + if (e2 > -dx) + { + err -= dy; + x0 += sx; + } + if (e2 < dy) + { + err += dx; + y0 += sy; + } + } +} + +void SSD1306_ON(void) +{ + SSD1306_WriteCommand(0x8D); + SSD1306_WriteCommand(0x14); + SSD1306_WriteCommand(0xAF); +} +void SSD1306_OFF(void) +{ + SSD1306_WriteCommand(0x8D); + SSD1306_WriteCommand(0x10); + SSD1306_WriteCommand(0xAE); +} diff --git a/demo/i2c/ssd1306/ssd1306.h b/demo/i2c/ssd1306/ssd1306.h new file mode 100755 index 0000000..34735b3 --- /dev/null +++ b/demo/i2c/ssd1306/ssd1306.h @@ -0,0 +1,117 @@ +// Copyright 2021 IOsetting +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef __FW_SSD1306_H__ +#define __FW_SSD1306_H__ + + +#include "fw_hal.h" +#include "string.h" + +/* I2C address +* address: 7 bit slave address, left aligned, bits 7:1 are used, LSB bit is not used +*/ +#ifndef SSD1306_I2C_ADDR +#define SSD1306_I2C_ADDR 0x78 +//#define SSD1306_I2C_ADDR 0x7A +#endif + +/* SSD1306 settings */ +/* SSD1306 width in pixels */ +#ifndef SSD1306_WIDTH +#define SSD1306_WIDTH 128 +#endif +/* SSD1306 LCD height in pixels */ +#ifndef SSD1306_HEIGHT +#define SSD1306_HEIGHT 64 +#endif + +#ifndef SSD1306_TIMEOUT +#define SSD1306_TIMEOUT 20000 +#endif + +/*!< Black color, no pixel */ +#define SSD1306_COLOR_BLACK 0x00 +/*!< Pixel is set. Color depends on LCD */ +#define SSD1306_COLOR_WHITE 0x01 + + +/** + * @brief Writes single byte command to slave + * @param command: command to be written + */ +void SSD1306_WriteCommand(uint8_t command); + +/** + * @brief Writes single byte data to slave + * @param dat: data to be written + */ +void SSD1306_WriteData(uint8_t dat); + +/** + * Initializes SSD1306 LCD + */ +void SSD1306_Init(void); + +/** + * @brief Updates buffer from internal RAM to LCD + * @note This function must be called each time you do some changes to LCD, to update buffer from RAM to LCD + */ +void SSD1306_UpdateScreen(void); + +/** + * @brief Toggles pixels invertion inside internal RAM + * @note @ref SSD1306_UpdateScreen() must be called after that in order to see updated LCD screen + */ +void SSD1306_ToggleInvert(void); + +/** + * @brief Fills entire LCD with desired color + * @note @ref SSD1306_UpdateScreen() must be called after that in order to see updated LCD screen + * @param Color: Color to be used for screen fill. This parameter can be a value of @ref SSD1306_COLOR_t enumeration + */ +void SSD1306_Fill(uint8_t Color); + +/** + * @brief Draws pixel at desired location + * @note @ref SSD1306_UpdateScreen() must called after that in order to see updated LCD screen + * @param x: X location. This parameter can be a value between 0 and SSD1306_WIDTH - 1 + * @param y: Y location. This parameter can be a value between 0 and SSD1306_HEIGHT - 1 + * @param color: Color to be used for screen fill. This parameter can be a value of @ref SSD1306_COLOR_t enumeration + * @retval None + */ +void SSD1306_DrawPixel(uint16_t x, uint16_t y, uint8_t color); + +/** + * @brief Sets cursor pointer to desired location for strings + * @param x: X location. This parameter can be a value between 0 and SSD1306_WIDTH - 1 + * @param y: Y location. This parameter can be a value between 0 and SSD1306_HEIGHT - 1 + * @retval None + */ +void SSD1306_GotoXY(uint16_t x, uint16_t y); + +/** + * @brief Draws line on LCD + * @note @ref SSD1306_UpdateScreen() must be called after that in order to see updated LCD screen + * @param x0: Line X start point. Valid input is 0 to SSD1306_WIDTH - 1 + * @param y0: Line Y start point. Valid input is 0 to SSD1306_HEIGHT - 1 + * @param x1: Line X end point. Valid input is 0 to SSD1306_WIDTH - 1 + * @param y1: Line Y end point. Valid input is 0 to SSD1306_HEIGHT - 1 + * @param c: Color to be used. This parameter can be a value of @ref SSD1306_COLOR_t enumeration + * @retval None + */ +void SSD1306_DrawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint8_t c); + + +#endif diff --git a/demo/i2c/ssd1306/ssd1306_stc8h3k.c b/demo/i2c/ssd1306/ssd1306_stc8h3k.c new file mode 100644 index 0000000..84d2d77 --- /dev/null +++ b/demo/i2c/ssd1306/ssd1306_stc8h3k.c @@ -0,0 +1,101 @@ +// Copyright 2021 IOsetting +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/*** + * Demo: SSD1306 12864 OLED + * Board: STC8H3K32 + * + * P32 -> SCL + * P33 -> SDA + * GND -> GND + * 3.3V -> VCC + */ + +#include "fw_hal.h" +#include "ssd1306.h" + +void I2C_Init(void) +{ + // Master mode + I2C_SetWorkMode(I2C_WorkMode_Master); + /** + * I2C clock = SYSCLK / 2 / (__prescaler__ * 2 + 4) + * SSD1306 works with i2c clock up to 1.3 MHz, beyond this value, display may fail. + */ + I2C_SetClockPrescaler(0x10); + // Switch alternative port + I2C_SetPort(I2C_AlterPort_P32_P33); + // Start I2C + I2C_SetEnabled(HAL_State_ON); +} + +void GPIO_Init(void) +{ + // SDA + GPIO_P3_SetMode(GPIO_Pin_3, GPIO_Mode_InOut_QBD); + // SCL + GPIO_P3_SetMode(GPIO_Pin_2, GPIO_Mode_Output_PP); +} + +int main(void) +{ + int y1, y2; + uint8_t d1, d2; + + SYS_SetClock(); + GPIO_Init(); + I2C_Init(); + SSD1306_Init(); + + while(1) + { + SSD1306_DrawLine(0, 0, 127, 0, 1); + SSD1306_DrawLine(0, 0, 0, 63, 1); + SSD1306_DrawLine(127, 0, 127, 63, 1); + SSD1306_DrawLine(0, 63, 127, 63, 1); + SSD1306_UpdateScreen(); // display + SYS_Delay(1000); + + SSD1306_Fill(0); + + SSD1306_ToggleInvert(); // Invert display + SSD1306_UpdateScreen(); + SYS_Delay(1000); + + SSD1306_ToggleInvert(); // Invert display + SSD1306_UpdateScreen(); + SYS_Delay(1000); + + y1 = 64, y2 = 0; + while (y1 > 0) + { + SSD1306_DrawLine(0, y1, 127, y2, 1); + SSD1306_UpdateScreen(); + y1 -= 2; + y2 += 2; + } + SYS_Delay(1000); + + SSD1306_Fill(0); + y1 = 127, y2 = 0; + while (y1 > 0) + { + SSD1306_DrawLine(y1, 0, y2, 63, 1); + SSD1306_UpdateScreen(); + y1 -= 2; + y2 += 2; + } + SYS_Delay(1000); + } +}