/***************************Lcd1602.c文件程序源代碼*****************************/
(此處省略,可參考之前章節的代碼)
/****************************RS485.c文件程序源代碼*****************************/
(此處省略,可參考之前章節的代碼)
/****************************CRC16.c文件程序源代碼****************************/
/* CRC16計算函數,ptr-數據指針,len-數據長度,返回值-計算出的CRC16數值 */
unsigned int GetCRC16(unsigned char *ptr, unsigned char len)
{
unsigned int index;
unsigned char crch = 0xFF; //高CRC字節
unsigned char crcl = 0xFF; //低CRC字節
unsigned char code TabH[] = { //CRC高位字節值表
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40
} ;
unsigned char code TabL[] = { //CRC低位字節值表
0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06,
0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD,
0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09,
0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A,
0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC, 0x14, 0xD4,
0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,
0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3,
0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4,
0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A,
0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29,
0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED,
0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,
0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60,
0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67,
0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F,
0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68,
0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E,
0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,
0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71,
0x70, 0xB0, 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92,
0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C,
0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B,
0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B,
0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,
0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42,
0x43, 0x83, 0x41, 0x81, 0x80, 0x40
} ;
while (len--) //計算指定長度的CRC
{
index = crch ^ *ptr++;
crch = crcl ^ TabH[index];
crcl = TabL[index];
}
return ((crch<<8) | crcl);
}
關于CRC校驗的算法,如果不是專門學習校驗算法本身,大家可以不去研究這個程序的細節,直接使用現成的函數即可。
/*****************************main.c文件程序源代碼******************************/
#include
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) // 01 03 [ 00 i ] [ xx cnt ] [ crch crcl ]
{
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) // 01 06 [ 00 i ] [ xx regG ] [ crch crcl ]
{
i = buf[3]; //提取寄存器地址
regGroup = buf[5]; //保存寄存器數據
cnt = regGroup >> 4; //顯示到液晶上 16進制轉string
if (cnt >= 0xA)
str[0] = cnt - 0xA + 'A';
else
str[0] = cnt + '0';
cnt = regGroup & 0x0F;
if (cnt >= 0xA)
str[1] = cnt - 0xA + 'A';
else
str[1] = cnt + '0';
str[2] = '';
LcdShowStr(i*3, 0, str);
}
else //地址0x05為蜂鳴器狀態 // 01 06 [ 00 05 ] [ xx on ] [ crch crcl ]
{
flagBuzzOn = (bit)buf[5]; //寄存器值轉為蜂鳴器的開關
}
len -= 2; //長度-2以重新計算CRC并返回原幀
|