当前位置: 首页 > news >正文

【modbus学习】野火Modbus主机接收到数据后的完整函数调用流程

文章目录

    • Modbus主机接收数据流程:
      • 1. **串口接收中断触发**
      • 2. **接收中断处理** (`prvvUARTRxISR`)
      • 3. **接收状态机** (`xMBMasterRTUReceiveFSM`)
      • 4. **定时器启动** (`vMBMasterPortTimersT35Enable`)
      • 5. **T3.5超时触发**
      • 6. **定时器中断处理** (`prvvTIMERExpiredISR`)
      • 7. **协议栈定时器处理** (`xMBMasterRTUTimerExpired`)
      • 8. **事件处理** (`xMBMasterPortEventPost`)
      • 9. **主程序轮询** (`eMBMasterPoll`)
        • 9.1 `eStatus = peMBMasterFrameReceiveCur( &ucRcvAddress, &ucMBFrame, &usLength );`
        • 9.2 后续判断代码:
      • 10. **帧处理** (`eMBMasterRTUReceive`)
      • 11. **回调函数调用**
    • 完整调用链:

Modbus主机接收数据流程:

1. 串口接收中断触发

硬件中断 → USART2_IRQHandler → HAL_UART_IRQHandler → prvvUARTRxISR()

2. 接收中断处理 (prvvUARTRxISR)

void prvvUARTRxISR(void)
{// 读取接收到的字节(void)xMBMasterPortSerialGetByte((CHAR*)&ucByte);// 调用接收状态机(void)pxMBMasterFrameCBByteReceived();// 计数器增加rx_int_count++;
}

3. 接收状态机 (xMBMasterRTUReceiveFSM)

BOOL xMBMasterRTUReceiveFSM(void)
{// 读取字节(void)xMBMasterPortSerialGetByte((CHAR*)&ucByte);switch(eRcvState) {case STATE_M_RX_IDLE:// 收到第一个字节usMasterRcvBufferPos = 0;ucMasterRTURcvBuf[usMasterRcvBufferPos++] = ucByte;eRcvState = STATE_M_RX_RCV;  // 切换到接收状态// 启动T3.5定时器vMBMasterPortTimersT35Enable();break;case STATE_M_RX_RCV:// 继续接收数据if(usMasterRcvBufferPos < MB_SER_PDU_SIZE_MAX) {ucMasterRTURcvBuf[usMasterRcvBufferPos++] = ucByte;} else {eRcvState = STATE_M_RX_ERROR;  // 缓冲区溢出}// 重启T3.5定时器vMBMasterPortTimersT35Enable();break;}
}

4. 定时器启动 (vMBMasterPortTimersT35Enable)

void vMBMasterPortTimersT35Enable()
{vMBMasterSetCurTimerMode(MB_TMODE_T35);  // 设置T3.5模式HAL_TIM_Base_Start_IT(&htim4);           // 启动定时器
}

5. T3.5超时触发

定时器中断 → TIM4_IRQHandler → HAL_TIM_IRQHandler → HAL_TIM_PeriodElapsedCallback → prvvTIMERExpiredISR()

6. 定时器中断处理 (prvvTIMERExpiredISR)

void prvvTIMERExpiredISR(void)
{// 调用协议栈定时器处理(void)pxMBMasterPortCBTimerExpired();// 计数器增加timer_int_count++;
}

7. 协议栈定时器处理 (xMBMasterRTUTimerExpired)

