stm8s003上实现的超简modbusRTU从机

admin 2017-10-25 4082

 在做modbus RTU串口从机的时候,多字节收发总是出现错误。或者发送间隔过长,严重影响轮询速度,于是自己动手写了一个单函数的modbus协议,支持03和06命令字,多字节读,单字节写,理论轮询间隔在20ms甚至10ms,50ms测试1000次无错误。
 

 

发送间隔50ms  

@near uchar data1[10];//串口数据缓存
@far @interrupt void UART1_int (void)//串口收中断
{
                uchar uarti;
                for(uarti=0;uarti<7;uarti++)
                {
                        data1[uarti]=data1[uarti+1];
                        
                }
                UART1_SR_RXNE = 0;
                data1[7]=UART1_DR;
                Printmodbus(data1,ddd);

}

该程序写得比较有针对性,比较适合小单片机低RAM使用,如果纯粹作为modbus从机数据传输,也基本够用,当然多字节写等其它命令字也是可以扩展的。


//CRC_16计算函数,注意这是stm8s大端,如果计算错误,请用下面第二个小端程序。
unsigned short int CRC_16(unsigned char *data,int len)
{
        unsigned char *buf;
        unsigned short int * CRC;
        unsigned short int crch,crcl;
        uchar p;
        uchar j;
        char err;

        buf= & data[len];
        CRC=(unsigned short int *)buf;
        buf[0]=0xff;//lsb
        buf[1]=0xff;//msb
        for(p=0;p<len;p++)
        {
                buf[1]=buf[1]^data[p];
                for(j=0;j<8;j++)
                {
                        err=buf[1]&1;
                        
                        *CRC=*CRC/2;
                        
                        if(err) *CRC=*CRC^0xa001;
                }
        }
          crch=*CRC>>8;
    crcl=*CRC<<8;
    *CRC=crch+crcl;
        return(*CRC);
}

//CRC16第二类计算
unsigned short int CRC_16(unsigned char *data,int len)
{
        unsigned char *buf;
        unsigned short int * CRC;
    unsigned short int crch,crcl;
        short int i;
        short int j;
        char err;

        buf= & data[len];
        CRC=(unsigned short int *)buf;
        buf[0]=0xff;
        buf[1]=0xff;
        for(i=0;i<len;i++)
        {
                buf[0]=buf[0]^data;
                for(j=0;j<8;j++)
                {
                        err=buf[0]&1;
                        *CRC=*CRC/2;
                        if(err) *CRC=*CRC^0xa001;
                }
        }
    crch=*CRC>>8;
    crcl=*CRC<<8;
    *CRC=crch+crcl;
        return(*CRC);
}


//简化版modbus协议单函数
//temp[]为串口收到命令,data为要发送数据。
unsigned char ddd[40]={0,0,0,0,0,0,0,0,0,0,
                                  0,0,0,0,0,0,0,0,0,0,
                                  0,0,0,0,0,0,0,0,0,0,
                                   0,0,0,0,0,0,0,0,0,0};//协议数据缓存
