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);
}

猜你喜欢

转载自blog.csdn.net/u013441358/article/details/105937883