singleyork 发表于 2020-2-12 15:35:48

【开源】基于HAL库的《STM32与迪文DGUS屏通信》项目实战

大家好,我是微信公众号『芯知识学堂』的SingleYork!前面一直都有在写51单片机跟迪文DGUS屏的串口通信例程,然后就有好多小伙伴们加入到了我的Q群里来咨询STM32与迪文DGUS屏的例程,由于时间关系,笔者一直没能写这方面的例程,正好最近做项目要用到STM32,因而在验证项目功能的同时,顺便写了一下STM32与迪文DGUS屏通信的这个例程。要说STM32的串口通信,相信很多人都比我熟悉的多了,笔者纯粹就是个菜鸟,在看了硬石团队的一些视频之后,对STM32有了大致的了解,然后,尝试移植硬石的例程到自己的项目中,庆幸的是,硬石团队的例程写的真心不错,个人比较喜欢他们的代码风格,而且还是基于HAL库,移植起来也是非常的方便。
http://b05.ickimg.com/201903/acfbdb74781382621ffa25168ad16d67.png
   从上面的图片中,我们大致可以看到该例程使用的一些硬件资源:
[*]lLED的控制
[*]l按键检测
[*]l串口通信
[*]l基本定时器
      该例程的硬件环境,是原子的《战舰V3开发板》,这款开发板还是笔者好2016年用《电子芯吧客(原:云汉电子社区)》论坛的年终奖兑换的,当然咯,屏幕和别的模块,是笔者后面自己买的:
http://b05.ickimg.com/201903/9a7a70e945e9066cb5c34914cc66f9d3.png
       虽然硬件采用的是原子的战舰V3,但是笔者还是不太习惯用STM32的标准库来做项目,毕竟笔者对STM32的标准库是不怎么懂的,由于笔者之前使用了硬石的开发板,然后对HAL有了一定的了解,所以在该例程中使用了目前比较流行的HAL库来进行开发,然后,一些配置部分,是从《硬石F103开发板》上 移植过来的。那么,接下来,我们就来详细介绍一下代码部分吧。先来看个最简单的“bsp_led.h”中的代码吧:
http://b05.ickimg.com/201903/b62b94aa87e1385767de21013e5f0e50.png
       首先,定义的是一个枚举类型LEDState_TypeDef,用来表示LED的三种状态,即:亮、灭和状态反转,然后就是几个宏定义用来检查输入参数的合法性。后面部分就是定义每个LED对于的GPIO口。笔者比较菜,也是在接触了硬石开发板之后,才学会了这种定义的方法,这样做的好处,不仅是为了让程序布局美观,更重要的是可以方便移植,当做别的项目的时候,只需要将定义的GPIO及其对应的时钟替换成项目中所使用的就可以了。接下里就是”bsp_led.c”里面的代码了:
http://b05.ickimg.com/201903/aaab426583544c7c8accb3f76028e2c0.png
       这里面主要就是两个函数,一个就是上图中的这个GPIO初始化函数,这个函数主要就是对GPIO口的时钟、模式、速度等参数的配置。另外一个函数,就是用来控制输出LED的状态,即:亮、灭及状态反转,如下图所示:
http://b05.ickimg.com/201903/ed3fbce1f4357d7a514bfc96188e1e44.png
      接下来,我们来看另一个头文件bsp_key.h,这个文件跟之前的bsp_led.h的功能类似,这里也主要是用来定义KEY0、KEY1、KEY2三个按键具体使用的哪个GPIO口,还有其他的一些定义,功能比较简单,笔者就不多说了,一看代码就很清楚了:
http://b05.ickimg.com/201903/b70e279358984860ceb77edca78dd49f.png
      接下来就是bsp_key.c文件了,这里主要也是两个功能,一个是跟按键相关的GPIO口的初始化函数:
http://b05.ickimg.com/201903/c8aced2971717e2d87aa361750182c07.png
      另一个则是按键状态的读取函数:
http://b05.ickimg.com/201903/da671deb85d3a8d4ebafc0d3e385affc.png
      关于这个按键读取的函数,笔者在这里做了修改,而并非直接使用硬石团队提供的c文件,硬石团队提供的例程源码如下:
