📄 modbus.c
字号:
#include "main.h"
//unsigned int GwRxCount; // GwSciRxBuffer[]数组的下标
//unsigned int GwTxCount; // GwSciTxBuffer[]数组的下标
//unsigned char GwRxFlag; // 接收标志 1: 准备接收; 0: 接收结束
//unsigned char GwTxFlag; // 发送标志 1: 准备发送; 0: 发送结束
unsigned char GwSciTxBuffer[32],GwSciRxBuffer[32];
unsigned int GwSciTxBytes; // 每帧信息中发送字节总数
unsigned int GwSciRxBytes; // 每帧信息中接收字节总数
int GwSciRxFlag = 0; //SCI接收中断标志,'1'表示接收中断发生
static unsigned char CRCHi[] = {
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01,
0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81,
0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01,
0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01,
0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01,
0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
0x40
};
static unsigned char CRCLo[] = {
0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, 0x07, 0xC7, 0x05, 0xC5, 0xC4,
0x04, 0xCC, 0x0C, 0x0D, 0xCD, 0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09,
0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A, 0x1E, 0xDE, 0xDF, 0x1F, 0xDD,
0x1D, 0x1C, 0xDC, 0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,
0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 0xF2, 0x32, 0x36, 0xF6, 0xF7,
0x37, 0xF5, 0x35, 0x34, 0xF4, 0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A,
0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29, 0xEB, 0x2B, 0x2A, 0xEA, 0xEE,
0x2E, 0x2F, 0xEF, 0x2D, 0xED, 0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,
0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60, 0x61, 0xA1, 0x63, 0xA3, 0xA2,
0x62, 0x66, 0xA6, 0xA7, 0x67, 0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F,
0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68, 0x78, 0xB8, 0xB9, 0x79, 0xBB,
0x7B, 0x7A, 0xBA, 0xBE, 0x7E, 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,
0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, 0x70, 0xB0, 0x50, 0x90, 0x91,
0x51, 0x93, 0x53, 0x52, 0x92, 0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C,
0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B, 0x99, 0x59, 0x58, 0x98, 0x88,
0x48, 0x49, 0x89, 0x4B, 0x8B, 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,
0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42, 0x43, 0x83, 0x41, 0x81, 0x80,
0x40
};
// 功能 :对SCI模块进行初始化,与油气分离单元进行通信以串口为硬件接口,采用MODBUS协议
// 返回 :无
// 形参 : 无
void SciInit(void)
{
MCRA=MCRA|0x0003; // IOPA0(SCITXD), IOPA1(SCIRXD)为串口
SCICCR=0x67; // 1位停止位,使能偶校验功能,数据字为8位长度
SCICTL1=0x03; // 不使能接收错误中断,软件复位,使能发送和接收,SLEEP位为0
// TXWAKE=0,没有选定的发送特征
//todo:暂不采用中断方式接收
//SCICTL2=0x02; //接收中断使能,不使能发送中断,发送采用置软件标志位方法
SCIPRI=0x00; //高优先级中断,使用INT1,INT1(高优先级中断)还是INT5(低优先级中断)
SCIHBAUD=0x01; //波特率设置为19200bit/s
SCILBAUD=0x03;
SCICTL1=0x23; //SW位置1,串口初始化完成
}
// 功能 :生成MODBUA协议中的CRC校验码(16位)
// 返回 :CRC校验码(16位)
// 形参 : 指向待发送或已接收到的数组的指针,数组长度
unsigned int CRC16( const unsigned char *buf, int len)
{
unsigned char Hi=0xFF;
unsigned char Lo=0xFF;
unsigned short index;
if (buf == 0 || len <= 0)
return(0);
while (len--)
{
index = Hi ^ (unsigned char)*buf++;
Hi = Lo ^ CRCHi[index];
Lo = CRCLo[index] ;
}
index=(Hi<<8)|Lo;
return(index);
}
// 功能 :对接收的数据进行CRC校验,与发送来的CRC校验值(在所接收的数据中)进行比较
// 返回 :如果CRC校验值与接收到的CRC校验值相等,则返回1,否则返回0
// 形参 :
unsigned char CheckCRC(unsigned char *crctmp,unsigned int length)
{
unsigned int crc_result, crc_tmp;
//取接收到数据中的CRC值
crc_tmp = *(crctmp + length-2); // CRC高字节
crc_tmp = crc_tmp << 8 + *( crctmp+length-1); // CRC高字节左移8位+CRC低字节 = CRC 值
//crc_tmp = crc_tmp * 256 + *( crctmp+length-1); // CRC高字节左移8位+CRC低字节 = CRC 值
crc_result = CRC16(crctmp, length-2); // 根据接收到的数据计算CRC 值
if ( crc_tmp != crc_result ) // 比较CRC值
return(0);
else
return(1);
}
// 功能 :把按MODBUS协议规定的数据形式组织好的数据发给油气分离单元
// 返回 :无
// 形参 : 无
void SciSend(void)
{
//开始发送,后续数据在中断中继续发送
//GwTxFlag = 1; //准备发送,发送完成后清零
unsigned int wTempSciRxBuf = 0;
unsigned int wTxCount = 0; //发送计数器初始化
// 在开始发送之前先清接收缓冲区(实际是清SCIRXST的RXRDY位),以保证SCIRXBUF中没有新数据(即未读过的数据)
wTempSciRxBuf = SCIRXBUF; // 通过读SCIRXBUF可以清SCIRXST的RXRDY位
// 开始发送数据
while (wTxCount < GwSciTxBytes)
{
SCITXBUF = GwSciTxBuffer[wTxCount]; //发送数据,写SCITXBUF会清除TXRDY位,即表示此时SCITXBUF满
while((SCICTL2 & 0x80) == 0); //等待TXRDY位变"1",即SCITXBUF为空,可以接收下一个数据
wTxCount ++;
}
}
// 功能 :接收响应
// 返回 :无
// 形参 : 无
void SciReceive(void)
{
//GwRxFlag = 1; //准备接收,接收完成后清零
//GwRxCount = 0; //接收指针清零,保证数据从缓冲区的首地址处开始存放
unsigned int wDelayCount = 0; //等待接收的延时
unsigned int wRxCount = 0; //接收计数器初始化
while (wRxCount < GwSciRxBytes) //等待接收完成,数据在中断服务程序中接收,GwRxCount在中断服务程序中累加
{
if (GwForceInitFlag == 1) //如果强制命令发生
{
wRxCount = GwSciRxBytes;
}
//等待SCIRXST的RXRDY位置'1',RXRDY位为'0'时wDelayCount++,以便超时退出
while ((SCIRXST & BIT6) == 0) //如果SCIRXBUF中没有新数据(SCIRXST的RXRDY位为'0',代表没有新数据)
{
wDelayCount ++;
//todo:延时多少次为宜,等待接收的延时
if (wDelayCount > 50000)
{
wRxCount = GwSciRxBytes;
break; //跳出while ((SCIRXST & BIT6) == 0)循环
}
}
wDelayCount = 0; //就马上对计数清零
GwSciRxBuffer[wRxCount] = SCIRXBUF; //读缓冲区内的数据(如果是由于超时退出的,则这里读到是上一次的数据)
wRxCount ++; //字节数计数加'1'
}
}
// 功能 :构建RTU模式帧,RTU模式的ADU
// 返回 :无
// 形参 :
void ConstructRtuFrame (unsigned char *dst_buf,unsigned char *src_buf,unsigned char lenth)
{
unsigned int crc_tmp;
crc_tmp = CRC16(src_buf, lenth); //对二进制串,计算getCRC16校验
*(src_buf+lenth) = crc_tmp >> 8; //CRC 高字节在前
*(src_buf+lenth+1) = crc_tmp & 0xff; //CRC 低字节在后
lenth++;
lenth++;
while ( lenth--) //形成Modbus通信的RTU串
{
*dst_buf = *src_buf;
dst_buf++;
src_buf++;
}
}
char ProcessMasterRTU( unsigned int *dest, unsigned char *src,unsigned int start_address, unsigned int fr_lenth)
{
unsigned int crc_result, crc_tmp;
unsigned char i, j, shift;
crc_tmp = *(src + fr_lenth-2); // crc 第一字节
crc_tmp = crc_tmp * 256 + *( src+fr_lenth-1); // CRC 值
crc_result = CRC16(src, fr_lenth-2); // 计算CRC 值
if ( crc_tmp != crc_result )
{
return CRC_ERR; // CRC 校验错误
}
switch ( *(src+1) ) // 功能码
{
case READ_COIL: //读取继电器状态 01
{
for ( i=0; i<*( src+2); i++)
{
shift = 1;
for ( j=0; j<8; j++)
{
*(dest+start_address+i*8+j) = shift & *( src+3+i);
*( src+3+i) >>= 1;
}
}
break;
}
case READ_DI: //读取开关量输入 02
{
for ( i=0; i<*( src+2); i++)
{
shift = 1;
for (j=0; j<8; j ++)
{
*(dest+start_address+i*8+j) = shift & *( src+3+i);
*( src+3+i)>>=1;
}
}
break;
}
case READ_HLD_REGs: //读取多个保持寄存器 03
{
for ( i=0; i<*( src+2); i+=2)
{
//todo: 这个式子是否正确
*(dest + start_address+ i/2)= *(src+i+3)*256 + *(src+i+4) ;
}
break ;
}
case READ_AI: //读取模拟量输入 04
{
for ( i=0; i<*( src+2); i+=2)
{
*(dest + start_address+ i/2) = *( src+i+3)*256 + *( src+i+4) ;
}
break;
}
case SET_COIL: //05强制单个线圈进行
{
break;
}
case SET_HLD_REG: //06写单个寄存器
{
break;
}
case SET_COILs: //15强制多个线圈进行
{
break;
}
case SET_HLD_REGs: //16写多个寄存器
{
break;
}
case READ_SET_HLD_REGs: //23读写多个寄存器
{
break;
}
default: //功能码错误
return(CMD_ERR);
break;
}
return(1);
}
// 功能 :读取继电器状态:CMD == 1
// 返回 :成功返回1,否则返回0
// 请求命令:[设备地址] [命令号01] [起始寄存器地址高8位] [低8位] [读取的线圈数高8位] [低8位] [CRC校验的高8位] [CRC校验的低8位]
// 设备响应:[设备地址] [命令号01] [返回的字节个数][数据1][数据2]...[数据n][CRC校验的高8位] [CRC校验的低8位]
// tmmp存放读回寄存器的状态
char ReadCoilStatus(unsigned int *pwCoilState, unsigned char DeviceID,unsigned int start_address,unsigned int lenth)
{
unsigned char tmp[64],tmp_lenth;
tmp[0] = DeviceID; //从设备地址
tmp[1] = 0x01; //命令
tmp[2] = start_address>>8; //起始地址 高字节
tmp[3] = start_address & 0xFF; //起始地址 低字节
tmp[4] = lenth>>8; //读取数据量 高字节
tmp[5] = lenth & 0xFF; //读取数据量 低字节
tmp_lenth = 6; //二进制包长度
ConstructRtuFrame(GwSciTxBuffer,tmp,tmp_lenth);
GwSciTxBytes = tmp_lenth+2;
if(lenth % 8==0) //如果余数为零
{
GwSciRxBytes=(lenth/8)+3+2; // lenth/8线圈状态的字节数,3为地址码,功能码,字节数,2为两个CRC字节
}
else
{
GwSciRxBytes=(lenth/8+1)+3+2;
}
SciSend(); //发送请求
SciReceive(); //接收设备响应
if(GwSciRxBuffer[1] == 0x01) //通信正常,数据处理
{
ProcessMasterRTU(pwCoilState,GwSciRxBuffer,0,GwSciRxBytes);
}
else //通信异常0x83,查询异常码,可重新发送请求或作其他处理
{
return(CMD_ERR);
}
}
// 功能 :读取开关量输入:CMD == 2
// 返回 :成功返回1,否则返回0
// 备注 :全局变量Modbus_mode,GwSciTxBuffer[],GwSciTxBytes,GwSciRxBuffer[],GwSciRxBytes
// 请求命令:[设备地址] [命令号02] [起始寄存器地址高8位] [低8位] [读取的寄存器数高8位] [低8位] [CRC校验的高8位] [CRC校验的低8位]
// 设备响应:[设备地址] [命令号02] [返回的字节个数][数据1][数据2]...[数据n][CRC校验的高8位] [CRC校验的低8位]
char ReadInStatus(unsigned int *pwInState, unsigned char DeviceID, unsigned int start_address, unsigned int lenth)
{
unsigned char tmp[64], tmp_lenth;
// int tmmp[256];
tmp[0] = DeviceID; //从设备地址
tmp[1] = 0x02; //命令
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -