
简介这是一份围绕STM32与迪文屏通信的嵌入式例程包适合需要实现MODBUS RTU主从通信的开发者参考。例程以STM32为主机、迪文屏为从机通过RS485串口4完成数据交换涵盖串口初始化、请求帧构造、CRC校验、响应解析及超时重发等关键环节并附带DMA优化串口收发的示例便于理解高频率数据交互下的效率提升思路。压缩包共678个文件约24.53MB以C语言源码c/h/s、编译产物o/axf/hex、工程配置icf/uvprojx及迪文屏界面文件hmi/bmp为主结构完整可直接对照工程学习。已有3084人浏览学习适合具备一定STM32基础、正着手调试迪文屏Modbus通信或希望了解RTU协议实现细节的开发者。1. 拿到例程包之后先搞懂迪文屏这头是怎么回事很多做单片机的朋友第一次接触“迪文屏”时都会有点懵它跟我熟悉的LCD1602、OLED、TFT彩屏完全不是一个思路。普通屏是你画好像素点、打包颜色数据发过去屏幕只是个显示终端但迪文串口屏不一样它本身就是一个带GUI内核的嵌入式系统你发的不是像素是“指令”。这条例程包存在的意义就是把STM32这头和迪文屏那头用一条串口线牵起来让单片机可以控制屏上显示的数值、按键状态切换画面、写文本、调背光本质上是让单片机“指挥”一块有界面的屏幕干活。迪文屏的GD32/STM32通信例程里最常用的是DGUS迪文图形化用户系统开发模式。这个模式有个核心特点所有在屏幕上看到的东西比如文本框、进度条、按钮、仪表盘都有一个编号叫“变量地址”。屏幕自己负责画、负责刷新、负责检测触摸而单片机只需要在通信帧里带上变量地址和对应的值屏幕收到就自动把数值怼到对应的控件上去。换句话说屏幕更像是你的“远程仪表板”而不是一块普通显示器这也是为什么工程里会用“变量地址”这种关键词到处出现。看这个例程之前我建议你先确认手里屏的具体型号和内核版本因为DGUS内核的版本会影响协议的细节。迪文屏开发时常见的是“DGUS一期”和“DGUS二期”两种屏内核版本不一样发指令的帧格式也不完全一样。例程包里的代码通常按一类屏写好如果你用的是二期屏却跑一期指令屏幕会完全没反应。检查方法很简单屏幕开机后按住屏幕有版本信息页面或者在串口调试里发一个查询指令看返回数据。2. 例程包的结构拆解哪些文件可以忽略哪些必须逐行读懂这类名字叫“STM32与迪文屏通信例程.zip”的压缩包解压之后一般不是什么高深莫测的东西。我见过很多网友下载后不知道该从哪个文件开始看几兆的东西打开一看全是文件夹就蒙了。这里我按常见结构给你拆一下不同版本可能略有差异但大方向逃不出这四块。工程文件区最常见的是KEIL和IAR两个文件夹或者工程文件直接丢在最外层比如STM32F103_DWIN.uvprojx、ADC_Serial_Loop.uvprojx这种名字。这一层是整个例程的入口用KEIL5直接双击打开就能编译开发环境无所谓只要保证芯片型号选对、魔术棒里Define的宏对、下载器配置好基本不用改就能跑。核心代码区常见名字是USER、HARDWARE、SYSTEM、CORE这些。USER里是主函数main.c、中断服务函数stm32f10x_it.cHARDWARE里是各个外设驱动文件比如usart.c、timer.c、dwin.c这种。dwin.c是这个例程的灵魂里面封装了向迪文屏发指令的函数比如变量地址写、文字下发、背光调节这些一定要逐行读一遍。文档和参考资料很多压缩包里会塞PDF或TXT比如迪文屏的开发者指南、通信协议说明、例程说明。别看不上这些文档里面往往写了例程配套的屏型号、波特率和协议版本没有这些信息你调半天都不知道为什么没反应。屏幕工程文件偶尔还会附带一个.zip或文件夹这是迪文屏的DGUS工程源文件用DGUS软件打开的。如果压缩包里没有也没关系例程代码里注释已经告诉你要在屏上放什么控件、地址设多少自己建工程也来得及。我给个优先级建议如果时间紧把dwin.c和main.c两个文件逐行读懂就够了其他文件知道在干什么就行。如果想把整个工程拿走改成自己的项目那usart.c里的串口初始化和中断接收也必须吃透因为迪文屏通信完全是建立在串口收发之上的中断处理不对帧解析就是一场灾难。3. 从串口到点屏通信协议里最关键的几条指令迪文屏的串口协议不像MODBUS那么复杂但它有一套属于自己的帧格式和校验方式。理解了下面这个框架你再看例程里任何一条发送函数都会觉得清清楚楚。迪文屏DGUS通信以最常见的指令集为例的每帧结构大致是这样名称长度说明帧头2字节通常是0x5A 0xA5这是迪文屏的标志性帧头帧长度1字节指的是从“指令”到“数据/校验”之前的所有字节数不含帧头也不含这个长度自己指令1字节操作类型比如0x82是写变量地址0x83是读变量地址0x80是写系统寄存器数据段若干字节具体内容比如变量地址加数据或者寄存器地址加值校验可选1字节有CRC校验模式和异或校验模式具体看内核和配置拿最常用的“写变量地址”举例假设我要把数值0x0001写到变量地址0x1000指令应该是5A A5 05 82 10 00 00 01。拆开来看05是长度因为从指令82开始到数据结束一共5个字节82 10 00 00 01后面没有校验。82表示写入10 00是变量地址00 01是要写进去的数据数据长度是2字节。我倾向于在博文里反复强调这个结构因为80%的通信问题都出在长度字节算错上多写一个、少写一个屏幕都会直接丢弃不响应。读变量地址的指令应该是5A A5 04 83 10 00 01其中04算的是83 10 00 01这四个字节数字01代表读1个字一个变量地址对应2字节。屏幕收到后会返回一帧格式大概像5A A5 06 83 10 00 01 00 01这里面前面是回显的帧头、长度、指令、变量地址、读取的字数后面跟的就是实际数据。例程里一般会写一个解析函数专门从接收缓冲区里判断帧头、切帧头、取有效数据这个解析函数值得好好学因为它解决的是“粘包怎么防、半包怎么等”的经典嵌入式问题。除了变量读写还有一类指令是给底层系统用的叫写系统寄存器指令码0x80。最实用的一条是设置背光往寄存器地址0x82写入背光亮度值0x00到0xFF。例程里如果提供了调背光功能一般就是用这条。背光这东西看似花哨实际在夜用设备里很关键我也见过有人把背光指令当成呼吸灯做的效果还挺好。4. 真正跑通例程的完整链路与避坑记录从解压到屏幕点亮中间有好几道门槛。这里按我自己的实操顺序给你完整的跑通链路每一处都可能卡住新手我会把容易翻车的细节单独标出来。4.1 接线电平匹配是第一道坑STM32的USART_TX/RX是3.3V电平迪文屏的串口要分情况5V供电的老款屏串口电平可能是5V也可能是3.3V兼容新出的多数型号已经标明是3.3V串口了。我建议你用之前先量一下屏幕输出的高电平电压或者直接查型号手册。真的拿5V电平怼到STM32的GPIO上长期运行有烧引脚的风险。如果非要接最简单的方案是串电阻分压RX/TX各串一个1K左右的电阻TTL电平基本不会出大问题。电源方面屏的供电电流不低尤其10寸以上的屏点亮背光能上几百毫安不要和STM32开发板共用一条杜邦线供电最好独立供电共地否则出现画面闪烁、复位重启这种事十有八九是供电不足。4.2 串口参数波特率/停止位/校验位一个都不能错例程里默认的波特率一般写的是115200数据位8停止位1无校验对应代码里就是USART_InitStructure.USART_BaudRate 115200。这里要提醒的是迪文屏的波特率是在屏幕的配置里决定的不是代码改了就能用。屏上有个CONFIG文件在SD卡根目录里面有一行BAUD115200之类的设置或者用迪文调试助手在屏幕上直接改然后重启屏生效。如果你下载别人的例程代码里写的波特率必须和当前屏的配置一致不一致会表现为串口发什么屏都没反应。正确的验证方式是先用USB转TTL接电脑用串口助手直接给屏发一条写变量指令如果能点亮控件说明屏侧协议和波特率都没问题再回头找STM32这边的问题。4.3 KEIL工程配置宏定义和芯片型号先对齐解压出的工程如果用的是STM32F103系列打开后第一件事是检查器件型号是否匹配。KEIL5装了对应器件包的情况下编译前点一下软件仿真图标如果在Build Output里看到“Error: Device not found”或者包加载报错先去Pack Installer装好Keil.STM32F1xx_DFP包。还有一类常见问题是工程里定义了STM32F10X_HD这种宏如果你的芯片其实是中容量或低容量内存地址和启动文件不对程序跑起来会莫名其妙进HardFault这时要把宏改成STM32F10X_MD或STM32F10X_LD并且启动文件也换成对应容量的startup_stm32f10x_md.s。这些都属于“例程打包时用特定板子验证过你换一块板子就得自己改”的典型坑。4.4 烧录与调试先把屏当哑终端用程序烧进去后不要急着看屏幕变化先在KEIL里打断点或者串口飞线接调试器看STM32实际往串口发了什么。我习惯的做法是在dwin.c的发送函数里加一个printf把每一帧的十六进制数据打印出来。这样能立刻确认两个东西一是程序跑没跑到发送代码二是发出来的帧长度对不对。如果发出来的帧每个字节都对屏还没反应那就是屏侧配置问题比如协议版本不匹配。如果发出来的帧本身就是错的重点检查数据长度字节我见过一个例程里用的长度是“包含帧头”的跟常规的“不包含帧头”是两种算法结果屏幕完全无响应很多人卡这一天都查不出原因。解决办法就是拿串口助手把屏的原始收发抓一遍拿返回帧对比屏的协议文档立马就能定位是哪个字节数错了。5. 例程之外我建议你继续折腾的几个方向例程能跑通只是“毕业”的第一步。迪文屏真正的价值在于人机交互而例程往往只能告诉你“数据怎么发过去”不会告诉你“人机界面怎么做优雅”。我梳理了几个值得从例程往外延伸的方向也是实际项目里最常遇到的场景。5.1 数值回传从单向写变成双向问答很多例程只做了单片机到屏幕的写数据真正项目里更常见的是触摸屏上设一个参数比如温度设定值然后屏幕把数值发给单片机。这就要用到了前面说的读变量地址指令。你需要在串口空闲中断或接收中断里缓存整帧然后判断指令码是不是0x83再根据帧里的变量地址去更新对应的全局变量。这个流程不难但有一点我特别提醒迪文屏返回的数据不一定只在触摸变化时才发如果你在屏上配置了“定时自动上传”功能它会按设定周期不断发数据你的串口缓冲区要能抗住这种持续流量否则丢一个字节整个帧错位后面的数据全废。对策是给帧解析加状态机和超时判定不要简单按“收到固定字节数”来判断一帧结束。5.2 多页交互切换页面其实控制的是另一个寄存器实际产品里不可能只有一个界面。迪文屏的页面切换是通过往系统寄存器地址0x84写入页面号实现的比如想跳到第2页发5A A5 05 80 84 00 02。例程里一般不主动演示这个但你要做菜单导航这条指令比去屏上逐个放按钮再绑定跳转要灵活得多。更进阶的玩法是把当前页面号读回来单片机根据页面号决定上报数据的优先级。比如第一页是总览只传主要参数第二页是设置页才需要传全部参数这样既能减少总线负载也让界面逻辑更清晰。5.3 文字下发中文字符串的编码要先规划好迪文屏的文字控件有“静态显示”和“文本显示”两种方式。静态显示是图片格式的底图那个不用管文本显示则支持单片机直接下发字符串。但这里面有个容易踩的坑屏幕端选择的字库编码方式是什么决定的你是发GBK编码还是GB2312编码。比如代码里如果用char* str 温度正常编译器会把字符串编码成源码文件保存的格式如果你的源文件是UTF-8而屏的字库是GBK就会显示乱码。解决办法是统一文件编码或者在代码里用\x{TEMP}这种转义序列明确指定字节。这块例程很少覆盖却是我见到的咨询最多的问题之一。5.4 用定时器做周期刷新而不是死循环狂发最后想聊一个工程化细节。见过很多新手拿到例程后把发数据写成for(;;)循环里一秒钟发几百次。实际使用中迪文屏的串口接收缓冲区有限你还得给触摸回传留缓冲区发太快反而更容易丢帧。正确写法是用一个定时器中断或者HAL_GetTick()这类系统时基每50ms或者100ms刷新一次界面数据既稳定又不占CPU。如果你的界面还有值动画进度条、曲线刷新率控制在50ms 到 200ms之间人眼就已经觉得流畅了没有必要跑满波特率的上限。我自己的习惯是建一个ui_refresh()函数在所有状态更新完后统一调用把界面需要显示的变量一次性写好。这样写出来的工程后面维护的时候思路特别清楚想看界面为什么不对直接奔ui_refresh()不用顺着收发流程到处翻函数。6. 我建议你保留例程里的原始注释和版本信息有一件事我特别想单独说很多人在网上下载例程后一打开工程就删注释、改文件结构想着“反正我能跑通就行了”。但迪文屏和STM32的通信例程因为涉及屏的内核版本、DGUS工程版本、STM32固件库版本这些多版本叠加的问题原始注释往往保命的关键信息。比如注释里写着“适用于DGUS II V2.0波特率115200变量地址范围0x1000-0x7FFF”如果你换成别的版本或改乱了再想找问题就难了。我的做法是拿到新的例程后先在项目根目录新建一个README.md写上来源、屏型号、协议版本、固件库版本、屏幕工程版本、修改时间和我的修改记录。别嫌这个动作多余通信例程的调试很多时候靠的就是“哪个版本做了什么改动”这种信息。我踩过最亏的一次坑就是把一份能正常用的例程改到了一个新版屏上结果死活不亮最后发现只是DGUS内核从V1.0升级到了V2.0变量读取方式变了一点对照原始注释和版本信息才发现问题。今天这篇东西虽然由“STM32与迪文屏通信例程.zip”这个标题而起但我想真正对你有用的不只是跑通例程而是建立起“协议帧如何拆分、变量地址如何操作、模块化函数如何封装”这套意识。照着我上面说的链路一步步走一两个小时之内让屏幕动起来是完全可行的。之后你再用迪文屏做产品原型或毕业设计心里就有底了。本文还有配套的精品资源点击获取