http://b05.ickimg.com/201903/8e454a66f90d3196dc74e1fc2f2a2219.png
      可以看到,硬石团队提供的源码跟大部分单片机的按键例程基本都类似,在按键按下后就一直在那死等按键松开,这个时候我们会发现,在按键按下而又没有松开的时候,程序会一直在那死等,别的事情都做不了,这样的方式,如果仅仅是实验或者说是学习,勉强还能凑合着用,但是,在实际的项目应用中是没多大用处的,所以笔记进行了一些小的修改,可以让我们既能方便的获取到按键的按下和谈起两种状态,又不必在那死等。当然咯,像这种扫描的方式,最好的办法那就是使用状态机了,网上也有很多的跟状态机相关的例程,感兴趣的小伙伴们也可以自行修改例程,如果不想自己去修改,那么笔者这种比较笨的办法,相信也能适应大部分场合了吧。除了扫描的方式,还有一种就是中断的方式,不过也不难,就留给读者们去自由发挥了吧,哈哈!接下来就是定时器相关的代码了,该例程中,定时器主要是用来设定一个时间让串口定时发送数据到迪文的DGUS屏上。笔者这里也是从硬石的例程中直接移植过来的,几乎也没做什么修改,这里使用的是基本定时器2,设置的定时器预分频系数为71,定时器周期为1000,即定时器每1ms产生一次中断,bsp_GeneralTIM.h代码如下:
http://b05.ickimg.com/201903/b1786184e2b8c503f1f4294138ac2273.png
      至于bsp_GeneralTIM.c文件,也是基本没做修改,在这个文件中,主要实现了三个功能,即:定时器的初始化、定时器硬件初始化配置、定时器反初始化配置:
http://b05.ickimg.com/201903/6e362a5cbe8a018441833b6b605112bc.png
      这个定时器反初始化函数的作用就是当不用这个串口外设的时候可以调用,将外设,或者引脚或者其他一些东西恢复默认值。
http://b05.ickimg.com/201903/8c99aea62037ce474044fc3973be69be.png
      接下来,我们就要来看下本项目的重点部分了,那就是串口通信部分的软件设计。笔者这里使用的是串口2,这也是为了配合原子的战舰V3开发板的硬件接口,串口2的数据发送,是利用DMA来实现的,我们先来看下bsp_usartx.h文件中代码:
http://b05.ickimg.com/201903/319dde1ce71b1918105723a9c3bd2900.png
      该文件中主要是跟串口相关的宏定义以及一些变量和函数的声明,这里使用宏定义,也是为了方便我们更新、修改和移植程序。眼尖的小伙伴们或许会有个疑问,为什么这里我们要选择DMA1的通道7来作为USART2的数据发送而不是别的通道呢?其实,这里也不是随便选的,而是通过查看《STMF10xxx编程手册》得知的,如下图所示:
http://b05.ickimg.com/201903/2ecc803baf1c6ab04ebc54d4e534321e.png
      接下来我们再来看bsp_usart.c文件的内容,首先是串口DMA传输初始化函数:
http://b05.ickimg.com/201903/b9678a7a6be2bfacef67d2494c6a065f.png
      这个函数中,前面部分主要是USART2相关的引脚相关参数的配置,后面部分就是DMA外设的各项参数的设定,需要注意以下几点:
[*]l传输方向需设定为存储器到外设的方向;
[*]l外设地址自增不使能,因为USART有固定的DMA通道,USART数据寄存器的地址也是固定的;
[*]l存储器使用自动递增,并采用常规发送模式,因为这个项目中,笔者使用了定时器来定时发送数据,并不需要DMA一直自动循环发送数据。
[*]接下来就是一个串口硬件反初始化函数,这个函数的作用就是当不用这个串口外设的时候可以调用,将外设,或者引脚或者其他一些东西恢复默认值:
[*]
http://b05.ickimg.com/201903/330d2b858fac2ffd978a397cbe3035bd.png
      接下来,就是中断配置函数,这里我们只需要设定中断优先级,然后使能中断即可:
