imx6ull交叉編譯libmodbus

版本和開發環境說明

  • libmodbus版本爲 3.1.6
  • 交叉編譯host爲 Ubuntu16.04
  • 交叉編譯target爲 arm A7內核的imx6ull
  • 交叉編譯器爲 arm-linux-gnueabihf-gcc

操作步驟

交叉編譯libmodbus的流程相對簡單:

#use alias setcc to config cross compile environment
setcc
cd ./libmodbus-3.1.6
./configure --host=arm-linux-gnueabihf --prefix=$(pwd)/install
make & make install
  • 如果需要靜態庫,則./configure加上選項:
    –enable-static
  • 如果需要動態庫,則./configure加上選項:
    –enable-shared

編譯完成後,在install生成三個目錄: include, lib, share,裏面是我們需要用到的庫文件與頭文件。
install目錄的文件結構lib文件夾下的libmodbus.so.5.1.0即爲可以被調用的庫文件,在實際編程的時候需要包含頭文件:#include"modbus.h"

源碼的修改

本章節是筆者在進行libmodbus開發時,根據項目需求對源碼進行修改以獲得新的功能特性。由於libmodbus已經對串口進行了較爲完善的封裝,我們無法利用這個庫發送自定義格式的一些數據(某些設備或者從機在某些情況下並不是採用標準的modbus協議,需要我們自己自定義每一幀的數據)。因此,本章節的修改主要目的在於開放串口發送任意數據的API,已增加庫的擴展性與通用性。同時,筆者需要在輪詢modbus接收數據的時候不造成阻塞,開放了結構體 modbus_t的內部,以獲得串口文件句柄,然後藉助select機制將讀取串口數據變爲不阻塞的。

開放串口發送任意數據的API

進入到./libmodbus-3.1.6/src/中,修改modbus.c與modbus.h文件:
在modbus.c中定義函數:

int modbus_send_msg(modbus_t *ctx, uint8_t *msg, int msg_length)
{
	 return ctx->backend->send(ctx, msg, msg_length);
}

在modbus.h中添加聲明:

MODBUS_API int modbus_send_msg(modbus_t *ctx, uint8_t *msg, int msg_length);

即可在項目代碼中調用modbus_send_msg(modbus_t *ctx, uint8_t *msg, int msg_length)函數發送任意數據。


輪詢modbus接收數據時不阻塞

在modbus-rtu.c中,函數:

static ssize_t _modbus_rtu_recv(modbus_t *ctx, uint8_t *rsp, int rsp_length)
{
#if defined(_WIN32)
    return win32_ser_read(&((modbus_rtu_t *)ctx->backend_data)->w_ser, rsp, rsp_length);
#else
    return read(ctx->s, rsp, rsp_length);
#endif
}

由於採用了read機制,會使得程序在輪詢接收數據的時候(在modbus.c中),造成阻塞。

int modbus_receive(modbus_t *ctx, uint8_t *req)
{
	if (ctx == NULL)
	{
		errno = EINVAL;
		return -1;
	}

	return ctx->backend->receive(ctx, req);
}

修改代碼,開放***typedef struct _modbus modbus_t***的內部,以獲得串口文件句柄,然後藉助select機制將讀取串口數據變爲不阻塞的。具體操作如下:

將modbus-private.h中關於_modbus結構體的定義、_modbus_backend結構體、_sft結構體的定義直接複製到已經編譯好的庫“./install/include/modbus/modbus.h”中。不需要修改源碼,亦不影響編譯過程。

/* This structure reduces the number of params in functions and so
 * optimizes the speed of execution (~ 37%). */
typedef struct _sft {
    int slave;
    int function;
    int t_id;
} sft_t;

typedef struct _modbus_backend {
    unsigned int backend_type;
    unsigned int header_length;
    unsigned int checksum_length;
    unsigned int max_adu_length;
    int (*set_slave) (modbus_t *ctx, int slave);
    int (*build_request_basis) (modbus_t *ctx, int function, int addr,
                                int nb, uint8_t *req);
    int (*build_response_basis) (sft_t *sft, uint8_t *rsp);
    int (*prepare_response_tid) (const uint8_t *req, int *req_length);
    int (*send_msg_pre) (uint8_t *req, int req_length);
    ssize_t (*send) (modbus_t *ctx, const uint8_t *req, int req_length);
    int (*receive) (modbus_t *ctx, uint8_t *req);
    ssize_t (*recv) (modbus_t *ctx, uint8_t *rsp, int rsp_length);
    int (*check_integrity) (modbus_t *ctx, uint8_t *msg,
                            const int msg_length);
    int (*pre_check_confirmation) (modbus_t *ctx, const uint8_t *req,
                                   const uint8_t *rsp, int rsp_length);
    int (*connect) (modbus_t *ctx);
    void (*close) (modbus_t *ctx);
    int (*flush) (modbus_t *ctx);
    int (*select) (modbus_t *ctx, fd_set *rset, struct timeval *tv, int msg_length);
    void (*free) (modbus_t *ctx);
} modbus_backend_t;

