东拼西凑的程序。
STC12C5A60S2 单片机 24M晶振,别的单片机可能要更改 串口初始化函数中波特率相关代码 与中断0 中断时间相关的代码
总体思想:在串口中断中判断数据 是否接收到 完整的帧,是则关中断。在主循环中 处理接收到的帧,处理好后开口中断。
#define OUTTIME 15//两数据帧最小间隔 毫秒
#define MOD_ADDR 1 //本modbus 地址
uint8 receCount; //接收到的字节个数
uint8 idata receBuf[16]; //发送接收缓冲区
uint8 idata receTimeOut; //接收超时
// 串行中断程序
void uartIntProc() interrupt 4
{
if(RI)
{
RI = 0;
if(receTimeOut==0)//两次接收的间隔时间大于OUTTIME receTimeOut在中断中每隔1ms减1
{
if(receCount>3) //如已收到的字节大于3个, 则认为已接到一帧数据,关中断 等待解析函数处理 解析函数判断 接收到的字节数与 是否超时 就可确定是否有数据包
{ //解析完后,接到的到字节数receCount清零
ES = 0; //关串口中断 如已收到一个数据包,则要到处理好之后 由处理函数打开
return;
}
else
receCount = 0;
}
receTimeOut = OUTTIME;//置计时寄存器 该寄存器 在中断0中,每隔1ms自动减1
receBuf[receCount] = SBUF;
// ACC = SBUF;
// if(P != RB8)
// checkoutError = 2; //偶校验出错
receCount++; //已接收到的数据量加1
receCount &= 0x0f; //最多一次只能接收16个字节
}
}
/**************************************************************************************************
NAME : function_MODBUS //检查与处理uart0数据
INPUT : NO
OUTPUT : NO
FUNCTION :
执行MODBUS功能函数
**************************************************************************************************/
void function_MODBUS(void)
{
bit bt;
if(receCount > 3) //接收到的数据大于4
{
if(receTimeOut==0) // 离上次接收到数据的最大间隔时 限 已等于0
{
bt = !checkPACK_MODBUS();// 校验接收数据包的正确性 正确返回零,错误返回非零值。
if((receBuf[0] == MOD_ADDR) && bt ) //是本机地址
{
switch(receBuf[1])
{
case 1://读取线圈状态(读取点 16位以内)
break;
case 3://读取保持寄存器(一个或多个)
read_reg();//读寄存器
sendPACK_MODBUS( ); //将CRC加入数据尾部,并发送数据包
break;
case 5://强制单个线圈
case 6://设置单个寄存器
TX_NByte(receBuf,receCount);
break;
default:
break;
}
}
receCount =0;//处理好数据后,接收到的字节个数清零
ES = 1; //开串口中断 如已收到一个数据包,则要到处理好之后才能再开接收数据
}
}
}
代码基本调通,已可以与 Modbus调试精灵 通信。不过有一疑问 ,在波特率9600时,理论上 “OUTTIME 15//两数据帧最小间隔 毫秒”为3ms就够,实际要设到9以上才行,好久没搞懂
浙公网安备 33010602011771号