使用模拟SPI接口驱动串行接口的LCD( STM32F4)

2024-05-13 16:28

本文主要是介绍使用模拟SPI接口驱动串行接口的LCD( STM32F4),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

概述

1. 硬件介绍

1.1 ST7796-LCD

1.2 MCU IO与LCD PIN对应关系

2 代码实现

2.1 STM32CubeMX 6.11生成工程

2.2 IO模拟SPI接口

2.3 实现LCD的驱动

3 测试


测试代码下载地址:

stm32-f407-lcd-ft6336-proj资源-CSDN文库

gitee下载地址:

https://gitee.com/mftang/stm32_open_test_proj/tree/master/stm32_f407_lcd_proj

概述

本文主要讲述使用模拟SPI接口驱动ST7796-LCD,主控MCU为STM32F407芯片。笔者详细介绍整个驱动的实现过程,并使用STM32Cube生成一个工程,测试驱动程序的功能。

1. 硬件介绍

1.1 ST7796-LCD

LCD的PIN引脚功能介绍

序号模块引脚引脚说明
1VCC屏电源正
2GND屏电源地
3LCD_CS液晶屏片选控制信号,低电平有效
4LCD_RST液晶屏复位控制信号,低电平复位
5LCD_RS液晶屏命令/数据选择控制信号

高电平:数据,低电平:命令

6SDI(MOSI)SPI总线写数据信号(SD卡和液晶屏共用)
7SCKSPI总线时钟信号(SD卡和液晶屏共用)
8LED液晶屏背光控制信号(如需要控制,请接引脚,如不需要控制,可以不接)
9SDO(MISO)SPI总线读数据信号(SD卡和液晶屏共用)
10CTP_SCL电容触摸屏IIC总线时钟信号(无触摸屏的模块不需连接)
11CTP_RST电容触摸屏复位控制信号,低电平复位(无触摸屏的模块不需连接)
12CTP_SDA电容触摸屏IIC总线数据信号(无触摸屏的模块不需连接)
13CTP_INT电容触摸屏IIC总线触摸中断信号,产生触摸时,输入低电平到主控(无触摸屏的模块不需连接)
14SD_CSSD卡片选控制信号,低电平有效(不使用SD卡功能,可不接)

实体LCD Port对应关系如下图所示

1.2 MCU IO与LCD PIN对应关系

STM32 PIN引脚LCD PIN引脚
PB5-MOSIMOSI
PB4-MISOMISO
PB3-SCKSCK
PB6CS
PB9RST
PB8RS

2 代码实现

2.1 STM32CubeMX 6.11生成工程

笔者尝试使用IO模拟SPI接口,以实现读写数据功能,其配置接口信息如下:

step-1: 配LCD对应的IO

step-2: 使能外部时钟

配置完成后,系统时钟关系如下:

 step-3: 生成工程

2.2 IO模拟SPI接口

1)头文件里定义MOSI,SCK和MISO的电平状态

#ifndef __LCD_SPI_H
#define __LCD_SPI_H#include <stdlib.h>
#include <stdio.h>
#include "main.h"// spi port io
// set IO to high 
#define SPI_SCLK_SET           HAL_GPIO_WritePin(lcd_sck_GPIO_Port,lcd_sck_Pin, GPIO_PIN_SET )
#define SPI_MOSI_SET           HAL_GPIO_WritePin(lcd_mosi_GPIO_Port,lcd_mosi_Pin, GPIO_PIN_SET )// set IO to low 
#define SPI_SCLK_CLR           HAL_GPIO_WritePin(lcd_sck_GPIO_Port,lcd_sck_Pin, GPIO_PIN_RESET )
#define SPI_MOSI_CLR           HAL_GPIO_WritePin(lcd_mosi_GPIO_Port,lcd_mosi_Pin, GPIO_PIN_RESET )#define SPI_MISO_READ          ((HAL_GPIO_ReadPin(lcd_miso_GPIO_Port, lcd_miso_Pin) == GPIO_PIN_SET)?1:0)  void SPI_WriteByte(uint8_t Byte);
uint8_t SPI_ReadByte(void);
void lcd_delay_us(uint32_t us);#endif /* __LCD_SPI_H */

2)实现SPI的读写接口

#include "lcd_spi.h"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)
{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;}
} uint8_t SPI_ReadByte(void)
{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;
} 

2.3 实现LCD的驱动

在该文件中实现初始化LCD,读写point等接口函数

