东拼西凑的程序。
STC12C5A60S2 单片机  24M晶振,别的单片机可能要更改 串口初始化函数中波特率相关代码   与中断0 中断时间相关的代码
总体思想:在串口中断中判断数据 是否接收到 完整的帧,是则关中断。在主循环中 处理接收到的帧,处理好后开口中断。


#define OUTTIME        15//两数据帧最小间隔  毫秒
#define        MOD_ADDR        1 //本modbus 地址
uint8        receCount;            //接收到的字节个数
uint8        idata receBuf[16]; //发送接收缓冲区
uint8        idata receTimeOut;                //接收超时

// 串行中断程序
void uartIntProc() interrupt 4
{
        if(RI)
        {
                RI = 0;
 
                if(receTimeOut==0)//两次接收的间隔时间大于OUTTIME        receTimeOut在中断中每隔1ms减1
                {
                        if(receCount>3)        //如已收到的字节大于3个, 则认为已接到一帧数据,关中断  等待解析函数处理   解析函数判断 接收到的字节数与 是否超时 就可确定是否有数据包
                        {                                //解析完后,接到的到字节数receCount清零
                                ES = 0;    //关串口中断        如已收到一个数据包,则要到处理好之后 由处理函数打开
                                return;
                        }
                        else
                                receCount = 0;
                }                                        
                receTimeOut = OUTTIME;//置计时寄存器 该寄存器 在中断0中,每隔1ms自动减1                
                receBuf[receCount] = SBUF;        
//                ACC = SBUF;
//                if(P != RB8)
//                        checkoutError = 2;        //偶校验出错
                receCount++;          //已接收到的数据量加1
                receCount &= 0x0f;    //最多一次只能接收16个字节
        }
}

/**************************************************************************************************
NAME : function_MODBUS         //检查与处理uart0数据
INPUT : NO
OUTPUT : NO
FUNCTION :
           执行MODBUS功能函数
**************************************************************************************************/
void function_MODBUS(void)
{
        bit bt;

        if(receCount > 3) //接收到的数据大于4
        {                                        
                if(receTimeOut==0) // 离上次接收到数据的最大间隔时 限 已等于0
                {                                
                        bt = !checkPACK_MODBUS();//        校验接收数据包的正确性        正确返回零,错误返回非零值。                         
                        if((receBuf[0] == MOD_ADDR) && bt ) //是本机地址
                        {        
                                switch(receBuf[1])
                                {
                                        case 1://读取线圈状态(读取点 16位以内)
                                                break;
                                        case 3://读取保持寄存器(一个或多个)
                                                read_reg();//读寄存器
                                                sendPACK_MODBUS( );        //将CRC加入数据尾部,并发送数据包
                                                break;
                                        case 5://强制单个线圈
                                        case 6://设置单个寄存器
                                                   TX_NByte(receBuf,receCount);
                                                break;
                                                                        
                                        default:
                                                break;                        
                                }
                        }
                        receCount =0;//处理好数据后,接收到的字节个数清零
                        ES = 1;    //开串口中断        如已收到一个数据包,则要到处理好之后才能再开接收数据                 
                }  
        }
}

代码基本调通,已可以与 Modbus调试精灵 通信。不过有一疑问 ,在波特率9600时,理论上 “OUTTIME        15//两数据帧最小间隔  毫秒”为3ms就够,实际要设到9以上才行,好久没搞懂

posted on 2017-07-19 11:55  劳斯机要开车了  阅读(713)  评论(0)    收藏  举报