modbus協議使用小記


下載了libmodbus庫,交叉編譯后運行,總是接收回復時不正確。原因不明。

由於使用到modbus的需求比較簡單,所以選擇直接拼出modbus的請求報文,然后用串口直接發送和接收的方式,

拼modbus的請求報文關鍵在於理解modbus協議,

比如請求報文:

   [01][03][00][00][00][02][c4][0b]

第一個字節(0x01)表示設備地址(從機地址),就是設備編號,可以同時接多個設備,設備之間用設備號區分。

第二個字節(0x03)表示功能號,常見的如下:

        01 (0x01)        讀線圈
        02 (0x02)        讀離散量輸入
        03 (0x03)        讀保持寄存器
        04 (0x04)         讀輸入寄存器
        05 (0x05)        寫單個線圈
        06 (0x06)        寫單個寄存器
        15 (0x0F)        寫多個線圈
        16 (0x10)        寫多個寄存器

        這里03表示讀保持寄存器,具體寄存器的意義需要根據廠家提供的說明。

第三、四字節 (0x00 0x00)表示寄存器起始地址,這里是0,表示從第一個寄存器開始。

第五、六字節 (0x00 0x02) 表示地址偏移量,這里是2,表示從起始地址開始偏移2個字節。

第七、八字節 (0xc4 0x0b) 是CRC校驗碼。0xc4 是低位,0x0b是高位。就是反序的。

以上一起連起來,就是讀取0x01號從機的保持寄存器中從0x00地址開始,到0x00+0x02地址為止的值。

一開始,在485接口上試的時候總是發送什么接收到什么,原因不明,后來再發送和接收之間加上了sleep, 

終於能接收到回復報文了,但是前面還是會接收到自己發送的請求報文,嘗試過清楚串口緩存,還是不行,

沒辦法,只能在接收到的報文中手動去掉前面8個字節的請求報文,這樣處理后是可以用的,接收后使用查表法

校驗CRC。

一開始接收的時候使用了select, 然后在接收時會有20%左右概率出現illegal seek,原因不明。后來去掉了select,

直接使用read讀,這樣效果好了很多,出現illegal seek的概率大概只有1%。已經夠用了,出現illegal seek的時候

檢查CRC報錯后直接放棄,依然使用上一次的數據。

checkcrc.h

/*
 * checkcrc.h
 *
 *  Created on: May 6, 2015
 *      Author: root
 */

#ifndef CHECKCRC_H_
#define CHECKCRC_H_
#include <stdint.h>

uint16_t CRC16(uint8_t *Pushdata, uint8_t length);

#endif /* CHECKCRC_H_ */

checkcrc.cpp

/*
 * checkcrc.c
 *
 *  Created on: May 6, 2015
 *      Author: root
 */

#include "checkcrc.h"

/* CRC 高位字節值表
   CRC high byte
*/
const uint8_t auchCRCHi[] = {
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
};
/* CRC低位字節值表
   CRC low byte
*/
const uint8_t auchCRCLo[] = {
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
} ;

uint16_t CRC16(uint8_t *Pushdata, uint8_t length)
{
    uint8_t uchCRCHi=0xFF;
    uint8_t uchCRCLo=0xFF;
    uint8_t uIndex;
    while( length-- )
    {
        uIndex=uchCRCHi^*Pushdata++;
        uchCRCHi=uchCRCLo^auchCRCHi[uIndex];
        uchCRCLo=auchCRCLo[uIndex];
    }
    //printf("crchi:%.2x crclo:%.2x \n", uchCRCHi, uchCRCLo);
    return (uchCRCHi<<8 | uchCRCLo);
}

main.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h> //文件控制定義
#include <termios.h>//終端控制定義
#include <errno.h>
//#include <stdint.h>
#include <string.h>
#include "checkcrc.h"

const char *comport = "/dev/ttySAC1";
int bRate = 38400;

int serial_fd = 0;