#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);     //set the position Lcd_WriteData_16Bit(color); 
}uint16_t LCD_ReadPoint(uint16_t x,uint16_t y)
{uint16_t color;LCD_SetCursor(x,y);     //set the position 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();	// prepare the RAM
} 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 ControlLCD_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 ControlLCD_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();          // reset the LCD//*************3.5 ST7796S IPS pannel **********LCD_WR_REG(0x11);     lcd_delay_us(120);    //Delay 120mslcd_delay_us(120);    //Delay 120msLCD_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 ControlLCD_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  //00LCD_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); // set the driectionLCD_Clear(GREEN);              // clear the screen as green
}/* End of this file */

该driver对应的头文件:

#ifndef __LCD_DRV_H
#define __LCD_DRV_H#include <stdlib.h>
#include <stdio.h>
#include "main.h"//LCD重要参数集
typedef struct  
{ uint16_t width;      //LCD 宽度uint16_t height;     //LCD 高度uint16_t id;         //LCD IDuint8_t  dir;        //横屏还是竖屏控制:0,竖屏;1,横屏。	uint16_t  wramcmd;   //开始写gram指令uint16_t  rramcmd;   //开始读gram指令uint16_t  setxcmd;   //设置x坐标指令uint16_t  setycmd;   //设置y坐标指令
}_lcd_dev; 	//LCD参数
extern _lcd_dev lcddev;	//管理LCD重要参数/用户配置区///	 
#define USE_HORIZONTAL        0//定义液晶屏顺时针旋转方向 	0-0度旋转,1-90度旋转,2-180度旋转,3-270度旋转//	  
//定义LCD的尺寸
#define LCD_W 320
#define LCD_H 480//如果使用官方库函数定义下列底层,速度将会下降到14帧每秒,建议采用我司推荐方法
//以下IO定义直接操作寄存器,快速IO操作,刷屏速率可以达到28帧每秒! #define	LCD_CS_SET     HAL_GPIO_WritePin(lcd_cs_GPIO_Port,lcd_cs_Pin, GPIO_PIN_SET )
#define	LCD_RS_SET     HAL_GPIO_WritePin(lcd_rs_GPIO_Port,lcd_rs_Pin, GPIO_PIN_SET ) 
#define	LCD_RST_SET    HAL_GPIO_WritePin(lcd_rst_GPIO_Port,lcd_rst_Pin, GPIO_PIN_SET )#define	LCD_CS_CLR     HAL_GPIO_WritePin(lcd_cs_GPIO_Port,lcd_cs_Pin, GPIO_PIN_RESET )
#define	LCD_RS_CLR     HAL_GPIO_WritePin(lcd_rs_GPIO_Port,lcd_rs_Pin, GPIO_PIN_RESET ) 
#define	LCD_RST_CLR    HAL_GPIO_WritePin(lcd_rst_GPIO_Port,lcd_rst_Pin, GPIO_PIN_RESET )//画笔颜色
#define WHITE       0xFFFF
#define BLACK       0x0000  
#define BLUE        0x001F  
#define BRED        0XF81F
#define GRED        0XFFE0
#define GBLUE       0X07FF
#define RED         0xF800
#define MAGENTA     0xF81F
#define GREEN       0x07E0
#define CYAN        0x7FFF
#define YELLOW      0xFFE0
#define BROWN       0XBC40 //棕色
#define BRRED       0XFC07 //棕红色
#define GRAY        0X8430 //灰色
//GUI颜色#define DARKBLUE    0X01CF //深蓝色
#define LIGHTBLUE   0X7D7C //浅蓝色  
#define GRAYBLUE    0X5458 //灰蓝色
//以上三色为PANEL的颜色 #define LIGHTGREEN   0X841F //浅绿色
#define LIGHTGRAY    0XEF5B //浅灰色(PANNEL)
#define LGRAY        0XC618 //浅灰色(PANNEL),窗体背景色#define LGRAYBLUE    0XA651 //浅灰蓝色(中间层颜色)
#define LBBLUE       0X2B12 //浅棕蓝色(选择条目的反色)void LCD_Init(void);
void LCD_DisplayOn(void);
void LCD_DisplayOff(void);
void LCD_Clear(uint16_t Color);	 
void LCD_SetCursor(uint16_t Xpos, uint16_t Ypos);
void LCD_DrawPoint(uint16_t x,uint16_t y, uint16_t color);
uint16_t  LCD_ReadPoint(uint16_t x,uint16_t y);              //读点
void LCD_DrawLine(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2);
void LCD_DrawRectangle(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2);
void LCD_SetWindows(uint16_t xStar, uint16_t yStar,uint16_t xEnd,uint16_t yEnd);uint8_t LCD_RD_DATA(void);                                 //读取LCD数据  
void LCD_WriteReg(uint8_t LCD_Reg, uint16_t LCD_RegValue);
void LCD_WR_DATA(uint8_t data);
uint8_t LCD_ReadReg(uint8_t LCD_Reg);
void LCD_WriteRAM_Prepare(void);
void LCD_WriteRAM(uint16_t RGB_Code);
uint16_t LCD_ReadRAM(void);
uint16_t LCD_BGR2RGB(uint16_t c);
void LCD_SetParam(void);
void Lcd_WriteData_16Bit(uint16_t Data);
void LCD_direction(uint8_t direction );
uint16_t LCD_Read_ID(void);#endif    /* __LCD_DRV_H */

