14

libmodbus源码分析(3)从机(服务端)功能源码分析

 4 years ago
source link: https://blog.csdn.net/u012351051/article/details/104092366
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

在上一篇文章《 libmodbus源码分析(2)主机(客户端)功能源码分析 》 从 主机的角度 分析了 源码,本文以 从机(服务器)的角度分析一下源码。同样的,我们以 modbus rtu 协议的 4x区保持寄存器功能进行举例说明。

我们简单的写一下 modbus rtu 下 响应客户端(主机)读4x 区保持寄存器的伪代码流程:

int main(void)
{
    modbus_t *ctx;
    modbus_mapping_t *mb_mapping;
    uint8_t *query;

    /* 创建并初始化 modbus_t 指针 */
    ctx = modbus_new_rtu("/dev/ttyUSB0", 115200, 'N', 8, 1);
    
    /* 设定本机设备地址 */
    modbus_set_slave(ctx, SERVER_ID);
    
    /* 用于接收主机消息的 缓存申请 */
    query = malloc(MODBUS_RTU_MAX_ADU_LENGTH);

    /* 0x、1x、3x、4x共4个区 寄存器的 缓存申请 */
     mb_mapping = modbus_mapping_new_start_address(
                            UT_BITS_ADDRESS, UT_BITS_NB,
                            UT_INPUT_BITS_ADDRESS, UT_INPUT_BITS_NB,
                            UT_REGISTERS_ADDRESS, UT_REGISTERS_NB_MAX,
                            UT_INPUT_REGISTERS_ADDRESS, UT_INPUT_REGISTERS_NB);

    /* 根据自己需要 填充 4个区 寄存器内容,这一部分可以在另外一个单独线程中,循环刷新寄存器值*/

    while(1){
        
        /* 等待接收主机读,直到 读到 指令,会阻塞*/
        do{
            rc = modbus_receive(ctx, query);
        }while(rc == 0);
        
        /* 根据接收到的主机 指令 query 内容,自动的回复 主机想要的 数据包 */
        rc = modbus_reply(ctx, query, rc, mb_mapping);
    
    }
    
    /* libmodbus 退出后,释放、关闭相关资源 */
    modbus_mapping_free(mb_mapping);
    free(query);
    /* For RTU */
    modbus_close(ctx);
    modbus_free(ctx);
}

上述代码中,大部分都是很容易理解的,modbus_receive 函数就是在上一篇文章中已经进行了分析,这里就不再赘述了,它最终是调用 _modbus_receive_msg 函数, 它采用 select 接收机制,而且当作为 从机使用时, select 的超时时间设定为 空,

if (msg_type == MSG_INDICATION) {
        /* Wait for a message, we don't know when the message will be
         * received */
        p_tv = NULL;
    }

这就意味着该函数会“阻塞“等待接收,直到有数据可接收,所以在写程序的时候,需要注意,可以考虑在一个单独线程中使用。

接下来,我们就分析一下 modbus_repley 函数的实现:

RbUfyq3.png!web

这里,我们再看看一下,libmodbus 是如何 知道主机要读那些寄存器,并且如何将主机想读的寄存器内容筛选打包的,源码如下:

case MODBUS_FC_READ_HOLDING_REGISTERS:
    case MODBUS_FC_READ_INPUT_REGISTERS: {
        /* 保持寄存器 or 输入寄存器判断 */
        unsigned int is_input = (function == MODBUS_FC_READ_INPUT_REGISTERS);
        /* modbus 寄存器区 首地址 获取 */
        int start_registers = is_input ? mb_mapping->start_input_registers : mb_mapping->start_registers;
        /* modbus 寄存器区 寄存器总数量,比如 4x区寄存器数量 */
        int nb_registers = is_input ? mb_mapping->nb_input_registers : mb_mapping->nb_registers;
        /* 对应区 寄存器 缓存 首地址 */
        uint16_t *tab_registers = is_input ? mb_mapping->tab_input_registers : mb_mapping->tab_registers;
        /* 调试,没用 */
        const char * const name = is_input ? "read_input_registers" : "read_registers";
        /* 筛选出 主机想要读的 寄存器 数量 */
        int nb = (req[offset + 3] << 8) + req[offset + 4];
        /* The mapping can be shifted to reduce memory consumption and it
           doesn't always start at address zero. */
        
        /* 计算出 主机要读的 寄存器起始地址 在 modbus 寄存器缓存中的 偏移地址 */
        int mapping_address = address - start_registers;
        
        /* 主机发送命令中的 首地址、寄存器数量大小 合法性判断 */
        if (nb < 1 || MODBUS_MAX_READ_REGISTERS < nb) {
            rsp_length = response_exception(
                ctx, &sft, MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE, rsp, TRUE,
                "Illegal nb of values %d in %s (max %d)\n",
                nb, name, MODBUS_MAX_READ_REGISTERS);
        } else if (mapping_address < 0 || (mapping_address + nb) > nb_registers) {
            rsp_length = response_exception(
                ctx, &sft, MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS, rsp, FALSE,
                "Illegal data address 0x%0X in %s\n",
                mapping_address < 0 ? address : address + nb, name);
        } else {
            
            /* 根据前面的 计算,将对应区的寄存器 数据从 modbus 缓存 拷贝到 rsp(回复给主机的数
             * 据帧包) 
             */
            int i;
            
            rsp_length = ctx->backend->build_response_basis(&sft, rsp);
            rsp[rsp_length++] = nb << 1;
            for (i = mapping_address; i < mapping_address + nb; i++) {
                rsp[rsp_length++] = tab_registers[i] >> 8;
                rsp[rsp_length++] = tab_registers[i] & 0xFF;
            }
        }
    }
        break;

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK