欧美极品高清xxxxhd,国产日产欧美最新,无码AV国产东京热AV无码,国产精品人与动性XXX,国产传媒亚洲综合一区二区,四库影院永久国产精品,毛片免费免费高清视频,福利所导航夜趣136

 找回密碼
 立即注冊

QQ登錄

只需一步,快速開始

搜索
查看: 3466|回復: 0
打印 上一主題 下一主題
收起左側

移植的《手把手教你51單片機》的Modbus 程序到普中的STC89C51單片機,用Modbus調試...

[復制鏈接]
跳轉到指定樓層
#
ID:904189 發表于 2021-5-20 18:28 | 只看該作者 回帖獎勵 |正序瀏覽 |閱讀模式
150黑幣

移植的《手把手教你51單片機》的Modbus 程序到普中的STC89C51單片機,移植的是案例二。用Modbus調試一直顯示讀入超時。然后老師幫忙修改后可以在LCD上顯示字符,但是Modbus調試精靈上一直顯示讀寫超時。不能返回值,也不能讀寫。請問除了要修改lcd1602的引腳外,還需要哪些修改???、單片機是普中的A6,請問怎么修改程序。



============================================================================
這是RS485.C的代碼,老師修改了
============================================================================

#include <reg52.h>
#include <intrins.h>
sbit BUZZ = P1^6;

sbit RS485_DIR = P1^7;  //RS485方向選擇引腳

bit flagOnceTxd = 0;  //單次發送完成標志,即發送完一個字節
bit cmdArrived = 0;   //命令到達標志,即接收到上位機下發的命令
unsigned char cntRxd = 0;
unsigned char idata bufRxd[20]; //串口接收緩沖區 idata

unsigned char regGroup[5];  //Modbus寄存器組,地址為0x00~0x04

extern void LcdWriteDat(unsigned char dat);  //寫入數據函數         /////
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)  //串口配置函數,baud為波特率
{
    RS485_DIR = 0; //RS485設置為接收方向
    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) //串口數據讀取函數,數據接收指針buf,讀取數據長度len,返回值為實際讀取到的數據長度
{
    unsigned char i;

    if (len > cntRxd) //讀取長度大于接收到的數據長度時,
    {
        len = cntRxd; //讀取長度設置為實際接收到的數據長度
    }
    for (i=0; i<len; i++) //拷貝接收到的數據
    {
        *buf = bufRxd[i];
               
        buf++;
    }
    cntRxd = 0;  //清零接收計數器

       

    return len;  //返回實際讀取長度
}
void DelayX10us(unsigned char t)  //軟件延時函數,延時時間(t*10)us
{
    do {
        _nop_();
        _nop_();
        _nop_();
        _nop_();
        _nop_();
        _nop_();
        _nop_();
        _nop_();
    } while (--t);
}
void UartWrite(unsigned char *buf, unsigned char len) //串口數據寫入函數,即串口發送函數,待發送數據指針buf,數據長度len
{
    RS485_DIR = 1;  //RS485設置為發送
    while (len--)   //發送數據
    {
        flagOnceTxd = 0;
        SBUF = *buf;
        buf++;
        while (!flagOnceTxd);
    }
    DelayX10us(5);  //等待最后的停止位完成,延時時間由波特率決定
    RS485_DIR = 0;  //RS485設置為接收
}

void UartDriver() //串口驅動函數,檢測接收到的命令并執行相應動作
{
    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)); //將接收到的命令讀取到緩沖區中
                LcdWriteDat(len+0x30);////
               

        if (buf[0] == 0x01)  //核對地址以決定是否響應命令,本例中的本機地址為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]) //按功能碼執行操作
                {
                    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,因Modbus定義的寄存器為16位
                                len = 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, 1, 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);      //發送響應幀
            }
        }
    }
}/// UartDriver

void UartDriver1() //串口驅動函數,檢測接收到的命令并執行相應動作
{
    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)); //將接收到的命令讀取到緩沖區中
        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);
    }
}/// UartDriver

