STM32F4使用FSMC驱动4.3寸触摸屏(HAL库+CubeMX)

编程入门 行业动态 更新时间:2024-10-09 16:28:47

STM32F4使用FSMC驱动4.3寸<a href=https://www.elefans.com/category/jswz/34/1759133.html style=触摸屏(HAL库+CubeMX)"/>

STM32F4使用FSMC驱动4.3寸触摸屏(HAL库+CubeMX)

首次更新时间:2023.5.23  

---------------------------------------------------------分割线---------------------------------------------------------------

目录

1、前言

2、硬件连接

3、使用STM32CUBEMX & HAL库来驱动屏幕

4、代码移植

5、触摸部分

6、效果图

7、裸机 + LVGL图形库

8、实时操作系统FreeRTOS + LVGL图形库

9、使用GUI Guider来生成LVGL工程,再导入配置好FreeRTOS的工程中


1、前言

        最近一个项目需要用到触摸屏,然后手头恰好有一个正点原子家的4.3寸触摸屏,就是下图这个:官网资料文档

 这是一个8080接口的TFT-LCD屏幕,可以用8080接口来驱动,也可以用stm32的FSMC(可变静态存储控制器——Flexible Static Memory Controller)接口来驱动,具体有关FSMC时序感兴趣的话可以去网上查,我这里就不再赘述了(我也不是很懂~)。然后它的触摸(touch)是采用IIC来进行通信的,比较奇怪的一点是PCBA上丝印却写着MOSI & MISO & SCK & CS,一度让我以为它是用SPI来通信。。。

        然后手头还有一块STM32F4的最小系统板,类似下图这种:

 主控芯片是STM32F407ZGT6,1M的flash以及192k的sram,恰好它有一个TFT接口,所以这两个搭配在一起可以满足我的需求。

2、硬件连接

        在移植前,必要的步骤是连线。说实话这要连30多根杜邦线挺让人头疼的。。。下面先来看一下二者的原理图以及部分必要的资料。

LCD原理图:

 部分参数以及驱动芯片(稍微看看就好,主要是驱动芯片型号):

 stm32f4的LCD驱动原理图:

 然后这里的FSMC对应主控引脚比较乱,可以将就看一下:

 

 

         上面的原理图部分比较乱,,,细心找一下对应引脚还是能够找出来的。用途就是待会开启FSMC的时候需要对应引脚,防止选错了别的引脚。下面放一张详细的引脚对应图:

         图中3.3V和GND连接就不用解释了,仔细检查别连错了就行(不然就准备出魔法烟雾了~);然后NC表示(Normal Close)不需要连接;D0~D15表示16bit的数据线,对应连接就可以了;另外屏幕的V5引脚需要接到板子上的5V来给背光供电,否则屏幕不会亮的(被坑过...);RST复位引脚、BLK背光引脚对应连接;重点来了:上方的RD、RS、WR、CS于下方的FSMC_A6、FSMC_NOE、FSMC_NE4、FSMC_NWE怎么连呢?如下图:

         以上就是屏幕显示部分的连线,下面来说一下touch部分的连线;其实是对应相连就好了,主要是要仔细检查一下是否连错线。CLK->T_SCK、MOSI->TMOSI、TCS->T_CS、PEN->T_PEN 然后NC和T_MISO是不用连接的(吐槽:明明是I2C通信,你就写SCL和SDA嘛,你写个MOSI和MISO啥意思啊,这不是纯纯坑人吗?它这里的SCK就是I2C里的SCL线,MOSI就是SDA线,然后CS是片选,PEN是读取触摸的时候使用,而MISO不需要连接)

3、使用STM32CUBEMX & HAL库来驱动屏幕

        接下来用stm32cubemx来驱动屏幕:

首先开启外部晶振:

打开串行调试接口:

 配置系统时钟(记住要选中HSE外部晶振,然后输入168M,再一直敲回车就行):

 选中Connectivity中的FSMC:

然后选NOR Flash/.../LCD1,重点来了:Chip Select选择NE4,这是因为前面连线步骤中连的是FSMC_NE4,需要一一对应;Memory type选择LCD接口;同理,LCD Register Select选择A6,这是因为连的是FSMC_A6;最后Data选择16bit,数据位宽16位(D0~D15一共16根线):

 选完之后才会弹出Configuration,参数照抄就行(我也不知道怎么解释):

 最后选择背光引脚,来控制屏幕的亮灭:

