一、首先 1、对于寄存器操作的一些宏进行理解。
#define SET_BIT(REG, BIT) ((REG) |= (BIT))
#define CLEAR_BIT(REG, BIT) ((REG) &= ~(BIT))
#define READ_BIT(REG, BIT) ((REG) & (BIT))
#define CLEAR_REG(REG) ((REG) = (0x0))
#define WRITE_REG(REG, VAL) ((REG) = (VAL))
#define READ_REG(REG) ((REG))
#define MODIFY_REG(REG, CLEARMASK, SETMASK) WRITE_REG((REG), (((READ_REG(REG)) & (~(CLEARMASK))) | (SETMASK)))
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;
Tx_Buf[TxCount++] = _num;
crc = MB_CRC16((uint8_t*)&Tx_Buf,TxCount);
Tx_Buf[TxCount++] = crc;
Tx_Buf[TxCount++] = crc>>8;
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,uint8_t *_databuf)
{
uint16_t i;
uint16_t TxCount = 0;
uint16_t crc = 0;
Tx_Buf[TxCount++] = _addr;
Tx_Buf[TxCount++] = 0x10;
Tx_Buf[TxCount++] = _reg >> 8;
Tx_Buf[TxCount++] = _reg;
Tx_Buf[TxCount++] = _num >> 8;
Tx_Buf[TxCount++] = _num;
Tx_Buf[TxCount++] = _num<<1;
for (i = 0; i < 2 * _num; i++)
{
Tx_Buf[TxCount++] = _databuf[i];
}
crc = MB_CRC16((uint8_t*)&Tx_Buf,TxCount);
Tx_Buf[TxCount++] = crc;
Tx_Buf[TxCount++] = crc>>8;
HAL_UART_Transmit(&husart_debug, (uint8_t *)&Tx_Buf, TxCount, 0xffff);
}
这是主机端对于功能码的函数封装。 对于主机端的处理,就是通过控制条件调用该功能码封装函数。实现对从机的发送(查询控制)。 对于从机端由于要做对主机端的接收判断反馈处理所以较为复杂繁琐写。(同时注意一个道理,提出问题,往往要比解决问题来的简单。)
一、由于从机需要接收来自主机端的功能码数据信息所以需要对地址,以及数据长度,帧内容进行检测判断,并对异常的功能码数据进行处理。
typedef struct {
__IO uint8_t Code ;
__IO uint8_t byteNums;
__IO uint16_t Addr ;
__IO uint16_t Num;
__IO uint16_t _CRC;
__IO uint8_t *ValueReg;
__IO uint16_t *PtrHoldingbase;
__IO uint16_t *PtrHoldingOffset;
}PDUData_TypeDef;
同时在公共功能码的基础上需要定义异常码,
并定义线圈,寄存器
#define FUN_CODE_01H 0x01
#define FUN_CODE_02H 0x02
#define FUN_CODE_03H 0x03
#define FUN_CODE_04H 0x04
#define FUN_CODE_05H 0x05
#define FUN_CODE_06H 0x06
#define FUN_CODE_10H 0x10
#define EX_CODE_NONE 0x00
#define EX_CODE_01H 0x01
#define EX_CODE_02H 0x02
#define EX_CODE_03H 0x03
#define EX_CODE_04H 0x04
uint8_t MB_JudgeAddr(uint16_t _Addr,uint16_t _RegNum)
{
uint8_t Excode = EX_CODE_NONE;
if( ((uint32_t)_RegNum+(uint32_t)_Addr) > (uint32_t)0xFFFF)
{
Excode = EX_CODE_02H;
}
return Excode;
}
uint8_t MB_JudgeNum(uint16_t _RegNum,uint8_t _FunCode,uint16_t _ByteNum)
{
uint8_t Excode = EX_CODE_NONE;
uint16_t _CoilNum = _RegNum;
switch(_FunCode)
{
case FUN_CODE_01H:
case FUN_CODE_02H:
if( (_CoilNum<0x0001) || (_CoilNum>0x07D0))
Excode = EX_CODE_03H;
break;
case FUN_CODE_03H:
case FUN_CODE_04H:
if( (_RegNum<0x0001) || (_RegNum>0x007D))
Excode = EX_CODE_03H;
break;
case FUN_CODE_10H:
if( (_RegNum<0x0001) || (_RegNum>0x007B))
Excode = EX_CODE_03H;
if( _ByteNum != (_RegNum<<1))
Excode = EX_CODE_03H;
break;
}
return Excode;
}
void MB_Parse_Data()
{
PduData.Code = Rx_Buf[1];
PduData.Addr = ((Rx_Buf[2]<<8) | Rx_Buf[3]);
PduData.Num = ((Rx_Buf[4]<<8) | Rx_Buf[5]);
PduData._CRC = MB_CRC16((uint8_t*)&Rx_Buf,RxCount-2);
PduData.byteNums = Rx_Buf[6];
PduData.ValueReg = (uint8_t*)&Rx_Buf[7];
PduData.PtrHoldingOffset = PduData.PtrHoldingbase + PduData.Addr;
}
uint8_t MB_Analyze_Execute(void )
{
uint16_t ExCode = EX_CODE_NONE;
if( IS_NOT_FUNCODE(PduData.Code) )
{
ExCode = EX_CODE_01H;
return ExCode;
}
switch(PduData.Code)
{
case FUN_CODE_01H:
case FUN_CODE_02H:
ExCode = MB_JudgeNum(PduData.Num,PduData.Code,1);
if(ExCode != EX_CODE_NONE )
return ExCode;
ExCode = MB_JudgeAddr( PduData.Addr,PduData.Num);
if(ExCode != EX_CODE_NONE )
return ExCode;
break;
case FUN_CODE_03H:
case FUN_CODE_04H:
ExCode = MB_JudgeNum(PduData.Num,PduData.Code,PduData.byteNums);
if(ExCode != EX_CODE_NONE )
return ExCode;
ExCode = MB_JudgeAddr( PduData.Addr,PduData.Num);
if(ExCode != EX_CODE_NONE )
return ExCode;
break;
case FUN_CODE_05H:
break;
case FUN_CODE_06H:
break;
case FUN_CODE_10H:
ExCode = MB_JudgeNum(PduData.Num,PduData.Code,PduData.byteNums);
if(ExCode != EX_CODE_NONE )
return ExCode;
ExCode = MB_JudgeAddr( PduData.Addr,PduData.Num);
if(ExCode != EX_CODE_NONE )
return ExCode;
break;
}
return ExCode;
}
异常的处理
void MB_Exception_RSP(uint8_t _FunCode,uint8_t _ExCode)
{
uint16_t TxCount = 0;
uint16_t crc = 0;
Tx_Buf[TxCount++] = MB_SLAVEADDR;
Tx_Buf[TxCount++] = _FunCode|0x80;
Tx_Buf[TxCount++] = _ExCode ;
crc = MB_CRC16((uint8_t*)&Tx_Buf,TxCount);
Tx_Buf[TxCount++] = crc;
Tx_Buf[TxCount++] = crc>>8;
UART_Tx((uint8_t*)Tx_Buf, TxCount);
}
正常的响应处理
void MB_RSP(uint8_t _FunCode)
{
uint16_t TxCount = 0;
uint16_t crc = 0; Tx_Buf[TxCount++] = MB_SLAVEADDR;
Tx_Buf[TxCount++] = _FunCode;
switch(_FunCode)
{
case FUN_CODE_01H:
TxCount = MB_RSP_01H(TxCount,PduData.Addr,PduData.Num);
break;
case FUN_CODE_02H:
TxCount = MB_RSP_02H(TxCount,PduData.Addr,PduData.Num);
break;
case FUN_CODE_03H:
TxCount = MB_RSP_03H(TxCount,(uint16_t*)PduData.PtrHoldingOffset,PduData.Num);
break;
case FUN_CODE_04H:
TxCount = MB_RSP_04H(TxCount,PduData.Addr,PduData.Num);
break;
case FUN_CODE_05H:
TxCount = MB_RSP_05H(TxCount,PduData.Addr,PduData.Num);
break;
case FUN_CODE_06H:
TxCount = MB_RSP_06H(TxCount,PduData.Addr,PduData.Num, (uint16_t*)PduData.PtrHoldingOffset);
break;
case FUN_CODE_10H:
TxCount = MB_RSP_10H(TxCount,PduData.Addr,PduData.Num ,(uint16_t*)PduData.PtrHoldingOffset,(uint8_t*)PduData.ValueReg);
break;
}
crc = MB_CRC16((uint8_t*)&Tx_Buf,TxCount);
Tx_Buf[TxCount++] = crc;
Tx_Buf[TxCount++] = crc>>8;
UART_Tx((uint8_t*)Tx_Buf, TxCount);
}
对于功能码的处理
static uint16_t MB_RSP_02H(uint16_t _TxCount,uint16_t _AddrOffset ,uint16_t _CoilNum)
{
uint16_t i = 0;
uint16_t m;
uint8_t status[10];
m = (_CoilNum+7)/8;
Tx_Buf[_TxCount++] = m;
if ((_AddrOffset >= COIL_D01) && (_CoilNum > 0))
{
for (i = 0; i < m; i++)
{
status[i] = 0;
}
for (i = 0; i < _CoilNum; i++)
{
if (Get_LEDx_State(i + 1 + _AddrOffset - COIL_D01))
{
status[i / 8] |= (1 << (i % 8));
}
}
}
for (i = 0; i < m; i++)
{
Tx_Buf[_TxCount++] = status[i];
}
return _TxCount;
}
static uint8_t MB_RSP_06H(uint16_t _TxCount,uint16_t _AddrOffset ,uint16_t _RegNum ,uint16_t *_AddrAbs)
{
Tx_Buf[_TxCount++] = _AddrOffset>>8;
Tx_Buf[_TxCount++] = _AddrOffset;
*_AddrAbs = _RegNum;
Tx_Buf[_TxCount++] = PduData.Num>>8;
Tx_Buf[_TxCount++] = PduData.Num;
return _TxCount;
}
main中使用注意
void FillBuf(uint8_t* buf,uint8_t Code)
{
uint16_t i = 0;
uint16_t j = 1;
switch(Code)
{
case FUN_CODE_03H:
case FUN_CODE_06H:
case FUN_CODE_10H:
j = 0x000F;
for(i= 0;i<0x250;i++)
buf[i] = j++;
break;
}
}
PduData.PtrHoldingbase = (uint16_t*)malloc(sizeof(uint16_t)*0x125);
FillBuf((uint8_t*)PduData.PtrHoldingbase,FUN_CODE_03H);
crc_check = ( (Rx_Buf[RxCount-1]<<8) | Rx_Buf[RxCount-2] );
if(crc_check == PduData._CRC)
{
Ex_code = MB_Analyze_Execute();
if(Ex_code !=EX_CODE_NONE)
{
MB_Exception_RSP(PduData.Code,Ex_code);
}
else
{
MB_RSP(PduData.Code);
}
}
|