2-无效地址
len=3;
break;
}
case0x06://写入单个寄存器
if((buf[2]==0x00)&&(buf[3]<=0x05))//只支持0x0000~0x0005
{
if(buf[3]<=0x04)
{
i=buf[3];//提取寄存器地址
regGroup[i]=buf[5];//保存寄存器数据
cnt=regGroup[i]>>4;//显示到液晶上
if(cnt>=0xA)
str[0]=cnt-0xA+A;
else
str[0]=cnt+0;
cnt=regGroup[i]&0x0F;
if(cnt>=0xA)
str[1]=cnt-0xA+A;
else
str[1]=cnt+0;
str[2]=;
LcdShowStr(i*3,0,str);
}
else//地址0x05为蜂鸣器状态
{
flagBuzzOn=(bit)buf[5];//寄存器值转为蜂鸣器的开关
}
len-=2;//长度-2以重新计算CRC并返回原帧
&nb
break;
}
else//寄存器地址不被支持时,返回错误码
{
buf[1]=0x86;//功能码最高位置1
buf[2]=0x02;//设置异常码为02-无效地址
len=3;
break;
}
default://其它不支持的功能码
buf[1]|=0x80;//功能码最高位置1
buf[2]=0x01;//设置异常码为01-无效功能
len=3;
break;
}
crc=GetCRC16(buf,len);//计算返回帧的CRC校验值
buf[len++]=crc>>8;//CRC高字节
buf[len++]=crc&0xFF;//CRC低字节
UartWrite(buf,len);//发送返回帧
}
/*配置并启动T0,ms-T0定时时间*/
voidConfigTimer0(unsignedintms)
{
unsignedlongtmp;//临时变量
tmp=11059200/12;//定时器计数频率
tmp=(tmp*ms)/1000;//计算所需的计数值
tmp=65536-tmp;//计算定时器重载值
tmp=tmp+33;//补偿中断响应延时造成的误差
T0RH=(unsignedchar)(tmp>>8);//定时器重载值拆分为高低字节
T0RL=(unsignedchar)tmp;
TMOD&=0xF0;//清零T0的控制位
TMOD|=0x01;//配置T0为模式1
TH0=T0RH;//加载T0重载值
TL0=T0RL;
ET0=1;//使能T0中断
TR0=1;//启动T0
}
/*T0中断服务函数,执行串口接收监控和蜂鸣器驱动*/
voidInterruptTimer0()interrupt1
{
TH0=T0RH;//重新加载重载值
TL0=T0RL;
if(flagBuzzOn)//执行蜂鸣器鸣叫或关闭
BUZZ=~BUZZ;
else
BUZZ=1;
UartRxMonitor(1);//串口接收监控
}
大家可以看到负责解析协议的UartAction函数很长,因为协议解析本来就是一件很繁琐的事情。我们的例程仅解析执行了两个功能命令,就已经有近百行程序了,如果你需要解析更多的功能命令的话,那么建议把每个功能都做一个函数,然后在相应的case分支里调用即可,这样就不会使单个函数过于庞大而难以维护。
1.1练习题
1、了解RS485通信以及和RS232的不同用法。
2、了解Modbus协议以及RTU数据帧的规则。
3、写一个电子钟程序,并且可以通过485调试器校时。
|