目录
概述
1. 硬件介绍
1.1 ST7796-LCD
1.2 MCU IO与LCD PIN对应关系
2 功能实现
2.1 使用STM32Cube配置Project
2.2 STM32Cube生成工程
3 代码实现
3.1 SPI接口实现
3.2 LCD驱动程序实现
3.3 测试程序实现
4 测试
源代码下载地址:
https://gitee.com/mftang/stm32_open_test_proj/tree/master/stm32_f407_lcd_proj/UserCode/lcd_drv
测试视频:
使用SPI驱动串行LCD的驱动实现(STM32F4)
概述
本文主要讲述使用STM32硬件SPI接口驱动ST7796-LCD,主控MCU为STM32F407芯片。笔者详细介绍整个驱动的实现过程,并使用STM32Cube生成一个工程,测试驱动程序的功能。
1. 硬件介绍
1.1 ST7796-LCD
LCD的PIN引脚功能介绍
序号 | 模块引脚 | 引脚说明 |
1 | VCC | 屏电源正 |
2 | GND | 屏电源地 |
3 | LCD_CS | 液晶屏片选控制信号,低电平有效 |
4 | LCD_RST | 液晶屏复位控制信号,低电平复位 |
5 | LCD_RS | 液晶屏命令/数据选择控制信号 高电平:数据,低电平:命令 |
6 | SDI(MOSI) | SPI总线写数据信号(SD卡和液晶屏共用) |
7 | SCK | SPI总线时钟信号(SD卡和液晶屏共用) |
8 | LED | 液晶屏背光控制信号(如需要控制,请接引脚,如不需要控制,可以不接) |
9 | SDO(MISO) | SPI总线读数据信号(SD卡和液晶屏共用) |
10 | CTP_SCL | 电容触摸屏IIC总线时钟信号(无触摸屏的模块不需连接) |
11 | CTP_RST | 电容触摸屏复位控制信号,低电平复位(无触摸屏的模块不需连接) |
12 | CTP_SDA | 电容触摸屏IIC总线数据信号(无触摸屏的模块不需连接) |
13 | CTP_INT | 电容触摸屏IIC总线触摸中断信号,产生触摸时,输入低电平到主控(无触摸屏的模块不需连接) |
14 | SD_CS | SD卡片选控制信号,低电平有效(不使用SD卡功能,可不接) |
实体LCD Port对应关系如下图所示
1.2 MCU IO与LCD PIN对应关系
STM32 PIN引脚 | LCD PIN引脚 |
---|---|
PB5-MOSI | MOSI |
PB4-MISO | MISO |
PB3-SCK | SCK |
PB6 | CS |
PB9 | RST |
PB8 | RS |
2 功能实现
2.1 使用STM32Cube配置Project
1) 配置SPI接口
SPI的参数
2)配置LCD的控制引脚
3)使能外部晶振
2.2 STM32Cube生成工程
使用STM32Cube生成工程,并创建两个目录
User/lcd_drv 驱动文件目录
User/test 测试文件目录
3 代码实现
3.1 SPI接口实现
在spi.c文件中实现读写接口函数,具体实现如下:
/* USER CODE BEGIN 1 */
void hal_spi_writebyte( uint8_t byte )
{
uint8_t buff[1];
buff[0] = byte;
HAL_SPI_Transmit( &hspi3, buff, 1, 1000);
}
uint8_t hal_spi_readbyte(void)
{
uint8_t buff[1];
buff[0] = 0xff;
return HAL_SPI_Receive( &hspi3, buff, 1, 1000);
}
/* USER CODE END 1 */
3.2 LCD驱动程序实现
创建lcd_drv.c实现驱动程序,lcd_spi.c实现和MCU之间的驱动接口
1)lcd_drv.c 程序实现
#include "lcd_drv.h"
#include "lcd_spi.h"
_lcd_dev lcddev;
void LCD_WR_REG(uint8_t data)
{
LCD_CS_CLR;
LCD_RS_CLR;
SPI_WriteByte(data);
LCD_CS_SET;
}
void LCD_WR_DATA(uint8_t data)
{
LCD_CS_CLR;
LCD_RS_SET;
SPI_WriteByte(data);
LCD_CS_SET;
}
uint8_t LCD_RD_DATA(void)
{
uint8_t data;
LCD_CS_CLR;
LCD_RS_SET;
data = SPI_ReadByte();
LCD_CS_SET;
return data;
}
void LCD_WriteReg(uint8_t LCD_Reg, uint16_t LCD_RegValue)
{
LCD_WR_REG(LCD_Reg);
LCD_WR_DATA(LCD_RegValue);
}
uint8_t LCD_ReadReg(uint8_t LCD_Reg)
{
LCD_WR_REG(LCD_Reg);
return LCD_RD_DATA();
}
void LCD_WriteRAM_Prepare(void)
{
LCD_WR_REG(lcddev.wramcmd);
}
void Lcd_WriteData_16Bit(uint16_t Data)
{
LCD_CS_CLR;
LCD_RS_SET;
SPI_WriteByte(Data>>8);
SPI_WriteByte(Data);
LCD_CS_SET;
}
uint16_t Lcd_ReadData_16Bit(void)
{
uint16_t r,g;
LCD_CS_CLR;
LCD_RS_CLR;
SPI_WriteByte(lcddev.rramcmd);
LCD_RS_SET;
SPI_ReadByte();
r = SPI_ReadByte();
g = SPI_ReadByte();
LCD_CS_SET;
r<<=8;
r|=g;
return r;
}
void LCD_DrawPoint(uint16_t x,uint16_t y, uint16_t color)
{
LCD_SetCursor(x,y);
Lcd_WriteData_16Bit(color);
}
uint16_t LCD_ReadPoint(uint16_t x,uint16_t y)
{
uint16_t color;
LCD_SetCursor(x,y);
color = Lcd_ReadData_16Bit();
return color;
}
void LCD_Clear(uint16_t Color)
{
uint16_t i,m;
LCD_SetWindows(0,0,lcddev.width-1,lcddev.height-1);
LCD_CS_CLR;
LCD_RS_SET;
for(i=0;i<lcddev.height;i++)
{
for(m=0;m<lcddev.width;m++)
{
SPI_WriteByte(Color>>8);
SPI_WriteByte(Color);
}
}
LCD_CS_SET;
}
void LCD_SetWindows(uint16_t xStar, uint16_t yStar,uint16_t xEnd,uint16_t yEnd)
{
LCD_WR_REG(lcddev.setxcmd);
LCD_WR_DATA(xStar>>8);
LCD_WR_DATA(0x00FF&xStar);
LCD_WR_DATA(xEnd>>8);
LCD_WR_DATA(0x00FF&xEnd);
LCD_WR_REG(lcddev.setycmd);
LCD_WR_DATA(yStar>>8);
LCD_WR_DATA(0x00FF&yStar);
LCD_WR_DATA(yEnd>>8);
LCD_WR_DATA(0x00FF&yEnd);
LCD_WriteRAM_Prepare();
}
void LCD_SetCursor(uint16_t Xpos, uint16_t Ypos)
{
LCD_SetWindows(Xpos,Ypos,Xpos,Ypos);
}
void LCD_direction(uint8_t direction)
{
lcddev.setxcmd=0x2A;
lcddev.setycmd=0x2B;
lcddev.wramcmd=0x2C;
lcddev.rramcmd=0x2E;
lcddev.dir = direction%4;
switch(lcddev.dir){
case 0:
lcddev.width=LCD_W;
lcddev.height=LCD_H;
LCD_WriteReg(0x36,(1<<3)|(1<<6));
break;
case 1:
lcddev.width=LCD_H;
lcddev.height=LCD_W;
LCD_WriteReg(0x36,(1<<3)|(1<<5));
break;
case 2:
lcddev.width=LCD_W;
lcddev.height=LCD_H;
LCD_WriteReg(0x36,(1<<3)|(1<<7));
break;
case 3:
lcddev.width=LCD_H;
lcddev.height=LCD_W;
LCD_WriteReg(0x36,(1<<3)|(1<<7)|(1<<6)|(1<<5));
break;
default:break;
}
}
uint16_t LCD_Read_ID(void)
{
uint8_t i,val[3] = {0};
LCD_WR_REG(0xF0); // Command Set Control
LCD_WR_DATA(0xC3);
LCD_WR_REG(0xF0);
LCD_WR_DATA(0x96);
LCD_CS_CLR;
for(i=1;i<4;i++)
{
LCD_RS_CLR;
SPI_WriteByte(0xFB);
LCD_RS_SET;
SPI_WriteByte(0x10+i);
LCD_RS_CLR;
SPI_WriteByte(0xD3);
LCD_RS_SET;
val[i-1] = SPI_ReadByte();
LCD_RS_CLR;
SPI_WriteByte(0xFB);
LCD_RS_SET;
SPI_WriteByte(0x00);
}
LCD_CS_SET;
LCD_WR_REG(0xF0); // Command Set Control
LCD_WR_DATA(0x3C);
LCD_WR_REG(0xF0);
LCD_WR_DATA(0x69);
lcddev.id=val[1];
lcddev.id<<=8;
lcddev.id|=val[2];
return lcddev.id;
}
void LCD_RESET(void)
{
LCD_RST_CLR;
lcd_delay_us(100);
LCD_RST_SET;
lcd_delay_us(50);
}
void LCD_Init(void)
{
LCD_RESET(); //LCD
//*************3.5 ST7796S IPS
LCD_WR_REG(0x11);
lcd_delay_us(120); //Delay 120ms
lcd_delay_us(120); //Delay 120ms
LCD_WR_REG(0x36); // Memory Data Access Control MY,MX~~
LCD_WR_DATA(0x48);
LCD_WR_REG(0x3A);
LCD_WR_DATA(0x55);
LCD_WR_REG(0xF0); // Command Set Control
LCD_WR_DATA(0xC3);
LCD_WR_REG(0xF0);
LCD_WR_DATA(0x96);
LCD_WR_REG(0xB4);
LCD_WR_DATA(0x01);
LCD_WR_REG(0xB7);
LCD_WR_DATA(0xC6);
//LCD_WR_REG(0xB9);
//LCD_WR_DATA(0x02);
//LCD_WR_DATA(0xE0);
LCD_WR_REG(0xC0);
LCD_WR_DATA(0x80);
LCD_WR_DATA(0x45);
LCD_WR_REG(0xC1);
LCD_WR_DATA(0x13); //18 //00
LCD_WR_REG(0xC2);
LCD_WR_DATA(0xA7);
LCD_WR_REG(0xC5);
LCD_WR_DATA(0x0A);
LCD_WR_REG(0xE8);
LCD_WR_DATA(0x40);
LCD_WR_DATA(0x8A);
LCD_WR_DATA(0x00);
LCD_WR_DATA(0x00);
LCD_WR_DATA(0x29);
LCD_WR_DATA(0x19);
LCD_WR_DATA(0xA5);
LCD_WR_DATA(0x33);
LCD_WR_REG(0xE0);
LCD_WR_DATA(0xD0);
LCD_WR_DATA(0x08);
LCD_WR_DATA(0x0F);
LCD_WR_DATA(0x06);
LCD_WR_DATA(0x06);
LCD_WR_DATA(0x33);
LCD_WR_DATA(0x30);
LCD_WR_DATA(0x33);
LCD_WR_DATA(0x47);
LCD_WR_DATA(0x17);
LCD_WR_DATA(0x13);
LCD_WR_DATA(0x13);
LCD_WR_DATA(0x2B);
LCD_WR_DATA(0x31);
LCD_WR_REG(0xE1);
LCD_WR_DATA(0xD0);
LCD_WR_DATA(0x0A);
LCD_WR_DATA(0x11);
LCD_WR_DATA(0x0B);
LCD_WR_DATA(0x09);
LCD_WR_DATA(0x07);
LCD_WR_DATA(0x2F);
LCD_WR_DATA(0x33);
LCD_WR_DATA(0x47);
LCD_WR_DATA(0x38);
LCD_WR_DATA(0x15);
LCD_WR_DATA(0x16);
LCD_WR_DATA(0x2C);
LCD_WR_DATA(0x32);
LCD_WR_REG(0xF0);
LCD_WR_DATA(0x3C);
LCD_WR_REG(0xF0);
LCD_WR_DATA(0x69);
lcd_delay_us(120);
LCD_WR_REG(0x21);
LCD_WR_REG(0x29);
LCD_direction(USE_HORIZONTAL);
LCD_Clear(DARKBLUE);
}
/* End of this file */
2)lcd_spi.c 程序实现
#include "lcd_spi.h"
#if !IO_SPI
#include "spi.h"
#endif
void lcd_gpio_init(void)
{
#if IO_SPI
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOB_CLK_ENABLE();
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(LCD_GPIO_PORT, lcd_sck_Pin|lcd_mosi_Pin|lcd_cs_Pin|lcd_rs_Pin
|lcd_rst_Pin, GPIO_PIN_RESET);
/*Configure GPIO pins : PBPin PBPin PBPin PBPin
PBPin */
GPIO_InitStruct.Pin = lcd_sck_Pin|lcd_mosi_Pin|lcd_cs_Pin|lcd_rs_Pin
|lcd_rst_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(LCD_GPIO_PORT, &GPIO_InitStruct);
/*Configure GPIO pin : PtPin */
GPIO_InitStruct.Pin = lcd_miso_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(lcd_miso_GPIO_Port, &GPIO_InitStruct);
#endif
}
void lcd_delay_us(uint32_t us)
{
uint32_t i=0;
while(us--){
for(i=0;i<1000;i++);
}
}
void SPI_WriteByte(uint8_t Byte)
{
#if IO_SPI
uint8_t i=0;
for(i=0;i<8;i++)
{
if(Byte&0x80)
{
SPI_MOSI_SET;
}
else
{
SPI_MOSI_CLR;
}
SPI_SCLK_CLR;
SPI_SCLK_SET;
Byte<<=1;
}
#else
hal_spi_writebyte( Byte );
#endif
}
uint8_t SPI_ReadByte(void)
{
#if IO_SPI
uint8_t value=0,i=0,byte=0xFF;
for(i=0;i<8;i++)
{
value<<=1;
if(byte&0x80)
{
SPI_MOSI_SET;
}
else
{
SPI_MOSI_CLR;
}
byte<<=1;
SPI_SCLK_CLR;
lcd_delay_us(100);
if(SPI_MISO_READ)
{
value += 1;
}
SPI_SCLK_SET;
lcd_delay_us(100);
}
return value;
#else
return hal_spi_readbyte();
#endif
}
3.3 测试程序实现
创建lcd_test.c文件,编写测试程序
4 测试
在如下文件中调用测试程序,其主要实现每隔1s时间刷新屏幕的颜色。
详细代码:
void StartDefaultTask(void *argument)
{
/* USER CODE BEGIN StartDefaultTask */
int count;
LCD_Init();
LCD_Read_ID();
/* Infinite loop */
for(;;)
{
osDelay(1);
if(count%1000 == 0)
{
lcd_test();
}
count++;
}
/* USER CODE END StartDefaultTask */
}
运行结果如下: