移植的《手把手教你51單片機》的Modbus 程序到普中的STC89C51單片機,移植的是案例二。用Modbus調(diào)試一直顯示讀入超時。然后老師幫忙修改后可以在LCD上顯示字符,但是Modbus調(diào)試精靈上一直顯示讀寫超時。不能返回值,也不能讀寫。請問除了要修改lcd1602的引腳外,還需要哪些修改???、單片機是普中的A6,請問怎么修改程序。
============================================================================
這是RS485.C的代碼,老師修改了
============================================================================
#include <reg52.h>
#include <intrins.h>
sbit BUZZ = P1^6;
sbit RS485_DIR = P1^7; //RS485方向選擇引腳
bit flagOnceTxd = 0; //單次發(fā)送完成標志,即發(fā)送完一個字節(jié)
bit cmdArrived = 0; //命令到達標志,即接收到上位機下發(fā)的命令
unsigned char cntRxd = 0;
unsigned char idata bufRxd[20]; //串口接收緩沖區(qū) idata
unsigned char regGroup[5]; //Modbus寄存器組,地址為0x00~0x04
extern void LcdWriteDat(unsigned char dat); //寫入數(shù)據(jù)函數(shù) /////
extern bit flagBuzzOn;
extern void LcdShowStr(unsigned char x, unsigned char y, const unsigned char *str);
extern unsigned int GetCRC16(unsigned char *ptr, unsigned char len);
void ConfigUART(unsigned int baud) //串口配置函數(shù),baud為波特率
{
RS485_DIR = 0; //RS485設(shè)置為接收方向
SCON = 0x50; //配置串口為模式1
TMOD &= 0x0F; //清零T1的控制位
TMOD |= 0x20; //配置T1為模式2
TH1 = 256 - (11059200/12/32) / baud; //計算T1重載值
TL1 = TH1; //初值等于重載值
ET1 = 0; //禁止T1中斷
ES = 1; //使能串口中斷
TR1 = 1; //啟動T1
}
unsigned char UartRead(unsigned char *buf, unsigned char len) //串口數(shù)據(jù)讀取函數(shù),數(shù)據(jù)接收指針buf,讀取數(shù)據(jù)長度len,返回值為實際讀取到的數(shù)據(jù)長度
{
unsigned char i;
if (len > cntRxd) //讀取長度大于接收到的數(shù)據(jù)長度時,
{
len = cntRxd; //讀取長度設(shè)置為實際接收到的數(shù)據(jù)長度
}
for (i=0; i<len; i++) //拷貝接收到的數(shù)據(jù)
{
*buf = bufRxd[i];
buf++;
}
cntRxd = 0; //清零接收計數(shù)器
return len; //返回實際讀取長度
}
void DelayX10us(unsigned char t) //軟件延時函數(shù),延時時間(t*10)us
{
do {
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
} while (--t);
}
void UartWrite(unsigned char *buf, unsigned char len) //串口數(shù)據(jù)寫入函數(shù),即串口發(fā)送函數(shù),待發(fā)送數(shù)據(jù)指針buf,數(shù)據(jù)長度len
{
RS485_DIR = 1; //RS485設(shè)置為發(fā)送
while (len--) //發(fā)送數(shù)據(jù)
{
flagOnceTxd = 0;
SBUF = *buf;
buf++;
while (!flagOnceTxd);
}
DelayX10us(5); //等待最后的停止位完成,延時時間由波特率決定
RS485_DIR = 0; //RS485設(shè)置為接收
}
void UartDriver() //串口驅(qū)動函數(shù),檢測接收到的命令并執(zhí)行相應(yīng)動作
{
unsigned char i;
unsigned char cnt;
unsigned char len;
unsigned char buf[30];
unsigned char str[4];
unsigned int crc;
unsigned char crch, crcl;
unsigned char tmp;///
unsigned char a;
if (cmdArrived) //有命令到達時,讀取處理該命令
{
cmdArrived = 0;
// LcdWriteDat(0x31);////
len = UartRead(buf, sizeof(buf)); //將接收到的命令讀取到緩沖區(qū)中
LcdWriteDat(len+0x30);////
if (buf[0] == 0x01) //核對地址以決定是否響應(yīng)命令,本例中的本機地址為0x01
{
crc = GetCRC16(buf, len-2); //計算CRC校驗值
crch = crc >> 8;
crcl = crc & 0xFF;
// if ((buf[len-2]!=crch) || (buf[len-1]!=crcl))
// {
// return; //如CRC校驗不符時直接退出
// }
////// 1
// tmp = crch; //crcl顯示到液晶上
tmp = buf[6]; //
tmp =tmp >> 4;
if (tmp >= 0xA)
str[0] = tmp - 0xA + 'A';
else
str[0] = tmp + '0';
LcdWriteDat(str[0]);
// tmp = crch & 0x0F;
tmp = buf[6] & 0x0F;
if (tmp >= 0xA)
str[1] = tmp - 0xA + 'A';
else
str[1] = tmp + '0';
LcdWriteDat(str[1]);
///// 1
if ((buf[len-2] == crch) && (buf[len-1] == crcl)) //判斷CRC校驗是否正確
{
switch (buf[1]) //按功能碼執(zhí)行操作
{
case 0x03: //讀取一個或連續(xù)的寄存器
if ((buf[2] == 0x00) && (buf[3] <= 0x05)) //寄存器地址支持0x0000~0x0005
{
if (buf[3] <= 0x04)
{
i = buf[3]; //提取寄存器地址
cnt = buf[5]; //提取待讀取的寄存器數(shù)量
buf[2] = cnt*2; //讀取數(shù)據(jù)的字節(jié)數(shù),為寄存器數(shù)*2,因Modbus定義的寄存器為16位
len = 3;
while (cnt--)
{
buf[len++] = 0x00; //寄存器高字節(jié)補0
buf[len++] = regGroup[i++]; //寄存器低字節(jié)
}
}
else //地址0x05為蜂鳴器狀態(tài)
{
buf[2] = 2; //讀取數(shù)據(jù)的字節(jié)數(shù)
buf[3] = 0x00;
buf[4] = flagBuzzOn;
len = 5;
}
break;
}
else //寄存器地址不被支持時,返回錯誤碼
{
buf[1] = 0x83; //功能碼最高位置1
buf[2] = 0x02; //設(shè)置異常碼為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]; //保存寄存器數(shù)據(jù)
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, 1, str);
}
else //地址0x05為蜂鳴器狀態(tài)
{
flagBuzzOn = (bit)buf[5]; //寄存器值轉(zhuǎn)換為蜂鳴器的開關(guān)
}
len -= 2; //長度-2以重新計算CRC并返回原幀
break;
}
else //寄存器地址不被支持時,返回錯誤碼
{
buf[1] = 0x86; //功能碼最高位置1
buf[2] = 0x02; //設(shè)置異常碼為02-無效地址
len = 3;
break;
}
default: //其它不支持的功能碼
buf[1] |= 0x80; //功能碼最高位置1
buf[2] = 0x01; //設(shè)置異常碼為01-無效功能
len = 3;
break;
}
crc = GetCRC16(buf, len); //計算CRC校驗值
buf[len++] = crc >> 8; //CRC高字節(jié)
buf[len++] = crc & 0xFF; //CRC低字節(jié)
UartWrite(buf, len); //發(fā)送響應(yīng)幀
}
}
}
}/// UartDriver
void UartDriver1() //串口驅(qū)動函數(shù),檢測接收到的命令并執(zhí)行相應(yīng)動作
{
unsigned char i;
unsigned char cnt;
unsigned char len;
unsigned char buf[30];
unsigned char str[4];
unsigned int crc;
unsigned char crch, crcl;
unsigned char tmp;
if (cmdArrived) //有命令到達時,讀取處理該命令
{
cmdArrived = 0;
LcdWriteDat(0x37);
len = UartRead(buf, sizeof(buf)); //將接收到的命令讀取到緩沖區(qū)中
i = buf[3]; //提取寄存器地址
regGroup[i] = buf[5]; //保存寄存器數(shù)據(jù)
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);
}
}/// UartDriver
void UartRxMonitor(unsigned char ms) //串口接收監(jiān)控函數(shù)
{
static unsigned char cntbkp = 0;
static unsigned char idletmr = 0;
if (cntRxd > 0) //接收計數(shù)器大于零時,監(jiān)控總線空閑時間
{
if (cntbkp != cntRxd) //接收計數(shù)器改變,即剛接收到數(shù)據(jù)時,清零空閑計時
{
cntbkp = cntRxd;
idletmr = 0;
}
else
{
if (idletmr < 5) //接收計數(shù)器未改變,即總線空閑時,累積空閑時間
{
idletmr += ms;
if (idletmr >= 5) //空閑時間超過4個字節(jié)傳輸時間即認為一幀命令接收完畢
{
cmdArrived = 1; //設(shè)置命令到達標志
}
}
}
}
else
{
cntbkp = 0;
}
}
void InterruptUART() interrupt 4 //UART中斷服務(wù)函數(shù)
{
if (RI) //接收到字節(jié)
{
RI = 0; //手動清零接收中斷標志位
if (cntRxd < sizeof(bufRxd)) //接收緩沖區(qū)尚未用完時,
{
bufRxd[cntRxd++] = SBUF; //保存接收字節(jié),并遞增計數(shù)器
}
}
if (TI) //字節(jié)發(fā)送完畢
{
TI = 0; //手動清零發(fā)送中斷標志位
flagOnceTxd = 1; //設(shè)置單次發(fā)送完成標志
}
}
================================================================
這是main.c 的源程序
================================================================
#include <reg52.h>
sbit BUZZ = P1^6; //蜂鳴器控制引腳
bit flagBuzzOn = 0; //蜂鳴器啟動標志
unsigned char T0RH = 0; //T0重載值的高字節(jié)
unsigned char T0RL = 0; //T0重載值的低字節(jié)
void ConfigTimer0(unsigned int ms);
extern void LcdInit();
extern void ConfigUART(unsigned int baud);
extern void UartRxMonitor(unsigned char ms);
extern void UartDriver();
void main ()
{
EA = 1; //開總中斷
ConfigTimer0(1); //配置T0定時1ms
ConfigUART(4800); //配置波特率為9600///4800
LcdInit(); //初始化液晶
while(1)
{
UartDriver();
}
}
void ConfigTimer0(unsigned int ms) //T0配置函數(shù)
{
unsigned long tmp;
tmp = 11059200 / 12; //定時器計數(shù)頻率
tmp = (tmp * ms) / 1000; //計算所需的計數(shù)值
tmp = 65536 - tmp; //計算定時器重載值
tmp = tmp + 34; //修正中斷響應(yīng)延時造成的誤差
T0RH = (unsigned char)(tmp >> 8); //定時器重載值拆分為高低字節(jié)
T0RL = (unsigned char)tmp;
TMOD &= 0xF0; //清零T0的控制位
TMOD |= 0x01; //配置T0為模式1
TH0 = T0RH; //加載T0重載值
TL0 = T0RL;
ET0 = 1; //使能T0中斷
TR0 = 1; //啟動T0
}
void InterruptTimer0() interrupt 1 //T0中斷服務(wù)函數(shù)
{
TH0 = T0RH; //定時器重新加載重載值
TL0 = T0RL;
if (flagBuzzOn) //蜂鳴器鳴叫或關(guān)閉
BUZZ = ~BUZZ;
else
BUZZ = 1;
UartRxMonitor(1); //串口接收監(jiān)控
}
====================================================================
這是LCD1602.c
===================================================================
////5-14
#include <reg52.h>
#define LCD1602_DB P0
sbit LCD1602_RS = P2^6;
sbit LCD1602_RW = P2^5;
sbit LCD1602_E = P2^7;
void LcdWaitReady() //等待液晶準備好
{
unsigned char sta;
LCD1602_DB = 0xFF;
LCD1602_RS = 0;
LCD1602_RW = 1;
do
{
LCD1602_E = 1;
sta = LCD1602_DB; //讀取狀態(tài)字
LCD1602_E = 0;
} while (sta & 0x80); //bit7等于1表示液晶正忙,重復檢測直到其等于0為止
}
void LcdWriteCmd(unsigned char cmd) //寫入命令函數(shù)
{
LcdWaitReady();
LCD1602_RS = 0;
LCD1602_RW = 0;
LCD1602_DB = cmd;
LCD1602_E = 1;
LCD1602_E = 0;
}
void LcdWriteDat(unsigned char dat) //寫入數(shù)據(jù)函數(shù)
{
LcdWaitReady();
LCD1602_RS = 1;
LCD1602_RW = 0;
LCD1602_DB = dat;
LCD1602_E = 1;
LCD1602_E = 0;
}
void LcdShowStr(unsigned char x, unsigned char y, const unsigned char *str) //顯示字符串,屏幕起始坐標(x,y),字符串指針str
{
unsigned char addr;
//由輸入的顯示坐標計算顯示RAM的地址
if (y == 0)
addr = 0x00 + x; //第一行字符地址從0x00起始
else
addr = 0x40 + x; //第二行字符地址從0x40起始
//由起始顯示RAM地址連續(xù)寫入字符串
LcdWriteCmd(addr | 0x80); //寫入起始地址
while (*str != '\0') //連續(xù)寫入字符串數(shù)據(jù),直到檢測到結(jié)束符
{
LcdWriteDat(*str);
str++;
}
}
void LcdInit() //液晶初始化函數(shù)
{
LcdWriteCmd(0x38); //16*2顯示,5*7點陣,8位數(shù)據(jù)接口
LcdWriteCmd(0x0C); //顯示器開,光標關(guān)閉
LcdWriteCmd(0x06); //文字不動,地址自動+1
LcdWriteCmd(0x01); //清屏
}
==============================================================
這是CRC16.c
==============================================================
unsigned int GetCRC16(unsigned char *ptr, unsigned char len)
{
unsigned int index;
unsigned char crch = 0xFF; //高CRC字節(jié)
unsigned char crcl = 0xFF; //低CRC字節(jié)
unsigned char code TabH[] = { //CRC高位字節(jié)值表
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低位字節(jié)值表
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);
}
|