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); //串口接收監控
}
|