【描述】用MODBUS调试精灵与单片机进行通讯调试,涉及写多个寄存器(功能码10)和读定值寄存器(功能码03)两种功能。调试精灵将0xAAFF分别写入5个寄存器(十六位单片机,一共10个字节),发送指令如下:00 10 00 00 00 05 0A AA FF AA FF AA FF AA FF AA FF 95 20.第一个字节00是从机单片机地址,10为功能码,0000为寄存器首地址,0005为写入寄存器数量,95为crc低八位,20为crc告八位。
主从机波特率均为9600,数据位8位,1停止位,无校验。
【错误形式】写多数据通讯错误,让检查通讯参数和地址设置。
但是发现这条指令是可以成功写入单片机的,也能得到反馈00 10 00 00 00 05 9D.要是正确的话,得到的反馈还少一个crc的高八位。查了无数遍,都是这样的问题。
【代码】
主函数
SCI0RxIntEnable();
EnableInterrupts;
while(1)
{
if(cmdArrived) //定时中断中监测到3.5字节以上时间没受到数据,说明命令到达,cmdArrived=1
{
cmdArrived=0;//清零命令到达标志
len=SCI0BufNum();//读取此时串口缓冲中的字节数
if(SCI0Buf[0]==0x00)//判断第一个字节是否为从机地址,是的话进行下面的操作
{
crc=GetCRC16(SCI0Buf,len-2); //计算串口缓冲中除CRC高低两字节以外其它数据的CRC
crch=crc>>8; //获得CRC的高八位
crcl=crc&0xFF; //获得CRC的低八位
if((SCI0Buf[len-2]==crch)&&(SCI0Buf[len-1]==crcl))//判断发来数据的CRC是否和自己计算的CRC一致,一致时进行下面操作
{
switch(SCI0Buf[1])//提取功能码
{
case 0x10://功能码0x10表示主机写多个寄存器到从机
if((SCI0Buf[2]==0x00) && (SCI0Buf[3]<=0x04)) //寄存器地址支持0x0000-0x0004
{
SCI0BufCopyToRegRxdGroup(); //将串口缓冲区中的数据拷贝到10个字节的RegRxdGroup寄存器中,其实是长度为5的字符数组
}
else//发来的寄存器地址若不在0x0000-0004当中,返回错误码
{
ReturnErrorCodeWhenRegAddressErrorIfWriteReg(); //错误响应0x90,错误码00000003
}
calculate();//计算压差阶升幅度fai,据此给出故障信息,写入RegTxdGroup寄存器中
break;
case 0x03://功能码0x03表示主机读取从机定值寄存器
if((SCI0Buf[2] == 0x00) && (SCI0Buf[3]<=0x04)) //判断寄存器地址是否正确
{
RegTxdGroupCopyToSCI0Buf(); //把RegTxdGroup寄存器中的数据写到串口缓冲区中,等待发送
}
else //寄存器地址不正确时返回错误码00000003
{
ReturnErrorCodeWhenRegAddressErrorIfReadReg();
}
break;
default: //其它不支持的功能码
ReturnErrorCodeWhenDefaultFunctionCode();//返回错误码00000002
break;
}
}
else //CRC校验不正确时
{
ReturnErrorCodeWhenCRCCheckWrong(); //返回错误码00000004
}
}
else//从机地址不正确时
{
ReturnErrorCodeWhenSlaveAddressWrong();//返回错误码00000001
}
crc = GetCRC16(SCI0Buf,len);//计算应答信息的CRC
SCI0Buf[len++]=crc>>8;
SCI0Buf[len++]=crc & 0xFF;
//crch = crc>>8;
//crcl = crc & 0xFF;
for(iii=0;iii<len;iii++) //发送应答信息主体
{
SCI0PutChar(SCI0Buf[iii]);
}
//SCI0PutChar(crcl); //发送应答信息的CRC部分
//SCI0PutChar(crch);
SCI0BufClear();//清零串口缓冲区
DAC_Delay();//延时
}
}
定时器中断函数(每1ms进一次中断):
ECT_T标志寄存器1 = 0x01U;
timecnt++;
if(timecnt>=125)
{
timecnt = 0;
flag_125ms = 1;
}
if(SCI0BufPt>0) /* 接收计数器大于0时开始监控总线空闲时间 */
{
if(SCI0BufNumTemp!=SCI0BufPt) /* 接收计数器未改变,即刚收到数据时,清零空闲计时 */
{
SCI0BufNumTemp=SCI0BufPt;
FrameIdleTmr=0;
}
else /* 接收计数器未改变,即刚收到数据时,累积空闲时间 */
{
if(FrameIdleTmr<4)
{
FrameIdleTmr += ms;
if(FrameIdleTmr>=4) /* 空闲时间超过3.5个字节传输时间,认为一帧命令接收完毕 */
{
cmdArrived=1; /* 设置命令到达标志*/
}
}
}
}
else
{
SCI0BufNumTemp=0;
}
串口接收中断函数
DisableInterrupts;
if(SCI0SR1_RDRF==1)
{
if(SCI0BufPt>=SCI0_BUFNUM_MAX)
SCI0BufPt=0;
//SCI0BufPt=SCI0_BUFNUM_MAX-1;
//SCI0S1;
SCI0Buf[SCI0BufPt]=SCI0DRL;
SCI0BufPt++;
SCI0Clearcnt=SCI0CLEARTIME;
}
EnableInterrupts;
串口发送函数
void SCI0PutChar(INT8U c)
{
while(SCI0SR1_TDRE==0);
SCI0DRL=c;
}