W tym poście chciałbym opisać sposób obsługi wyświetlacza Oled ze sterownikiem SSD1306. Komunikacja następuje przez interfejs I2C.
Program został przetestowany na wyświetlaczu 128x64.
Przygotowanie projektu CubeMx:
Tutaj jedyne co trzeba obowiązkowo uruchomić to interfejs I2C.
Program:
Poniżej opiszę kilka funkcji zawartych w programie.
Wygenerowana inicjalizacja interfejsu I2C:
- static void MX_I2C1_Init(void)
- {
- /* USER CODE BEGIN I2C1_Init 0 */
- /* USER CODE END I2C1_Init 0 */
- /* USER CODE BEGIN I2C1_Init 1 */
- /* USER CODE END I2C1_Init 1 */
- hi2c1.Instance = I2C1;
- hi2c1.Init.ClockSpeed = 400000;
- hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2;
- hi2c1.Init.OwnAddress1 = 0;
- hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
- hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
- hi2c1.Init.OwnAddress2 = 0;
- hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
- hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
- if (HAL_I2C_Init(&hi2c1) != HAL_OK)
- {
- Error_Handler();
- }
- /* USER CODE BEGIN I2C1_Init 2 */
- /* USER CODE END I2C1_Init 2 */
- }
Uruchomienie wyświetlacza:
- uint8_t SSD1306_Init(void) {
- if (HAL_I2C_IsDeviceReady(&hi2c1, SSD1306_I2C_ADDR, 1, 20000) != HAL_OK) { return 0; }
- /* Init LCD */
- SSD1306_WriteCommand(SSD1306_DISPLAYOFF);
- SSD1306_WriteCommand(SSD1306_MEMORYMODE);
- SSD1306_WriteCommand(SSD1306_SETHIGHCOLUMN);
- SSD1306_WriteCommand(0xB0); //Set Page Start Address for Page Addressing Mode
- SSD1306_WriteCommand(SSD1306_COMSCANDEC);
- SSD1306_WriteCommand(0x00); //Set lower column start address for page addressing mode
- SSD1306_WriteCommand(0x10); //Set higher column start address for page addressing mode
- SSD1306_WriteCommand(0x40); //Start adres
- SSD1306_WriteCommand(0x81); //--set contrast control register
- SSD1306_WriteCommand(0xFF);
- SSD1306_WriteCommand(0xA1); //--set segment re-map 0 to 127
- SSD1306_WriteCommand(0xA6); //--set normal display
- SSD1306_WriteCommand(SSD1306_SETMULTIPLEX);
- SSD1306_WriteCommand(0x3F);
- SSD1306_WriteCommand(SSD1306_DISPLAYALLON_RESUME);
- SSD1306_WriteCommand(SSD1306_SETDISPLAYOFFSET); //-set display offset
- SSD1306_WriteCommand(0x00); //NO OFFSET
- SSD1306_WriteCommand(0xD5); //--set display clock divide ratio/oscillator frequency
- SSD1306_WriteCommand(0xF0); //--set divide ratio
- SSD1306_WriteCommand(SSD1306_SETPRECHARGE);
- SSD1306_WriteCommand(SSD1306_PAGEADDR);
- SSD1306_WriteCommand(SSD1306_SETCOMPINS);
- SSD1306_WriteCommand(0x12);
- SSD1306_WriteCommand(SSD1306_SETVCOMDETECT);
- SSD1306_WriteCommand(0x20); //0x20,0.77xVcc
- SSD1306_WriteCommand(SSD1306_CHARGEPUMP);
- SSD1306_WriteCommand(0x14); //
- SSD1306_WriteCommand(SSD1306_DISPLAYON);
- SSD1306_WriteCommand(SSD1306_DEACTIVATE_SCROLL);
- SSD1306_FillBuffer(SSD1306_COLOR_BLACK);
- SSD1306_UpdateScreen();
- SSD1306_GoToPosition(0, 0);
- return 1;
- }
Powyżej jednym z elementów jest ustawienie początkowy adres w pamięci:
- Set Lower Column Start Address for Page Addressing Mode - 0x00 do 0x0F - zapisuje ona cztery młodsze bity licznika w pamięci RAM. Wykorzystywane jest to do adresowania stron.
- Set Higher Column Start Address for Page Addressing Mode - 0x10 do 0x1F - zapis czterech starszych bitów licznika.
- Set Page Address for Page Addressing Mode - 0xB0 do 0xB7 - Ustawienie licznika adresu stron w pamięci RAM.
Po zapisaniu licznika każdy dostęp wykonuje inkementację licznika. Po osiągnięciu maksymalnej wartości licznik zostaje wyzerowany. W bibliotece wykorzystywany jest tryb adresowania Page Addressing Mode.
Pozostałe opisy rejestrów można znaleźć w dokumentacji do sterownika wyświetlacza :
Funkcje obsługujące transfer danych do urządzenia:
- void SSD1306_WriteCommand(uint8_t data)
- {
- uint8_t dataToSend[2];
- dataToSend[0] = 0x00; //REGISTER
- dataToSend[1] = data;
- HAL_I2C_Master_Transmit(&hi2c1, SSD1306_I2C_ADDR, dataToSend, 2, 10);
- }
- void __attribute__((unused)) SSD1306_WriteData(uint8_t data)
- {
- uint8_t dataToSend[2];
- dataToSend[0] = 0x40; //Register
- dataToSend[1] = data;
- HAL_I2C_Master_Transmit(&hi2c1, SSD1306_I2C_ADDR, dataToSend, 2, 10);
- }
- void SSD1306_WriteMultipleData(uint8_t address, uint8_t registerToWrite, uint8_t* dataToWrite, uint16_t dataCount) {
- uint8_t data[256];
- data[0] = registerToWrite;
- for(uint8_t i = 0; i < dataCount; i++) {
- data[i+1] = dataToWrite[i];
- }
- HAL_I2C_Master_Transmit(&hi2c1, address, data, dataCount+1, 10);
- }
Dane przesłane do wyświetlacza są przechowywane w statycznej pamięci RAM. Dla większego wyświetlacza wynosi ona 128x64 bity. Każdy z bitów odpowiada za jeden piksel znajdujący się na matrycy. Pamięć została podzielona na 8 stron. Oznacza to, że pojedyncza strona zawiera 128 bajtów.
Podczas aktualizacji danych w wyświetlaczu przesyłana jest pełna tablica z danymi która przechodzi pomiędzy poszczególnymi stronami:
- void SSD1306_UpdateScreen(void) {
- for (uint8_t i = 0; i < 8; i++) {
- SSD1306_WriteCommand(0xB0 + i);
- SSD1306_WriteCommand(0x00);
- SSD1306_WriteCommand(0x10);
- SSD1306_WriteMultipleData(SSD1306_I2C_ADDR, 0x40, &SSD1306_Buffer[SSD1306_WIDTH * i], SSD1306_WIDTH);
- }
- }
Projekt został dodatkowo wyposażony w funkcje pozwalające na:
- //Wyświetlenie znaku oraz ciągu znaków
- uint8_t SSD1306_DisplayChar(char ch, FontTypedef* Font, SSD1306_COLOR_t color);
- uint8_t SSD1306_DisplayString(char* str, FontTypedef* Font, SSD1306_COLOR_t color);
- //Przesunięcie obrazu na wyświetlaczu
- void SSD1306_ScrollRightDisplay(uint8_t start, uint8_t stop);
- void SSD1306_ScrollLeftDisplay(uint8_t start, uint8_t stop);
- void SSD1306_ScrollDiagRightDisplay(uint8_t start, uint8_t stop);
- void SSD1306_ScrollDiagLeftDisplay(uint8_t start, uint8_t stop);
- void SSD1306_StopScroll(void);
- //Wyświetlenie obrazu oraz figur geometrycznych
- void SSD1306_DrawBitmap(int16_t posX, int16_t posY, const unsigned char* bitmap, int16_t width, int16_t height, SSD1306_COLOR_t color);
- void SSD1306_DrawPixel(uint16_t posX, uint16_t posY, SSD1306_COLOR_t color);
- void SSD1306_DrawLine(uint16_t posXStart, uint16_t posYStart, uint16_t posXEnd, uint16_t posYEnd, SSD1306_COLOR_t color);
- void SSD1306_DrawRectangle(uint16_t posX, uint16_t posY, uint16_t width, uint16_t height, SSD1306_COLOR_t color);
- void SSD1306_DrawFilledRectangle(uint16_t posX, uint16_t posY, uint16_t width, uint16_t height, SSD1306_COLOR_t color);
- void SSD1306_DrawCircle(int16_t posX, int16_t posY, int16_t rad, SSD1306_COLOR_t color);
- void SSD1306_DrawFilledCircle(int16_t posX, int16_t posY, int16_t rad, SSD1306_COLOR_t color);
- void SSD1306_DrawTriangle(uint16_t posX1, uint16_t posY1, uint16_t posX2, uint16_t posY2, uint16_t posX3, uint16_t posY3, SSD1306_COLOR_t color);
- void SSD1306_DrawFilledTriangle(uint16_t posX1, uint16_t posY1, uint16_t posX2, uint16_t posY2, uint16_t posX3, uint16_t posY3, SSD1306_COLOR_t color);
- //Pozostałe
- void SSD1306_GoToPosition(uint16_t posX, uint16_t posY);
- void SSD1306_ClearScrean(void);
- void SSD1306_GotoXY(uint16_t x, uint16_t y);
- void SSD1306_FillBuffer(SSD1306_COLOR_t color);