一、首先
1、对于寄存器操作的一些宏进行理解。
#define SET_BIT(REG, BIT) ((REG) |= (BIT))//设置寄存器的第bit位值为1,SET_BIT(RCC->AHB2ENR,1) 或者 SET_BIT(RCC->AHB2ENR,2)
#define CLEAR_BIT(REG, BIT) ((REG) &= ~(BIT))//清除寄存器的第bit位值为1的值,即将第bit位置0,CLEAR_BIT(RCC->CR, 64)
#define READ_BIT(REG, BIT) ((REG) & (BIT))//读取寄存器中的值,分两种情况:1.REG为FF,BIT为任意数,结果为BIT对应的数据,2.REG为固定数,BIT为任意数,结果0或者固定数,第三种情况不考虑。
#define CLEAR_REG(REG) ((REG) = (0x0))//清空寄存器值
#define WRITE_REG(REG, VAL) ((REG) = (VAL))//对寄存器写入VAL值
#define READ_REG(REG) ((REG)) //读取寄存器内容 ok
#define MODIFY_REG(REG, CLEARMASK, SETMASK) WRITE_REG((REG), (((READ_REG(REG)) & (~(CLEARMASK))) | (SETMASK))) //对寄存器REG 写入值(清除和设置掩码)
2、对modebus 中RTU模式的应用理解。
modebus为工业领域的一种通信协议。
其占应用层,数据链路层,物理层等3大数据通信层。
同时其遵循一主机对多从机的通信方式。
而却分从机主要从,从机的地址进行区分。
同时存在功能码实现主机和从机的通信功能的控制。
物理层上(485,232等物理上通信芯片为基础)。
1.RTU 通信的帧格式
主机端:
从地址 | 功能码 | 寄存器地址高低 | 寄存器数量 | CRC |
---|---|---|---|---|
0x01 | 0x1 | 0x00 0x02 | 0x00 0x008 | 0x9c 0x0c |
从机端:
从地址 | 功能码 | 数据长度 | 数据 | CRC |
---|---|---|---|---|
0x01 | 0x1 | 0x01 | 0xb3 | 0x9c 0x0c |
2.通信时对应的功能码含意
公用功能码:
线圈,8bit(r),
离散输入8bit(rw),
寄存器 16bit®,
保持寄存器 16bit(rw),
功能码 | 含义 |
---|---|
01h | 读线圈 |
02h | 读离散量输入 |
03h | 读保持(多个)寄存器 |
04h | 读输入寄存器 |
05h | 写单个线圈 |
06h | 写多个线圈 |
0fh | 写多个线圈 |
10h | 写多个寄存器 |
14h | 记录读文件 |
15h | 写文件记录 |
16h | 屏蔽写寄存器 |
17h | 读写多个寄存器 |
3.通信时的数据中每个字符于每段数据帧间的时间控制 (需要控制并检测超过1.5个字符的时间是否收到数据 ,同时对超过3.5个字符的时间没收到数据,认为是空闲状态)。
即: T1.5 <x && T3.5<x
当超过1.5个字符的时间未收到数据即数据出现异常,该帧数据异常。
在通信时RTU方式传输的每帧数据长度需要在256之内。
4.在主机端:
需要考虑的是对从机的发送指令,(对从机的响应接收通过串口的中断来处理)。
主机对从机而言需要做的就是查询,和写入指令两类操作。
这两类操作通过功能码实现。
第一类:
主机要读取从机的信息。
函数功能: 读输入状态状态(InputStatue)
* 输入参数: _addr:从站地址,_reg:寄存器地址,_num:待读取的输入数量
* 返 回 值: 无
* 说 明: 填充数据发送缓存区,然后发送
void MB_ReadInput_02H(uint8_t _addr, uint16_t _reg, uint16_t _num)
{
uint16_t TxCount = 0;
uint16_t crc = 0;
Tx_Buf[TxCount++] = _addr; /* 从站地址 */
Tx_Buf[TxCount++] = 0x02; /* 功能码 */
Tx_Buf[TxCount++] = _reg >> 8; /* 寄存器地址 高字节 */
Tx_Buf[TxCount++] = _reg; /* 寄存器地址 低字节 */
Tx_Buf[TxCount++] = _num >> 8; /* 开关(Input)个数 高字节 */
Tx_Buf[TxCount++] = _num; /* 开关(Input)个数 低字节 */
crc = MB_CRC16((uint8_t*)&Tx_Buf,TxCount);
Tx_Buf[TxCount++] = crc; /* crc 低字节 */
Tx_Buf[TxCount++] = crc>>8; /* crc 高字节 */
HAL_UART_Transmit(&husart_debug, (uint8_t *)&Tx_Buf, TxCount, 0xffff);
}
第二类是写指令到从机
* 函数功能: 写N个保持寄存器(HoldingRegister)
* 输入参数: _addr:从站地址,_reg:寄存器地址,_num:待写入的寄存器数量,_databuf:待写入的寄存器数据
* 返 回 值: 无
* 说 明: 填充数据发送缓存区,然后发送._databuf的长度需 >= _num*2
void MB_WriteNumHoldingReg_10H(uint8_t _addr, uint16_t _reg, uint16_t _num,