//打開串口並初始化設置
int init_serial(void)
{
    serial_fd = open(comport, O_RDWR | O_NOCTTY | O_NDELAY);
    if (serial_fd < 0) {
        perror("open");
        return -1;
    }
    else
        printf("open %s success! \n", comport);

    //串口主要設置結構體termios <termios.h>
    struct termios options;

    /**1. tcgetattr函數用於獲取與終端相關的參數。
    *參數fd為終端的文件描述符,返回的結果保存在termios結構體中
    */
    tcgetattr(serial_fd, &options);
    /**2. 修改所獲得的參數*/
    options.c_cflag |= (CLOCAL | CREAD);//設置控制模式狀態,本地連接,接收使能
    options.c_cflag &= ~CSIZE;//字符長度,設置數據位之前一定要屏掉這個位
    options.c_cflag &= ~CRTSCTS;//無硬件流控
    options.c_cflag |= CS8;//8位數據長度
    options.c_cflag &= ~CSTOPB;//1位停止位
    options.c_iflag |= IGNPAR;//無奇偶檢驗位
    options.c_oflag = 0; //輸出模式
    options.c_lflag = 0; //不激活終端模式

    cfsetospeed(&options, B38400);//設置波特率

    /**3. 設置新屬性,TCSANOW:所有改變立即生效*/
    tcflush(serial_fd, TCIFLUSH);//溢出數據可以接收,但不讀
    tcsetattr(serial_fd, TCSANOW, &options);

    return 0;
}

void uart_send(int fd)
{
    const unsigned char buf[8] = {0x01, 0x03, 0x00, 0x00, 0x00, 0x02, 0xc4, 0x0b};
    int len = write(fd, buf, sizeof(buf));
    //tcflush(fd, TCIOFLUSH);
    printf("send length = %d \n", len);
    int i;
    for (i = 0; i < sizeof(buf); i++)
        printf("[%.2x]", buf[i]);
    printf("\n");
}

void uart_recv(int fd, unsigned char *modbusdata, int *length)
{
    unsigned char data[64];
    int len=0, ret = 0;
    fd_set fs_read;
    struct timeval tv_timeout;

    //FD_ZERO(&fs_read);
    //FD_SET(fd, &fs_read);
    tv_timeout.tv_sec  = 2;
    tv_timeout.tv_usec = 0;
    /*
    ret = select(fd+1, &fs_read, NULL, NULL, &tv_timeout);
    if (ret == -1)
    {
        printf("select time out\n");
    }
    if (FD_ISSET(fd, &fs_read)) {
        len = read(fd, data, sizeof(data));
        printf("receive len: %d (bytes)\n", len);
    } else {
        perror("select");
    }
    */
    len = read(fd, data, sizeof(data));
    printf("receive len: %d (bytes)\n", len);
    unsigned char * p = NULL;
    p = data;
    p += 8;
    len -= 8;
    printf("modbus data length: %d (bytes)\n", len);
    memcpy(modbusdata, p, len);
    *length = len;
}

void modbusCompute(unsigned char * modbusdata, int len)
{
    unsigned char * p;
    p = modbusdata;
    int i;
    for (i = 0; i < len; i++)
    {
        printf("<%.2x>",modbusdata[i]);
    }
    printf("\n");
    p += 3;
    uint16_t hiValue = *p * 256 + *(p+1);
    uint16_t loValue = *(p+2) * 256 + *(p+3);
    uint16_t sp = hiValue * 256 * 256 + loValue;
    printf("[ sp = %d ]\n", sp);
    uint16_t sl = 200;
    uint16_t sh = 32200;
    double x;
    x = 80 * (sp - sl)*1.0/(sh - sl);
    printf("[x = %.2lf]\n", x);

    double y;
    if (x > 0 && x <= 1)
        y = 0.7 * x;
    else if (x > 1 && x <= 4)
        y = 0.6 * x + 0.1;
    else if (x > 4 && x <= 10)
        y = 0.25 * x + 1.5;
    else if (x > 10 && x <= 20)
        y = 0.36 * x + 0.4;
    else if (x > 20 && x <= 30)
        y = 0.29 * x + 1.8;
    else if (x > 30)
        y = 0.25 * x + 3;
    printf("[y = %.2lf]\n", y);
}

int main(int argc, char **argv)
{
    unsigned char modbusdata[64];
    int length;
    init_serial();
    uart_send(serial_fd);
    usleep(100000);
    uart_recv(serial_fd, modbusdata, &length);

    uint16_t calCrc = modbusdata[length-2] * 256 + modbusdata[length-1];

    if (calCrc == CRC16(modbusdata, length-2))
    {
        printf("crc ok\n");
        modbusCompute(modbusdata, length);
    }else{
        printf("crc error\n");
    }

    close(serial_fd);
    return 0;
}

(完)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM