32单片机与迪文屏通信的开发学习
本章主要说一下,一个初学者学习迪文屏的经历。适用于略懂、接触过modbus的人。
有一说一,这个迪文屏上位机的软件开发确实简单,比我之前使用的eWin方便了不少。不过迪文屏使用的是RS232接口,它比TFT-LCD驱动起来可能要麻烦一些,对于初学者来说,SPI可能用的还不错,但是MODBUS协议可以说是一个分水岭。能不能用好迪文屏的关键就在于对modbus协议的理解。
迪文屏的接口协议是串口,所以说只需要使用单片机的串口外设即可。
rs232的简介就不多说了,既然要驱动迪文屏,首先就要选择合适的数据协议,说到数据协议,我对于数据协议的通俗理解就是为了让两种不同的东西能够进行沟通的介质。比如一个中国人在和一个美国人打电话,中国人说中国话美国人说美国话,哎这下子美国人蒙了,听不懂说什么,毕竟汉语是世界上最难学的语言,这个时候就需要用到协议的概念了,两个人同时约定说什么语言,这就完美解决了听不懂的问题。 当然数据协议最初的产生就是为了解决这类问题,就像 单片机和迪文屏进行通信,它俩不能各发各的数据吧,要以固定形式发送数据,这时候对方接收以后已固定形式去解析,这样就可以进行通信了。
接着就不扯皮直接说重点
简单的串口配置就不多写了,配置成串口接收中断即可,发送中断不要求。
先简单介绍一下数据帧格式
以03为例,其他功能码可参考modbus协议文档
接收时寄存器数量为N,那为什么寄存器值为2*N呢?请看下边
03这个功能码的寄存器字节是采用这种方式的,哪怕你的寄存器值是1,也要写成0x00、0x01的形式。本次用到的两个功能码相同,还有10
modbus完整支持很多功能码,但是实际在应用的时候常用的也就那么几个。具体如下:
0x01: 读线圈寄存器 写单个寄存器
0x02: 读离散输入寄存器
0x03: 读保持寄存器 读整页的寄存器
0x04: 读输入寄存器
0x05: 写单个线圈寄存器
0x06: 写单个保持寄存器 写单个寄存器
0x0F: 写多个线圈寄存器
0x10: 写多个保持寄存器 写整页的寄存器
如上所示一共8种功能码。所谓的线圈是由于modbus一开始是由PLC诞生的,对应在迪文屏上就是寄存器而已,而寄存器这个概念其实就是迪文屏页面的不同的控件而已。
我在迪文屏中使用到的功能码:
1、功能码03:用来读取整个迪文屏页面的寄存器,当下位机中的数据发生改变,那么上位机界面上也要进行改变(最典型的就是倒计时,下位机定时器,上位机显示倒计时时间值)
此处的寄存器指的就是迪文屏页面的全部变量、控件。当地址为当前页面的起始地址时,而寄存器数量就是当前页面的变量控件的数量。例如当前页面有一个按键开关,一个灯,那么这页就只有两个寄存器。
直接来一段代码:一下代码中的地址全部为0x01
先定义几个指针和结构体:
u8 Rcv_Buffer[210]; //用来存放接收到的完整的一帧数据 (第一个字节用来存放接收到的有效字节数,也就是数组中的有效字节数)
u8 Send_Buffer[210];//用来存放待发送的完整的一帧数据(第一个字节用来存放待发送的有效字节数,也就是数组中的有效字节数)
u8 *PointToRcvBuf; //用来指向接收的数据缓存
u8 *PointToSendBuf; //用来指向带发送的数据缓存` PointToRcvBuf=Rcv_Buffer;PointToSendBuf=Send_Buffer;
03功能码程序:
/* 函数功能:读取保持寄存器 //读取页面的全部寄存器函数输入:两个指针,pointer_1指向用来存放输入信息帧的数组,pointer_2用来指向存放输出信息帧的数组(两个数组的第一个元素都用来存放信息帧的有效字节个数)后面的元素按照Modbus协议组织。函数输出:无。
*/
void ReadHoldingReg(u8 *pointer_1,u8 *pointer_2)//pointer_1用作输入,pointer_2用作输出
{u16 Address=0;//待读取寄存器起始地址(HoldReg[i],i为0-99对应地址从0到99)u16 Num=0; //要读取的寄存器个数u16 SendKey; //要发送数据的校验值Address=(u16)(*(pointer_1+3))*256+(*(pointer_1+4));//先得到寄存器起始地址Num=(u16)(*(pointer_1+5))*256+(*(pointer_1+6)); //先得到要读取的寄存器个数*(pointer_2+2)=0x03; //第三个字节为功能码if(*(pointer_1)==8) //如果接收到的字节数不是8个,就是一个错误帧{if(Address<100) //只要地址小于100,就是合法地址{if(Address+Num<=100&&Num>0) //只要地址加数量大于0小于100,就是合法数量//自己定义即可{//用于for循环u8 i;u8 j;*(pointer_2+3)=Num*2; //第四个字节为要发送的字节个数//重点在下边*(pointer_2)=1+1+1+Num*2+2;//有效字节个数等于丛机地址+功能码+字节个数+寄存器信息+CRC校验for(i=Address,j=4; i<Address+Num; i++,j+=2){*(pointer_2+j)=(u8)(HoldReg[i]>>8);//先放高位*(pointer_2+j+1)=(u8)(HoldReg[i]&0x00FF);//再放低位}//写入校验码SendKey=CRC16(pointer_2+1,*pointer_2-2);//将计算出来的校验码装入输出数据缓存中*(pointer_2+(*pointer_2-1))=(u8)(SendKey>>8);*(pointer_2+(*pointer_2))=(u8)(SendKey&0x00FF);//启动数据发送ModSend();}else{ErrorHandle(3,pointer_2);//错误读取数量}}else{ErrorHandle(2,pointer_2);//错误起始地址}}else{Comu_Busy=0;}
}
2、10功能码
再配着写一个10的
10功能码是写当前页面的全部寄存器。下位机数据改变那上位机也要随着改变,因此需要这个功能码。
直接上代码:
/* 函数功能:预制多个寄存器函数输入:两个指针,pointer_1指向用来存放输入信息帧的数组,pointer_2用来指向存放输出信息帧的数组(两个数组的第一个元素都用来存放信息帧的有效字节个数)后面的元素按照Modbus协议组织。函数输出:无。
*/
void PresetMulReg(u8 *pointer_1,u8 *pointer_2)//pointer_1用作输入,pointer_2用作输出
{u16 Address=0;//待预制寄存器的起始地址(HoldReg[i],i为0-99对应地址从0到99)u16 Num=0;//要预制的寄存器数量u8 ByteCount;//预制值的字节个数u16 PresetValue=0;//预制数值u16 SendKey;//要发送数据的校验值Address=(u16)(*(pointer_1+3))*256+(*(pointer_1+4));//先得到寄存器地址Num=(u16)(*(pointer_1+5))*256+(*(pointer_1+6)); //先得到待预制寄存器数量*(pointer_2+2)=0x10; //第三个字节为功能码ByteCount= *(pointer_1+7); //表示命令值的字节数if((*(pointer_1)==9+ByteCount)&&ByteCount>0&&ByteCount<=200&&ByteCount==(u8)(Num*2))//如果接收到的字节数不是预定的个数,或者命令字节数超出允许范围就是一个错误帧{if(Address<100) //只要地址小于100,就是合法地址{if(Address+Num<=100&&Num>0) //只要地址加数量大于0小于100,就是合法数量{//用于for循环u8 i;u8 j;*(pointer_2)=1+1+2+2+2; //有效字节个数等于丛机地址+功能码+寄存器地址+寄存器数量+CRC校验*(pointer_2+3)=*(pointer_1+3);//将地址值写入输出的寄存器中*(pointer_2+4)=*(pointer_1+4);*(pointer_2+5)=*(pointer_1+5);//将数量写入输出寄存器中*(pointer_2+6)=*(pointer_1+6);for(i=0,j=0; i<Num; i++,j+=2){PresetValue=(u16)(*(pointer_1+8+j))*256+(*(pointer_1+9+j));//先得到预制值HoldReg[Address+i]=PresetValue; //将预制值写入保持寄存器}//写入校验码SendKey=CRC16(pointer_2+1,*pointer_2-2);//将计算出来的校验码装入输出数据缓存中*(pointer_2+(*pointer_2-1))=(u8)(SendKey>>8);*(pointer_2+(*pointer_2))=(u8)(SendKey&0x00FF);//启动数据发送ModSend();}else{ErrorHandle(3,pointer_2);//错误读取数量}}else{ErrorHandle(2,pointer_2);//错误起始地址}}else{Comu_Busy=0;}
}
附加:错误帧处理和CRC校验
在这里插入代码片/* 函数功能:错误帧处理(处理1,2,3,6四类错误,其中1为不合法功能码,2不合法数据地址,3不合法数据,6从机设备忙碌)函数输入:第一个参数Mode用来指示哪一类错误,pointer用来指向存放输出信息帧的数组(两个数组的第一个元素都用来存放信息帧的有效字节个数)后面的元素按照Modbus协议组织。函数输出:无。
*/
void ErrorHandle(u8 Mode,u8 *Pointer)
{u16 SendKey;//要发送数据的校验值HaveMes=0;//清除信息位TIM_Cmd(TIM2,DISABLE);TIM_SetCounter(TIM3,0);Rcv_Complete=1;Comu_Busy=1;Rcv_Buffer[0]=Rcv_Num;switch(Mode){case 1:*(Pointer+3)=0x01;//错误功能码break;case 2:*(Pointer+3)=0x02;//错误地址break;case 3:*(Pointer+3)=0x03;//错误数据break;case 6:*(Pointer+3)=0x06;//从设备忙break;}*Pointer=0x05;//输出寄存器有效数据个数*(Pointer+2)|=0x80;//功能码最高位置一//写入校验码SendKey=CRC16(Pointer+1,*Pointer-2);//将计算出来的校验码装入输出数据缓存中*(Pointer+(*Pointer-1))=(u8)(SendKey>>8);*(Pointer+(*Pointer))=(u8)(SendKey&0x00FF);//启动数据发送ModSend();printlog("error:%d\r\n",Mode);
}/* 函数功能:CRC校验用函数函数输入:puchMsgg是要进行CRC校验的消息,usDataLen是消息中字节数
函数输出:计算出来的CRC校验码。
*/
u16 CRC16(u8 *puchMsgg,u8 usDataLen)//puchMsgg是要进行CRC校验的消息,usDataLen是消息中字节数
{u8 uchCRCHi = 0xFF ; /* 高CRC字节初始化*/u8 uchCRCLo = 0xFF ; /* 低CRC 字节初始化*/u8 uIndex ; /* CRC循环中的索引*/while (usDataLen--) /* 传输消息缓冲区*/{uIndex = uchCRCHi ^ *puchMsgg++ ; /* 计算CRC */uchCRCHi = uchCRCLo ^ auchCRCHi[uIndex] ;uchCRCLo = auchCRCLo[uIndex] ;}return ((uchCRCHi << 8) | uchCRCLo) ;
}
如果有开发问题,可直接与我沟通联系,作者每天都会看博客,欢迎指正。
32单片机与迪文屏通信的开发学习相关推荐
- 1、迪文屏基于T5L_C51开发手势6宫格解锁
演示视频 1.概述 基于迪文屏DMG32240C028-03WTC屏的滑动手势6宫格解锁功能,该功能运用了触摸屏状态读取和坐标读取的功能(系统变量接口0x0016),绘图功能,需要配合变量图标显示等基 ...
- STM32串口中断程序-迪文屏,
STM32串口中断程序-迪文屏,串口接收准确数值 这几天一直在做原子开发板与迪文屏的通信,开发板向迪文屏写数据已经完成,但是从迪文屏读的数据总是无法返回开发板.原始及调试程序见下图: 在串口接收到从d ...
- 【OKT507】迪文屏事件
前言 本章介绍如何实现迪文屏与飞凌OKT507的uart事件通信. 代码 从上到下是uart_example.c.dwin.c.dwin.h.makefile #include "dwin. ...
- 22、T5L 迪文屏 C51开发之Hello World例程
T5L 迪文屏 C51开发之Hello World例程 1.写在前面 2.例程功能简介 3.GUI界面设计 4.C51代码设计 1.写在前面 前面已经介绍了如何使用DGUS Tool软件来进行迪文 ...
- 30、T5L 迪文屏 C51开发之 ADC模数转换
T5L 迪文屏 C51开发之 ADC模数转换 1.介绍 2.例程功能介绍 3.GUI界面设计 4.C51程序设计 1.介绍 T5L 芯片内部的 ADC 模数转换外设,有一点需要注意的是此 ADC ...
- 分享:STM32与迪文屏交互,轻松实现一些简单的功能!
最近,托朋友用STM32F103评估板和迪文屏做了交互(@田),没写多少代码,就实现了一些简单常用的功能,还是很简单的!今天跟大家分享一下: 一.评估板使用说明 1.功能 图1:整体效果图 图2:MC ...
- 一种迪文屏软件在线升级方法
--来自迪文开发者论坛 开发自己的项目中遇到文件升级不方便的问题,因此设计了一个在线升级方案,可以有效解决如下问题: 1. 当产品已发出需要修复Bug时,无法在线修复. 2. 无法判断新老版本,当数据 ...
- 【资料分享】迪文屏使用经验分享
DIWEN 写在前面的话 我的屏幕 显示图片 SD卡 FLASH分配 举例工程 Diwen ico生成工具 使用经验总结 写在前面的话 以下内容是本人在观看迪文科技在哔哩哔哩的教学视频所做的笔记 几乎 ...
- 迪文屏OS汇编代码开发-参数修改 保存 翻页(七)
; DWIN OS ;程序功能:上翻页,下翻页,参数修改,保存 ;软件环境: DWIN OS ASM Builder V1.5 ;硬件环境:DW K600+平台 ;变量 ;用户数据区地址从0x0600 ...
- 迪文屏的音乐播放文件配置
针对T5L平台产品,WAE文件播放是通过生成软件将一个或多个WAV格式文件进行压缩,生成一个WAE文件(库),并将此文件存放在T5L的片内Flash中,开发者按需进行调用,最长可播放1024s.此功能 ...
最新文章
- 计算机全球服务器,云计算的宿命:全球合并成一台计算机,支持无服务器运行...
- 物理化学 焓变的计算和相变焓
- FP error code老是忘记的看这里:只给出最常用的几个。
- SVN还原与SVN更新区别
- 手动创建git忽略push清单,node_module以及自身
- WebViewJavascriptBridge用法
- 10.4. 嗅探工具
- 浙江省大学计算机一级考试试题,大学生计算机一级考试试题
- java如何做数据归档_一次生产环境mysql迁移操作(一)数据归档
- 基于dsp语音降噪算法c语言,基于DSP的语音降噪实时实现.PDF
- 作为无人机方面做嵌入式编写的飞控总结6--IMU惯性系统和GPS导航系统融合小结1(惯性导航算法)
- html中橘色代码,javascript HTML+CSS实现经典橙色导航菜单
- HTTPS之SNI介绍
- 常州大学计算机课程表,常州大学公课表
- 自定义组件中添加其他组件-1 83课 左边部分,右边部分的测试
- 云原生是什么?细数云原生的5大特征
- android qq接口,手机QQ Scheme接口
- (转)it界的大师手笔
- steam下载捆绑流氓软件??!
- C++中如何区分左值和右值
热门文章
- 华为认证数通HCIE面试之Qos完整解析
- python爬取小猪短租信息
- 32程序员面试被拒,嫌弃太老,“大龄程序员”将何去何从
- 【CV】胶囊网络 CapsNet:胶囊之间的动态路由机制
- Safari 安装 Tampermonkey(油猴)插件
- P2627 [USACO11OPEN]Mowing the Lawn G(单调队列优化dp)
- pytorch drop_last参数
- 解决win8 64位版本下无法使用debug
- 英文数字验证码识别包
- 幼儿园语言活动包括哪几类_幼儿园语言教学活动提问的类型及优化设计