問題:
在使用modbus時,選擇了github上的libmodbus庫,但是在實際使用中,不知道是自己不會用,還是libmodbus不支持,發現使用232時,可以正常使用,但是使用485接口時,就不能正常運行,自己在拿示波器抓波形等一通研究之後,先想了一種解決方法,但是個人感覺不是太好,但起碼先能跑通了485,寫一下,供大家參考。
libmodbus的github倉庫鏈接:https://github.com/stephane/libmodbus
下圖爲libmodbus源碼的文件:
解決方法:
1.我的解決方法是自己添加了兩個文件:modbus-485.c和modbus-485.h
/**
* @file modbus-485.c
* @brief modbus使用485通信的相關程序
* @date 2020.03.30
*
* @author Lei
* @version ver 1.0
*/
#include <stdio.h>
#include <termios.h>
#include <linux/ioctl.h>
#include <linux/serial.h>
#include <asm-generic/ioctls.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <stdlib.h>
#include <getopt.h>
#include "modbus-485.h"
/**
* @fn rs485_enable
* @brief 485使能函數
* @param fd 485設備號
* @param enable 是否使能
* @return 成功:0 錯誤:其他
*/
int rs485_enable(const int fd, const RS485_ENABLE_t enable)
{
struct serial_rs485 rs485conf;
int res;
/* Get configure from device */
res = ioctl(fd, TIOCGRS485, &rs485conf);
if(res < 0)
{
perror("Ioctl error on getting 485 configure:");
close(fd);
return res;
}
/* Set enable/disable to configure */
if(enable) // Enable rs485 mode
{
rs485conf.flags |= SER_RS485_ENABLED;
}
else // Disable rs485 mode
{
rs485conf.flags &= ~(SER_RS485_ENABLED);
}
rs485conf.delay_rts_before_send = 0x00000004;
/* Set configure to device */
res = ioctl(fd, TIOCSRS485, &rs485conf);
if(res < 0)
{
perror("Ioctl error on setting 485 configure:");
close(fd);
}
return res;
}
/**
* @file modbus-485.h
* @brief modbus使用485通信的相關程序
* @date 2020.03.30
*
* @author Lei
* @version ver 1.0
*/
#ifndef _MODBUS_485_H_
#define _MODBUS_485_H_
typedef enum
{
DISABLE_485 = 0,
ENABLE_485
}RS485_ENABLE_t;
int rs485_enable(const int fd, const RS485_ENABLE_t enable);
#endif
2.然後需要在modbus-rtu.c文件中添加幾句程序:
在該文件中添加一句宏定義:(添加這一句即使用485,不添加則使用232)
#define RS485_MODE 1
3.同時modbus-rtu.c文件中大約273行位置的static ssize_t _modbus_rtu_send(modbus_t *ctx, const uint8_t *req, int req_length)函數進行修改:
修改前該函數程序:
static ssize_t _modbus_rtu_send(modbus_t *ctx, const uint8_t *req, int req_length)
{
#if defined(_WIN32)
modbus_rtu_t *ctx_rtu = ctx->backend_data;
DWORD n_bytes = 0;
return (WriteFile(ctx_rtu->w_ser.fd, req, req_length, &n_bytes, NULL)) ? (ssize_t)n_bytes : -1;
#else
#if HAVE_DECL_TIOCM_RTS
modbus_rtu_t *ctx_rtu = ctx->backend_data;
if (ctx_rtu->rts != MODBUS_RTU_RTS_NONE) {
ssize_t size;
if (ctx->debug) {
fprintf(stderr, "Sending request using RTS signal\n");
}
ctx_rtu->set_rts(ctx, ctx_rtu->rts == MODBUS_RTU_RTS_UP);
usleep(ctx_rtu->rts_delay);
size = write(ctx->s, req, req_length);
usleep(ctx_rtu->onebyte_time * req_length + ctx_rtu->rts_delay);
ctx_rtu->set_rts(ctx, ctx_rtu->rts != MODBUS_RTU_RTS_UP);
return size;
} else {
#endif
return write(ctx->s, req, req_length);
#if HAVE_DECL_TIOCM_RTS
}
#endif
#endif
}
修改後該函數程序:
static ssize_t _modbus_rtu_send(modbus_t *ctx, const uint8_t *req, int req_length)
{
#if defined(_WIN32)
modbus_rtu_t *ctx_rtu = ctx->backend_data;
DWORD n_bytes = 0;
return (WriteFile(ctx_rtu->w_ser.fd, req, req_length, &n_bytes, NULL)) ? (ssize_t)n_bytes : -1;
#else
#if HAVE_DECL_TIOCM_RTS
modbus_rtu_t *ctx_rtu = ctx->backend_data;
if (ctx_rtu->rts != MODBUS_RTU_RTS_NONE) {
ssize_t size;
if (ctx->debug) {
fprintf(stderr, "Sending request using RTS signal\n");
}
ctx_rtu->set_rts(ctx, ctx_rtu->rts == MODBUS_RTU_RTS_UP);
usleep(ctx_rtu->rts_delay);
size = write(ctx->s, req, req_length);
usleep(ctx_rtu->onebyte_time * req_length + ctx_rtu->rts_delay);
ctx_rtu->set_rts(ctx, ctx_rtu->rts != MODBUS_RTU_RTS_UP);
return size;
} else {
#endif
#if RS485_MODE
int num = write(ctx->s, req, req_length);
usleep(10 * 1000);
rs485_enable(ctx->s, ENABLE_485);
return num;
#else
return write(ctx->s, req, req_length);
#endif
#if HAVE_DECL_TIOCM_RTS
}
#endif
#endif
}
4.這樣,libmodbus就能在Linux開發板上正常使用485進行通信了。
注意:這種方法不是特別好,但是能暫時解決問題。個人感覺這裏延時的時間,可以根據發送的數據長度和波特率來進行計算得出,而不只是給一個固定值(time = 10 * ByteLenth / bandrate * 1000(單位爲ms)),但這種方法還沒進行實驗測試,後續測試完成,再進行修改發博。如果各位有更好的方法,或者使用上述程序遇到問題,可以私信博主互相交流。