modbus应用

简介

这个示例为了方便,使用qt+x86平台,但是对于modbus的应用是一样的,无论是在linux,还是在mcu上使用都是一样的。
实现功能:
·串口通信
·modbus功能码03,04,06,10
·16位crc校验算法
·IEEE 754浮点数在程序中的应用

串口通信

qt5以后的中串口通信使用的是qt自带的类QSerialPort,老版本中一般使用开源的类qextserialport,使用方式基本相同,我在这里使用qt5.6.1,所以使用QSerialPort就行了。
串口放到一个单独的线程里,来保证数据接收的实时性。多线程通信一般用全局变量,用互斥锁mutex来保证数据不会同时读写(qt中有QMutex),这里为了省事没有用。

1.定义串口发送和接收的buf

uchar modbus_Rxbuf[20];
uchar modbus_Txbuf[20];
uint16_t rxlen;//接收数据的时候用来计数

2.初始化串口

void comthread::cominit()
{
	//cc是用来中转数据的缓存	
	cc = (unsigned char*)malloc(10*sizeof (unsigned char));
    serial = new QSerialPort;
	
	if(serial->isOpen())
	{
	    serial->clear();
	    serial->close();
	}
	serial->setPortName("com1");
	if(!serial->open(QIODevice::ReadWrite))
	    qDebug("serial opend failed!");
	
	
	serial->setBaudRate(QSerialPort::Baud1200,QSerialPort::AllDirections);//设置波特率为1200  modbus常用的波特率
	serial->setDataBits(QSerialPort::Data8);//设置数据位8
	serial->setParity(QSerialPort::NoParity); //校验位设置为0
	serial->setStopBits(QSerialPort::OneStop);//停止位设置为1
	serial->setFlowControl(QSerialPort::NoFlowControl);//设置为无流控制
}

3.串口接收数据

void comthread::run()
{
    cominit();

    int num=0;
    int len = 0;
    QByteArray comdata;
    //waitForReadyRead参数是接收数据的等待时间,这里设为-1是为了实现同步接收方式,一直等待数据的到来
    while(serial->waitForReadyRead(-1))
    {
        if(bquit)//如果进程退出了
        {
            free(cc);//释放为cc开辟的空间
            return;
        }

        comdata = serial->readAll();
        cc=(unsigned char*)comdata.data();
        len = comdata.length();

        for(int i=0;i<len;i++)
        {
            modbus_Rxbuf[rxlen]=cc[i];
            rxlen++;
        }
        //下面对计时器操作跟modbus协议有关,在modbus部分会说明
        emit mmmstop();  //清空计时器
        emit mmmstart(); //重启计时器
    }
}

4.发送数据的函数

void comthread::modbuswrite( char *buf, qint64 len)
{
    serial->write(buf,len);
}

16位crc计算

crc是个啥?请百度,下面上代码,代码有注释

#define POLYNOMIAL 0xA001 //crc默认多项式  这个是计算crc的一个基数,基数不同,计算出来的结果不同

//计算16Crc校验码
//buf是要计算crc的数据
//len是取buf中的几个字节来计算crc
uint16_t Calculate_CRC(uchar *buf,uchar len)
{
    uint16_t Rcrc=0xFFFF;   //crc寄存器默认值
    uint8_t i,j;

    for(i = 0; i < len; i++){// 要校验的字节为len个
        Rcrc = buf[i] ^ Rcrc;
        for(j = 0;j < 8;j++){  //此处的8 -- 指每一个uint8类型有8bit,每bit都要处理
            if(Rcrc & 0x01){
                Rcrc = Rcrc >> 1;
                Rcrc = Rcrc ^ POLYNOMIAL;
            }
            else{
                Rcrc = Rcrc >> 1;
            }
        }
    }
    return Rcrc;
}

modbus协议

modbus是个通信协议,一个标准化的协议。串口,网络都可以使用这个协议。
参考链接: lModbus 通信协议详解.
消息帧:两种传输模式中(ASCII或RTU),传输设备以将Modbus消息转为有起点和终点的帧,这就允许接收的设备在消息起始处开始工作,读地址分配信息,判断哪一个设备被选中(广播方式则传给所有设备),判知何时信息已完成。部分的消息也能侦测到并且错误能设置为返回结果。

我只用到rtu帧格式,如下图:在这里插入图片描述
T1-T4的含义应该是:每个T表示传输一个Byte的时间(我是这样理解,也是这样用,不对请指正,感谢!)。
所以在使用modbus协议的时候,没有我们常用的帧头和帧尾,通过判断时间来确定一帧的开始和结束。

帧开始:接收第一个字节就认为是开始
帧结束:当超过4个T(也可以是一段时间,我程序里设置的5ms)没有接收到数据就是一帧的结束

功能码:功能码可以百度搜索modbus功能码,资料挺多。这里设计03,04,06,10(都是16进制)。
03:读取一个寄存器的值
04:和03一样
06:写一个寄存器的值(2个字节的值-整数)
10:写一个寄存器的值(4个字节的值-浮点数)

IEEE754浮点数

ieee754可以百度出很多文章,写的也很详细,然并卵。
原理我们能理解,但是实际使用的时候是要能把浮点数转化到我们要发送的数据buf中,和从结束buf中转为浮点数。

示例-接收数据:从485数据中读取一个浮点数放到内存里

float data_32;//接收的数据放到这里
unsigned char modbus_Rxbuf[10];//接收buf
unsigned char tmpbuf[4];//中转buf
float *p;//一个浮点型指针
p=(float*)(&tmpbuf);//让指针指向中转buf

//把接收的数据放到中转buf  注意字节顺序
tmpbuf[0] = modbus_Rxbuf[5];
tmpbuf[1] = modbus_Rxbuf[4];
tmpbuf[2] = modbus_Rxbuf[7];
tmpbuf[3] = modbus_Rxbuf[6];
data_32 = *p;

示例-发送数据:把内存的浮点数放到发送buf

float data_32;//要发送的数据放到这里
unsigned char modbus_Txbuf[10];//发送buf
unsigned char tmpbuf[4];//中转buf
float *p;//一个浮点型指针

p=(float*)(&tmpbuf);//让指针指向中转buf
*p=data_32;
//把要发送的数据放到发送buf  注意字节顺序
modbus_Txbuf[7]=tmpbuf[1];
modbus_Txbuf[8]=tmpbuf[0];
modbus_Txbuf[9]=tmpbuf[3];
modbus_Txbuf[10]=tmpbuf[2];
发布了5 篇原创文章 · 获赞 0 · 访问量 39

猜你喜欢

转载自blog.csdn.net/Mr_Ding123/article/details/104959458
今日推荐