最后设置好Project Manager之后就可以愉快地Generate Code了~ 

 4、代码移植

        博主用的是原子家的代码,然后“略微”修改(开玩笑,CV战士可不是盖的),这里主要讲一下移植过程需要修改的地方,总的代码太长了,放上来影响观感,而且也很难看。(文末有链接,直接下载源工程;ps:最讨厌那种设置ji fen下载的,开源相互学习不好么hhh~)

        正点原子家的屏幕驱动代码是3个文件(不包含touch),分别是lcd.c、lcd.h以及font.h;拿到代码之后添加到自己工程中,font.h是字库文件,不需要修改;lcd.h是函数原型声明、宏定义、类型定义以及一些全局变量,也不需要怎么修改;重点是lcd.c源文件:首先找到HAL_SRAM_MspInit函数,然后全部注释掉,因为cubemx已经全部帮我们生成了;然后找到初始化函数LCD_Init,将所有初始化FSMC的部分注释掉,因为cubemx也已经全部生成了;最后找到LCD_Init函数的最尾部,关注点亮背光的那一句:

LCD_LED=1;				//点亮背光

如果屏幕不亮的话,尝试改为LCD_LED=0;我这款板子就是下图这样:

 如果给1的话三极管是不导通的,所以屏幕点不亮;所以要给0才能点亮。最后在主函数初始化部分调用LCD_Init();即可初始化屏幕显示。

5、触摸部分

        接下来是触摸部分。根据硬件连线部分连好线之后,将下面的代码全部添加到工程中:

         这里之所以不用Cubemx来初始化是因为它代码里面已经用HAL库写好了对应的GPIO初始化,所以就没必要再在Cubemx里面再初始化一遍;这几个文件中,除了touch.c之外,其他的文件只要引脚没错就不需要修改代码;下面是关于如何修改touch.c:

它的源代码中囊括了电阻触摸屏和电容触摸屏的代码,而电阻触摸屏初始化的时候需要配合EEPROM来进行校准,但是我们这块屏幕是电容触摸,不需要校准,所以要将电阻触摸以及EEPROM的部分都注释掉;此外,它有一个全局变量tp_dev,这个结构体初始化的时候需要传入TP_Adjust函数指针,虽然我们用不到这个函数,但是不定义这个函数的话就会报错,所以我定义了一个空函数来避免报错:

void TP_Adjust(void)
{//as function pointer,avoid error//作为一个函数指针传给那个全局结构体,避免报错
}

另外,我通过调试发现这款LCD的ID值是0X5510,所以将TP_Init里面的if(lcddev.id==0X5510)之外的所有代码都注释掉了。最后在主函数初始化部分调用tp_dev.init();即可初始化触摸接口。

        那关于主函数的剩余部分均是用来测试显示以及测试触摸的,直接复制运行即可。

6、效果图

效果图1:

效果图2:

 

工程链接: 
提取码:0xFF

---------------------------------------------------------分割线---------------------------------------------------------------

更新日期:2023.5.27

7、裸机 + LVGL图形库

lvgl的版本是release 8.3.3

没下载源码的先去Github下载源代码,然后再移植

进去GitHub之后,点左上角master,选择tag,再选版本8.3.3,然后再下载:

 下载后得到的文件夹如下:

 下面开始移植。首先打开我之前提供的工程文件夹,再打开.ioc文件(cubemx),然后只需要做一件事:修改最小的栈大小为0x1000(十进制下就是4kB),这是因为lvgl最小要求2kB,我们可以适当给大一点,最后直接生成代码即可:

进去之后,可以把之前的测试函数全部删了,就是在/* USER CODE BEGIN 0 */和/* USER CODE END 0 */之间的代码,还有主函数中,只需要留下初始化代码即可:

 然后编译优化改为Level 0,compiler勾上C99(一般情况下debug时用Level 0,release的时候用Level 1即可,然后C99模式是LVGL必须的,不然会报几千个错):

 然后取消勾选Use MicroLIB,这是因为LVGL在用QR Code控件的时候,勾上这个会报错,当然你不用的话就无所谓:

 最后编译一下,如果像下面这样报了很多Warning:

 就去魔术棒->C/C++那里,取消勾选Split Load and Store Multiple选项,然后重新编译:

 结果:

 下面开始移植文件:

首先在工程目录下新建一个新文件夹(名字任意):

 进入GUI文件夹,再新建两个文件夹lvgl以及lvgl_port:

 然后将lvgl-8.3.3下的src整个文件夹以及lvgl.h文件复制到刚刚新建的lvgl中:

 再然后将“lvgl-8.3.3\examples\porting”下的所有文件复制到刚刚新建的lvgl_port中:

 再将各个文件重命名:

接着将“lvgl-8.3.3\lv_conf_template.h”复制到GUI下面,并改名为lv_conf.h:

 接下来进入Keil中,添加源码到工程目录中。

首先新建两个工程子目录GUI/lvgl以及GUI/lvgl_port(名字随意都可以,但是最好方便辨认):

 然后将“f407zgt6\GUI\lvgl\src”下的所有.c文件加入GUI/lvgl中(注意是所有!过程有点繁琐,可以用Ctrl+A全选加入),就是一个一个文件夹点开,看到.c就add就可以了:

 最终结果:

然后再给GUI/lvgl_port加入“f407zgt6\GUI\lvgl_port”的3个.c文件:

 最后再将“f407zgt6\GUI\lv_conf.h”加入到User组,方便找到:

 然后进入魔术棒,修改头文件包含路径,主要是添加这3个:

 接下来开始修改代码。

首先打开lv_conf.h,将#if 0 改为#if 1,使能编译:

然后这行的16表示RGB565(5bit作为R,6bit作为G,5bit作为B)格式,正好符合我用的屏幕,所以不需要修改:

 还有第52行,可以视情况设置内存,这里给的是48K:

 还有这几行,如果设置为1的话,到时候就会显示CPU运行以及显示帧率,这里没有打开:

 接下来打开lv_port_disp.c(这个就是显示接口文件),首先将#if 0改为1,使能编译;再将#include "lv_port_disp_template.h"改为#include "lv_port_disp.h"(因为之前给文件改过名了);接着再定义分辨率如下:

 这里我准备选择横屏,所以水平设置为800,垂直设置为480,如果想竖屏的话就换过来;接着打开lv_port_disp.h,将#if 0改为1:

 然后打开lv_port_indev.c(这个就是触摸接口文件),首先将#if 0改为1,使能编译;再将#include "lv_port_indev_template.h"改为#include "lv_port_indev.h"(因为之前给文件改过名了),最后再将#include "../../lvgl.h"改为#include "lvgl.h":

 接着打开lv_port_indev.h,将#if 0改为1:

下面开始配置显示屏接口函数 :

 回到lv_port_disp.c文件,往下找到lv_port_disp_init函数:

 然后将后面两个Example注释掉,用单缓冲(Example1)来画,节省一点ram,当然也可以用另外的两个,不过会占用更多的内存:

然后找到disp_flush函数:

首先去前面#include我们的lcd.h文件,因为待会要用里面的函数;接着在color_p++;上面加入一个快速画点函数:

 这里简单说说怎么写出来的:这个形参color_p是一个指向某一色块的起始点的指针,拿到之后就强制转换为u32型的指针,接着把它的内容读出来(就是色块的颜色了),最后利用快速画点函数来绘制;color_p++就是移动这个色块指针,直到绘制完整个色块。

 下面开始配置触摸接口函数:

