完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
扫一扫,分享给好友
1.3寸屏幕调试
测试平台:STM32F103RFT6 库版本:官方标准库3.5.0版本 屏幕:中景园1.3寸24Pin接插式LCD屏幕 分辨率:240*240像素 驱动芯片:ST7789 驱动方式:4线SPI 本文记录LCD屏幕的调试方法以及遇到的问题,选取STM32F103RFT6这款芯片主要是看重其高达96K的SRAM,在屏幕刷写时最多3次就能刷完整屏,同时也有SDIO功能,在后期还能加入SD卡读写功能(当然用SPI读写也可以),另外该芯片属于超大容量芯片,MDK配置的启动文件应选取【startup_stm32f10x_xl.s】 另外,为啥要表明该屏幕是中景园的呢?这里并不是打广告啊,本人早期也购买过其他店家的屏幕,但是几乎不提供技术支持,而且在商家发过来的例程中,明明白白的写着中景园几个字,我才知道有这么一家专门卖屏幕的,后来就只在该店购买了,一开始不太懂,调试一个0.96寸的小屏幕怎么也搞不定,还是联系客服找技术人员解决的,而且该商家有屏幕的生产资料以及配套的例程,这是极其重要的!!这个之后再解释。 注: 文中首次出现的代码块会标注[xxx.c]或[xxx.h],表明该代码是属于对应的文件,未标注的即为重复出现的 注: 附录中有驱动芯片下载网址 1、电路设计 如果你的LCD屏幕是带底板的模块,可以跳过该章节 如果你的LCD屏幕是裸屏的话,那么这章节的电路部分可以参考下 在中景园拿到的资料中有屏幕厂商的出厂参数,其中就有裸屏的尺寸参数以及驱动芯片的引出管脚功能描述: ▼尺寸参数,在PCB设计中可以保持屏幕居中 ▼引脚描述(自行翻译,搞这些的一般都看得懂简单的英语吧) ▼电路设计参考 其实该屏幕的驱动芯片ST7789并不止【4-SPI】和【8-并口】这两种驱动功能,还有其他的例如【3-SPI】等,具体参考ST7789数据手册的第8章节(FUNCTION DESCRIPTION ),但是在屏幕生产厂商的设计下,只配置出这两种驱动方式,这也是为啥要强调屏幕的生产资料的重要性,对于其他尺寸的屏幕也是如此。 本文中采用【4-SPI】,4线SPI模式,具体的应用电路设计如下▼ 背光: 图中的LEDA是背光的正极,LEDK是背光的负极,采用n-mos管【SI2302】驱动,在LCD_BKL输入高电平时n-mos管导通,LEDK接到GND,通过R13电阻可以调节背光的最大亮度,如果在LCD_BLK输入PWM,那么就可以调节屏幕亮度了 当然也可以用p-mos管,则R15接到VCC,在LCD_BKL输出低电平时导通,或者用三极管都是可以的。 信号接口: SPI_MOSI 主出从入 SPI_CLK 时钟线 LCD_CS LCD片选 LCD_DC 数据/命令端 LCD_RES 重启 LCD_TE 该管脚是用于处理屏幕撕裂效果的,目前调试没有用到,只是给出连接,以后估计会用到 没啥好说的,参考【引脚描述】连接上就行 这是我的PCB最终成品,大小是3x4cm,集成了SD卡,GT30L34S4W字库芯片,上方的排针是预留的接口,用来调试其他的屏幕: 电路设计基本上就这些,给用裸屏的小伙伴一个参考吧 2、程序设计 硬件连接: PA0 -> LCD_TE PA1 -> LCD_BLK PA2 -> LCD_RES PA3 -> LCD_DC PA4 -> LCD_CS PA5 -> SPI_CLK PA6 -> 没用到 PA7 -> SPI_MOSI 2.1、SPI配置(spi.c/spi.h) SPI配置按常规配置就行,这里使用的是SPI1,需要注意的是时钟极性CPOL和时钟相位CPHA需根据ST7789数据手册的时序图确定 在datasheet的8.4.2章节有4-line的时序图 串行时钟稳态是高电平,下降沿捕获数据 [spi.c] SPI_InitStructure.SPI_BaudRatePrescaler = speed; SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge; //数据捕获于第一个时钟沿 SPI_InitStructure.SPI_CPOL = SPI_CPOL_High; //串行时钟稳态 SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; //每次收发数据大小 SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //设置SPI单向/双向 SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //设置SPI模式,主/从模式 SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //指定数据传输从MSB位还是LSB位开始 SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //NSS信号由硬件(NSS管脚)还是软件(使用SSI位)控制 SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC值计算的多项式 为了提高写入速度,这里编写一个SPI发送函数,直接操作寄存器 [spi.c] void SPI1_WriteByte(u8 data) { SPI1->DR = data; } 2.2、LCD引脚配置(lcd.c/lcd.h) 就是普通的GPIO通用推挽输出配置,如果背光想要PWM调节,那么配置IO对应的TIM输出就行了,这里PA1接LCD_BLK,对应的是TIM5_CH2或者TIM2_CH2,选一个就行。本文不使用PWM调节。 2.3、LCD数据写入 需要编写三个函数: [lcd.h] #define LCD_WR_BYTE(Data) SPI1_WriteByte(Data) void LCD_WR_Cmd(u8 Data); //发送命令 void LCD_WR_Data8(u8 Data); //发送1byte数据 void LCD_WR_Data16(u16 Data); //发送2byte数据 函数原型: [lcd.c] /******************************************************************************** * @brief 写入1byte指令 * @param Data 写入的数据 * @retval none *******************************************************************************/ void LCD_WR_Cmd(u8 Data) { //DC为0时发送命令 LCD_DC_0; LCD_WR_BYTE(Data); } /******************************************************************************** * @brief 写入1byte数据 * @param Data 写入的数据 * @retval none *******************************************************************************/ void LCD_WR_Data8(u8 Data) { //DC为1时发送数据 LCD_DC_1; LCD_WR_BYTE(Data); } /******************************************************************************** * @brief 写入2byte数据 * @param Data 写入的数据 * @retval none *******************************************************************************/ void LCD_WR_Data16(u16 Data) { //DC为1时发送数据 LCD_DC_1; LCD_WR_BYTE(Data >> 8); //先发送高8位 LCD_WR_BYTE(Data); //后发送低8位 } LCD_WR_BYTE(Data)是通过宏定义替换的SPI1_WriteByte(Data),链接到SPI底层, 当LCD_DC = 0时,发送的是命令 当LCD_DC = 1时,发送的是数据 发送2byte数据主要是用在像素点颜色信息写入 LCD_DC_0和LCD_DC_1是宏定义,可以根据需求修改▼ [lcd.h] //----------------- LCD端口定义 -----------------// /****** 数据/命令控制端 ******/ //0:命令 1:数据 #define LCD_DC_0 (GPIOA->BRR = GPIO_Pin_3) #define LCD_DC_1 (GPIOA->BSRR = GPIO_Pin_3) /************ 片选 ***********/ //0:有效 1:无效 #define LCD_CS_0 (GPIOA->BRR = GPIO_Pin_4) #define LCD_CS_1 (GPIOA->BSRR = GPIO_Pin_4) /************ 重启 ***********/ //0:重启 1:正常 #define LCD_RES_0 (GPIOA->BRR = GPIO_Pin_2) #define LCD_RES_1 (GPIOA->BSRR = GPIO_Pin_2) /************ 背光 ***********/ //0:灭 1:亮 也可以用PWM调节亮度 #define LCD_BLK_0 (GPIOA->BRR = GPIO_Pin_1) #define LCD_BLK_1 (GPIOA->BSRR = GPIO_Pin_1) 2.4、 LCD初始化 有了LCD数据发送函数,就可以对驱动芯片ST7789进行初始化了 ▼屏幕重启 [lcd.c] /******************************************************************************** * @brief 重置屏幕 * @param none * @retval none *******************************************************************************/ void LCD_Reset(void) { LCD_RES_0; delay_ms(50); LCD_RES_1; delay_ms(50); } 这里并不涉及数据传输,只是将对应的IO置低再置高 ▼初始化 [lcd.c] /******************** TFT液晶屏初始化函数 **************************/ //液晶屏初始化 驱动:ST7789S void LCD_Config(void) { LCD_GPIO_Config(); //Reset before LCD Init. LCD_Reset(); //----------- Display and color format setting ---------// //图像翻转等操作,见9.1.29章节 #if USE_HORIZONTAL == 0 LCD_WR_Cmd(0x36); LCD_WR_Data8(0x00); #elif USE_HORIZONTAL == 1 LCD_WR_Cmd(0x36); LCD_WR_Data8(0xC0); #elif USE_HORIZONTAL == 2 LCD_WR_Cmd(0x36); LCD_WR_Data8(0x70); #elif USE_HORIZONTAL == 3 LCD_WR_Cmd(0x36); LCD_WR_Data8(0xA0); #endif //颜色数据格式,见8.8和9.1.33章节 LCD_WR_Cmd(0x3A); LCD_WR_Data8(0x05); //----------- ST7789S Frame rate setting ---------// LCD_WR_Cmd(0xB2); LCD_WR_Data8(0x0C); LCD_WR_Data8(0x0C); LCD_WR_Data8(0x00); LCD_WR_Data8(0x33); LCD_WR_Data8(0x33); LCD_WR_Cmd(0xB7); LCD_WR_Data8(0x35); //----------- ST7789S Power setting ---------// LCD_WR_Cmd(0xBB); LCD_WR_Data8(0x19); LCD_WR_Cmd(0xC0); LCD_WR_Data8(0x2C); LCD_WR_Cmd(0xC2); LCD_WR_Data8(0x01); LCD_WR_Cmd(0xC3); LCD_WR_Data8(0x12); LCD_WR_Cmd(0xC4); LCD_WR_Data8(0x20); LCD_WR_Cmd(0xC6); LCD_WR_Data8(0x0F); LCD_WR_Cmd(0xD0); LCD_WR_Data8(0xA4); LCD_WR_Data8(0xA1); //----------- Posistive Voltage Gamma Control ---------// //9.2.19 伽马调整,用于调节色彩对比度等参数 LCD_WR_Cmd(0xE0); LCD_WR_Data8(0xD0); LCD_WR_Data8(0x04); LCD_WR_Data8(0x0D); LCD_WR_Data8(0x11); LCD_WR_Data8(0x13); LCD_WR_Data8(0x2B); LCD_WR_Data8(0x3F); LCD_WR_Data8(0x54); LCD_WR_Data8(0x4C); LCD_WR_Data8(0x18); LCD_WR_Data8(0x0D); LCD_WR_Data8(0x0B); LCD_WR_Data8(0x1F); LCD_WR_Data8(0x23); LCD_WR_Cmd(0xE1); LCD_WR_Data8(0xD0); LCD_WR_Data8(0x04); LCD_WR_Data8(0x0C); LCD_WR_Data8(0x11); LCD_WR_Data8(0x13); LCD_WR_Data8(0x2C); LCD_WR_Data8(0x3F); LCD_WR_Data8(0x44); LCD_WR_Data8(0x51); LCD_WR_Data8(0x2F); LCD_WR_Data8(0x1F); LCD_WR_Data8(0x1F); LCD_WR_Data8(0x20); LCD_WR_Data8(0x23); //----------- Setting ---------// LCD_WR_Cmd(0x21); LCD_WR_Cmd(0x11);//关闭睡眠模式 LCD_WR_Cmd(0x29); } LCD初始化配置是根据中景园例程修改的,电压设置和伽马色彩调整部分不解释,需要注意的是颜色数据格式和屏幕方向设置 颜色数据格式(0x3A): //颜色数据格式,见8.8和9.1.33章节 LCD_WR_Cmd(0x3A); LCD_WR_Data8(0x05); 对应的寄存器是0x3A 在ST7789的datasheet中8.8章节DATA COLOR CODING 对各种控制方式下的色彩模式有详述的描写,在8.8.40小节是关于4-line SPI的色彩描述: 配置的参数是0x05,就是配置成65k真彩色,颜色格式是5:6:5的RGB比例 如果要显示图片,取模时就要注意按照这个颜色格式来取模了。 还记得在LCD数据写入小节中,发送2byte数据的写入函数吗?那个函数是配合这个格式来使用的,5-6-5的颜色是16bit(2byte),色彩也比4k彩色多,而262k则需要18bit的大小,数据传输不方便(如果用模拟SPI的话可以自己定制单次传输的数据长度) 屏幕方向(0x36): //----------- Display and color format setting ---------// //图像翻转等操作,见9.1.28章节 #if USE_HORIZONTAL == 0 LCD_WR_Cmd(0x36); LCD_WR_Data8(0x00); #elif USE_HORIZONTAL == 1 LCD_WR_Cmd(0x36); LCD_WR_Data8(0xC0); #elif USE_HORIZONTAL == 2 LCD_WR_Cmd(0x36); LCD_WR_Data8(0x70); #elif USE_HORIZONTAL == 3 LCD_WR_Cmd(0x36); LCD_WR_Data8(0xA0); #endif 这部分介绍见9.1.28章节▼ 主要是对[7:1]位进行配置,具体效果大家可以一位位的尝试,最终可以得出四个方向对应的值,分别是 0x00 0xC0 0x70 0xA0 当然,正确的数据配置还能实现X/Y轴翻转,实现镜像等操作,在8.12章节有很直观的图片说明。 ▼下面是用来判断使用哪个方向的宏定义,该屏幕是240x240的分辨率,在横竖屏下长宽都一样,不同尺寸的屏幕还需注意下,长宽在屏幕显示方向设置中很重要 [lcd.h] //----------------- 屏幕方向 -----------------// #define USE_HORIZONTAL 1 //设置横屏或者竖屏显示 0或1为竖屏 2或3为横屏 #if USE_HORIZONTAL == 0 || USE_HORIZONTAL == 1 #define LCD_W 240 #define LCD_H 240 #else #define LCD_W 240 #define LCD_H 240 #endif 初始化配置其他寄存器的值不再深入描述,想了解的看datasheet的第9章节【COMMAND 】 2.5、LCD显示 在完成LCD的配置后,要想在屏幕上显示东西,只须实现两个步骤 (1)设置显示区域 (2)发送显示内容 设置显示区域 [lcd.h] /******************************************************************************** * @brief 设置显示区域 * @param (x0,y0) 起点坐标 * @param (x1,y1) 终点坐标 * @retval none *******************************************************************************/ void LCD_SetRegion(u16 x0, u16 y0, u16 x1, u16 y1) { #if USE_HORIZONTAL == 1 y0 = y0 + 80; //Y轴中心点偏移80格 y1 = y1 + 80; #elif USE_HORIZONTAL == 3 x0 = x0 + 80; //X轴中心点偏移80格 x1 = x1 + 80; #endif LCD_WR_Cmd(0x2a);//列地址设置 LCD_WR_Data16(x0); LCD_WR_Data16(x1); LCD_WR_Cmd(0x2b);//行地址设置 LCD_WR_Data16(y0); LCD_WR_Data16(y1); LCD_WR_Cmd(0x2c);//储存器写 } 首先忽略【#if】到【#endif】之间的内容,可以看到发送了三个寄存器地址: 【0x2a】见9.1.20章节 【0x2b】见9.1.21章节 【0x2c】见9.1.22章节 ▼发送0x2a指令,再发送两个数据,包含4byte数据,设置显示的起始列和结束列 ▼发送0x2b指令,再发送两个数据,包含4byte数据,设置显示的起始行和结束行 ▼发送0x2a指令,则是告诉ST7789,下次进来的数据是用来更新显存RAM的数据,也就是更新每个像素点的颜色信息 中心点偏移: 现在回到【#if】到【#endif】这段内容,为啥在不同的方向下要偏移80格? 这就是一个比较有趣的问题了,先了解一些内容: (1) 宏定义▼ #define USE_HORIZONTAL 1 //设置横屏或者竖屏显示 0或1为竖屏 2或3为横屏 (2) 在初始化中关于方向设置里提到的8.12章节里有对显示方向的描述,这里是其中一部分的截图▼ 当【USE_HORIZONTAL】的值为0的时候,对应的是上图的【Normal】情况 当【USE_HORIZONTAL】的值为1的时候,对应的是上图的【X-Mirror Y-Mirror】情况,"F"旋转180° 这里定义这个方向为竖屏 (3) ST7789芯片描述▼ 很明显ST7789是可以驱动240x320像素点的一款芯片,而该屏幕只用到240x240,还剩下了240x80个 相信现在大家能知道这80格偏移是怎么来的了吧 以【Normal】为竖屏的正方向来说明(下图左),实际在屏幕设计生产时,芯片的显存RAM的映射到对应到屏幕的点阵是固定好的,无法通过软件修改,是硬件上的连接▼ 由于映射是固定的,在修改显示方向的时候(上图右),RAM的起点(0,0)移到右下角,RAM上对应的有效显示区域起点不再是(0,0),而是(0,80),因此在Y方向上需要偏移80格,把第一个像素点的颜色数据写到RAM上的(0,80)上,才能正常显示 对于横屏也是如此,可根据8.12章节图表自行分析 需要注意的是,这个偏移是由屏幕生产厂商决定的,不同厂商的映射地址不一定是相同的,对于上图左边的正常模式,也有可能是RAM的(0,20)点对应屏幕的(0,0)点,因此配套的资料和例程很重要,不然得自己去测试究竟偏移了多少 ▲上图右边是本人手上另一块屏幕,中景园的0.96寸彩色LCD,ST7735驱动芯片,最大可支持像素点为132x162,但是屏幕分辨率为80x160,RAM和屏幕点阵映射对应关系如图(在配套的例程中有给出),是RAM的(26,1)对应屏幕点阵的(0,0),因此四个方向的中心点偏移都是不同的 到此,显示的第一步:设置显示区域完成 发送显示内容 设置好区域后,只需要往里边发送数据,就能在该区域内显示,每2byte数据对应一个像素点的颜色信息,数据是【从左到右,从上到下】地连续写入每个像素点,在显示区域自动换行,直到显示区域的最后一个像素点,若此时继续写入数据,则从显示区域的第一个像素点开始重新写入 [lcd.c] /******************************************************************************** * @brief 整屏刷单色 * @param color 颜色 * @retval none *******************************************************************************/ void LCD_Single_Color(u16 color) { u8 i,j; LCD_SetRegion(0,0,LCD_W,LCD_H); for (i = 0; i < LCD_W; i++) for (j = 0; j < LCD_H; j++) LCD_WR_Data16(color); } 这是一个整屏刷单色的实现函数,其中LCD_W和LCD_H是之前出现的屏幕高宽宏定义 3、总结 除去SPI配置和LCD对应IO口配置外,本文给出了以下函数 void LCD_WR_Cmd(u8 Data); void LCD_WR_Data8(u8 Data); void LCD_WR_Data16(u16 Data); void LCD_Config(void); void LCD_SetRegion(u16 x0, u16 y0, u16 x1, u16 y1); void LCD_Single_Color(u16 color); 后 调用函数LCD_Single_Color(YELLOW);即可以整屏刷颜色 这是对LCD屏幕的基础配置,对于不同尺寸的LCD屏幕配置大同小异,查看驱动芯片数据手册,屏幕出厂手册,了解了其中的各种细节,基本上不会有问题 对于这个1.3寸屏幕,本文只是进行配置以及简单的单色刷写,其价值并没有体现出来,下一篇将会讲解如何显示图片。 |
|
|
|
只有小组成员才能发言,加入小组>>
3319 浏览 9 评论
2997 浏览 16 评论
3495 浏览 1 评论
9065 浏览 16 评论
4088 浏览 18 评论
1185浏览 3评论
611浏览 2评论
const uint16_t Tab[10]={0}; const uint16_t *p; p = Tab;//报错是怎么回事?
601浏览 2评论
用NUC131单片机UART3作为打印口,但printf没有输出东西是什么原因?
2337浏览 2评论
NUC980DK61YC启动随机性出现Err-DDR是为什么?
1898浏览 2评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-25 19:51 , Processed in 0.983213 second(s), Total 50, Slave 41 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号