void UartRxMonitor(unsigned char ms)  //串口接收監控函數
{
    static unsigned char cntbkp = 0;
    static unsigned char idletmr = 0;

    if (cntRxd > 0)  //接收計數器大于零時,監控總線空閑時間
    {
        if (cntbkp != cntRxd)  //接收計數器改變,即剛接收到數據時,清零空閑計時
        {
            cntbkp = cntRxd;
            idletmr = 0;
        }
        else
        {
            if (idletmr < 5)  //接收計數器未改變,即總線空閑時,累積空閑時間
            {
                idletmr += ms;
                if (idletmr >= 5)  //空閑時間超過4個字節傳輸時間即認為一幀命令接收完畢
                {
                    cmdArrived = 1; //設置命令到達標志
                }
            }
        }
    }
    else
    {
        cntbkp = 0;
    }
}
void InterruptUART() interrupt 4  //UART中斷服務函數
{
        if (RI)  //接收到字節
    {
                RI = 0;   //手動清零接收中斷標志位
        if (cntRxd < sizeof(bufRxd)) //接收緩沖區尚未用完時,
        {
            bufRxd[cntRxd++] = SBUF; //保存接收字節,并遞增計數器
        }
        }
        if (TI)  //字節發送完畢
    {
                TI = 0;   //手動清零發送中斷標志位
        flagOnceTxd = 1;  //設置單次發送完成標志
        }
}

================================================================
這是main.c 的源程序
================================================================

#include <reg52.h>

sbit BUZZ = P1^6;  //蜂鳴器控制引腳

bit flagBuzzOn = 0;   //蜂鳴器啟動標志
unsigned char T0RH = 0;  //T0重載值的高字節
unsigned char T0RL = 0;  //T0重載值的低字節

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配置函數
{
    unsigned long tmp;

    tmp = 11059200 / 12;      //定時器計數頻率
    tmp = (tmp * ms) / 1000;  //計算所需的計數值
    tmp = 65536 - tmp;        //計算定時器重載值
    tmp = tmp + 34;           //修正中斷響應延時造成的誤差

    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
}
void InterruptTimer0() interrupt 1  //T0中斷服務函數
{
    TH0 = T0RH;  //定時器重新加載重載值
    TL0 = T0RL;
    if (flagBuzzOn)  //蜂鳴器鳴叫或關閉
        BUZZ = ~BUZZ;
    else
        BUZZ = 1;
    UartRxMonitor(1);  //串口接收監控
}

====================================================================
這是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; //讀取狀態字
        LCD1602_E = 0;
    } while (sta & 0x80); //bit7等于1表示液晶正忙,重復檢測直到其等于0為止
}
void LcdWriteCmd(unsigned char cmd)  //寫入命令函數
{
    LcdWaitReady();
    LCD1602_RS = 0;
    LCD1602_RW = 0;
    LCD1602_DB = cmd;
    LCD1602_E  = 1;
    LCD1602_E  = 0;
}
void LcdWriteDat(unsigned char dat)  //寫入數據函數
{
    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地址連續寫入字符串
    LcdWriteCmd(addr | 0x80); //寫入起始地址
    while (*str != '\0')      //連續寫入字符串數據,直到檢測到結束符
    {
        LcdWriteDat(*str);
        str++;
    }
}
void LcdInit()  //液晶初始化函數
{
    LcdWriteCmd(0x38);  //16*2顯示,5*7點陣,8位數據接口
    LcdWriteCmd(0x0C);  //顯示器開,光標關閉
    LcdWriteCmd(0x06);  //文字不動,地址自動+1
    LcdWriteCmd(0x01);  //清屏
}

==============================================================
這是CRC16.c
==============================================================

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);  
}


分享到:  QQ好友和群QQ好友和群 QQ空間QQ空間 騰訊微博騰訊微博 騰訊朋友騰訊朋友
收藏收藏3 分享淘帖 頂 踩
回復

使用道具 舉報

您需要登錄后才可以回帖 登錄 | 立即注冊

本版積分規則

小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術交流QQ群281945664

Powered by 單片機教程網

快速回復 返回頂部 返回列表