sobota, 22 sierpnia 2020

[16] STM32F1 - HAL - OLED SSD1306 I2C

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:

  1. static void MX_I2C1_Init(void)
  2. {
  3.  
  4.   /* USER CODE BEGIN I2C1_Init 0 */
  5.  
  6.   /* USER CODE END I2C1_Init 0 */
  7.  
  8.   /* USER CODE BEGIN I2C1_Init 1 */
  9.  
  10.   /* USER CODE END I2C1_Init 1 */
  11.   hi2c1.Instance = I2C1;
  12.   hi2c1.Init.ClockSpeed = 400000;
  13.   hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2;
  14.   hi2c1.Init.OwnAddress1 = 0;
  15.   hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
  16.   hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
  17.   hi2c1.Init.OwnAddress2 = 0;
  18.   hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
  19.   hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
  20.   if (HAL_I2C_Init(&hi2c1) != HAL_OK)
  21.   {
  22.     Error_Handler();
  23.   }
  24.   /* USER CODE BEGIN I2C1_Init 2 */
  25.  
  26.   /* USER CODE END I2C1_Init 2 */
  27.  
  28. }

Uruchomienie wyświetlacza:

  1. uint8_t SSD1306_Init(void) {
  2.     if (HAL_I2C_IsDeviceReady(&hi2c1, SSD1306_I2C_ADDR, 1, 20000) != HAL_OK) { return 0; }
  3.  
  4.     /* Init LCD */
  5.     SSD1306_WriteCommand(SSD1306_DISPLAYOFF);
  6.     SSD1306_WriteCommand(SSD1306_MEMORYMODE);
  7.     SSD1306_WriteCommand(SSD1306_SETHIGHCOLUMN);
  8.     SSD1306_WriteCommand(0xB0); //Set Page Start Address for Page Addressing Mode
  9.     SSD1306_WriteCommand(SSD1306_COMSCANDEC);
  10.     SSD1306_WriteCommand(0x00); //Set lower column start address for page addressing mode
  11.     SSD1306_WriteCommand(0x10); //Set higher column start address for page addressing mode
  12.     SSD1306_WriteCommand(0x40); //Start adres
  13.     SSD1306_WriteCommand(0x81); //--set contrast control register
  14.     SSD1306_WriteCommand(0xFF);
  15.     SSD1306_WriteCommand(0xA1); //--set segment re-map 0 to 127
  16.     SSD1306_WriteCommand(0xA6); //--set normal display
  17.     SSD1306_WriteCommand(SSD1306_SETMULTIPLEX);
  18.     SSD1306_WriteCommand(0x3F);
  19.     SSD1306_WriteCommand(SSD1306_DISPLAYALLON_RESUME);
  20.     SSD1306_WriteCommand(SSD1306_SETDISPLAYOFFSET); //-set display offset
  21.     SSD1306_WriteCommand(0x00); //NO OFFSET
  22.     SSD1306_WriteCommand(0xD5); //--set display clock divide ratio/oscillator frequency
  23.     SSD1306_WriteCommand(0xF0); //--set divide ratio
  24.     SSD1306_WriteCommand(SSD1306_SETPRECHARGE);
  25.     SSD1306_WriteCommand(SSD1306_PAGEADDR);
  26.     SSD1306_WriteCommand(SSD1306_SETCOMPINS);
  27.     SSD1306_WriteCommand(0x12);
  28.     SSD1306_WriteCommand(SSD1306_SETVCOMDETECT);
  29.     SSD1306_WriteCommand(0x20); //0x20,0.77xVcc
  30.     SSD1306_WriteCommand(SSD1306_CHARGEPUMP);
  31.     SSD1306_WriteCommand(0x14); //
  32.     SSD1306_WriteCommand(SSD1306_DISPLAYON);
  33.     SSD1306_WriteCommand(SSD1306_DEACTIVATE_SCROLL);
  34.  
  35.     SSD1306_FillBuffer(SSD1306_COLOR_BLACK);
  36.     SSD1306_UpdateScreen();
  37.     SSD1306_GoToPosition(0, 0);
  38.  
  39.     return 1;
  40. }

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:

  1. void SSD1306_WriteCommand(uint8_t data)
  2. {
  3.     uint8_t dataToSend[2];
  4.     dataToSend[0] = 0x00; //REGISTER
  5.     dataToSend[1] = data;
  6.     HAL_I2C_Master_Transmit(&hi2c1, SSD1306_I2C_ADDR, dataToSend, 2, 10);
  7. }
  8.  
  9. void __attribute__((unused)) SSD1306_WriteData(uint8_t data)
  10. {
  11.     uint8_t dataToSend[2];
  12.     dataToSend[0] = 0x40;   //Register
  13.     dataToSend[1] = data;
  14.     HAL_I2C_Master_Transmit(&hi2c1, SSD1306_I2C_ADDR, dataToSend, 2, 10);
  15. }
  16.  
  17. void SSD1306_WriteMultipleData(uint8_t address, uint8_t registerToWrite, uint8_t* dataToWrite, uint16_t dataCount) {
  18.     uint8_t data[256];
  19.     data[0] = registerToWrite;
  20.  
  21.     for(uint8_t i = 0; i < dataCount; i++) {
  22.         data[i+1] = dataToWrite[i];
  23.     }
  24.  
  25.     HAL_I2C_Master_Transmit(&hi2c1, address, data, dataCount+1, 10);
  26. }

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:

  1. void SSD1306_UpdateScreen(void) {
  2.     for (uint8_t i = 0; i < 8; i++) {
  3.         SSD1306_WriteCommand(0xB0 + i);
  4.         SSD1306_WriteCommand(0x00);
  5.         SSD1306_WriteCommand(0x10);
  6.        
  7.     SSD1306_WriteMultipleData(SSD1306_I2C_ADDR, 0x40, &SSD1306_Buffer[SSD1306_WIDTH * i], SSD1306_WIDTH);
  8.     }
  9. }

