1、regGroup[5]; //Modbus 寄存器组,地址为 0x00~0x04,这5个寄存器包含了什么?
2、功能码最高置1位,怎么不一样:A:buf[1] = 0x83; //功能码最高位置 1
B:buf[1] = 0x86; //功能码最高位置 1
C:buf[1] |= 0x80; //功能码最高位置 13、buf[0]~buf[5]是指什么?
单片机源代码如下:
#include <reg52.h>
sbit BUZZ = P1^6;
bit flagBuzzOn = 0; //蜂鸣器启动标志
unsigned char T0RH = 0; //T0 重载值的高字节
unsigned char T0RL = 0; //T0 重载值的低字节
unsigned char regGroup[5]; //Modbus 寄存器组,地址为 0x00~0x04
void ConfigTimer0(unsigned int ms);
extern void UartDriver();
extern void ConfigUART(unsigned int baud);
extern void UartRxMonitor(unsigned char ms);
extern void UartWrite(unsigned char *buf, unsigned char len);
extern unsigned int GetCRC16(unsigned char *ptr, unsigned char len);
extern void InitLcd1602();
extern void LcdShowStr(unsigned char x, unsigned char y, unsigned char *str);
void main()
{
EA = 1; //开总中断
ConfigTimer0(1); //配置 T0 定时 1ms
ConfigUART(9600); //配置波特率为 9600
InitLcd1602(); //初始化液晶
while (1)
{
UartDriver(); //调用串口驱动
}
}
/* 串口动作函数,根据接收到的命令帧执行响应的动作
buf-接收到的命令帧指针,len-命令帧长度 */
void UartAction(unsigned char *buf, unsigned char len)
{
unsigned char i;
unsigned char cnt;
unsigned char str[4];
unsigned int crc;
unsigned char crch, crcl;
if (buf[0] != 0x01) //本例中的本机地址设定为 0x01,
{ //如数据帧中的地址字节与本机地址不符,
return; //则直接退出,即丢弃本帧数据不做任何处理
}
//地址相符时,再对本帧数据进行校验
crc = GetCRC16(buf, len-2); //计算 CRC 校验值
crch = crc >> 8;
crcl = crc & 0xFF;
if ((buf[len-2]!=crch) || (buf[len-1]!=crcl))
{
return; //如 CRC 校验不符时直接退出
}
//地址和校验字均相符后,解析功能码,执行相关操作
switch (buf[1])
{
case 0x03: //读取一个或连续的寄存器
if ((buf[2]==0x00) && (buf[3]<=0x05)) //只支持 0x0000~0x0005
{
if (buf[3] <= 0x04)
{
i = buf[3]; //提取寄存器地址
cnt = buf[5]; //提取待读取的寄存器数量
buf[2] = cnt*2; //读取数据的字节数,为寄存器数*2
len = 3; //帧前部已有地址、功能码、字节数共 3 个字节
while (cnt--)
{
buf[len++] = 0x00; //寄存器高字节补 0
buf[len++] = regGroup[i++]; //寄存器低字节
}
}
else //地址 0x05 为蜂鸣器状态
{
buf[2] = 2; //读取数据的字节数
buf[3] = 0x00;
buf[4] = flagBuzzOn;
len = 5;
}
break;
}
else //寄存器地址不被支持时,返回错误码
{
buf[1] = 0x83; //功能码最高位置 1
buf[2] = 0x02; //设置异常码为 02-无效地址
len = 3;
break;
}
case 0x06: //写入单个寄存器
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] = '\0';
LcdShowStr(i*3, 0, str);
}
else //地址 0x05 为蜂鸣器状态
{
flagBuzzOn = (bit)buf[5]; //寄存器值转为蜂鸣器的开关
}
len -= 2; //长度-2 以重新计算 CRC 并返回原帧
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 定时时间 */
void ConfigTimer0(unsigned int ms)
{
unsigned long tmp; //临时变量
tmp = 11059200 / 12; //定时器计数频率
tmp = (tmp * ms) / 1000; //计算所需的计数值
tmp = 65536 - tmp; //计算定时器重载值
tmp = tmp + 33; //补偿中断响应延时造成的误差
T0RH = (unsigned char)(tmp>>8); //定时器重载值拆分为高低字节
T0RL = (unsigned char)tmp;
TMOD &= 0xF0; //清零 T0 的控制位
TMOD |= 0x01; //配置 T0 为模式 1
TH0 = T0RH; //加载 T0 重载值
TL0 = T0RL;
ET0 = 1; //使能 T0 中断
TR0 = 1; //启动 T0
}
/* T0 中断服务函数,执行串口接收监控和蜂鸣器驱动 */
void InterruptTimer0() interrupt 1
{
TH0 = T0RH; //重新加载重载值
TL0 = T0RL;
if (flagBuzzOn) //执行蜂鸣器鸣叫或关闭
BUZZ = ~BUZZ;
else
BUZZ = 1;
UartRxMonitor(1); //串口接收监控
}
|