BOOL xMBMasterRTUTimerExpired(void)
{switch(eRcvState) {case STATE_M_RX_RCV:// 帧接收完成,发送事件xNeedPoll = xMBMasterPortEventPost(EV_MASTER_FRAME_RECEIVED);break;}// 重置状态eRcvState = STATE_M_RX_IDLE;eSndState = STATE_M_TX_IDLE;// 停止定时器vMBMasterPortTimersDisable();return xNeedPoll;
}

8. 事件处理 (xMBMasterPortEventPost)

BOOL xMBMasterPortEventPost(eMBMasterEventType eEvent)
{xMasterEventInQueue = eEvent;  // 保存事件return TRUE;
}

9. 主程序轮询 (eMBMasterPoll)

eMBErrorCode eMBMasterPoll(void)
{eMBMasterEventType eEvent;// 获取事件if(xMBMasterPortEventGet(&eEvent) == TRUE) {switch(eEvent) {case EV_MASTER_FRAME_RECEIVED:// 处理接收到的帧eStatus = peMBMasterFrameReceiveCur(&ucRcvAddress, &ucMBFrame, &usLength);break;}}
}
9.1 eStatus = peMBMasterFrameReceiveCur( &ucRcvAddress, &ucMBFrame, &usLength );

这个函数的作用是解析接收到的Modbus帧

参数说明:
&ucRcvAddress:从机地址指针,函数会提取帧中的从机地址
&ucMBFrame:数据帧指针,函数会提取帧中的数据部分
&usLength:数据长度指针,函数会返回数据部分的长度

返回值:
eStatus:解析结果 MB_ENOERR:解析成功 MB_EIO:解析失败(CRC错误、长度错误等)

9.2 后续判断代码:
if ( ( eStatus == MB_ENOERR ) && ( ucRcvAddress == ucMBMasterGetDestAddress() ) )
{( void ) xMBMasterPortEventPost( EV_MASTER_EXECUTE );
}
else
{vMBMasterSetErrorType(EV_ERROR_RECEIVE_DATA);( void ) xMBMasterPortEventPost( EV_MASTER_ERROR_PROCESS );
}

判断逻辑:

  1. eStatus == MB_ENOERR
    检查帧解析是否成功
    如果CRC校验失败或长度错误,eStatus就不是MB_ENOERR

  2. ucRcvAddress == ucMBMasterGetDestAddress()
    检查接收到的从机地址是否匹配
    ucRcvAddress:接收帧中的从机地址
    ucMBMasterGetDestAddress():当前主机请求的目标从机地址

处理结果:

  • 如果两个条件都满足
    发送EV_MASTER_EXECUTE事件
    表示"帧解析成功且地址匹配,可以执行功能处理"

  • 如果任一条件不满足
    设置错误类型为EV_ERROR_RECEIVE_DATA
    发送EV_MASTER_ERROR_PROCESS事件
    表示"接收数据错误,需要错误处理"

简单说:

这段代码就是在验证接收到的Modbus帧是否有效

  1. 帧格式是否正确(CRC、长度等)
  2. 从机地址是否匹配
  3. 根据验证结果决定下一步处理

10. 帧处理 (eMBMasterRTUReceive)

eMBErrorCode eMBMasterRTUReceive(UCHAR *pucRcvAddress, UCHAR **pucFrame, USHORT *pusLength)
{// CRC校验if(usMBCRC16(ucMasterRTURcvBuf, usMasterRcvBufferPos) == 0) {// 提取地址和数据*pucRcvAddress = ucMasterRTURcvBuf[MB_SER_PDU_ADDR_OFF];*pusLength = usMasterRcvBufferPos - MB_SER_PDU_PDU_OFF - MB_SER_PDU_SIZE_CRC;*pucFrame = &ucMasterRTURcvBuf[MB_SER_PDU_PDU_OFF];}
}

这个eMBMasterRTUReceive函数是Modbus RTU帧解析函数

函数功能:
解析接收到的Modbus RTU帧,提取地址、数据和长度信息。

参数说明:
pucRcvAddress:从机地址指针,函数会将解析出的从机地址存储到这里
pucFrame:数据帧指针,函数会将解析出的数据部分存储到这里
pusLength:数据长度指针,函数会将数据部分的长度存储到这里

解析过程:

  1. 长度检查
if( ( usMasterRcvBufferPos >= MB_SER_PDU_SIZE_MIN )&& ( usMBCRC16( ( UCHAR * ) ucMasterRTURcvBuf, usMasterRcvBufferPos ) == 0 ) )

检查接收缓冲区长度是否大于等于最小长度
检查CRC16校验是否正确(CRC校验结果为0表示正确)

  1. 提取从机地址
*pucRcvAddress = ucMasterRTURcvBuf[MB_SER_PDU_ADDR_OFF];

从接收缓冲区中提取从机地址(第0个字节)

  1. 计算数据长度
*pusLength = ( USHORT )( usMasterRcvBufferPos - MB_SER_PDU_PDU_OFF - MB_SER_PDU_SIZE_CRC );

总长度 = 接收长度 - 地址长度(1) - CRC长度(2)
得到实际的数据部分长度

  1. 设置数据指针
*pucFrame = ( UCHAR * ) & ucMasterRTURcvBuf[MB_SER_PDU_PDU_OFF];

指向数据部分的起始位置(第1个字节开始)

返回值:
MB_ENOERR:解析成功
MB_EIO:解析失败(长度不够或CRC错误)

11. 回调函数调用

// 根据功能码调用相应的回调函数
switch(ucFunctionCode) {case MB_FUNC_READ_HOLDING_REGISTERS:eMBMasterRegHoldingCB(&ucMBFrame, &usLength);callback_count++;  // 回调计数器增加break;
}

完整调用链:

串口中断 → 接收状态机 → 启动定时器 → T3.5超时 → 定时器中断 → 协议栈处理 → 事件通知 → 主程序轮询 → 帧处理 → 回调函数
http://www.dtcms.com/a/349805.html

相关文章:

  • Ajax笔记(上)
  • Vue3在一个对象的list中,找出update_date最大的一条数据
  • MCP 协议原理与系统架构详解—从 Server 配置到 Client 应用
  • vscode--快捷键
  • ESP8266学习
  • 嵌入式开发学习———Linux环境下网络编程学习(六)
  • 驾驭 Glide 的引擎:深度解析 Module 与集成之道
  • 音视频学习(五十六):单RTP包模式和FU-A分片模式
  • ESP32开发WSL_VSCODE环境搭建
  • *解决 Visual Studio 编译时的字符编码问题
  • 41_基于深度学习的小麦病害检测识别系统(yolo11、yolov8、yolov5+UI界面+Python项目源码+模型+标注好的数据集)
  • 服务器(Linux)新账户搭建Pytorch深度学习环境
  • Linux(从入门到精通)
  • Wisdom SSH 核心功能定位与 sudo 权限配置指南
  • 四层Nginx代理日志配置
  • 2024年Engineering SCI2区,面向工程管理的无人机巡检路径与调度,深度解析+性能实测
  • Vue 2 中的 v-model和Vue3中的v-model
  • 设计模式:原型模式(Prototype Pattern)
  • 使用 HandlerMethodReturnValueHandler 在SpringBoot项目 实现 RESTful API 返回值自动封装,简化开发
  • 数据结构青铜到王者第三话---ArrayList与顺序表(2)
  • 零知开源——基于STM32F103RBT6和ADXL335实现SG90舵机姿态控制系统
  • three.js+WebGL踩坑经验合集(9.1):polygonOffsetUnits工作原理大揭秘
  • 【数据结构】LeetCode160.相交链表 138.随即链表复制 牛客——链表回文问题
  • [SC]SystemC动态进程概述及案例
  • LinkedIn 自动消息发送工具
  • 网络编程——TCP、UDP
  • 人工智能(AI)与网络安全
  • 【Linux】协议的本质
  • 一键脚本:自动安装 Nginx + Certbot + HTTPS(Let‘s Encrypt)
  • QT-QSS样式表