下載了libmodbus庫,交叉編譯後運行,老是接收回復時不正確。緣由不明。ios
因爲使用到modbus的需求比較簡單,因此選擇直接拼出modbus的請求報文,而後用串口直接發送和接收的方式,緩存
拼modbus的請求報文關鍵在於理解modbus協議,函數
好比請求報文:ui
[01][03][00][00][00][02][c4][0b]spa
第一個字節(0x01)表示設備地址(從機地址),就是設備編號,能夠同時接多個設備,設備之間用設備號區分。code
第二個字節(0x03)表示功能號,常見的以下:blog
01 (0x01) 讀線圈
02 (0x02) 讀離散量輸入
03 (0x03) 讀保持寄存器
04 (0x04) 讀輸入寄存器
05 (0x05) 寫單個線圈
06 (0x06) 寫單個寄存器
15 (0x0F) 寫多個線圈
16 (0x10) 寫多個寄存器接口
這裏03表示讀保持寄存器,具體寄存器的意義須要根據廠家提供的說明。get
第3、四字節 (0x00 0x00)表示寄存器起始地址,這裏是0,表示從第一個寄存器開始。string
第5、六字節 (0x00 0x02) 表示地址偏移量,這裏是2,表示從起始地址開始偏移2個字節。
第7、八字節 (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; }
(完)