http://b05.ickimg.com/201903/81541dd41391c0bb27196a49468ebf87.png
      最后,就是串口参数的配置了,首先开启串口功能引脚的时钟和串口DMA时钟 ,接着,通过引用结构体UART_HandleTypeDef中的成员,达到配置串口参数的目的,配置完成后,初始化UART及初始化中断。
http://b05.ickimg.com/201903/79e7c484c1642b93721ae9d9a27f0631.png
       这里需要注意的是,在使用定时器中断、串口中断、DMA中断等中断的时候,我们需要在stm32f1xx_it.c文件中添加对应的中断处理程序:
http://b05.ickimg.com/201903/7be2787a7ccbe807e4e606431be1588e.png
      接下来就是main.c里面的代码了,这部分的代码就是整个项目的核心。首先骂我们来看下系统时钟配置:
http://b05.ickimg.com/201903/09fe23e76414bc40ddc9f05e9d1a76d2.png
      这里其实也没什么好说的,基本上每个项目中都会用到,而且基本不需要怎么修改,接下来我们来看下跟按键和指示灯相关的这个函数吧,这个也是笔者自己改写的,虽然算不上有多好,但是简单的项目中,还是可以作为一个参考吧:
http://b05.ickimg.com/201903/b62cf8cafab21b13a5294c5152079f3b.png
      在这个函数中,主要实现了以下几个功能:
[*]l实时获取每个按键的状态,并在该按键按下后,执行相应的动作,同时,该按键的按键次数加“1”;
[*]l按键按下后,必须松开,才能对下一次按键按下状态进行检测,而这一过程中,通过一个标志位来实现,使得程序不必一直在死等按键松开,不影响其他程序的正常运行;
[*]l实时获取按键和指示灯的状态并将该状态取反,用于发送到迪文DGUS屏上显示状态;
      接下来,我们来看下这个数据填充的函数,这个也是自定义的一个函数,用来实时刷新我们要发送的数据:
http://b05.ickimg.com/201903/a68d19491095668f23a7b63c983be826.png
      然后就是另外一个自定义的函数,用来解析串口收到的指令,并且根据接收到的指令,完成相应的动作:
http://b05.ickimg.com/201903/b3394e35b1b1fddf272fff8e5fb32c6d.png
      接下来就是一个串口接收完成的回调函数,笔者在这里主要用来做帧校验和接收数据的缓存:
http://b05.ickimg.com/201903/d3803b3a9470d56f4d4ac9a1d951e6d4.png
      最后,就是一个定时器的回调函数,笔者在这个函数中设置了一个变量用来定时:
http://b05.ickimg.com/201903/6606fdb9ce625fa6b382bab58ac9928b.png
      在所有准备工作就绪后,我们只需要在主函数中调用对应的函数,即可实现我们需要的功能了:
http://b05.ickimg.com/201903/a56ef15a4328929b0a2aa6fcde6f54c0.png
      至此,整个项目的源码部分就完成了,接下来我们只需要将代码下载到开发板中,然后将开发板的串口2跟迪文的DGUS屏连起来,接可以看到效果了:
http://b05.ickimg.com/201903/38f08ba40de9c8a76686332f981dfdf3.png
      如果你喜欢这篇文章,可以使用下方的“打赏”功能,给笔者一些鼓励哟,当然,要是有什么问题,也可以私信笔者,笔者看到后也会及时回复。感谢大家的支持!

本例源码下载链接:
链接:https://pan.baidu.com/s/1FL7iwP_eHcMWgCRotCsN1g
提取码:c0h6

569507090 发表于 2022-5-26 22:09:51

谢谢了,很用的资料

pinapple 发表于 2023-6-27 10:39:36

源码怎么打开呀

pinapple 发表于 2023-6-27 11:58:40

关于协议代码的在工程的哪个文件中呀,没找到

www1 发表于 2023-7-22 17:14:02

请问我两者为何联动不起来,迪文屏可以实现触摸,32程序烧录成功,但是两者联系不起来

一袋米好重 发表于 2024-1-24 09:52:32

屏和单片机主板通讯需要保持电平和波特率一致
页: [1]
查看完整版本: 【开源】基于HAL库的《STM32与迪文DGUS屏通信》项目实战