来到lv_port_indev.c文件,找到lv_port_indev_init函数,将Touchpad以外的部分都删掉(输入设备可以是触摸屏、鼠标、键盘、编码器、按键,因为这里我只使用触摸屏,所以其余的删掉),剩下如下部分:

 然后找到touchpad_is_pressed和touchpad_get_xy两个函数:

 修改如下(记得在前部分#include "touch.h"):

接着编译一下:

没有报错,有很多警告都是函数定义未使用,可以先忽略;接着来到主函数,包含三个头文件:

然后在主函数中进行初始化(注意顺序,lv_init()必须最先调用):

 

 再配置LVGL的心跳,这个是必须的,不然LVGL无法运转:

来到lv_conf.h,找到88行:

修改为如下(采用SysTick滴答定时器来提供1ms心跳):

 回到main.c,下面可以来跑一个demo【网上找的,别人写的----->CV攻城狮(自豪)】了:

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
static void btn_event_cb(lv_event_t * event)
{lv_obj_t *btn = lv_event_get_target(event);              //获得事件最初瞄准的对象。即使事件是冒泡的,也是一样的。if(event->code == LV_EVENT_CLICKED) {static uint8_t cnt = 0;cnt++;/*Get the first child of the button which is the label and change its text*/lv_obj_t * label = lv_obj_get_child(btn, NULL);lv_label_set_text_fmt(label, "Button: %d", cnt);}
}static void lvgl_first_demo_start(void)
{lv_obj_t * btn = lv_btn_create(lv_scr_act());           /*Add a button the current screen*/lv_obj_set_pos(btn, 10, 10);                            /*Set its position*/lv_obj_set_size(btn, 120, 50);                          /*Set its size*/lv_obj_add_event_cb(btn, (lv_event_cb_t)btn_event_cb, LV_EVENT_CLICKED, NULL);/*Assign a callback to the button*/lv_obj_t * label = lv_label_create(btn);                /*Add a label to the button*/lv_label_set_text(label, "Yeah");                       /*Set the labels text*/lv_obj_t * label1 = lv_label_create(lv_scr_act());lv_label_set_text(label1, "Hello world!"); lv_obj_align(label1, LV_ALIGN_CENTER, 0, 0);lv_obj_align_to(btn, label1, LV_ALIGN_OUT_TOP_MID, 0, -10);
}
/* USER CODE END 0 */

 然后写如下代码:

 接着编译:

大功告成! 

下面来看看效果:

工程链接: 
提取码:0xFF

--------------------------------------------------------分割线---------------------------------------------------------------

更新日期:2023.6.2

8、实时操作系统FreeRTOS + LVGL图形库

我又来了!这部分我加入了实时操作系统FreeRTOS,话不多说,直接开始移植。

首先打开我之前的工程,点一下全局编译(rebuild),确保无错:

然后打开cubemx,找到中间件Middleware的FREERTOS,选择CMSIS_V2:

 然后会蹦出一堆参数,先别急,我们只需要修改少数几个参数就行。

 首先在Kernel setting(内核设置)中找到USE_MUTEXES,确保其为Enable;这是一个宏定义,作用是使能mutex——互斥信号量,待会要用到:

然后再往下找,找到Hook function related definitions(钩子函数相关定义),把tick的钩子函数打开,这样,在每个systick时钟1ms中断的时候,就会自动调用一个特定的函数,作用就是我可以用来提供LVGL的1ms心跳:

 接下来去创建LVGL任务:

第一个是任务句柄的名字,随便取都可以,最好能够简洁地表示意思;第二个是任务优先级,这里我设置得比defaultTask高一级;第三个是任务堆栈大小,单位是字(Words),可以尽量给高一些,到时候可以在程序里面用获取高水位值的函数来读取任务剩余堆栈大小,就可以根据需求来调整堆栈大小(1024个words就是1024 * 4 = 4096个bytes);第四个是任务调用的函数名字,也可以随便取,最好跟任务句柄挂钩方便确认;然后下面的参数保持默认即可:

 

 接下来去创建一个互斥锁,因为LVGL是线程不安全的,所以需要一个互斥锁来避免出错(可以简这样简单地理解:当一个任务在访问LVGL中的变量,如果另外一个任务来修改了LVGL中的变量,那么就会导致变量值不确定,从而导致错误的结果):

 最后去修改一下堆栈大小(这里提一下,一般正常用的话给个几k就可以了,假如程序报错,再改),接着生成代码:

 然后就出现了一个警告:

 这是因为用了FreeRTOS之后,RTOS默认使用Systick来作为时基单元,而HAL库也默认用了Systick来作为时基单元,二者冲突了,有可能导致出错(具体出说明错我也不太清楚,反正既然报了警告的话咱就去改一下)。

点击No,回到cubemx工程面板,找到HAL库时基配置面板,如下:

可以随便选一个定时器作为HAL库的时基单元,这里我选了定时器6,接着生成代码:

打开keil,先不点编译,首先打开delay.c文件,把第6行注释,第7行放出来,接着修改10,11行,选择HAL库时钟为htim6(因为我刚刚选了定时器6),让延时函数能够正常运行:

接着打开lv_conf.h文件,找到88行,这次我们用systick的钩子函数来提供lvgl心跳,所以这里直接把LV_TICK_CUSTOM取消掉 :

然后打开freertos.c文件,往下找到84行的钩子函数:

在这里添加LVGL的心跳(记得先去前面include一下lvgl.h):

接着往下找到LVGL的任务函数:

 

 前面说了,LVGL线程不安全,需要互斥锁来互斥访问变量,FreeRTOS中获取互斥锁的函数为xSemaphoreTake,这是一个宏函数,但是当我输入这个函数的时候报错,说函数无定义,这是因为没有包含特定头文件的缘故:

其实这个宏函数包含在头文件semphr.h里面,包含进来即可:

 可以看到需要两个参数,第一个是互斥量的句柄,第二个是等待的时间,选择一直等待即可,也就是只有当前任务拿到了互斥量,才去访问LVGL,否则不访问LVGL:

然后进行触摸扫描以及lvgl的处理函数;结束之后还要通过 xSemaphoreGive函数来释放互斥量,等待下一次调用;xSemaphoreGive函数只有一个参数,就是传进去互斥量的句柄即可;最终函数如下:

 接下来还有最后一步,打开touch.c文件,找到63行TP_Read_AD函数,将整个代码加入任务切换的临界段,也就是扫描的时候不进行任务切换(需要先在前面include FreeRTOS.h以及task.h):

 然后编译:

下载后发现可以和之前裸机得到一样的效果! 

链接: 
提取码:0xFF

---------------------------------------------------------分割线---------------------------------------------------------------

9、使用GUI Guider来生成LVGL工程,再导入配置好FreeRTOS的工程中

        这应该是最后一部分了,整篇文章就完结了;GUI Guider可以通过绘制界面来自动生成lvgl代码,大大方便了界面的设计,有关GUI Guider的相关资料以及安装教程可以去网上查,注意的是GUI Guider依托java环境的,所以需要先装一个java环境,具体可以去网上查,下面我来介绍如何移植。

首先打开GUI Guider页面:

选定路径创建工程(不能有中文路径,外国软件,你懂的~);然后模板选择空白模板,颜色选择16bit,SIZE选择800 * 480,最后点CREATE:

然后进入主界面:

选择一个按钮,拖到空白界面处,这里我拖了两个按钮:

选中按钮,然后修改参数如下图:

同理,另外一个按钮同样设置,并且贴入一个文本label标签:

然后选中按键1,添加事件:

如下图,触发事件是点击,也就是点一下就触发;触发源就选btn0,也就是按键1,行为选择改变按键背景颜色为红色:

按键2同样设置:

接下来运行模拟看看效果(如果运行模拟报错的话大概率是java环境没配好):

 

 点一下变红:

再点另外一个变绿:

 

然后回到工程中,点运行模拟旁边的生成代码按钮,生成代码后再开始移植:

下面开始移植代码;首先拿到最初始的版本,也就是只有fsmc驱动的工程,接着打开cubemx,按照第7部分移植好FreeRTOS(记得调堆栈大小);然后打开工程文件夹,新建文件夹GUI:

进入该文件夹,新建三个文件夹:

打开GUI Guider生成的工程文件夹, 将lvgl文件夹中的所有文件copy到f407zgt6文件夹的lvgl中:

 接着将generated的所有文件copy到lvgl_app中:

然后打开lvgl/example/porting,将其中的6个文件复制到lvgl_port中,并且改名:

 

 

然后再将lvgl/src中的两个配置文件copy到GUI下,并改名:

 

接下来进入Keil,配置工程目录;首先新建3个工程目录:

然后将lvgl_app、lvgl_port、lvgl中的c文件加入对应目录:

 

注意的是GUI/lvgl组的是src下的所有c文件(除了lv_gpu文件夹) 

然后添加头文件路径,并修改优化选项为Level 0:

下面开始修改文件,首先打开lv_conf.h,使能文件,并修改分辨率:

接着修改显示接口以及触摸接口的.c和.h文件:

 

然后找到头文件 guider_fonts.h,修改包含内容如下:

接着去到显示文件,采用单缓冲:

然后修改分辨率以及显示buffer:

再找到屏幕刷新处,加入画点函数:

然后去到触摸接口文件,修改触摸驱动如下:

接着回到主函数,包含其他头文件:

并添加如下代码:

然后进行初始化:

 接下来在Tick的钩子函数中给lvgl提供心跳:

之后,去到events_init.c文件,初始化按键事件(这个初始化函数在文件最下面): 

最后是去freertos.c中执行lvgl任务即可(具体过程参考第7点):

点击编译,经过漫长等待后,结果如下:

大功告成!

下载进板子中,效果如下:

工程链接: 
提取码:0xFF

以上均为个人学习心得,如有错误,请不吝赐教~

THE END

更多推荐

STM32F4使用FSMC驱动4.3寸触摸屏(HAL库+CubeMX)

本文发布于:2024-02-25 11:50:02,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1698879.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:触摸屏   FSMC   CubeMX   HAL

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!