|
从大学毕业到现在,重新学习了下51单片机,最近站在各位前辈大佬的肩膀上,调试了一下STC15W408AS的一个MODBUS_RTU的通讯,程序能够调通,但是用modscan32的时候,只能读连续的18个数值,搞不懂了。贴上代码,请各位大神帮忙看看
4002是程序做的自加不用管,其他初始化为0
4002是程序做的自加不用管,其他初始化为0
****本行不是代码*****只是说明 ****** 以下是modbusRTU.c *************************************************************
单片机源程序如下:
- #include "config.h"
- #include "modbusRTU.h"
- #include "CRC16.h"
- bit flagFrame = 0; /*帧接收完成标志*/
- bit flagTxd = 0; /*单字节发送完成标志*/
- unsigned char cntRxd = 0; /*接收字节计数器*/
- unsigned char bufRxd[128]; /*接收字节缓冲区*/
- /*预定义一个或多个实参数组,用于存放需要处理的数据*/
- unsigned int idata regGroup[60]={0}; //数据缓存区,全局变量,
- unsigned int idata modbusData[2]={0}; //该数组用来存放modbus数据,分别为波特率和地址
- /*串口配置函数,baud-波特率*/
- void UartInit(unsigned int baud) //9600bps@22.1184MHz 16位自动重装载
- {
- RS485_DIR = 0;
- SCON = 0x50; //8位数据,可变波特率
- AUXR |= 0x01; //串口1选择定时器2为波特率发生器
- AUXR |= 0x04; //定时器2时钟为Fosc,即1T
- T2L = (65536-SYSclk/baud/4) & 0xff; //设定定时初值,取低8位
- T2H = (65536-SYSclk/baud/4) >> 8; //设定定时初值,取高8位
- AUXR |= 0x10; //启动定时器2
- AUXR1 = 0x40; //将串口切换到P36P37,此处之所以要将串口切换,是因为P30/P31需要作为下载口
- ES = 1;
- }
- /*串口数据写入,即串口发送函数,buf-待发送数据指针,len-指定的发送长度*/
- void UartWrite(unsigned char *buf, unsigned char len)
- {
- RS485_DIR = 1;
- while(len--)
- {
- flagTxd = 0; //清零发送标志
- SBUF = *buf++; //发送一个字节数据
- while(!flagTxd);
- }
- // DelayX10us(5);
- Delay5ms(); //延时5ms,等待一帧数据的最后3.5字符发送完成
- RS485_DIR = 0;
- }
- /*串口数据读取,buf-接收数据指针,len-指定的读取长度,返回值,实际读到的数据长度*/
- unsigned char UartRead(unsigned char *buf, unsigned char len)
- {
- unsigned char i;
- if(len > cntRxd) //指定读取长度 大于 实际接收到的长度时,
- { //读取长度设置为实际接收到的数据长度
- len = cntRxd;
- }
- for(i=0;i<len;i++)
- {
- *buf++ = bufRxd[i];
- }
- cntRxd = 0;
- return len;
- }
- /*串口接收监控,由空闲时间判定帧结束,需在定时中断中调用,ms-定时间隔*/
- void UartRxMonitor(unsigned char ms)
- {
- static unsigned char cntbkp = 0;
- static unsigned char idlemr = 0;
- if(cntRxd >0)
- {
- if(cntbkp != cntRxd)
- {
- cntbkp = cntRxd;
- idlemr = 0;
- }
- else
- {
- if(idlemr < 50)
- {
- idlemr = idlemr + ms;
- }
- if(idlemr >= 50)
- {
- flagFrame = 1;
- }
- }
- }
- else
- {
- cntbkp = 0;
- }
- }
- /*串口驱动函数,监测数据帧的接收,调用函数功能,需要在主函数中调用*/
- void UartDriver()
- {
- unsigned char len;
- unsigned char xdata buf[40];
- if(flagFrame)
- {
- flagFrame = 0;
- len = UartRead(buf,sizeof(buf)-2);
- UartAction(buf,len);
- }
- }
- void InterruptUART() interrupt 4
- {
- if(RI)
- {
- RI = 0;
- if(cntRxd < sizeof(bufRxd))
- {
- bufRxd[cntRxd++] = SBUF;
- }
-
- }
- if(TI)
- {
- TI = 0;
- flagTxd = 1;
- }
- }
- /*串口动作函数,根据接收到的信号执行相应的动作
- buf-接收到的命令帧指针,len - 命令帧长度*/
- void UartAction(unsigned char *buf, unsigned char len)
- {
- unsigned int crc;
- unsigned char crcl,crch;
- unsigned char i = 0;
- unsigned char cnt = 0;
-
- if(buf[0] != modbusData[1])
- {
- return;
- }
- crc = GetCRC16(buf,len-2); //计算CRC
- crcl = crc & 0xff; //取低8位 CA
- crch = crc >> 8; //取高8位 D5 举例 01 03 00 01 00 01 D5 CA
- if((buf[len-2] != crch) || (buf[len-1] != crcl)) //CRC校验不通过,则退出该函数
- {
- return;
- }
- switch(buf[1])
- {
- case 0x03:
- if ((buf[2]==0x00) && (buf[3]<=0x3c))
- {
- i = buf[3]; //提取寄存器地址
- cnt = buf[5]; //提取寄存器数量
- buf[2] = cnt*2; //提取数据的字节数,为寄存器数量 *2
- len = 3; // 帧前部已经有地址、功能、字节数,所以len 从3开始算
- while(cnt--) //cnt=2
- {
- buf[len] = regGroup[i] >> 8; //高字节 len=3
- buf[len+1] = regGroup[i] & 0xff; //低字节 len = 3+1
- len = len + 2;
- i = i + 1;
- }
- break;
- }
- else //寄存器地址不被支持时,返回错误码
- {
- buf[1] = 0x83; // 此处错误码 为 0x03(功能码) + 0x80(功能码出错时)
- buf[2] = 0x02;
- len = 3;
- break;
- }
- case 0x06:
- if((buf[2]==0x00) && (buf[3]<=0x3c))
- {
- i = buf[3]; //需要写入的寄存器 的 地址低位
- regGroup[i] = 256 * buf[4] + buf[5];
- len = 6;
- break;
- }
- else
- {
- buf[1] = 0x86; // 此处错误码 为 0x06(功能码) + 0x80(功能码出错时)
- buf[2] = 0x02;
- len = 3;
- break;
- }
- default:
- buf[1] = 0x80;
- buf[2] = 0x01;
- len = 3;
- break;
- }
- crc = GetCRC16(buf,len); //计算返回帧的CRC校验值
- buf[len] = crc >> 8; //高字节
- buf[len+1] = crc & 0xFF; //低字节
- UartWrite(buf, len+2);
- }
- ****本行不是代码*****只是说明 ****** 以下是main.c *************************************************************
- #include "config.h"
- #include "power_led.h" /*开发板专有,启动STC的电源和LED*/
- #include "modbusRTU.h"
- void Timer0Init(void);
- void main()
- {
- modbusData[0]=9600; //此处暂时先用常数,后期从EEPROM中读取
- modbusData[1]=2; //此处暂时先用常数,后期从EEPROM中读取
- STP_power();
- EA = 1;
- UartInit(modbusData[0]);
- Timer0Init();
- while(1)
- {
- UartDriver();
- }
- }
- void Timer0Init(void) //1毫秒@22.1184MHz
- {
- AUXR |= 0x80; //定时器时钟1T模式
- TMOD &= 0xF0; //设置定时器模式
- TL0 = 0x9A; //设置定时初值
- TH0 = 0xA9; //设置定时初值
- TF0 = 0; //清除TF0标志
- TR0 = 1; //定时器0开始计时
- ET0 = 1;
- }
- /*T0中断函数,执行串口接收监控和蜂鸣器驱动*/
- void InterruptTimer0() interrupt 1
- {
- UartRxMonitor(1);
- regGroup[1] ++;
- if(regGroup[1]>65535)regGroup[1]=0;
- }
复制代码 |
|