void Printmodbus(unsigned char temp[],unsigned char data[])
{
    unsigned char temp1[5];
    unsigned  int get,get2;
    unsigned char crch,crcl;
    unsigned char len=5+temp[5]*2;
    unsigned char temp2[60];
    unsigned char temp3[8];
    unsigned char m,n,g;
    temp1[0]=temp[0];temp1[1]=temp[1];temp1[2]=temp[2];
    temp1[3]=temp[3];temp1[4]=temp[4];temp1[5]=temp[5];
    get=CRC_16(temp1,6);
    crch=(unsigned char)(get>>8);
    crcl=(unsigned char)(get);
               
    if(temp[6]==crch&&temp[7]==crcl)
    {
                        UART1_CR2_RIEN=0;  //关串口中断,重要
                        if((temp[0]==0)||(temp[0]==data[39]))     //判断地址匹配,协定0号地址为广播地址。data【39】存储设备地址,值在设备启动时从eeprom加载
                                        {
                                                
                                 switch(temp[1])
                                                        {
                                                        
                                        case 3: //读取命令字
                                                                        temp2[0]=data[39];
                                                                        temp2[1]=temp[1];
                                                                        temp2[2]=temp[5]*2;
                                                                        for( m=3;m<len-2;m++)
                                                                        {
                                                                         temp2[m]=data[m-3+temp1[3]*2];
                                                                        }
                                                                        get2=CRC_16(temp2,len-2);
                                                                        temp2[len-2]=(unsigned char)(get2>>8);
                                                                        temp2[len-1]=(unsigned char)(get2);
                                                                        
                                                                        PD4=1;                        //PD1为MAX485芯片控制引脚
                                                                        PD4=1;
                                                                        delay_ms(1);
                                                                        for(n=0;n<len;n++)
                                                                        {
                                                                                uart1_send(temp2[n]);             //发送返回字串
                                                                
                                                                        }
                                                                        delay_ms(5);                       //如果需要返回更长字节,每增加10个字节需要增加1ms延时以保证数据发送完整。
                                                                        PD4=0;
                                                                        PD4=0;                             
                                                                        UART1_CR2_RIEN=1;            //打开串口中断
                                                                        break;
                                                                        
                                                                        case 6://修改命令字,为了防止寄存器数据被误修改,这里设定400018寄存器为锁定位,锁定密码为0x18,要修改其他寄存器需先将此位赋值0x18;
                                                                        if(temp[3]*2==34) //如果是修改400018寄存器则可直接修改
                                                                        {
                                                                                ddd[temp[3]*2]=temp[4];
                                                                                ddd[temp[3]*2+1]=temp[5]; //这两条为寄存器修改
                                                                                if((ddd[temp[3]*2]==temp[4])&&(ddd[temp[3]*2+1]==temp[5]))
                                                                                {
                                                                                                temp3[0]=data[39];
                                                                                                temp3[1]=temp[1];
                                                                                                temp3[2]=temp[2];
                                                                                                temp3[3]=temp[3];
                                                                                                temp3[4]=temp[4];
                                                                                                temp3[5]=temp[5];
                                                                                                get2=CRC_16(temp3,6);
                                                                                                temp3[6]=(unsigned char)(get2>>8);
                                                                                                temp3[7]=(unsigned char)(get2);
                                                                                                PD4=1;
                                                                                                PD4=1;
                                                                                                delay_ms(1);
                                                                                                for(n=0;n<8;n++)
                                                                                                {
                                                                                                        uart1_send(temp3[n]);
                                                                                                }
                                                                                                delay_ms(5);
                                                                                                PD4=0;
                                                                                                PD4=0;
                                                                                }
                                                                        
                                                                        }
                                                                        else if(data[35]==0x18)       //非400018寄存器则先判断锁定位是否解锁
                                                                        {   
                                                                                         uint  addsh,addsl;
                                                                                         addsh=0x4000+temp[3]*2;
                                                                                         addsl=0x4001+temp[3]*2;
                                                                                                __eeprom_write_8(addsh,temp[4]);  // 将固定参数写入对应EEPROM,考虑eeprom寿命问题,请不要将变化平凡的值写入eeprom
                                                                                                __eeprom_write_8(addsl,temp[5]);   //将固定参数写入对应EEPROM
                                                                                                ddd[temp[3]*2]=eep_read(addsh);
                                                                                                ddd[temp[3]*2+1]=eep_read(addsl); //这两条为寄存器修改
                                                                                         
                                                                                                if((ddd[temp[3]*2]==temp[4])&&(ddd[temp[3]*2+1]==temp[5]))
                                                                                                {
                                                                                                        temp3[0]=data[39];
                                                                                                        temp3[1]=temp[1];
                                                                                                        temp3[2]=temp[2];
                                                                                                        temp3[3]=temp[3];
                                                                                                        temp3[4]=temp[4];
                                                                                                        temp3[5]=temp[5];
                                                                                                        get2=CRC_16(temp3,6);
                                                                                                        temp3[6]=(unsigned char)(get2>>8);
                                                                                                        temp3[7]=(unsigned char)(get2);
                                                                                                        PD4=1;
                                                                                                        PD4=1;
                                                                                                        delay_ms(1);
                                                                                                        for(n=0;n<8;n++)
                                                                                                        {
                                                                                                        //printf("%x\n",temp3[n]);
                                                                                                        uart1_send(temp3[n]);    //修改成功发送返回值
                                                                                                        }
                                                                                                        delay_ms(5);
                                                                                                        PD4=0;
                                                                                                        PD4=0;
                                                                                                }
                                                                        }
                                                                        UART1_CR2_RIEN=1;        //打开串口中断
                                                                        break;
                                                                        UART1_CR2_RIEN=1;       //打开串口中断
                                                        }
                                        }
                        }
        else
    {
                        UART1_CR2_RIEN=1;                                                     //打开串口中断
                        
    }
                UART1_CR2_RIEN=1;                                                           //打开串口中断

}



 

最新回复 [0]
返回