struct _modbus {
    /* Slave address */
    int slave;
    /* Socket or file descriptor */
    int s;
    int debug;
    int error_recovery;
    struct timeval response_timeout;
    struct timeval byte_timeout;
    struct timeval indication_timeout;
    const modbus_backend_t *backend;
    void *backend_data;
};

modbus_t的成員s即串口文件句柄(Socket or file descriptor)


項目代碼示例

在實際項目中(以C++/Qt4.8爲例)可以通過以下方式輪詢modbus,將接收數據變爲不阻塞:

modbus_mapping_t *mb_mapping;
modbus_t *ctx;

void ModbusThread::run()
{
    int rc;
    fd_set set;
    timeval timeout;
    timeout.tv_sec = 0;
    timeout.tv_usec = 1000;
    //    int use_backend;
    //初始化modbus rtu
    ctx = modbus_new_rtu(SLAVE_PORT, 9600, 'N', 8, 1);
    //設定從設備地址
    modbus_set_slave(ctx, SLAVE_ADDR);
    //modbus連接
    modbus_connect(ctx);
	//qDebug()<<"serial port fd is: "<<ctx->s;

    //寄存器map初始化
    mb_mapping = modbus_mapping_new(MODBUS_MAX_WRITE_BITS, MODBUS_MAX_READ_BITS , MODBUS_MAX_WR_WRITE_REGISTERS, MODBUS_MAX_READ_REGISTERS);
    if (mb_mapping == NULL)
    {
        fprintf(stderr, "Failed to allocate the mapping: %s\n",modbus_strerror(errno));
        modbus_free(ctx);
        return;
    }

    if (isRemoteMonitoringUsed_dialog == true)
    {
        qDebug()<<"isRemoteMonitoringUsed_dialog:"<<isRemoteMonitoringUsed_dialog;
        mainWidget->timer_uploadDataToCloud.start(10000);
        qDebug()<<"Remote Monitoring is used.";
    }
    else {
        qDebug()<<"isRemoteMonitoringUsed_dialog:"<<isRemoteMonitoringUsed_dialog;
        qDebug()<<"Remote Monitoring is not used.";
    }
    
    while(true)
    {
        uint8_t query[MODBUS_TCP_MAX_ADU_LENGTH];
        SyncModbusRegisters();

        FD_ZERO(&set);
        FD_SET(ctx->s, &set);                       //此處將讀取的串口fd加入隊列
        select(ctx->s+1,&set,NULL,NULL,&timeout);   //此次判斷讀取的隊列
        if(!FD_ISSET(ctx->s, &set))
        {
            continue;
        }
        
        //輪詢接收數據,並做相應處理
        QMutexLocker locker1(&modbusPortLock);
        rc = modbus_receive(ctx, query);
        locker1.unlock();

        if (rc > 0)
        {
			//QString receivedData = "";
			//for(int i=0; i< rc ; i++)
			//{
			//	receivedData.append(QString::number(query[i],16)).append(" ");
			//}
			//qDebug()<<receivedData;
            modbus_reply(ctx, query, rc, mb_mapping);
            ModbusSyncAction();
        }
        else if (rc == -1)
        {
            //qDebug()<<"Modbus connection closed by the client or error.";
            //break;
        }
        else
        {

        }
        
        QMutexLocker locker(&m_lock);
        if(!isThreadLoop)
        {
            qDebug()<<"isThreadLoop:"<<isThreadLoop;
            break;
        }
    }
    
    printf("Modbus thread is terminated: %s\n", modbus_strerror(errno));
    modbus_mapping_free(mb_mapping);
    /* For RTU, skipped by TCP (no TCP connect) */
    modbus_close(ctx);
    modbus_free(ctx);
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章