1205 lines
26 KiB
C
1205 lines
26 KiB
C
|
|
#include "modbus_s.h"
|
|
|
|
#ifdef MODBUS_S_PORT
|
|
|
|
#include "inout.h"
|
|
#include "delay.h"
|
|
#include "trigger.h"
|
|
|
|
//-----------------------------------------------------------------
|
|
// 控制结构
|
|
u16 * mdbsReg[MODBUS_REG_MAX - MODBUS_REG_MIN + 1] = {NULL}; // Modbus 从设备地址空间
|
|
|
|
ModbusCtrl g_modBusSCtrl;
|
|
Rs485Ctrl g_rs485SCtrl;
|
|
|
|
int ModbusSTx(Rs485Cmd * pCmd); // 功能:发送一条MODBUS协议的应答
|
|
int ModbusSRx(u8 rDat); // 功能:接收一条MODBUS协议的命令
|
|
|
|
int AddModbusSCmd(Rs485Cmd * pCmd); // 添加命令到队列中
|
|
int GetModbusSCmd(Rs485Cmd * pCmd); // 从队列中读取命令
|
|
int GetModbusSCmdBufLen(void); // 得到队列中命令个数
|
|
|
|
void ReadModbusSData(u16 addr, u16 * pBuf, u16 len); // 读数据
|
|
void WriteModbusSData(u16 addr, u16 * pBuf, u16 len); // 写数据
|
|
|
|
//-----------------------------------------------------------------
|
|
|
|
/*-----------------------------------------------------------------
|
|
功能:初始化 Modbus 从设备串口
|
|
baud : 波特率
|
|
dat: 数据长度
|
|
parity: 校验位
|
|
stop: 停止位
|
|
mode: 数据模式
|
|
*/
|
|
void InitModbusS(BAUD_TypeDef baud, char dat, char parity, char stop, char mode)
|
|
{
|
|
memset(&g_modBusSCtrl, 0, sizeof(ModbusCtrl));
|
|
|
|
if (mode == 'A' || mode == 'a')
|
|
{
|
|
g_modBusSCtrl.modBusMode = 1;
|
|
}
|
|
else
|
|
{
|
|
g_modBusSCtrl.modBusMode = 0;
|
|
}
|
|
|
|
// 延时
|
|
switch(baud)
|
|
{
|
|
// 数据位 + 1个起始位 + 奇偶校验位(无校验则没有) + 停止位
|
|
// 根据波特率来计算 通讯延时, 单位us = 10000000 / baud(按照一个字节10位计算, 8 N 1)
|
|
case B4800:
|
|
{
|
|
g_modBusSCtrl.modBusWait = 2200;
|
|
break;
|
|
}
|
|
case B9600:
|
|
{
|
|
g_modBusSCtrl.modBusWait = 1100;
|
|
break;
|
|
}
|
|
case B14400:
|
|
{
|
|
g_modBusSCtrl.modBusWait = 750;
|
|
break;
|
|
}
|
|
case B19200:
|
|
{
|
|
g_modBusSCtrl.modBusWait = 600;
|
|
break;
|
|
}
|
|
case B38400:
|
|
{
|
|
g_modBusSCtrl.modBusWait = 300;
|
|
break;
|
|
}
|
|
case B57600:
|
|
{
|
|
g_modBusSCtrl.modBusWait = 200;
|
|
break;
|
|
}
|
|
case B115200:
|
|
{
|
|
g_modBusSCtrl.modBusWait = 100;
|
|
break;
|
|
}
|
|
case B230400:
|
|
{
|
|
g_modBusSCtrl.modBusWait = 50;
|
|
break;
|
|
}
|
|
case B460800:
|
|
{
|
|
g_modBusSCtrl.modBusWait = 25;
|
|
break;
|
|
}
|
|
case B921600:
|
|
{
|
|
g_modBusSCtrl.modBusWait = 13;
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
g_modBusSCtrl.modBusWait = 0;
|
|
}
|
|
}
|
|
if ((dat == '8') && (parity != 'N') && (parity != 'n')) // 7位数据过滤校验位
|
|
{
|
|
g_modBusSCtrl.filterChk = 1; // 过滤数据校验位标志 1, 过滤
|
|
}
|
|
else
|
|
{
|
|
g_modBusSCtrl.filterChk = 0;
|
|
}
|
|
|
|
|
|
#ifndef USART1_485OutEn
|
|
#define USART1_485OutEn DefOutEn
|
|
#endif
|
|
|
|
#ifndef USART1_485OutDis
|
|
#define USART1_485OutDis DefOutDis
|
|
#endif
|
|
|
|
#ifndef USART2_485OutEn
|
|
#define USART2_485OutEn DefOutEn
|
|
#endif
|
|
|
|
#ifndef USART2_485OutDis
|
|
#define USART2_485OutDis DefOutDis
|
|
#endif
|
|
|
|
#ifndef USART3_485OutEn
|
|
#define USART3_485OutEn DefOutEn
|
|
#endif
|
|
|
|
#ifndef USART3_485OutDis
|
|
#define USART3_485OutDis DefOutDis
|
|
#endif
|
|
|
|
#ifndef USART4_485OutEn
|
|
#define USART4_485OutEn DefOutEn
|
|
#endif
|
|
|
|
#ifndef USART4_485OutDis
|
|
#define USART4_485OutDis DefOutDis
|
|
#endif
|
|
|
|
#ifndef USART5_485OutEn
|
|
#define USART5_485OutEn DefOutEn
|
|
#endif
|
|
|
|
#ifndef USART5_485OutDis
|
|
#define USART5_485OutDis DefOutDis
|
|
#endif
|
|
|
|
#ifndef USART6_485OutEn
|
|
#define USART6_485OutEn DefOutEn
|
|
#endif
|
|
|
|
#ifndef USART6_485OutDis
|
|
#define USART6_485OutDis DefOutDis
|
|
#endif
|
|
|
|
// 初始化串口
|
|
#if (MODBUS_S_PORT == COMM_USART1)
|
|
InitUsart1(baud, dat, parity, stop); // 串口1
|
|
RegisterModbusCommFunc(&g_modBusSCtrl, Usart1SendData, Usart1GetData, Usart1CleanRsBuf, IsUsart1SendOver, USART1_485OutEn, USART1_485OutDis);
|
|
#elif (MODBUS_S_PORT == COMM_USART2)
|
|
InitUsart2(baud, dat, parity, stop); // 串口2
|
|
RegisterModbusCommFunc(&g_modBusSCtrl, Usart2SendData, Usart2GetData, Usart2CleanRsBuf, IsUsart2SendOver, USART2_485OutEn, USART2_485OutDis);
|
|
#elif (MODBUS_S_PORT == COMM_USART3)
|
|
InitUsart3(baud, dat, parity, stop); // 串口3
|
|
RegisterModbusCommFunc(&g_modBusSCtrl, Usart3SendData, Usart3GetData, Usart3CleanRsBuf, IsUsart3SendOver, USART3_485OutEn, USART3_485OutDis);
|
|
#elif (MODBUS_S_PORT == COMM_USART4)
|
|
InitUsart4(baud, dat, parity, stop); // 串口4
|
|
RegisterModbusCommFunc(&g_modBusSCtrl, Usart4SendData, Usart4GetData, Usart4CleanRsBuf, IsUsart4SendOver, USART4_485OutEn, USART4_485OutDis);
|
|
#elif (MODBUS_S_PORT == COMM_USART5)
|
|
InitUsart5(baud, dat, parity, stop); // 串口5
|
|
RegisterModbusCommFunc(&g_modBusSCtrl, Usart5SendData, Usart5GetData, Usart5CleanRsBuf, IsUsart5SendOver, USART5_485OutEn, USART5_485OutDis);
|
|
#elif (MODBUS_S_PORT == COMM_USART6)
|
|
InitUsart6(baud, dat, parity, stop); // 串口6
|
|
RegisterModbusCommFunc(&g_modBusSCtrl, Usart6SendData, Usart6GetData, Usart6CleanRsBuf, IsUsart6SendOver, USART6_485OutEn, USART6_485OutDis);
|
|
#else
|
|
RegisterModbusCommFunc(&g_modBusSCtrl, DefSendData, DefGetData, DefCleanRsBuf, DefIsSendOver, DefOutEn, DefOutDis);
|
|
#endif
|
|
|
|
g_modBusSCtrl.CommOutDis(); // 关闭发送
|
|
}
|
|
|
|
/*-----------------------------------------------------------------
|
|
功能:发送一条ASCII格式MODBUS协议的应答,回应03控制字命令
|
|
*/
|
|
int Modbus_TX03A(Rs485Cmd * pCmd)
|
|
{
|
|
int i = 0;
|
|
u8 pSDat[MDBSRXLEN_A]; // 待发送数据
|
|
u8 Lrc_check = 0; // 和校验
|
|
u16 data[MODBUS_REG_MAX - MODBUS_REG_MIN + 1] = {0};
|
|
u8 temp; // 临时变量
|
|
u8 temp_data; // 临时变量
|
|
|
|
if (pCmd == NULL)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
pSDat[i++] = ':'; //':'
|
|
|
|
temp_data = pCmd->index;
|
|
temp = HIHFBYTE(temp_data);
|
|
pSDat[i++] = RtuToAscii(temp); //IDX_H
|
|
temp = LOHFBYTE(temp_data);
|
|
pSDat[i++] = RtuToAscii(temp); //IDX_L
|
|
Lrc_check += temp_data;
|
|
|
|
temp_data = pCmd->cmd;
|
|
temp = HIHFBYTE(temp_data);
|
|
pSDat[i++] = RtuToAscii(temp); //CMD_H
|
|
temp = LOHFBYTE(temp_data);
|
|
pSDat[i++] = RtuToAscii(temp); //CMD_L
|
|
Lrc_check += temp_data;
|
|
|
|
//数据个数
|
|
temp_data = pCmd->rlen << 1;
|
|
temp = HIHFBYTE(temp_data);
|
|
pSDat[i++] = RtuToAscii(temp); //Bytes Count_H
|
|
temp = LOHFBYTE(temp_data);
|
|
pSDat[i++] = RtuToAscii(temp); //Bytes Count_L
|
|
Lrc_check += temp_data;
|
|
|
|
//数据字节
|
|
ReadModbusSData(pCmd->addr, &data[0], pCmd->rlen);
|
|
for (int j = 0; j < pCmd->rlen; j++)
|
|
{
|
|
temp_data = HIBYTE(data[j]);
|
|
temp = HIHFBYTE(temp_data);
|
|
pSDat[i++] = RtuToAscii(temp);
|
|
temp = LOHFBYTE(temp_data);
|
|
pSDat[i++] = RtuToAscii(temp);
|
|
Lrc_check += temp_data;
|
|
|
|
temp_data = LOBYTE(data[j]);
|
|
temp = HIHFBYTE(temp_data);
|
|
pSDat[i++] = RtuToAscii(temp);
|
|
temp = LOHFBYTE(temp_data);
|
|
pSDat[i++] = RtuToAscii(temp);
|
|
Lrc_check += temp_data;
|
|
}
|
|
|
|
Lrc_check = ~Lrc_check + 1;
|
|
pSDat[i++] = RtuToAscii(HIHFBYTE(Lrc_check)); //LRC_H
|
|
pSDat[i++] = RtuToAscii(LOHFBYTE(Lrc_check)); //LRC_L
|
|
pSDat[i++] = 0x0D; //CR
|
|
pSDat[i++] = 0x0A; //LF
|
|
|
|
g_modBusSCtrl.ModBusCommSend(pSDat, i); // 将数据放入发送缓冲区
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------
|
|
功能:发送一条ASCII格式MODBUS协议的应答,回应06控制字命令
|
|
*/
|
|
int Modbus_TX06A(Rs485Cmd * pCmd)
|
|
{
|
|
int i = 0;
|
|
u8 pSDat[MDBSRXLEN_A]; //待发送数据
|
|
u8 Lrc_check = 0; //和校验
|
|
u8 temp; //临时变量
|
|
u8 temp_data; //临时变量
|
|
|
|
if (pCmd == NULL)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
pSDat[i++] = ':'; //':'
|
|
|
|
temp_data = pCmd->index;
|
|
temp = HIHFBYTE(temp_data);
|
|
pSDat[i++] = RtuToAscii(temp); //IDX_H
|
|
temp = LOHFBYTE(temp_data);
|
|
pSDat[i++] = RtuToAscii(temp); //IDX_L
|
|
Lrc_check += temp_data;
|
|
|
|
temp_data = pCmd->cmd;
|
|
temp = HIHFBYTE(temp_data);
|
|
pSDat[i++] = RtuToAscii(temp); //CMD_H
|
|
temp = LOHFBYTE(temp_data);
|
|
pSDat[i++] = RtuToAscii(temp); //CMD_L
|
|
Lrc_check += temp_data;
|
|
|
|
temp_data = HIBYTE(pCmd->addr);
|
|
temp = HIHFBYTE(temp_data);
|
|
pSDat[i++] = RtuToAscii(temp); //Addr_H
|
|
temp = LOHFBYTE(temp_data);
|
|
pSDat[i++] = RtuToAscii(temp); //Addr_L
|
|
Lrc_check += temp_data;
|
|
|
|
temp_data = LOBYTE(pCmd->addr);
|
|
temp = HIHFBYTE(temp_data);
|
|
pSDat[i++] = RtuToAscii(temp); //Addr_H
|
|
temp = LOHFBYTE(temp_data);
|
|
pSDat[i++] = RtuToAscii(temp); //Addr_L
|
|
Lrc_check += temp_data;
|
|
|
|
temp_data = HIBYTE(pCmd->wrdat[0]);
|
|
temp = HIHFBYTE(temp_data);
|
|
pSDat[i++] = RtuToAscii(temp); //Data_H
|
|
temp = LOHFBYTE(temp_data);
|
|
pSDat[i++] = RtuToAscii(temp); //Data_L
|
|
Lrc_check += temp_data;
|
|
|
|
temp_data = LOBYTE(pCmd->wrdat[0]);
|
|
temp = HIHFBYTE(temp_data);
|
|
pSDat[i++] = RtuToAscii(temp); //Data_H
|
|
temp = LOHFBYTE(temp_data);
|
|
pSDat[i++] = RtuToAscii(temp); //Data_L
|
|
Lrc_check += temp_data;
|
|
|
|
Lrc_check = ~Lrc_check + 1;
|
|
pSDat[i++] = RtuToAscii(HIHFBYTE(Lrc_check)); //LRC_H
|
|
pSDat[i++] = RtuToAscii(LOHFBYTE(Lrc_check)); //LRC_L
|
|
pSDat[i++] = 0x0D; //CR
|
|
pSDat[i++] = 0x0A; //LF
|
|
|
|
g_modBusSCtrl.ModBusCommSend(pSDat, i); // 将数据放入发送缓冲区
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------
|
|
功能:发送一条ASCII格式MODBUS协议的应答,回应10H控制字命令
|
|
*/
|
|
int Modbus_TX10A(Rs485Cmd * pCmd)
|
|
{
|
|
int i = 0;
|
|
u8 pSDat[MDBSRXLEN_A]; //待发送数据
|
|
u8 Lrc_check = 0; //和校验
|
|
u8 temp; //临时变量
|
|
u8 temp_data; //临时变量
|
|
|
|
if (pCmd == NULL)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
pSDat[i++] = ':'; //':'
|
|
|
|
temp_data = pCmd->index;
|
|
temp = HIHFBYTE(temp_data);
|
|
pSDat[i++] = RtuToAscii(temp); //IDX_H
|
|
temp = LOHFBYTE(temp_data);
|
|
pSDat[i++] = RtuToAscii(temp); //IDX_L
|
|
Lrc_check += temp_data;
|
|
|
|
temp_data = pCmd->cmd;
|
|
temp = HIHFBYTE(temp_data);
|
|
pSDat[i++] = RtuToAscii(temp); //CMD_H
|
|
temp = LOHFBYTE(temp_data);
|
|
pSDat[i++] = RtuToAscii(temp); //CMD_L
|
|
Lrc_check += temp_data;
|
|
|
|
temp_data = HIBYTE(pCmd->addr);
|
|
temp = HIHFBYTE(temp_data);
|
|
pSDat[i++] = RtuToAscii(temp); //Addr_H
|
|
temp = LOHFBYTE(temp_data);
|
|
pSDat[i++] = RtuToAscii(temp); //Addr_L
|
|
Lrc_check += temp_data;
|
|
|
|
temp_data = LOBYTE(pCmd->addr);
|
|
temp = HIHFBYTE(temp_data);
|
|
pSDat[i++] = RtuToAscii(temp); //Addr_H
|
|
temp = LOHFBYTE(temp_data);
|
|
pSDat[i++] = RtuToAscii(temp); //Addr_L
|
|
Lrc_check += temp_data;
|
|
|
|
temp_data = HIBYTE(pCmd->wlen);
|
|
temp = HIHFBYTE(temp_data);
|
|
pSDat[i++] = RtuToAscii(temp); //len_H
|
|
temp = LOHFBYTE(temp_data);
|
|
pSDat[i++] = RtuToAscii(temp); //len_L
|
|
Lrc_check += temp_data;
|
|
|
|
temp_data = LOBYTE(pCmd->wlen);
|
|
temp = HIHFBYTE(temp_data);
|
|
pSDat[i++] = RtuToAscii(temp); //len_H
|
|
temp = LOHFBYTE(temp_data);
|
|
pSDat[i++] = RtuToAscii(temp); //len_L
|
|
Lrc_check += temp_data;
|
|
|
|
Lrc_check = ~Lrc_check + 1;
|
|
pSDat[i++] = RtuToAscii(HIHFBYTE(Lrc_check)); //LRC_H
|
|
pSDat[i++] = RtuToAscii(LOHFBYTE(Lrc_check)); //LRC_L
|
|
pSDat[i++] = 0x0D; //CR
|
|
pSDat[i++] = 0x0A; //LF
|
|
|
|
g_modBusSCtrl.ModBusCommSend(pSDat, i); // 将数据放入发送缓冲区
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------
|
|
功能:发送一条RTU格式MODBUS协议的应答,回应03控制字命令
|
|
*/
|
|
int Modbus_TX03R(Rs485Cmd * pCmd)
|
|
{
|
|
int i = 0;
|
|
u8 pSDat[MDBSRXLEN_R]; //待发送数据
|
|
u16 checkcrc; //crc校验
|
|
u16 data[MODBUS_REG_MAX - MODBUS_REG_MIN + 1] = {0};
|
|
|
|
if (pCmd == NULL)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
pSDat[i++] = pCmd->index; // ADR
|
|
pSDat[i++] = pCmd->cmd; // CMD
|
|
|
|
pSDat[i++] = pCmd->rlen << 1; // 字节数
|
|
|
|
ReadModbusSData(pCmd->addr, &data[0], pCmd->rlen);
|
|
for (int j = 0; j < pCmd->rlen; j++)
|
|
{
|
|
pSDat[i++] = HIBYTE(data[j]); // DATA_H
|
|
pSDat[i++] = LOBYTE(data[j]); // DATA_L
|
|
}
|
|
|
|
checkcrc = ModbusCrc16(&pSDat[0], i);
|
|
|
|
pSDat[i++] = HIBYTE(checkcrc); // CRC_H
|
|
pSDat[i++] = LOBYTE(checkcrc); // CRC_L
|
|
|
|
g_modBusSCtrl.ModBusCommSend(pSDat, i); // 将数据放入发送缓冲区
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------
|
|
功能:发送一条RTU格式MODBUS协议的应答,回应06控制字命令
|
|
*/
|
|
int Modbus_TX06R(Rs485Cmd * pCmd)
|
|
{
|
|
int i = 0;
|
|
u8 pSDat[MDBSRXLEN_R]; //待发送数据
|
|
u16 checkcrc; //crc校验
|
|
|
|
if (pCmd == NULL)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
pSDat[i++] = pCmd->index; // ADR
|
|
pSDat[i++] = pCmd->cmd; // CMD
|
|
pSDat[i++] = HIBYTE(pCmd->addr); // ADDR_H
|
|
pSDat[i++] = LOBYTE(pCmd->addr); // ADDR_L
|
|
|
|
pSDat[i++] = HIBYTE(pCmd->wrdat[0]); // DATA_H
|
|
pSDat[i++] = LOBYTE(pCmd->wrdat[0]); // DATA_L
|
|
|
|
checkcrc = ModbusCrc16(&pSDat[0], i);
|
|
|
|
pSDat[i++] = HIBYTE(checkcrc); // CRC_H
|
|
pSDat[i++] = LOBYTE(checkcrc); // CRC_L
|
|
|
|
g_modBusSCtrl.ModBusCommSend(pSDat, i); // 将数据放入发送缓冲区
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------
|
|
功能:发送一条RTU格式MODBUS协议的应答,回应10H控制字命令
|
|
*/
|
|
int Modbus_TX10R(Rs485Cmd * pCmd)
|
|
{
|
|
int i = 0;
|
|
u8 pSDat[MDBSRXLEN_R]; //待发送数据
|
|
u8 checkcrc; //crc校验
|
|
|
|
if (pCmd == NULL)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
pSDat[i++] = pCmd->index; // ADR
|
|
pSDat[i++] = pCmd->cmd; // CMD
|
|
pSDat[i++] = HIBYTE(pCmd->addr); // ADDR_H
|
|
pSDat[i++] = LOBYTE(pCmd->addr); // ADDR_L
|
|
|
|
// 数据个数,单位字
|
|
pSDat[i++] = HIBYTE(pCmd->wlen); // LEN_H
|
|
pSDat[i++] = LOBYTE(pCmd->wlen); // LEN_L
|
|
|
|
checkcrc = ModbusCrc16(&pSDat[0], i);
|
|
|
|
pSDat[i++] = HIBYTE(checkcrc); // CRC_H
|
|
pSDat[i++] = LOBYTE(checkcrc); // CRC_L
|
|
|
|
g_modBusSCtrl.ModBusCommSend(pSDat, i); // 将数据放入发送缓冲区
|
|
|
|
return 0;
|
|
}
|
|
|
|
//-----------------------------------------------------------------
|
|
// 功能:发送一条MODBUS协议的回包,将数据放入发送缓冲区内
|
|
// 参数: pCmd:
|
|
// 返回值: 0:发送正常
|
|
// -1:参数错误
|
|
|
|
int ModbusSTx(Rs485Cmd * pCmd)
|
|
{
|
|
int result = 0;
|
|
u8 cmd;
|
|
|
|
if (pCmd == NULL)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
cmd = pCmd->cmd;
|
|
|
|
if ((cmd != MODBUS_RD) &&
|
|
(cmd != MODBUS_WR) &&
|
|
(cmd != MODBUS_WR_MULTI) &&
|
|
1)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
if (pCmd->index != 0 &&
|
|
((pCmd->index & 0x80) == 0 || cmd == MODBUS_RD) &&
|
|
1)
|
|
{
|
|
if (g_modBusSCtrl.modBusMode != 0)
|
|
{// ASCII码模式
|
|
if (cmd == MODBUS_RD)
|
|
{
|
|
Modbus_TX03A(pCmd);
|
|
}
|
|
else if (cmd == MODBUS_WR)
|
|
{
|
|
Modbus_TX06A(pCmd);
|
|
}
|
|
else if (cmd == MODBUS_WR_MULTI)
|
|
{
|
|
Modbus_TX10A(pCmd);
|
|
}
|
|
}
|
|
else
|
|
{// RTU模式
|
|
if (cmd == MODBUS_RD)
|
|
{
|
|
Modbus_TX03R(pCmd);
|
|
}
|
|
else if (cmd == MODBUS_WR)
|
|
{
|
|
Modbus_TX06R(pCmd);
|
|
}
|
|
else if (cmd == MODBUS_WR_MULTI)
|
|
{
|
|
Modbus_TX10R(pCmd);
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
//-----------------------------------------------------------------
|
|
// 功能:接收一条MODBUS协议的命令
|
|
// 参数:
|
|
// rDat: 本次收到的串口数据
|
|
|
|
// 返回值: 0: 接收正常 (收到的命令存放在 Rs485Cmd cmdbuf[MAX_485CMD] 中)
|
|
|
|
int ModbusSRx(u8 rDat)
|
|
{
|
|
int i;
|
|
int rst = -1;
|
|
u32 time;
|
|
static u32 timeBuf = 0;
|
|
static u8 rxDat[MDBSRXLEN_MAX] = {0}; // 待接收数据缓冲区
|
|
static u8 beg = 0; // 接收到起始符
|
|
static u16 idx = 0; // 有效索引
|
|
static u16 num = 0; // 接收到字符数
|
|
static u8 rxDat_H = 0; // 接收前半字节数据
|
|
|
|
u16 checkcrc = 0; // crc校验(RTU)
|
|
u8 checksum = 0; // 和校验(ASCII)
|
|
|
|
Rs485Cmd newCmd;
|
|
|
|
memset(&newCmd, 0, sizeof(Rs485Cmd));
|
|
|
|
if (g_modBusSCtrl.modBusMode != 0)
|
|
{// ASCII码模式
|
|
if (g_modBusSCtrl.filterChk != 0) // 7位数据过滤校验位
|
|
{
|
|
rDat &= (0x7F);
|
|
}
|
|
|
|
time = GetUsSoftTimer();
|
|
|
|
if (((time - timeBuf) > (g_modBusSCtrl.modBusWait * g_rs485SCtrl.modrsvTimout)) || // 读取超时时间
|
|
(idx > MDBSRXLEN_A) || // 超长复位
|
|
0)
|
|
{// 重新接收数据包
|
|
beg = 0;
|
|
idx = 0;
|
|
num = 1;
|
|
}
|
|
else
|
|
{
|
|
num++;
|
|
}
|
|
|
|
timeBuf = time;
|
|
|
|
if (((rDat == ':') || (rDat == 0x0D) || (rDat == 0x0A)) ||
|
|
((rDat >= '0') && (rDat <= '9')) ||
|
|
((rDat >= 'a') && (rDat <= 'f')) ||
|
|
((rDat >= 'A') && (rDat <= 'F')) ||
|
|
0)
|
|
{// 接收到正确的数据
|
|
if (beg == 1)
|
|
{
|
|
idx++;
|
|
}
|
|
}
|
|
else
|
|
{// 从新接收数据包
|
|
beg = 0;
|
|
idx = 0;
|
|
}
|
|
|
|
if (rDat == ':')
|
|
{// 接收到起始符
|
|
beg = 1;
|
|
idx = 0;
|
|
}
|
|
else if (rDat != 0x0A)
|
|
{// 接收到普通数据
|
|
if ((idx & 0x01) == 0)
|
|
{//接收到偶数个数据,进行数据转换
|
|
rxDat[(idx >> 1)-1] = MAKEBYTE(AsciiToRtu(rDat),AsciiToRtu(rxDat_H));
|
|
}
|
|
else
|
|
{//接收到奇数个数据,将数据存放到临时寄存器中
|
|
rxDat_H = rDat;
|
|
}
|
|
}
|
|
else if (rDat == 0x0A) // \n
|
|
{// 接收到终止符
|
|
if (rxDat_H == 0x0D) // \r
|
|
{// 终止符正常
|
|
if ((idx & 0x01) == 0)
|
|
{//数据个数基本符合要求
|
|
idx = (idx >> 1) - 2; //确定有效数据数组大小(不含校验位)
|
|
|
|
//校验数据是否正确
|
|
checksum = 0;
|
|
for (i = 0; i < idx; i++)
|
|
{
|
|
checksum += rxDat[i];
|
|
}
|
|
checksum = ~checksum + 1;
|
|
|
|
if(checksum == rxDat[idx])
|
|
{//lrc校验正确
|
|
u8 index = rxDat[0]; // 数据站号
|
|
int cur = 0;
|
|
|
|
index &= 0x7f;
|
|
for (i = 0; i < g_rs485SCtrl.idxNum; i++)
|
|
{// 遍历查找是否是发给自己的命令
|
|
if ((index == 0) ||
|
|
(index == g_rs485SCtrl.idxList[i]) ||
|
|
0)
|
|
{
|
|
cur = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(cur == 1)
|
|
{//站号正确
|
|
//判断命令
|
|
newCmd.index = rxDat[0]; // 站号
|
|
newCmd.cmd = rxDat[1]; // 命令码
|
|
newCmd.addr = MAKEWORD(rxDat[3],rxDat[2]); // 数据地址
|
|
|
|
switch(newCmd.cmd)
|
|
{
|
|
case MODBUS_RD: // 03H (读取保持寄存器)
|
|
{
|
|
newCmd.rlen = MAKEWORD(rxDat[5],rxDat[4]); //数据长度
|
|
AddModbusSCmd(&newCmd); // 增加命令
|
|
rst = 0;
|
|
break;
|
|
}
|
|
|
|
case MODBUS_WR: // 06H (写单个保持寄存器)
|
|
{
|
|
newCmd.wrdat[0] = MAKEWORD(rxDat[5],rxDat[4]); //数据
|
|
newCmd.wlen = 1; //数据长度
|
|
AddModbusSCmd(&newCmd); // 增加命令
|
|
rst = 0;
|
|
break;
|
|
}
|
|
|
|
case MODBUS_WR_MULTI: // 10H (写多个保持寄存器)
|
|
{
|
|
newCmd.wlen = MAKEWORD(rxDat[5],rxDat[4]); //数据长度
|
|
for (i = 0; i < (rxDat[6]/2); i++)
|
|
{
|
|
newCmd.wrdat[i] = MAKEWORD(rxDat[i*2+8],rxDat[i*2+7]); //数据
|
|
}
|
|
AddModbusSCmd(&newCmd); // 增加命令
|
|
rst = 0;
|
|
break;
|
|
}
|
|
|
|
default:
|
|
break;
|
|
} //switch(newCmd.cmd)
|
|
} //if (cur == 1)
|
|
} //if (checksum == rxDat[idx])
|
|
} //if ((idx & 0x01) == 0)
|
|
} //if (rxDat_H == 0x0D)
|
|
idx = 0;
|
|
beg = 0; // 从新接收数据包
|
|
} //if (rDat == 0x0A)
|
|
}
|
|
else
|
|
{// RTU模式
|
|
time = GetUsSoftTimer();
|
|
|
|
if (((time - timeBuf) > (g_modBusSCtrl.modBusWait * g_rs485SCtrl.modrsvTimout)) || // 读取超时时间
|
|
(idx > (MDBSRXLEN_R)) || // 超长复位
|
|
0)
|
|
{// 重新接收数据包
|
|
if (num > 1)
|
|
{
|
|
printf("data packet restart, old num=%d, rDat=0x%x\r\n", num, rDat);
|
|
}
|
|
idx = 0;
|
|
num = 1;
|
|
}
|
|
else
|
|
{
|
|
num++;
|
|
}
|
|
|
|
timeBuf = time;
|
|
|
|
int receive = 0; // 接收完成标志
|
|
|
|
if (num < 8) // 接收未完成
|
|
{
|
|
rxDat[idx] = rDat;
|
|
idx++;
|
|
}
|
|
else if (num >= 8)
|
|
{
|
|
u16 len;
|
|
|
|
if (rxDat[1] == MODBUS_WR_MULTI)
|
|
{
|
|
len = 9 + rxDat[6];
|
|
}
|
|
else
|
|
{
|
|
len = 8;
|
|
}
|
|
|
|
if (num < len) // 接收未完成
|
|
{
|
|
rxDat[idx] = rDat;
|
|
idx++;
|
|
}
|
|
else // 接收完成
|
|
{
|
|
rxDat[idx] = rDat;
|
|
receive = 1;
|
|
}
|
|
}
|
|
|
|
if (receive == 1)
|
|
{
|
|
/*
|
|
printf("receive a RTU modbus packet:");
|
|
for (int j = 0; j < num; j++)
|
|
{
|
|
printf("\t0x%x", rxDat[j]);
|
|
}
|
|
printf("\r\n");
|
|
*/
|
|
|
|
//校验数据是否正确
|
|
checkcrc = ModbusCrc16(&rxDat[0], num-2);
|
|
|
|
if (rxDat[num-2] == HIBYTE(checkcrc) && rxDat[num-1] == LOBYTE(checkcrc))
|
|
{//crc校验正确
|
|
u8 index = rxDat[0]; // 站号
|
|
int cur = 0;
|
|
|
|
index &= 0x7f; // 只查找小于128的站号
|
|
for (i = 0; i < g_rs485SCtrl.idxNum; i++)
|
|
{// 遍历查找是否是发给自己的命令
|
|
if ((index == 0) ||
|
|
(index == g_rs485SCtrl.idxList[i]) ||
|
|
0)
|
|
{
|
|
cur = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(cur == 1)
|
|
{//站号正确
|
|
newCmd.index = rxDat[0]; // 站号
|
|
newCmd.cmd = rxDat[1]; // 命令码
|
|
newCmd.addr = MAKEWORD(rxDat[3],rxDat[2]); // 数据地址
|
|
|
|
switch(newCmd.cmd)
|
|
{
|
|
case MODBUS_RD: // 03H (读取保持寄存器)
|
|
{
|
|
newCmd.rlen = MAKEWORD(rxDat[5],rxDat[4]); //数据长度
|
|
AddModbusSCmd(&newCmd); // 增加命令
|
|
rst = 0;
|
|
break;
|
|
}
|
|
|
|
case MODBUS_WR: // 06H (写单个保持寄存器)
|
|
{
|
|
newCmd.wrdat[0] = MAKEWORD(rxDat[5],rxDat[4]); //数据
|
|
newCmd.wlen = 1; //数据长度
|
|
AddModbusSCmd(&newCmd); // 增加命令
|
|
rst = 0;
|
|
break;
|
|
}
|
|
|
|
case MODBUS_WR_MULTI: // 10H (写多个保持寄存器)
|
|
{
|
|
newCmd.wlen = MAKEWORD(rxDat[5],rxDat[4]); //数据长度
|
|
for (i = 0; i < newCmd.wlen; i++)
|
|
{
|
|
newCmd.wrdat[i] = MAKEWORD(rxDat[8+i*2], rxDat[7+i*2]); //数据
|
|
}
|
|
AddModbusSCmd(&newCmd); // 增加命令
|
|
rst = 0;
|
|
break;
|
|
}
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
printf("index error, index=%d\r\n", index);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
printf("check crc error, calc crc:0x%x 0x%x, receive crc:0x%x 0x%x\r\n",
|
|
HIBYTE(checkcrc), LOBYTE(checkcrc), rxDat[num-2], rxDat[num-1]);
|
|
}
|
|
idx = 0;
|
|
num = 0; // 重新接收数据包
|
|
}
|
|
}
|
|
|
|
return rst;
|
|
}
|
|
|
|
//-----------------------------------------------------------------
|
|
|
|
// idx 从站号
|
|
void InitModbusSCtrl(u8 idx, int rsvtimout)
|
|
{
|
|
memset(&g_rs485SCtrl, 0, sizeof(Rs485Ctrl));
|
|
|
|
g_rs485SCtrl.init = 1; // 初始化完成
|
|
g_rs485SCtrl.idxList[g_rs485SCtrl.idxNum] = idx; // 站号
|
|
g_rs485SCtrl.idxNum++;
|
|
|
|
// 读取超时时间
|
|
if (rsvtimout < 10)
|
|
{
|
|
g_rs485SCtrl.modrsvTimout = 10;
|
|
}
|
|
else if (rsvtimout > 100)
|
|
{
|
|
g_rs485SCtrl.modrsvTimout = 100;
|
|
}
|
|
else
|
|
{
|
|
g_rs485SCtrl.modrsvTimout = rsvtimout;
|
|
}
|
|
}
|
|
|
|
// 注册外部响应函数
|
|
void RegModbusSCmdProc(Resv485ExProc exProc)
|
|
{
|
|
g_rs485SCtrl.resv485exproc = exProc;
|
|
}
|
|
|
|
// 增加附属从站号
|
|
void AddModbusSIdx(u8 idx)
|
|
{
|
|
if (g_rs485SCtrl.idxNum < MODBUS_IDXMAX)
|
|
{
|
|
g_rs485SCtrl.idxList[g_rs485SCtrl.idxNum] = idx; // 站号
|
|
g_rs485SCtrl.idxNum++;
|
|
}
|
|
}
|
|
|
|
// 将命令放入缓冲区内
|
|
int AddModbusSCmd(Rs485Cmd * pCmd)
|
|
{
|
|
Rs485Ctrl * p485Ctrl = &g_rs485SCtrl;
|
|
|
|
if (p485Ctrl != NULL && pCmd != NULL)
|
|
{
|
|
if (p485Ctrl->bufCmdNum < MAX_485CMD && p485Ctrl->bufTail < MAX_485CMD)
|
|
{
|
|
memcpy(&(p485Ctrl->cmdbuf[p485Ctrl->bufTail]), pCmd, sizeof(Rs485Cmd));
|
|
p485Ctrl->bufTail++;
|
|
if (p485Ctrl->bufTail >= MAX_485CMD)
|
|
{
|
|
p485Ctrl->bufTail = 0;
|
|
}
|
|
p485Ctrl->bufCmdNum++;
|
|
return p485Ctrl->bufCmdNum;
|
|
}
|
|
return -2;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
// 得到一条缓冲区内的命令
|
|
int GetModbusSCmd(Rs485Cmd * pCmd)
|
|
{
|
|
Rs485Ctrl * p485Ctrl = &g_rs485SCtrl;
|
|
|
|
if (p485Ctrl != NULL && pCmd != NULL)
|
|
{
|
|
if (p485Ctrl->bufCmdNum > 0 && p485Ctrl->bufHead < MAX_485CMD)
|
|
{
|
|
memcpy(pCmd, &(p485Ctrl->cmdbuf[p485Ctrl->bufHead]), sizeof(Rs485Cmd));
|
|
p485Ctrl->bufHead++;
|
|
if (p485Ctrl->bufHead >= MAX_485CMD)
|
|
{
|
|
p485Ctrl->bufHead = 0;
|
|
}
|
|
p485Ctrl->bufCmdNum--;
|
|
return p485Ctrl->bufCmdNum;
|
|
}
|
|
else
|
|
{
|
|
return -2;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
int GetModbusSCmdBufLen(void)
|
|
{
|
|
return g_rs485SCtrl.bufCmdNum;
|
|
}
|
|
|
|
// modbus从设备,异步响应函数
|
|
int ModbusSTask(void)
|
|
{
|
|
static u32 lastTime; // 单位us
|
|
|
|
static Rs485Cmd cmd;
|
|
|
|
Rs485Ctrl * p485Ctrl = &g_rs485SCtrl;
|
|
|
|
if (p485Ctrl == NULL)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
if (p485Ctrl->init == 0)
|
|
{// 没有初始化,直接跳出
|
|
return -1;
|
|
}
|
|
|
|
switch(p485Ctrl->steps)
|
|
{
|
|
case 0: // 待机状态
|
|
{
|
|
if (1)
|
|
{// 接收串口缓冲区内数据
|
|
u8 rDat;
|
|
int len;
|
|
int rslt;
|
|
do
|
|
{
|
|
len = g_modBusSCtrl.ModBusCommReceive(&rDat, 1); // 从缓冲区读取数据
|
|
if (len == 1)
|
|
{// 接收到串口数据
|
|
rslt = ModbusSRx(rDat);
|
|
if (rslt == 0)
|
|
{// 接收到一个数据包
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}while(1);
|
|
}
|
|
|
|
if (GetModbusSCmdBufLen() != 0)// 缓冲区有数据
|
|
{
|
|
memset(&cmd, 0, sizeof(Rs485Cmd));
|
|
GetModbusSCmd(&cmd); // 得到命令
|
|
if (cmd.index != 0 &&
|
|
((cmd.index & 0x80) == 0 || cmd.cmd == MODBUS_RD) &&
|
|
1)
|
|
{// 需要回包
|
|
lastTime = GetUsSoftTimer();
|
|
p485Ctrl->steps++;
|
|
}
|
|
else
|
|
{// 不需要回包
|
|
p485Ctrl->steps = 5;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case 1: // 数据发送允许
|
|
{
|
|
if ((GetUsSoftTimer() - lastTime) > (g_modBusSCtrl.modBusWait * 2))
|
|
{
|
|
g_modBusSCtrl.CommOutEn();
|
|
lastTime = GetUsSoftTimer();
|
|
p485Ctrl->steps++;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case 2: // 填充回包数据
|
|
{
|
|
if (GetUsSoftTimer() - lastTime > 100)
|
|
{
|
|
ModbusSTx(&cmd);
|
|
p485Ctrl->steps++;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case 3: // 等待发送完成
|
|
{
|
|
if (g_modBusSCtrl.IsModBusCommSendOver() == TRUE)
|
|
{
|
|
lastTime = GetUsSoftTimer();
|
|
p485Ctrl->steps++;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case 4: // 等待最后一个字节发送完成
|
|
{
|
|
if ((GetUsSoftTimer() - lastTime) > (g_modBusSCtrl.modBusWait * 2))
|
|
{
|
|
p485Ctrl->steps++;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case 5: // 执行外部函数
|
|
{
|
|
g_modBusSCtrl.CommOutDis(); // 数据发送禁止
|
|
if (cmd.cmd == MODBUS_WR || cmd.cmd == MODBUS_WR_MULTI)
|
|
{
|
|
WriteModbusSData(cmd.addr, &cmd.wrdat[0], cmd.wlen);
|
|
}
|
|
if (g_rs485SCtrl.resv485exproc != NULL)
|
|
{
|
|
g_rs485SCtrl.resv485exproc(&cmd);
|
|
}
|
|
else
|
|
{
|
|
printf("resv485exproc is NULL\r\n");
|
|
}
|
|
p485Ctrl->steps = 0;
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
g_modBusSCtrl.CommOutDis(); // 数据发送禁止
|
|
p485Ctrl->steps = 0;
|
|
break;
|
|
}
|
|
}
|
|
return p485Ctrl->steps;
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------------------------
|
|
// 初始化寄存器组
|
|
int InitModbusReg(u16 addr, u16 * pBuf, u16 len)
|
|
{
|
|
if (len != 0 && pBuf != NULL)
|
|
{
|
|
if ((addr + len - 1) > MODBUS_REG_MAX)
|
|
{
|
|
printf("[Warning]: modbus addr outsize !! addr=0x%x, max=0x%x\r\n", (addr + len - 1), MODBUS_REG_MAX);
|
|
}
|
|
else if (addr < MODBUS_REG_MIN)
|
|
{
|
|
printf("[Warning]: modbus addr outsize !! addr=0x%x, min=0x%x\r\n", addr, MODBUS_REG_MIN);
|
|
}
|
|
else
|
|
{
|
|
for (int i = addr; i < (addr+len); i++)
|
|
{
|
|
mdbsReg[i - MODBUS_REG_MIN] = pBuf++;
|
|
}
|
|
return len;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// 读数据
|
|
void ReadModbusSData(u16 addr, u16 * pBuf, u16 len)
|
|
{
|
|
if (len != 0 && pBuf != NULL)
|
|
{
|
|
if ((addr + len - 1) > MODBUS_REG_MAX)
|
|
{
|
|
printf("[Warning]: modbus addr outsize !! addr=0x%x, max=0x%x\r\n", (addr + len - 1), MODBUS_REG_MAX);
|
|
}
|
|
else if (addr < MODBUS_REG_MIN)
|
|
{
|
|
printf("[Warning]: modbus addr outsize !! addr=0x%x, min=0x%x\r\n", addr, MODBUS_REG_MIN);
|
|
}
|
|
else
|
|
{
|
|
for (int i = addr; i < (addr+len); i++)
|
|
{
|
|
if (mdbsReg[i - MODBUS_REG_MIN] == NULL)
|
|
{
|
|
printf("[Warning]: modbus addr NULL !! addr=0x%x\r\n", i);
|
|
}
|
|
else
|
|
{
|
|
*pBuf++ = *mdbsReg[i - MODBUS_REG_MIN];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// 写数据
|
|
void WriteModbusSData(u16 addr, u16 * pBuf, u16 len)
|
|
{
|
|
if (len != 0 && pBuf != NULL)
|
|
{
|
|
if ((addr + len - 1) > MODBUS_REG_MAX)
|
|
{
|
|
//printf("[Warning]: modbus addr outsize !! addr=0x%x, max=0x%x\r\n", (addr + len - 1), MODBUS_REG_MAX);
|
|
}
|
|
else if (addr < MODBUS_REG_MIN)
|
|
{
|
|
//printf("[Warning]: modbus addr outsize !! addr=0x%x, min=0x%x\r\n", addr, MODBUS_REG_MIN);
|
|
}
|
|
else
|
|
{
|
|
for (int i = addr; i < (addr+len); i++)
|
|
{
|
|
if (mdbsReg[i - MODBUS_REG_MIN] == NULL)
|
|
{
|
|
//printf("[Warning]: modbus addr NULL !! addr=0x%x\r\n", i);
|
|
}
|
|
else
|
|
{
|
|
*mdbsReg[i - MODBUS_REG_MIN] = *pBuf++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|