3 测试

1)调用接口,并编译项目

   LCD_Init();              // 初始化LCD 
   LCD_Read_ID();     // 读取LCD的驱动芯片ID

2) 运行代码

读取id测试:

清屏测试:

 

这篇关于使用模拟SPI接口驱动串行接口的LCD( STM32F4)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/986220

相关文章

Python常用命令提示符使用方法详解

《Python常用命令提示符使用方法详解》在学习python的过程中,我们需要用到命令提示符(CMD)进行环境的配置,:本文主要介绍Python常用命令提示符使用方法的相关资料,文中通过代码介绍的... 目录一、python环境基础命令【Windows】1、检查Python是否安装2、 查看Python的安

Python并行处理实战之如何使用ProcessPoolExecutor加速计算

《Python并行处理实战之如何使用ProcessPoolExecutor加速计算》Python提供了多种并行处理的方式,其中concurrent.futures模块的ProcessPoolExecu... 目录简介完整代码示例代码解释1. 导入必要的模块2. 定义处理函数3. 主函数4. 生成数字列表5.

Python中help()和dir()函数的使用

《Python中help()和dir()函数的使用》我们经常需要查看某个对象(如模块、类、函数等)的属性和方法,Python提供了两个内置函数help()和dir(),它们可以帮助我们快速了解代... 目录1. 引言2. help() 函数2.1 作用2.2 使用方法2.3 示例(1) 查看内置函数的帮助(

Linux脚本(shell)的使用方式

《Linux脚本(shell)的使用方式》:本文主要介绍Linux脚本(shell)的使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录概述语法详解数学运算表达式Shell变量变量分类环境变量Shell内部变量自定义变量:定义、赋值自定义变量:引用、修改、删

Java使用HttpClient实现图片下载与本地保存功能

《Java使用HttpClient实现图片下载与本地保存功能》在当今数字化时代,网络资源的获取与处理已成为软件开发中的常见需求,其中,图片作为网络上最常见的资源之一,其下载与保存功能在许多应用场景中都... 目录引言一、Apache HttpClient简介二、技术栈与环境准备三、实现图片下载与保存功能1.

Python中使用uv创建环境及原理举例详解

《Python中使用uv创建环境及原理举例详解》uv是Astral团队开发的高性能Python工具,整合包管理、虚拟环境、Python版本控制等功能,:本文主要介绍Python中使用uv创建环境及... 目录一、uv工具简介核心特点:二、安装uv1. 通过pip安装2. 通过脚本安装验证安装:配置镜像源(可

LiteFlow轻量级工作流引擎使用示例详解

《LiteFlow轻量级工作流引擎使用示例详解》:本文主要介绍LiteFlow是一个灵活、简洁且轻量的工作流引擎,适合用于中小型项目和微服务架构中的流程编排,本文给大家介绍LiteFlow轻量级工... 目录1. LiteFlow 主要特点2. 工作流定义方式3. LiteFlow 流程示例4. LiteF

使用Python开发一个现代化屏幕取色器

《使用Python开发一个现代化屏幕取色器》在UI设计、网页开发等场景中,颜色拾取是高频需求,:本文主要介绍如何使用Python开发一个现代化屏幕取色器,有需要的小伙伴可以参考一下... 目录一、项目概述二、核心功能解析2.1 实时颜色追踪2.2 智能颜色显示三、效果展示四、实现步骤详解4.1 环境配置4.

使用jenv工具管理多个JDK版本的方法步骤

《使用jenv工具管理多个JDK版本的方法步骤》jenv是一个开源的Java环境管理工具,旨在帮助开发者在同一台机器上轻松管理和切换多个Java版本,:本文主要介绍使用jenv工具管理多个JD... 目录一、jenv到底是干啥的?二、jenv的核心功能(一)管理多个Java版本(二)支持插件扩展(三)环境隔

SQL中JOIN操作的条件使用总结与实践

《SQL中JOIN操作的条件使用总结与实践》在SQL查询中,JOIN操作是多表关联的核心工具,本文将从原理,场景和最佳实践三个方面总结JOIN条件的使用规则,希望可以帮助开发者精准控制查询逻辑... 目录一、ON与WHERE的本质区别二、场景化条件使用规则三、最佳实践建议1.优先使用ON条件2.WHERE用