ÍNDICE DE CONTEÚDO
- STM32F0DISCOVERY + LCD
- STM32F0Discovery + LCD + Bluetooth
Objetivo
Neste projeto faremos uma aplicação utilizando o kit STM32F0Discovery para imprimir caracteres em um display LCD alfanumérico 16×4.
Materiais
Serão necessários:
- Kit STM32F0Discovery;
- Display LCD 16×2 ou 16×4 com controlador HD44780, KS0066 ou SPLC780D1;
- Protoboard;
- Trimpot de 10k Ohm;
- Jumpers.
Neste projeto está sendo utilizado o display LCD modelo AGM 1604A-801 (azul – 16×04).
Esquema de conexão
A conexão entre a Discovery e o display é dada da seguinte forma:
Ferramentas
Para agilizarmos o desenvolvimento deste projeto, faremos o uso do STM32CubeMX, ferramenta já apresentada no artigo “Crie projetos com o STM32Cube“. Neste, o projeto exemplo tem como base o mesmo kit deste artigo. Assim, inicializaremos um novo projeto no STM32CubeMX da mesma forma que foi feito, porém com algumas ressalvas: depois que realizar as configurações necessárias, a ferramenta “alvo” para o desenvolvimento da aplicação será o MDK-ARM v5, esta opção é feita através de “Project Settings/Project/Toolchain IDE”.
Antes de pressionar “OK” para gerar o projeto, escolha a opção “Copy only the necessary library files”, assim, serão gerados somente arquivos pertinentes aos periféricos ativados e para escolher esta opção, realize o seguinte caminho: “Project Settings/Code Generator/STM32Cube Firmware Libray Package” (figura 2).
Depois de escolhido o diretório para salvar o projeto STM32CubeMX e pressionado o botão “OK”, abra este mesmo diretório para visualizar os arquivos, cujos conteúdos podem ser vistos conforme a figura 3.
Abra o arquivo do projeto MDK-ARM v5 com a extensão .uvprojx para iniciarmos a ferramenta (uVision 5) e, consequentemente, a escrita do código da aplicação.
Código
O código fonte do driver para controlar e escrever no display LCD, pode ser visto a seguir:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 |
/* * * LCD driver for STM32F051R8T6 microcontroller. * * */ #include "LCD.h" #include "stm32f0xx_hal_gpio.h" #include "Delay.h" /*---------------------------------------------------------- * UserFont: characters to be loaded into CGRAM. * *---------------------------------------------------------*/ const char UserFont[8][8] = { { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, { 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10 }, { 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18 }, { 0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C }, { 0x1E,0x1E,0x1E,0x1E,0x1E,0x1E,0x1E,0x1E }, { 0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F }, { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 } }; /*---------------------------------------------------------- * LCD_ReadByte: reading byte from LCD. * * Parameters: none. * Return: uint8_t. *---------------------------------------------------------*/ uint8_t LCD_ReadByte(void) { uint8_t ReadedData=0; GPIO_InitTypeDef GPIO_Conf; HAL_GPIO_WritePin(LCD_PORT, LCD_D_ALL, GPIO_PIN_SET); GPIO_Conf.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3; GPIO_Conf.Mode = GPIO_MODE_INPUT; GPIO_Conf.Pull = GPIO_NOPULL; GPIO_Conf.Speed = GPIO_SPEED_LOW; HAL_GPIO_Init(LCD_PORT, &GPIO_Conf); HAL_GPIO_WritePin(LCD_PORT, LCD_RW, GPIO_PIN_SET); HAL_GPIO_WritePin(LCD_PORT, LCD_EN, GPIO_PIN_SET); if(HAL_GPIO_ReadPin(LCD_PORT, LCD_D7)) ReadedData |= 0x80; if(HAL_GPIO_ReadPin(LCD_PORT, LCD_D6)) ReadedData |= 0x40; if(HAL_GPIO_ReadPin(LCD_PORT, LCD_D5)) ReadedData |= 0x20; if(HAL_GPIO_ReadPin(LCD_PORT, LCD_D4)) ReadedData |= 0x10; HAL_GPIO_WritePin(LCD_PORT, LCD_EN, GPIO_PIN_RESET); TIM6delay_us(50); HAL_GPIO_WritePin(LCD_PORT, LCD_EN, GPIO_PIN_SET); if(HAL_GPIO_ReadPin(LCD_PORT, LCD_D7)) ReadedData |= 0x08; if(HAL_GPIO_ReadPin(LCD_PORT, LCD_D6)) ReadedData |= 0x04; if(HAL_GPIO_ReadPin(LCD_PORT, LCD_D5)) ReadedData |= 0x02; if(HAL_GPIO_ReadPin(LCD_PORT, LCD_D4)) ReadedData |= 0x01; HAL_GPIO_WritePin(LCD_PORT, LCD_EN, GPIO_PIN_RESET); GPIO_Conf.Pin = (GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3); GPIO_Conf.Mode = GPIO_MODE_OUTPUT_PP; GPIO_Conf.Pull = GPIO_NOPULL; GPIO_Conf.Speed = GPIO_SPEED_LOW; HAL_GPIO_Init(LCD_PORT, &GPIO_Conf); return ReadedData; } /*---------------------------------------------------------- * LCD_SendByte: sending byte to LCD. * * Parameters: uint8_t. * Return: none. *---------------------------------------------------------*/ void LCD_SendByte(uint8_t cmd) { uint8_t tcmd = 0; HAL_GPIO_WritePin(LCD_PORT, LCD_RW, GPIO_PIN_RESET); HAL_GPIO_WritePin(LCD_PORT, LCD_D_ALL, GPIO_PIN_RESET); HAL_GPIO_WritePin(LCD_PORT, LCD_EN, GPIO_PIN_SET); tcmd = cmd >> 4; if( tcmd & 0x01 ) HAL_GPIO_WritePin(LCD_PORT, LCD_D4, GPIO_PIN_SET); if( tcmd & 0x02 ) HAL_GPIO_WritePin(LCD_PORT, LCD_D5, GPIO_PIN_SET); if( tcmd & 0x04 ) HAL_GPIO_WritePin(LCD_PORT, LCD_D6, GPIO_PIN_SET); if( tcmd & 0x08 ) HAL_GPIO_WritePin(LCD_PORT, LCD_D7, GPIO_PIN_SET); HAL_GPIO_WritePin(LCD_PORT, LCD_EN, GPIO_PIN_RESET); TIM6delay_us(50); HAL_GPIO_WritePin(LCD_PORT, LCD_EN, GPIO_PIN_SET); HAL_GPIO_WritePin(LCD_PORT, LCD_D_ALL, GPIO_PIN_RESET); cmd &= 0x0F; if( cmd & 0x01 ) HAL_GPIO_WritePin(LCD_PORT, LCD_D4, GPIO_PIN_SET); if( cmd & 0x02 ) HAL_GPIO_WritePin(LCD_PORT, LCD_D5, GPIO_PIN_SET); if( cmd & 0x04 ) HAL_GPIO_WritePin(LCD_PORT, LCD_D6, GPIO_PIN_SET); if( cmd & 0x08 ) HAL_GPIO_WritePin(LCD_PORT, LCD_D7, GPIO_PIN_SET); HAL_GPIO_WritePin(LCD_PORT, LCD_EN, GPIO_PIN_RESET); HAL_GPIO_WritePin(LCD_PORT, LCD_D_ALL, GPIO_PIN_RESET); HAL_GPIO_WritePin(LCD_PORT, LCD_RS, GPIO_PIN_RESET); while(LCD_ReadByte() & 0x80); } /*---------------------------------------------------------- * LCD_SendCmd: sending command to LCD. * * Parameters: uint8_t. * Return: none. *---------------------------------------------------------*/ void LCD_SendCmd(uint8_t cmd) { HAL_GPIO_WritePin(LCD_PORT, LCD_RS, GPIO_PIN_RESET); TIM6delay_us(50); LCD_SendByte(cmd); } /*---------------------------------------------------------- * LCD_SendData: sending data to LCD. * * Parameters: uint32. * Return: none. *---------------------------------------------------------*/ void LCD_SendData(uint32_t data) { HAL_GPIO_WritePin(LCD_PORT, LCD_RS, GPIO_PIN_SET); TIM6delay_us(50); LCD_SendByte(data); } /*---------------------------------------------------------- * LCD_Num: print a number up to 9999. * * Parameters: uint32. * Return: none. *---------------------------------------------------------*/ void LCD_Num(uint32_t x) { LCD_SendData((x/1000) + 0x30); LCD_SendData((x/100)%10 + 0x30); LCD_SendData((x%100)/10 + 0x30); LCD_SendData((x%10) + 0x30); } /*---------------------------------------------------------- * LCD_SendText: print a string on LCD. * * Parameters: char. * Return: none. *---------------------------------------------------------*/ void LCD_SendText(char *text) { while(*text) { LCD_SendData(*text); text++; } } /*---------------------------------------------------------- * LCD_Clear: clear the LCD. * * Parameters: none. * Return: none. *---------------------------------------------------------*/ void LCD_Clear(void) { LCD_SendCmd(0x01); } /*---------------------------------------------------------- * LCD_GoTo: set cursor position on LCD. * * Parameters: column and line. * Return: none. *---------------------------------------------------------*/ void LCD_GoTo(unsigned char column, unsigned char line) { uint8_t position = 0; switch(line) { case 0: position = 0x00; break; case 1: position = 0x40; break; case 2: position = 0x10; break; case 3: position = 0x50; break; default: position = 0x00; break; } LCD_SendCmd(0x80 | (position + column)); } /*---------------------------------------------------------- * LCD_Init: initialize the LCD. * * Parameters: none. * Return: none. *---------------------------------------------------------*/ void LCD_Init(void) { uint8_t i; char const *p; GPIO_InitTypeDef GPIO_InitStructure; /* GPIO Port Clock Enable */ __GPIOC_CLK_ENABLE(); InitDelayTIM6(); /*Configure GPIO pins */ GPIO_InitStructure.Pin = LCD_RS | LCD_RW | LCD_EN | LCD_D4 | LCD_D5 | LCD_D6 | LCD_D7; GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStructure.Pull = GPIO_NOPULL; GPIO_InitStructure.Speed = GPIO_SPEED_LOW; HAL_GPIO_Init(LCD_PORT, &GPIO_InitStructure); TIM6delay_ms(30); HAL_GPIO_WritePin(LCD_PORT, LCD_RS, GPIO_PIN_RESET); HAL_GPIO_WritePin(LCD_PORT, LCD_EN, GPIO_PIN_RESET); for(i = 0; i<3; i++) { HAL_GPIO_WritePin(LCD_PORT, LCD_EN, GPIO_PIN_SET); HAL_GPIO_WritePin(LCD_PORT, LCD_D4 | LCD_D5, GPIO_PIN_SET); HAL_GPIO_WritePin(LCD_PORT, LCD_D6 | LCD_D7, GPIO_PIN_RESET); HAL_GPIO_WritePin(LCD_PORT, LCD_EN, GPIO_PIN_RESET); } HAL_GPIO_WritePin(LCD_PORT, LCD_EN, GPIO_PIN_SET); TIM6delay_ms(50); HAL_GPIO_WritePin(LCD_PORT, LCD_D5, GPIO_PIN_SET); HAL_GPIO_WritePin(LCD_PORT,LCD_D4 | LCD_D6 | LCD_D7, GPIO_PIN_RESET); TIM6delay_ms(50); HAL_GPIO_WritePin(LCD_PORT, LCD_EN, GPIO_PIN_RESET); LCD_SendCmd(0x28); /* 2 lines, 5x8 character matrix */ LCD_SendCmd(0x08); /* Turn off display */ LCD_SendCmd(0x01); /* Clean display with home cursor */ LCD_SendCmd(0x06); /* Entry mode: Move right, no shift */ LCD_SendCmd(0x0C); /* Display ctrl:Disp=ON,Curs/Blnk=OFF */ TIM6delay_ms(5); /* Load user-specific characters into CGRAM */ LCD_SendCmd(0x40); /* Set CGRAM address counter to 0 */ p = &UserFont[0][0]; for (i = 0; i < sizeof(UserFont); i++, p++) LCD_SendData (*p); LCD_SendCmd(0x80); /* Set DDRAM address counter to 0 */ } |
O arquivo header pode ser visto também a seguir:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
#ifndef __LCD_H #define __LCD_H #include "stm32f0xx_hal.h" #define LCD_RS GPIO_PIN_13 #define LCD_RW GPIO_PIN_14 #define LCD_EN GPIO_PIN_15 #define LCD_D4 GPIO_PIN_3 #define LCD_D5 GPIO_PIN_2 #define LCD_D6 GPIO_PIN_1 #define LCD_D7 GPIO_PIN_0 #define LCD_PORT GPIOC #define LCD_D_ALL (GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3) unsigned char LCD_ReadByte(void); void LCD_SendByte(unsigned char cmd); void LCD_SendCmd (unsigned char cmd); void LCD_SendData(uint32_t data); void LCD_SendText(char *text); void LCD_GoTo (unsigned char line, unsigned char column); void LCD_Clear (void); void LCD_Init (void); void LCD_Num(uint32_t x); #endif |
Após a criação tanto dos arquivos fonte (LCD.c) quanto do header (LCD.h), devemos adicioná-los ao mesmo diretório criado para STM32CubeMX, pois será criado um path deste arquivo no uVision 5.
No arquivo fonte, percebe-se a utilização de rotinas de “delays” como: “TIM6delay_ms” e “TIM6delay_us”. Estas foram desenvolvidas em outros arquivos (intuito genérico) e também são de suma importância para o funcionamento deste driver. Assim, também deve-se criar outros dois arquivos (“Delay.c” e “Delay.h”), conforme a seguir:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
#include "Delay.h" uint32_t prescaler_ms; uint32_t prescaler_us; /*---------------------------------------------------------- * LCD_ReadByte: delay of miliseconds. * * Parameters: uint16_t. * Return: none. *---------------------------------------------------------*/ void TIM6delay_ms(uint16_t value) { TIM6->PSC = prescaler_ms; TIM6->ARR = value; TIM6->CNT = 0; TIM6->CR1 |= TIM_CR1_CEN; while((TIM6->SR & TIM_SR_UIF)==0){} TIM6->SR &=~ TIM_SR_UIF; } /*---------------------------------------------------------- * TIM6delay_us: delay of microseconds. * * Parameters: uint16_t. * Return: none. *---------------------------------------------------------*/ void TIM6delay_us(uint16_t value) { TIM6->PSC = prescaler_us; TIM6->ARR = value; TIM6->CNT = 0; TIM6->CR1 |= TIM_CR1_CEN; while((TIM6->SR & TIM_SR_UIF)==0){} TIM6->SR &=~ TIM_SR_UIF; } /*---------------------------------------------------------- * InitDelayTIM6: initialize the timer 6. * * Parameters: none. * Return: none. *---------------------------------------------------------*/ void InitDelayTIM6(void) { prescaler_ms = SystemCoreClock / 1000-1; prescaler_us = SystemCoreClock / 1000000-1; RCC->APB1ENR |= RCC_APB1ENR_TIM6EN; } |
Header:
1 2 3 4 5 6 7 8 9 10 |
#ifndef __DELAY_H #define __DELAY_H #include "stm32f0xx_hal.h" void InitDelayTIM6(void); void TIM6delay_ms(uint16_t value); void TIM6delay_us(uint16_t value); #endif |
Assim sendo, adicione também estes dois arquivos no mesmo diretório em que estão os arquivos do LCD (vide figura 4).
Feito isto e com a ferramenta já aberta como descrito antes, agora devemos adicionar o path para que esses arquivos sejam encontrados durante o processo de compilação. Assim, execute o seguinte caminho: Options for Target/C/C++/Include Paths/Folder Setup/New, depois disso, informe apenas qual o diretório (figura 5).
Finalizado o processo, você pode também adicioná-los à árvore do projeto, basta apenas clicar com o botão direito sobre o diretório “Application/User” criado pelo STM32CubeMX e depois em “Add Existing Files…” (figura 6).
Por fim, demonstraremos uma aplicação simples no arquivo main.c:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
int main(void) { /* USER CODE BEGIN 1 */ uint8_t i, j; /* USER CODE END 1 */ /* MCU Configuration----------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* Configure the system clock */ SystemClock_Config(); /* Initialize all configured peripherals */ MX_GPIO_Init(); /* USER CODE BEGIN 2 */ LCD_Init(); LCD_Clear(); /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ for(;;) { /* USER CODE END WHILE */ for(i = 0; i < 4; i++) { for(j = 0; j < 7; j++) { LCD_GoTo(j,i); LCD_SendText("EMBARCADOS"); HAL_Delay(400); LCD_Clear(); } } /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */ } |
É importante destacar que: insira seu código dentro dos comentários “User Code Begin X e User Code End X”, isto protegerá seu código de ser apagado se houver alguma atualização no projeto do STM32CubeMX!
Funcionamento
Realizada a montagem conforme o Esquema de Conexão, criado o projeto, adicionados os arquivos pertinentes e compilado sem qualquer erro, podemos verificar o funcionamento da aplicação conforme a seguir:
No próximo artigo utilizaremos este mesmo setup com um módulo Bluetooth para comunicar com um terminal em um PC.
Para usar com a placa Blue Pill (STM32F103C8T – R$20,00 no ML), não tem os pinos GPIO da porta C na quantidade necessária. Em LCD.C tem definições que deveriam estar somente em LCD.H. Arrumando isso funciona. em LCD.C de: GPIO_Conf.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3; alterar para: GPIO_Conf.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_3 | GPIO_PIN_4; em LCD.H de: #define LCD_RS GPIO_PIN_13 #define LCD_RW GPIO_PIN_14 #define LCD_EN GPIO_PIN_15 #define LCD_D4 GPIO_PIN_3 #define LCD_D5 GPIO_PIN_2 #define LCD_D6 GPIO_PIN_1 #define LCD_D7 GPIO_PIN_0 #define LCD_PORT GPIOC #define LCD_D_ALL (GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3) alterar para: #define LCD_RS GPIO_PIN_7… Leia mais »
Para mim o programa esta travando bem nessa parte:
HAL_GPIO_WritePin(LCD_PORT, LCD_EN, GPIO_PIN_RESET);
HAL_GPIO_WritePin(LCD_PORT, LCD_D_ALL, GPIO_PIN_RESET);
HAL_GPIO_WritePin(LCD_PORT, LCD_RS, GPIO_PIN_RESET);
while(LCD_ReadByte() & 0x80);
dentro da rotina do SendByte.
Simplesmente não sai desta condição LCD_ReadByte()
Eder, excelente artigo! Parabéns. Obrigado por compartilhar.
Obrigado Fabio!