Ten post chciałbym poświęcić na opisanie kontrolera I2C, za pomocą którego można poprzez tą magistralę sterować wyświetlaczem ze sterownikiem HD44780.
Konwerter komunikuje się poprzez magistralę I2C. Na płytce z układem zawarto dodatkowo potencjometr do regulacji kontrastu oraz zworkę pozwalającą na wyłączenie podświetlenia.
Wyprowadzenia z układu są takie same:
W układzie zastosowano obudowę SO16 z następującym rozłożeniem wyprowadzeń:
Opis konwertera:
Konwerter komunikuje się poprzez magistralę I2C. Na płytce z układem zawarto dodatkowo potencjometr do regulacji kontrastu oraz zworkę pozwalającą na wyłączenie podświetlenia.
Wyprowadzenia z układu są takie same:
W układzie zastosowano obudowę SO16 z następującym rozłożeniem wyprowadzeń:
Cube Mx:
Po standardowym wygenerowaniu projektu, należy uruchomić blok RCC oraz I2C.
Konfigurację I2C nie trzeba zmieniać.
Wygenerowana konfiguracja wygląda następująco:
- static void MX_I2C1_Init(void)
- {
- hi2c1.Instance = I2C1;
- hi2c1.Init.ClockSpeed = 100000;
- 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();
- }
- }
Przygotowanie programu:
Do obsługi wyświetlacza przygotowałem dwa pliki .h oraz .c poniżej przedstawię oba wraz z opisem:
W pierwszej kolejności w pliku .h należy zdefiniować potrzebne wartości do ustawienia odpowiednich pinów w wyświetlaczu oraz definicje wymaganych komend.
- #define Enable_SET() LCD_Write_I2C_LCD(portlcd |= 0x04)
- #define ENABLE_RESET() LCD_Write_I2C_LCD(portlcd &=~ 0x04)
- #define RS_SET() LCD_Write_I2C_LCD(portlcd |= 0x01)
- #define RS_RESET() LCD_Write_I2C_LCD(portlcd &=~ 0x01)
- #define LED_SET() LCD_Write_I2C_LCD(portlcd |= 0x08)
- #define WRITE_SET() LCD_Write_I2C_LCD(portlcd &=~ 0x02)
- #define HD44780_CLEAR 0x01
- #define HD44780_HOME 0x02
- #define HD44780_ENTRY_MODE 0x04
- #define HD44780_EM_SHIFT_CURSOR 0
- #define HD44780_EM_SHIFT_DISPLAY 1
- #define HD44780_EM_DECREMENT 0
- #define HD44780_EM_INCREMENT 2
- #define HD44780_DISPLAY_ONOFF 0x08
- #define HD44780_DISPLAY_OFF 0
- #define HD44780_DISPLAY_ON 4
- #define HD44780_CURSOR_OFF 0
- #define HD44780_CURSOR_ON 2
- #define HD44780_CURSOR_NOBLINK 0
- #define HD44780_CURSOR_BLINK 1
- #define HD44780_DISPLAY_CURSOR_SHIFT 0x10
- #define HD44780_SHIFT_CURSOR 0
- #define HD44780_SHIFT_DISPLAY 8
- #define HD44780_SHIFT_LEFT 0
- #define HD44780_SHIFT_RIGHT 4
- #define HD44780_FUNCTION_SET 0x20
- #define HD44780_FONT5x7 0
- #define HD44780_FONT5x10 4
- #define HD44780_ONE_LINE 0
- #define HD44780_TWO_LINE 8
- #define HD44780_4_BIT 0
- #define HD44780_8_BIT 16
- #define HD44780_CGRAM_SET 0x40
- #define HD44780_DDRAM_SET 0x80
Ostatnim elementem w tym pliku są prototypy funkcji:
- void LCD_Init(void);
- void LCD_Clear(void);
- void LCD_Send_Char(char ch);
- void LCD_Send_String(char* st);
- void LCD_Set_Position(uint8_t x, uint8_t y);
- void LCD_Send_Str_Pos(char* st, uint8_t x, uint8_t y);
Teraz czas na plik .c. W pierwszej kolejności funkcje wewnętrzne odpowiedzialne za przesyłanie danych:
Jedna z nich przesyła do wyświetlacza 4 bity, druga natomiast przesyła cały bajt wykorzystując funkcje przesyłające po pół.
- void Priv_Send_Halfbyte(uint8_t c)
- {
- c<<=4;
- ENABLE_SET();
- Delay_us(50);
- LCD_Write_I2C_LCD(portlcd|c);
- ENABLE_RESET();
- Delay_us(50);
- }
- void Priv_Send_Byte(uint8_t c, uint8_t mode)
- {
- uint8_t hc=0;
- if (mode==0) { RS_RESET(); }
- else { RS_SET(); }
- hc=c>>4;
- Priv_Send_Halfbyte(hc);
- Priv_Send_Halfbyte(c);
- }
Następna w kolejności będzie funkcja przesyłające dane za pomocą I2C do LCD:
- uint8_t bufer_i2c[1] = {0};
- void LCD_Write_I2C_LCD(uint8_t to_send)
- {
- bufer_i2c[0] = to_send;
- HAL_I2C_Master_Transmit(&hi2c1, (uint16_t)0x4E, bufer_i2c, sizeof(bufer_i2c), 1000);
- }
Realizuje się ją za pomocą funkcji wbudowanej w biblioteki hala(definicja w pliku stm32f4xx_hal_i2c.h). Wysyła ona dane wybraną magistralą I2C do adresu urządzenia, (0x4E). Przesyłany jest jeden bajt danych. Ostatni parametr to tzw. timeout, który określa ile czasu funkcja będzie przesyłała dane.
Funkcja odpowiedzialna za włączenie wyświetlacza:
- void LCD_Init(void)
- {
- uint8_t i=0;
- //Wybranie trybu czterobitowego dane wysylane cztery razy
- HAL_Delay(100);
- for(i=0;i<3;i++)
- {
- Priv_Send_Halfbyte(0x03);
- HAL_Delay(45);
- }
- //Wlaczenie trybu czterobitowego
- Priv_Send_Halfbyte(0x02);
- HAL_Delay(100);
- Priv_Send_Byte(HD44780_FUNCTION_SET | HD44780_FONT5x7 | HD44780_TWO_LINE | HD44780_4_BIT,0);
- HAL_Delay(1);
- Priv_Send_Byte(HD44780_DISPLAY_ONOFF | HD44780_DISPLAY_OFF,0);
- HAL_Delay(1);
- Priv_Send_Byte(HD44780_ENTRY_MODE | HD44780_EM_SHIFT_CURSOR | HD44780_EM_INCREMENT,0);
- HAL_Delay(1);
- Priv_Send_Byte(HD44780_DISPLAY_ONOFF | HD44780_DISPLAY_ON | HD44780_CURSOR_OFF | HD44780_CURSOR_NOBLINK,0);
- HAL_Delay(1);
- LED_SET();
- WRITE_SET();
- LCD_Clear();
- }
Poniżej funkcje odpowiedzialne za czyszczenie wyświetlacza, przesyłania pojedynczego znaku, całego ciągu znaków bądź tekstu wraz z pozycją startową jego umieszczenia.
- void LCD_Clear(void)
- {
- Priv_Send_Byte(HD44780_CLEAR, 0);
- HAL_Delay(1000);
- }
- void LCD_Send_Char(char ch)
- {
- Priv_Send_Byte(ch, 1);
- }
- void LCD_String(char* str)
- {
- uint8_t i=0;
- while(str[i] != 0)
- {
- Priv_Send_Byte(str[i],1);
- i++;
- }
- }
- void LCD_Send_Str_Pos(char* st, uint8_t x, uint8_t y)
- {
- LCD_Set_Position(x, y);
- LCD_Send_String(st);
- }
Kolejnym elementem jest funkcja ustawiająca pozycję wyświetlacza dla dwóch wierszy:
- void LCD_Set_Position(uint8_t x, uint8_t y)
- {
- switch(y)
- {
- case 0:
- Priv_Send_Byte(x|0x80,0);
- HAL_Delay(1);
- break;
- case 1:
- Priv_Send_Byte((0x40+x)|0x80,0);
- HAL_Delay(1);
- break;
- }
- }
W przypadku wykorzystywania czterech linii funkcje należy zmodyfikować do następującej postaci:
- void LCD_Set_Position(uint8_t x, uint8_t y)
- {
- switch(y)
- {
- case 0:
- Priv_Send_Byte(x|0x80,0);
- HAL_Delay(1);
- break;
- case 1:
- Priv_Send_Byte((0x40+x)|0x80,0);
- HAL_Delay(1);
- break;
- case 2:
- Priv_Send_Byte((0x14+x)|0x80,0);
- HAL_Delay(1);
- break;
- case 3:
- Priv_Send_Byte((0x54+x)|0x80,0);
- HAL_Delay(1);
- break;
- }
- }
Dalszym elementem jest włączenie biblioteki, poprzez wywołanie funkcji LCD_Init. Następnie można wysyłać dane na wyświetlacz lub poprzez zwykłe wpisanie ciągu znaków w cudzysłowie, bądź za pomocą funkcje sprintf.
Cały projekt można pobrać z dysku Google pod tym linkiem.