作者:瓴(L)
时间:2021.05.13
说明:部分文章内容引用,若有侵权联系删除
一、电气接口
1、5V供电,1:VDD5;2:A;3: B;4:GND
2、板子上没有配终端电阻,需要自己根据应用需要配电阻
3、板子外径30mm,安装孔内径2.7mm,可配2.5的螺钉,两孔中心间距21mm.
二、RS485接口通信协议
编码器485波特率范围:1200-57600,可以根据具体实际请款设置波特率,默认设率9600,8,n,1。
三、modbus通信协议格式
(1)、moubus协议概要
1、Modbus协议是一种单主/多从的通信协议,其特点是在同一时间,总线上只能有一个主设备,但可以有一个或者多个(最多247个)从设备。
2、Modbus通信总是由主设备发起,当从设备没有收到来自主设备的请求时,不会主动发送数据。
3、从设备之间不能相互通信,主设备同时只能启动一个Modbus访问事务处理。
(2)、modbus寄存器
1、四种寄存器
注:1)采用保持寄存器传递信息;
2)默认节点号1(可修改);
2、寄存器地址以及命令
寄存器地址 | 00 | 01 | 02 | 03 | 04 | 05 | 06 | 07 |
---|---|---|---|---|---|---|---|---|
寄存器功能 | 角度符号 | 角度 | 圈数符号 | 圈数 | 波特率 | 波特率(备用) | 地址1 | 置零2 |
注:1)如果要修改编码器的设备地址,只要按照modbus协议修改该处值即可 | ||||||||
2) 对于置零操作,只要修改该寄存器值为1即可完成置零。新设零点位置掉电后不丢失。 | ||||||||
3) 波特率(备用)寄存器用于以后提高波特率用。 | ||||||||
4) 符号寄存器均设定为:0为正,1为负; | ||||||||
5) 红色寄存器代表后续添加功能。 |
(3)、Modbus功能码
1、modbus功能码表
功能码 | 名称 | 作用 |
---|---|---|
01 | 读取线圈状态 | 取得一组逻辑线圈的当前状态(ON/OFF) |
02 | 读取输入状态 | 取得一组开关输入的当前状态(ON/OFF) |
03 | 读取保持寄存器 | 在一个或多个保持寄存器中取得当前的二进制值 |
04 | 读取输入寄存器 | 在一个或多个输入寄存器中取得当前的二进制值 |
05 | 强置单线圈 | 强置一个逻辑线圈的通断状态 |
06 | 预置单寄存器 | 把具体二进值装入一个保持寄存器 |
07 | 读取异常状态 | 取得8个内部线圈的通断状态,这8个线圈的地址由控制器决定,用户逻辑可以将这些线圈定义,以说明从机状态,短报文适宜于迅速读取状态 |
08 | 回送诊断校验 | 把诊断校验报文送从机,以对通信处理进行评鉴 |
09 | 编程(只用于484) | 使主机模拟编程器作用,修改PC从机逻辑 |
10 | 控询(只用于484) | 可使主机与一台正在执行长程序任务从机通信,探询该从机是否已完成其操作任务,仅在含有功能码 9 的报文发送后,本功能码才发送 |
11 | 读取事件计数 | 可使主机发出单询问,并随即判定操作是否成功,尤其是该命令或其它应答产生通信错误时 |
12 | 读取通信事件记录 | 可使主机检索每台从机的ModBus事务处理通信事件记录。如果某项事务处理完成,记录会给出有关错误 |
13 | 编程(184/384 484 584 ) | 可使主机模拟编程器功能修改PC从机逻辑 |
14 | 探询(184/384 484 584) | 可使主机与正在执行任务的从机通信,定期控询该从机是否已完成其程序操作,仅在含有功能13的报文发送后,本功能码才得发送 |
15 | 强置多线圈 | 强置一串连续逻辑线圈的通断 |
16 | 预置多寄存器 | 把具体的二进制值装入一串连续的保持寄存器 |
17 | 报告从机标识 | 可使主机判断编址从机的类型及该从机运行指示灯的状态 |
18 | 884 和MICRO 84 | 可使主机模拟编程功能,修改PC状态逻辑 |
19 | 重置通信链路 | 发生非可修改错误后,是从机复位于已知状态,可重置顺序字节 |
20 | 读取通用参数(584L) | 显示扩展存储器文件中的数据信息 |
21 | 写入通用参数(584L) | 把通用参数写入扩展存储文件,或修改 |
22~64 | 保留作扩展功能备用 | |
65~72 | 保留以备用户功能所用 | 留作用户功能的扩展编码 |
73~119 | 非法功能 | |
120~127 | 保留 | 留作内部作用 |
128~255 | 保留 | 用于异常应答 |
2、常用modbus功能码 | ||
1)读线圈状态功能码 01 | ||
2)读(离散)输入状态功能码 02 | ||
3)读保持寄存器功能码 03 | ||
4)读输入寄存器功能码 04 | ||
5)写单个线圈功能码 05 | ||
6)写单个保持寄存器功能码 06 | ||
7)写多个线圈功能码 15 | ||
8)写多个保持寄存器功能码 16 |
四、modbus调试软件说明
Modbus调试精灵无需安装,打开软件界面如下。
1、插入485模块后,电脑会自动分配串口号,根据设备管理器中显示的串口选择。
2、点击打开串口,绿色显示连接成功
3、在右边寄存器区进行操作,我们只需要输入的是寄存器地址和数值,然后点击写入或者读出。
4、当我们点击写入或者读出后,发送区域会显示发送出去的十六进制数(发送命令),接收区域会显示接收到的十六进制数据。
五、STM32读取modbus磁旋转编码器方法
(1)、硬件部分
1、该编码器使用的是RS485通信协议,属于串口的一种协议。RS485是电压差分信号传输,而stm32根据电平分辨0和1。所以需要RS485转TTL模块。
2、RS485转TTL模块硬件连接图,根据下图红色框对应进行连接。
可根据正点原子STM32库函数版本中文手册进行理解。
//模式控制
#define RS485_TX_EN PDout(7) //485模式控制.0,接收;1,发送.
(注:根据硬件图,应该将RE、DE从背面焊接起来,然后与我们定义的引脚相接,如上代码这里定义了PD7引脚控制)
(2)软件部分
1、需要向该编码器发送命令,编码器才会发送返回数据。
2、发送命令即为我们根据寄存器区操作时,发送区域自动生成的十六进制数,直接复制过来使用即可。
如下代码,我通过定时器周期发送命令,从而磁旋转编码器周期返回数据。
u8 order[8]={0x01,0x03,0x00,0x01,0x00,0x02,0x95,0xCB};
//定时器3中断服务程序
void TIM3_IRQHandler(void) //TIM3中断
{
if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) //检查TIM3更新中断发生与否
{
RS485_Send_Data(order,8);//发送5个字节
TIM_ClearITPendingBit(TIM3, TIM_IT_Update ); //清除TIMx更新中断标志
LED1=!LED1;
}
}
3、经过转接模块之后我们就可以适用普通给串口接收方式进行接收,但要注意由于RS485是半双工通信,所以RE、DE引脚是控制RS485处于接收或者发送模式,因此stm32控制程序还要定义一个gpio引脚进行控制。通过正点原子485库函数源码也不难发现,485相比串口接收只是多一个引脚控制,接收函数还是基于普通串口接收函数。
如下是485接收和发送代码
//RS485发送len个字节.
//buf:发送区首地址
//len:发送的字节数(为了和本代码的接收匹配,这里建议不要超过64个字节)
void RS485_Send_Data(u8 *buf,u8 len)
{
u8 t;
RS485_TX_EN=1; //设置为发送模式
for(t=0;t<len;t++) //循环发送数据
{
while(USART_GetFlagStatus(USART2, USART_FLAG_TC) == RESET);
USART_SendData(USART2,buf[t]);
}
while(USART_GetFlagStatus(USART2, USART_FLAG_TC) == RESET);
RS485_RX_CNT=0;
RS485_TX_EN=0; //设置为接收模式
}
//RS485查询接收到的数据
//buf:接收缓存首地址
//len:读到的数据长度
void RS485_Receive_Data(u8 *buf,u8 *len)
{
u8 rxlen=RS485_RX_CNT;
u8 i=0;
*len=0; //默认为0
delay_ms(10); //等待10ms,连续超过10ms没有接收到一个数据,则认为接收结束
if(rxlen==RS485_RX_CNT&&rxlen)//接收到了数据,且接收完成了
{
for(i=0;i<rxlen;i++)
{
buf[i]=RS485_RX_BUF[i];
}
*len=RS485_RX_CNT; //记录本次数据长度
RS485_RX_CNT=0; //清零
}
}
可以发现RS485基于普通串口接收数据填充到485数据接收缓冲区,这里为什么接收8个数据就等待处理呢,是因为我从01寄存器起始读取两个寄存器,接收数据长度就8个字节。所以这里的接收字节数取决于你想接收的数据长度。
void USART2_IRQHandler(void)
{
u8 res;
if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET) //接收到数据
{
res =USART_ReceiveData(USART2); //读取接收到的数据
if(RS485_RX_CNT<8)
{
RS485_RX_BUF[RS485_RX_CNT]=res; //记录接收到的值
RS485_RX_CNT++; //接收数据增加1
}
}
}
主函数处理数据,然后输出记录转角值
int main(void)
{
u8 len1=8;
u8 rs485buf[8];
// u16 t;
u16 date1,date2;
// u16 times=0;
delay_init(); //延时函数初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
uart_init(9600); //串口初始化为115200
LED_Init(); //LED端口初始化
KEY_Init(); //初始化与按键连接的硬件接口
RS485_Init(9600); //初始化RS485
TIM3_Int_Init(4999,7199);//计数频率10KHZ,500ms
while(1)
{
if(RS485_RX_CNT>=8)//接收数据完成
{
RS485_Receive_Data(rs485buf,&len1);
// for(t=0;t<8;t++) //将接收到的数据发送到串口助手显示
// { USART_SendData(USART1, rs485buf[t]);//向串口1发送数据
// while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);//等待发送结束
// }
if (rs485buf[2]==0x04)
{
date1=(rs485buf[4])|(rs485buf[3]<<8);
date1=(int)( ((double)date1/4096)*360 ) ;
printf("\r\n angle=:%d ",date1);//读取磁编码器旋转角度
}
}
}
}
(3)、使用举例说明
1、写寄存器
如果我们要把01写到一个地址是0000的寄存器地址里,点一下“写入”,就会出现发送指令:01 06 00 00 00 01 48 0A。我们来分析一下这帧数据,其中01是设备地址,06是功能码,代表写寄存器这个功能,后边跟00 00表示的是要写入的寄存器的地址,00 01就是要写入的数据,48 0A就是CRC校验码,这是软件自动算出来的。而根据Modbus协议,当写寄存器的时候,从机成功完成该指令的操作后,会把主机发送的指令直接返回,我们的调试精灵会接收到这样一帧数据:01 06 00 00 00 01 48 0A。
2、读寄存器
假如我们现在要从寄存器地址0002开始读取寄存器,并且读取的数量是2个。点一下“读出”,就会出现发送指令:01 03 00 02 00 02 65 CB。其中01是设备地址,03是功能码,代表读寄存器这个功能,00 02就是读寄存器的起始地址,后一个00 02就是要从寄存器起始地址读取2个寄存器的数值,65 CB就是CRC校验。而接收到的数据是:01 03 04 00 00 00 00 FA 33。其中01是设备地址,03是功能码,04代表的是后边读到的数据字节数是4个,00 00 00 00分别是地址为00 02和00 03的寄存器内部的数据,而FA 33就是CRC校验了。
3、CRC校验说明
CRC校验:CRC校验是一种数据算法,是用来校验数据对错的。CRC校验函数把一帧数据除最后两个字节外,前边所有的字节进行特定的算法计算,计算完后生成了一个16bit的数据,作为CRC校验码,添加在一帧数据的最后。接收方接收到数据后,同样会把前边的字节进行CRC计算,计算完了再和发过来的16bit的CRC数据进行比较,如果相同则认为数据正常,没有出错,如果比较不相同,则说明数据在传输中发生了错误,这帧数据将被丢弃,就像没收到一样,而发送方会在得不到回应后做相应的处理错误处理。
注:校验码算法比较复杂,需要了解可自行搜索,发送时的校验码软件已经自动生成了,所以我们不用考虑,直接复制适用。