Projekt został dodatkowo wyposażony w funkcje pozwalające na:

  1. //Wyświetlenie znaku oraz ciągu znaków
  2. uint8_t SSD1306_DisplayChar(char ch, FontTypedef* Font, SSD1306_COLOR_t color);
  3. uint8_t SSD1306_DisplayString(char* str, FontTypedef* Font, SSD1306_COLOR_t color);
  4. //Przesunięcie obrazu na wyświetlaczu
  5. void SSD1306_ScrollRightDisplay(uint8_t start, uint8_t stop);
  6. void SSD1306_ScrollLeftDisplay(uint8_t start, uint8_t stop);
  7. void SSD1306_ScrollDiagRightDisplay(uint8_t start, uint8_t stop);
  8. void SSD1306_ScrollDiagLeftDisplay(uint8_t start, uint8_t stop);
  9. void SSD1306_StopScroll(void);
  10. //Wyświetlenie obrazu oraz figur geometrycznych
  11. void SSD1306_DrawBitmap(int16_t posX, int16_t posY, const unsigned char* bitmap, int16_t width, int16_t height, SSD1306_COLOR_t color);
  12. void SSD1306_DrawPixel(uint16_t posX, uint16_t posY, SSD1306_COLOR_t color);
  13. void SSD1306_DrawLine(uint16_t posXStart, uint16_t posYStart, uint16_t posXEnd, uint16_t posYEnd, SSD1306_COLOR_t color);
  14. void SSD1306_DrawRectangle(uint16_t posX, uint16_t posY, uint16_t width, uint16_t height, SSD1306_COLOR_t color);
  15. void SSD1306_DrawFilledRectangle(uint16_t posX, uint16_t posY, uint16_t width, uint16_t height, SSD1306_COLOR_t color);
  16. void SSD1306_DrawCircle(int16_t posX, int16_t posY, int16_t rad, SSD1306_COLOR_t color);
  17. void SSD1306_DrawFilledCircle(int16_t posX, int16_t posY, int16_t rad, SSD1306_COLOR_t color);
  18. 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);
  19. 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);
  20. //Pozostałe
  21. void SSD1306_GoToPosition(uint16_t posX, uint16_t posY);
  22. void SSD1306_ClearScrean(void);
  23. void SSD1306_GotoXY(uint16_t x, uint16_t y);
  24. void SSD1306_FillBuffer(SSD1306_COLOR_t color);

Biblioteka opisana w tym poście można pobrać z dysku Google pod tym linkiem.