STM32CubeMX实战教程(七)——TFT_LCD液晶显示(附驱动代码)
液晶显示
- 前言
- 材料
- TFT_LCD
- FSMC接口
- 原理图
- 工程配置
- 进入代码
- 驱动程序引入
- 代码分析
- TFTLCD_Init
- LCD_Drawxxx
- LCD_Fill
- LCD_Showxxxx
- 功能实现
- 下载验证
- 结语
前言
想来想去,也不知道更新什么内容比较好了,犹豫了好久还是先跟大家讲讲液晶显示的配置吧,毕竟我觉得这个在很多项目中都非常实用,我个人是比较喜欢用一块TFT液晶来做显示终端的,大大的屏幕显示什么都方便,接到产品上面也显得特别高端,当然在考虑成本的情况下OLED和12864这些也是不错的选择。
材料
- STM32F4正点原子探索者
- 开发板原理图
- TFT_LCD(我这里用的是4.3寸的液晶,芯片为ILI9341,但理论上本驱动程序支持的芯片包括ILI9341/ILI9325/RM68042/RM68021/ILI9320/ILI9328/LGDP4531/LGDP4535/SPFD5408/1505/B505/C505/NT35310/NT35510/SSD1963等)
- LCD驱动芯片的手册(这里用的是ILI9341)
- 有手就行
TFT_LCD
由于TFT_LCD的知识,比较多,我这里尽量简明扼要地讲讲我认为开发中需要用的部分
FSMC接口
FSMC,即灵活的静态存储控制器,能够与同步或异步存储器和16位PC存储器卡连接,STM32的FSMC接口支持包括SRAM、NAND FLASH、NOR FLASH和PSRAM等存储器。
FSMC的框图如下图所示:
TFTLCD通过RS信号来决定传送的数据是数据还是命令,本质上可以理解为一个地址信号,比如我们把RS接在A0上面,那么当FSMC控制器写地址0的时候,会使得A0变为0,对TFTLCD来说,就是写命令。而FSMC写地址1的时候,A0将会变为1,对TFTLCD来说,就是写数据了。这样,就把数据和命令区分开了,他们其实就是对应SRAM操作的两个连续地址。当然RS也可以接在其他地址线上,而这个板子是把RS接在A6上面。
这里需要注意:FSMC接口驱动LCD时,其实是将LCD当作一个外部的SRAM来驱动的,唯一不同就是TFTLCD有RS信号,但是没有地址信号
FSMC驱动外部SRAM时,外部SRAM的控制一般有:地址线(如A0-A25)、数据线(如D0-D15)、写信号(WE,即WR)、读信号(OE,即RD)、片选信号(CS),如果SRAM支持字节控制,那么还有UB/LB信号。
STM32的FSMC支持8/16/32位数据宽度,我们这里用到的LCD是16位宽度的,所以在设置的时候,选择16位宽就OK了。FSMC的外部设备地址映像,STM32的FSMC将外部存储器划分为固定大小为256M字节的四个存储块。
STM32的FSMC存储块1(Bank1)用于驱动NOR FLASH/SRAM/PSRAM,被分为4个区,每个区管理64M字节空间,每个区都有独立的寄存器对所连接的存储器进行配置。Bank1的256M字节空间由28根地址线(HADDR[27:0])寻址。
这里HADDR,是内部AHB地址总线,其中,HADDR[25:0]来自外部存储器地址FSMC_A[25:0],而HADDR[26:27]对4个区进行寻址。如下表所示:
当Bank1接的是16位宽度存储器的时候:HADDR[25:1]-> FSMC_A[24:0]
当Bank1接的是8位宽度存储器的时候:HADDR[25:0]->FSMC_A[25:0]
不论外部接8位/16位宽设备,FSMC_A[0]永远接在外部设备地址A[0]
STM32的FSMC存储块1 支持的异步突发访问模式包括:模式1、模式A~D等多种时序模型,驱动SRAM时一般使用模式1或者模式 A,这里我们使用模式A来驱动LCD(当SRAM用)
原理图
这里跟之前的实验一样是用的正点原子探索者开发板,MCU为STM32F407ZGT6,其中原理图的LCD部分如下:
对应芯片中的引脚呢,是这样的(有点多):
其中部分引脚的功能呢,前面也已经讲到了,这里不再重复
复位脚和单片机的复位是接到一起的,也就是整个系统的复位,在程序中并没有额外操作
背光电源脚,开了屏幕才能亮
剩下几个脚是触摸屏才需要用到的,这里先不详说
ps:找到这些引脚的目的是在STM32CubeMX中进行比对,确保生成代码的引脚是对应的,要是引脚都对不上的话肯定驱动不起来咯,代码跑不了的第一步也是检查底层硬件对应情况,是个好习惯。
工程配置
这次做的是LCD显示字符串的配置,老规矩,基础配置不多说了,直接上图
我这边是开了一个LED灯作为系统运行指示的,LED配置具体也不说了哈,前面的
《STM32CubeMX实战教程(二)——按键点个灯》有详细介绍了。
然后这边最好开一个串口,并在程序中进行重定向,因为在驱动程序中需要打印一下LCD的ID,如果不开的话会卡在这里,具体会在后面程序分析的时候讲到,如果实在不想开启也可以将串口相关代码删掉,不影响系统运行。串口开启及重定向方法也可以参考 《STM32CubeMX实战教程(六)——串口通信(为啥你的中文会乱码)》
在配置FSMC之前先把背光电源脚使能一下,这个不属于FSMC的内容,如果下载程序后屏幕亮不起来那么八成是这里出问题了。
根据原理图背光脚是PB15,配置为上拉高速,初始电平为低。
下面是FSMC的配置,先上图
外设中选择FSMC,配置如下:
这边的选择根据在FSMC介绍的时候都已经详细讲到了,这里就只进行大致的说明。
1.NOR Flash/PSRAM/SRAM/ROM/LCD 1,这里选择这个也就是STM32的FSMC存储块1(Bank1)了
2. Chip Select,选择Bank1的第四区,是根据原理图的映射管脚进行选择的,这里选择不同区对应的引脚是不同的
3. Memory Type,存储类型,这里当然是选择LCD接口,那么里面还可以选择其他的存储类型,如果需要使用其他类型的存储设备也可以详细了解
4. LCD Register Select,这里是选择RS脚,也就是命令/数据选择位,同样是根据原理图得知这里应该选择A6
5. Data,数据位,很明显从原理图看出有16个数据引脚,这里选择16bits就好
接下来这里有一件事情需要大家注意一下,就是最好将目前为止的引脚使能情况跟原理图中的一一对应,也就是检查一遍,虽然花不了多少时间,但是由于使能了大量的引脚,一旦出错那么整个工程就是失败的
参数配置如下:
这里需要使能读写不同的时序,也就是Write operation,下面的参数分别是:
- 地址建立的时钟周期
- 数据建立的时钟周期
- 总线转阶段持续时间
- 扩展地址建立时间
- 扩展数据建立时间
- 扩展总线建立时间
这里扩展的意思就是写时序,而上面几条是读时序。
参数设置的根据是LCD芯片手册中的驱动时序,不同的芯片需要根据不同的手册进行计算,但计算的方法都是一样的,时序如下:
其中红框中的部分就是我们需要的,显然,WR就是写时序,RD就是读时序,对ILI9341来说,数据保持时间,其实就是低电平持续时间,地址建立时间,相当于高电平持续时间。那么剩下就很简单了,由于F4的一个HCLK=6ns(1/168M),稍加计算就可以得到需要多少个HCLK了。
到这里所有的工程配置工作就结束了,接下来偷个懒就进代码吧,怎么生成代码这里就不多说了。
进入代码
驱动程序引入
进来后第一件事当然是编译了,编译完后,需要加入一份驱动程序.,里面有头文件,.c文件和英文的字库,提取码为ljf5,是由正点原子的LCD驱动程序修改而来的,稍后我讲进行细致讲解。
我习惯将文件拷贝到工程目录的Drivers文件夹下,当然其他地方也是可以的,打开编译完成的工程,双击User目录,找到LCD文件夹里面,加入ILI93xx.c文件
并在C/C++选项卡中加入头文件路径。
然后如果是按照我之前的步骤配置过来的话,这个时候直接编译其实是没有任何问题的,非常简单(ps:串口重定向时需要在usart.h中包含头文件stdio.h,否则报错)
/* Includes ------------------------------------------------------------------*/
#include "main.h"/* USER CODE BEGIN Includes */
#include "stdio.h"
/* USER CODE END Includes */extern UART_HandleTypeDef huart1;
如果没有使能串口USART的话,在TFTLCD_Init函数里面有一句话需要注释掉,就是printf打印的这一句,应该是在482行这个位置,这句话负责打印检测到的LCD ID号,可以直接用串口助手看到。
if(lcddev.id==0X5761)lcddev.id=0X1963;//SSD1963读回的ID是5761H,为方便区分,我们强制设置为1963} }} printf(" LCD ID:%x\r\n",lcddev.id); //打印LCD ID 未使能串口则注释掉 if(lcddev.id==0X9341) //9341初始化{ LCD_WR_REG(0xCF); LCD_WR_DATA(0x00);
代码分析
在完成整个工程之前我先将这份驱动代码稍加分析,以及对其功能进行介绍,不然白拿的东西也不是自己的。
首先,打开ILI93xx.h也就是头文件,前面的一些变量申明暂且不用理会,那是在ILI93xx.c中调用的,这里注意一下第38行的
#define LCD_BASE ((uint32_t)(0x6C000000 | 0x0000007E))
这个0x6C000000的地址指的就是NE4,如果要选用其他区,或者移植其他型号的芯片上,那么这个位置需要对应修改!!!
。可以直接看到最下面的函数部分,这边我将几个常用的,没讲到的也可以自己调用试试,现象也是显而易见的。
TFTLCD_Init
void TFTLCD_Init(void); //初始化
LCD的初始化函数,这个是屏幕初始化必须的,这个函数非常长,里面也有详细的注释,主要是初始化各种不同型号的屏幕,所以适用的LCD也是非常多的,至于每一句是什么意思,这里也不需要深究了,因为不太建议自己重新写一份驱动,工程量大不说,还容易出错。
另外背光引脚在这里是有用到的,也就是倒数第二句点亮背光,所以整个工程我们不需要再去额外地操作这个引脚,只需要将其使能即可。
LCD_Display_Dir(0); //默认为竖屏GPIOB->ODR |= 1<<15; //点亮背光LCD_Clear(WHITE);
LCD_Drawxxx
void LCD_Draw_Circle(uint16_t x0,uint16_t y0,uint8_t r); //画圆
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); //画矩形
这几个是画图函数,x、y都是坐标,画圆中是原点坐标,划线中是起点和终点的坐标,矩形就是对角坐标(其实就是画四条线)
LCD_Fill
void LCD_Fill(uint16_t sx,uint16_t sy,uint16_t ex,uint16_t ey,uint32_t color); //填充单色
填充函数,通常用来清楚某一行或者某一个区域内已经显示的内容,填充的颜色内容可以在上面的define找到,这里的所有需要颜色的函数都是取自这里的值
//画笔颜色
#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 //浅棕蓝色(选择条目的反色)
LCD_Showxxxx
void LCD_ShowChar(uint16_t x,uint16_t y,uint8_t num,uint8_t size,uint8_t mode); //显示一个字符
void LCD_ShowNum(uint16_t x,uint16_t y,uint32_t num,uint8_t len,uint8_t size); //显示一个数字
void LCD_ShowxNum(uint16_t x,uint16_t y,uint32_t num,uint8_t len,uint8_t size,uint8_t mode); //显示 数字
void LCD_ShowString(uint16_t x,uint16_t y,uint16_t width,uint16_t height,uint8_t size,uint8_t *p); //显示一个字符串,12/16字体
字符、数字、字符串的显示函数,这里单个字符/数字也可以用字符/数字串显示函数来显示,数字也可以用字符串显示函数来显示,但字符不能用数字显示函数来显示
- 输入的参数也是横纵坐标
- 字体大小参数(size)选择12/16/24/32分别指代不同的字号
- 字符显示的模式参数(mode)取1时为叠加方式,即不改变原来坐标内已有的东西,在上面进行显示,相当于和原来的东西重叠在一起,0则清除原来的东西并进行显示
- 该程序不支持中文显示,因为中文显示需要中文字库,所以会占用很大内存,非常浪费,以后会讲到如何利用外部内存加载字库进行中文显示
功能实现
说了这么多,其实就是为了点亮这个屏幕,那么怎么点亮呢,其实也很简单,初始化好了自然就亮了,这边我写了一套测试程序,大家可以借鉴一下。
- main.c中引入头文件ILI93xx.h
- 在main函数中调用TFTLCD_Init函数,注意:必须在FSMC初始化函数也就是MX_FSMC_Init之后调用,因为这个函数是使能底层硬件的
- 可以写入你想显示的东西了
头文件
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "ILI93xx.h"
/* USER CODE END Includes */
main函数
/* Initialize all configured peripherals */MX_GPIO_Init();MX_FSMC_Init();MX_USART1_UART_Init();/* USER CODE BEGIN 2 */TFTLCD_Init(); /* USER CODE END 2 *//* Infinite loop */LCD_Clear(GREEN);LCD_ShowString(30,40,210,24,24,"What a nice day!"); LCD_ShowString(30,70,200,16,16,"TFTLCD TEST");LCD_ShowString(30,90,200,16,16,"2020/7/29"); /* USER CODE BEGIN WHILE */while (1){HAL_Delay(500);HAL_GPIO_TogglePin(LED_GPIO_Port,LED_Pin);/* USER CODE END WHILE *//* USER CODE BEGIN 3 */}
到这里就结束了,是不是非常简单~
下载验证
这边是我的板子上下载的现象,顺带拓展两点
- 本程序不支持中文显示,因为需要字库(前面也已经说过了)
- 不是说会驱动LCD就可以做出像手机一样的各种很漂亮的界面了,如果需要做的话,需要学习STemwin,相当于界面设计,且需要用到第三方库,emmm这个学起来还是比较复杂的。
具体下载方法这里不再重复,可查看《STM32CubeMX实战教程(一)——软件入门》,工程源文件我已经上传,在《STM32F4基于HAL库的LCD显示实验》
非常抱歉由于CSDN官网上传的资源必须要设定积分,否则几乎无法通过审核,这里就没有办法免费开发给大家,不过源码在教程里已经非常详细了。
结语
非常感谢大家的阅读,如有不当或者错误的地方,欢迎指正,谢谢支持。
一个字一个字敲出来不容易,如果觉得有帮助,点个赞再走呗
祝大家事业蒸蒸日上!
奥里给~
STM32CubeMX实战教程(七)——TFT_LCD液晶显示(附驱动代码)相关推荐
- STM32CubeMX 实战教程:定时器中断实验(LL 库)
1.资源的获取:关注博主并点赞对应博客,并在文章下留言邮箱索取: 2.资源的类型:获取资源仅限于免费公开文章或粉丝免费专享文章,不包括付费文章: 博主分享不易,请给三连哦(关注 + 点赞 + 收藏 ...
- STM32CubeMX实战教程(三)——外部中断(中断及HAL_Delay函数避坑)
外部中断 前言 材料 NVIC 新建及配置工程 外部中断和事件中断 NVIC配置 进入代码 回调函数 HAL_Delay()分析 下载验证 结语 前言 学单片机的,相信对中断的概念都已经了如指掌了,中 ...
- Linux创建线程读取双口数据,linux环境下读写一次双口ram尽然要十几个毫秒。(附驱动代码)...
linux环境下读写一次双口ram尽然要十几个毫秒.(附驱动代码) 我用的双口ram是IDT70V28,手册上说的读写时间应该是几个纳秒,我写了个linux驱动,然后做测试,发现读写一次的时间尽然是十 ...
- STM32CubeMX实战教程(一)——软件入门
软件入门 前言 新建工程 界面简介 MCU 外设配置 时钟树 工程设置 工具 生成代码 代码分析 main.c gpio.c stm32f4xx_it.c 程序下载 现象 结语 前言 STM32Cub ...
- 51单片机实战教程(33 步进电机的驱动与控制)
步进电机虽然同为电机的一种,但不能像直流电机或者交流电机那样按额定电压与电流给线圈通上直流或交流电机就可正常运转.它需要专用的驱动器或驱动电路供电,才可正常工作.每给驱动器一个脉冲信号,电机就转动一定 ...
- Android实战:网易新闻(附完整代码)
一.源码地址:https://github.com/ambition-hb/News_1 二.部分内容效果图如下: (1)广告页面 (2)新闻界面 (3)热点新闻 (4)热点新闻详情界面 (5)新闻页 ...
- R语言实战案例-蒙特卡罗方法(附实现代码)
前言 蒙特卡罗模拟已成为衍生证券定价和风险管理的重要工具,这些应用反过来刺激了对新的蒙特卡罗方法的研究,并重新引起了人们对某些旧有技术的兴趣. Monte Carlo simulation has b ...
- 数据分析实战之超市零售分析(附python代码)
目录 1.明确需求和目的 对一家全球超市四年(2011-2014)的销售数据进行 人.货.场分析,并给出提升销量的针对性建议. 场:整体运营情况分析,包括销售额.销量.利润.客单价.市场布局等具体情况 ...
- python下载某短视频平台音视频、评论、点赞数(详细教程,文末附完整代码
前言 整理一下python在抖音网页版的应用 以抓取一个视频及评论为例进行讲解 获取视频 url 随便找一个视频,点击进入详情,页面中地址栏里就是该视频的 url . 手机端复制链接后,先粘到浏览器地 ...
最新文章
- STM32中GPIO的8种工作模式
- 贝叶斯推断方法 —— 从经验知识到推断未知
- CCAI 2020 | 黄铁军:“仿真主义”下的人工智能发展之路
- MVC的Model Binder总结
- dwr框架ajax验证账号,dwr-Ajax开源框架
- Android APK 签名文件MANIFEST.MF、CERT.SF、CERT.RSA分析
- RTC实时时钟实验(低功耗、纽扣电池供电)
- C语言精髓篇|函数的参数和返回值
- ipad连接电脑_这些应用让iPad生产力分分钟UP
- 前端学习(3227):回调形式的ref的调用次数
- drop.delete.trauncat的区别
- mysql 刚安装完的用户_mysql 离线安装后用户初始化
- 基于FTP4J组件的FTP操作客户端
- matlab e52pt,matlab 神经网络的文字识别 有详细注释-补充样本
- 微信小程序学习资料[转]
- 环境试验之单臂跌落试验机操作规程
- android pak文件_Android 动态加载 (三) PAK 详解
- 计算机的按cpu分类,英特尔处理器分类有哪些 英特尔处理器分类详解
- linux md5加密文件,linux md5 加密字符串和文件方法
- specular BRDF