找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 2507|回复: 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 单片机教程网

快速回复 返回顶部 返回列表