STM32F0DISCOVERY + LCD

STM32F0DISCOVERY
Este post faz parte da série STM32F0DISCOVERY

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: 

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:

Figura 1

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). 

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.

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:

/*
 *
 *		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:

#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”“Delay.h”), conforme a seguir:

#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:

#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).

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).

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).

Figura 6

Por fim, demonstraremos uma aplicação simples no arquivo main.c:

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.

STM32F0DISCOVERY

STM32F0Discovery + LCD + Bluetooth
Licença Creative Commons Esta obra está licenciada com uma Licença Creative Commons Atribuição-CompartilhaIgual 4.0 Internacional.
Comentários:
Notificações
Notificar
4 Comentários
recentes
antigos mais votados
Inline Feedbacks
View all comments
Flavio Ferreira
Flavio Ferreira
14/12/2018 08:49

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 »

William Furlaneto
William Furlaneto
02/10/2018 16:35

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()

Fabio_Souza_Embarcados
Fabio_Souza_Embarcados
26/05/2017 08:13

Eder, excelente artigo! Parabéns. Obrigado por compartilhar.

Eder
Eder
Reply to  Fabio_Souza_Embarcados
26/05/2017 08:15

Obrigado Fabio!

Home » Hardware » STM32F0DISCOVERY + LCD

EM DESTAQUE

WEBINARS

VEJA TAMBÉM

JUNTE-SE HOJE À COMUNIDADE EMBARCADOS

Talvez você goste: