[APLIKACJA] STM32 i monochromatyczny wyświetlacz OLED
Wyświetlacz prezentowany w artykule (modOLED13_I2C – fotografia 1) ma matrycę o przekątnej 1,3 cala (aktywna część wyświetlacza ma wymiary 29,4 x 14,7 mm) i rozdzielczość 128×64 piksele. Szkło wyświetlacza przymocowano do płytki drukowanej modułu, na której umieszczono wszystkie niezbędne dodatkowe elementy, w tym: punkty do przylutowania taśmy wyświetlacza, stabilizator LDO napięcia +3,3 V oraz goldpiny do podłączenia sygnałów magistrali przeznaczonej do podłączenia mikrokontrolera – hosta (fotografia 2).
Fot. 1. Wygląd modułu modOLED13_I2C (w tle płytka DISCOVERY z STM32)
Fot. 2. Wygląd drugiej strony płytki modułu modOLED13
Sterownik wyświetlacza (SH1106) w module modOLED13_I2C komunikuje się z hostem za pomocą magistrali I2C. Każdy transfer danych na tej magistrali rozpoczyna się od wysłania sekwencji START, a potem bajtu z adresem urządzenia slave (7 starszych bitów) i najmłodszego bitu kierunku R/W. Adres slave ma wartość 0111100b, czyli kompletny bajt zapisu danych do SH1106 to 0x78 (R/W=0) i 0x79 dla odczytu danych (R/W=1). Po przesłaniu adresu protokół transmisji SH1106 wymaga przesłania bajtu kontrolnego. W tym bajcie mają znaczenie 2 najstarsze bity: Co i D/C. Pozostałych sześć młodszych bitów musi być zerami. Jeżeli bit Co jest wyzerowany, to następne bajty są bajtami danych. Po wpisaniu do Co jedynki sterownik interpretuje dwa następne bajty jako dane i kolejny bajt jako bajt kontrolny. Wyzerowania bitu R/W oznacza, ze następny bajt jest komendą. Dla R/W=1 kolejne bajty są danymi wpisywanymi do pamięci obrazu sterownika.
Do testowania wyświetlacza jako hosta użyto zestawu STM32VLDISCOVERY (z mikrokontrolerem STM32F100RB8T6). Mikrokontroler ma wbudowany sprzętowy interfejs I2C, który zostanie wykorzystany do komunikacji z wyświetlaczem. Sposób połączenia modułu wyświetlacza z zestawem DISCOVERY pokazano na rysunku 3.
Rys. 3. Schemat połączeń testowych pomiędzy STM32VLDISCOVERY i modOLED13_I2C
Oprogramowanie
Pierwszą rzeczą przy pisaniu oprogramowania będzie skonfigurowanie interfejsu I2C. Sprzętowy interfejs STM32 może pracować w trybie z wieloma masterami i obsługuje adresowanie 7- i 10-bitowe. Dostępne są standardowe prędkości transmisji (100 kHz) i szybkie prędkości (Fast Speed – 400 kHz). Do obsługi transmisji można wykorzystać mechanizm przerwań, lub pooling. Możliwe jest też przesyłanie danych z wykorzystaniem kanału DMA.
W naszym przypadku będziemy wykorzystywać wyłącznie tryb master (z jednym masterem na magistrali), a dane będą tylko wysyłane przez mastera – ze sterownika wyświetlacza nic nie będziemy odczytywali. Oprogramowanie skomplikowanego interfejsu mimo, że nie jest specjalnie trudne, to jest zadaniem żmudnym, na które trzeba poświęcić sporo czasu. Żeby tego uniknąć można skorzystać z gotowego rozwiązania. Firma STM udostępnia notę aplikacyjną AN 2824 opisującą sposób obsługi I2C oraz dołączony do niej program demonstracyjny. Po krótkim przetestowaniu procedur z tej noty postanowiłem wykorzystać je do testowania sterowania wyświetlaczem. Trzeba tylko pamiętać, że projekt przykładowy jest skonfigurowany dla taktowania z częstotliwością 72 MHz. Rdzeń zastosowanego mikrokontrolera może pracować z maksymalną częstotliwością 24 MHz. Jeżeli tego nie zmienimy, to mikrokontroler nie będzie działał. Predefiniowane ustawienia taktowania umieszczono w pliku system_stm32f10x.c (listing 1).
List. 1. Definicja taktowania mikrokontrolera z częstotliwością 24 MHz
#if defined (STM32F10X_LD_VL) || (defined STM32F10X_MD_VL) /* #define SYSCLK_FREQ_HSE HSE_Value */ #define SYSCLK_FREQ_24MHz 24000000 #else /* #define SYSCLK_FREQ_HSE HSE_Value */ #define SYSCLK_FREQ_24MHz 24000000 /* #define SYSCLK_FREQ_36MHz 36000000 */ /* #define SYSCLK_FREQ_48MHz 48000000 */ /* #define SYSCLK_FREQ_56MHz 56000000 */ //#define SYSCLK_FREQ_72MHz 72000000 #endif
Interfejs I2C przed użyciem musi zostać zainicjowany. Pierwsza cześć inicjacji dotyczy konfigurowania linii portów dla sygnałów SDA i SCL, druga część to konfiguracja samego modułu I2C, a trzecia opcjonalnie konfiguruje kanał DMA (listing 2).
List. 2. Konfiguracja interfejsu I2C I kanału DMA
// konfiguracja portów GPIO_InitTypeDef GPIO_InitStructure; I2C_InitTypeDef I2C_InitStructure; /* GPIOB clock enable */ RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); /* Enable the DMA1 clock */ RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //konfiguracja interfejsu I2C /* I2C1 clock enable */ RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE); /* I2C1 SDA and SCL configuration */ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD; GPIO_Init(GPIOB, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7; GPIO_Init(GPIOB, &GPIO_InitStructure); /* Enable I2C1 reset state */ RCC_APB1PeriphResetCmd(RCC_APB1Periph_I2C1, ENABLE); /* Release I2C1 from reset state */ RCC_APB1PeriphResetCmd(RCC_APB1Periph_I2C1, DISABLE); //konfiguracja kanału DMA DMA_DeInit(I2C1_DMA_CHANNEL_TX); I2CDMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)I2C1_DR_Address; I2CDMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)0; /* This parameter will be configured durig communication */ I2CDMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; /* This parameter will be configured durig communication */ I2CDMA_InitStructure.DMA_BufferSize = 0xFFFF; /* This parameter will be configured durig communication */ I2CDMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; I2CDMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; I2CDMA_InitStructure.DMA_PeripheralDataSize = DMA_MemoryDataSize_Byte; I2CDMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; I2CDMA_InitStructure.DMA_Mode = DMA_Mode_Normal; I2CDMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh; I2CDMA_InitStructure.DMA_M2M = DMA_M2M_Disable; DMA_Init(I2C1_DMA_CHANNEL_TX, &I2CDMA_InitStructure); /* I2C1 RX DMA Channel configuration */ DMA_DeInit(I2C1_DMA_CHANNEL_RX); DMA_Init(I2C1_DMA_CHANNEL_RX, &I2CDMA_InitStructure); }