打印
[STM32WB]

STM32CubeMX | Modbus RTU 主机协议栈实现

[复制链接]
手机看帖
扫描二维码
随时随地手机跟帖
41
实际测量不符|  楼主 | 2024-1-31 22:56 | 只看该作者 回帖奖励 |倒序浏览
其中,mbrtu_master.h和mbrtu_master.c是协议栈实现,无需动,mbrtu_master_example.c是移植参考示例。

下面讲解一下移植过程。

使用特权

评论回复
42
实际测量不符|  楼主 | 2024-1-31 22:56 | 只看该作者
首先定义一个modbus主机的全局控制结构并初始化:
MBRTUMaterTypeDef MBRTUHandle =
{
    .delayms                      = delayms,
    .timerStart                   = timerStart,
    .timerStop                    = timerStop,
    .sendData                     = sendData,

#ifdef USE_RTOS  // 使用了RTOS那么需要实现互斥
    .lock                         = mutex_lock,
    .unlock                       = mutex_unlock,
#endif
};

使用特权

评论回复
43
实际测量不符|  楼主 | 2024-1-31 22:56 | 只看该作者
注意:如果使用了实时系统,需要实现lock和unlock函数。

使用特权

评论回复
44
实际测量不符|  楼主 | 2024-1-31 22:57 | 只看该作者
结构体中的函数实现如下:
#ifdef USE_RTOS

static void mutex_lock(void)
{

}

static void mutex_unlock(void)
{

}

#endif

static void timerStop(void)
{
    HAL_TIM_Base_Stop_IT(&htim3);
}

static void timerStart(void)
{
    __HAL_TIM_SET_COUNTER(&htim3, 0);
    HAL_TIM_Base_Start_IT(&htim3);
}

static void delayms(uint32_t nms)
{
#ifdef USE_RTOS
        osDelay(nms);
#else
    HAL_Delay(nms);
#endif
}

static uint32_t sendData(const void* buf, uint32_t len)
{
    if(HAL_UART_Transmit(&huart3, (uint8_t *)buf, len, 100) != HAL_OK)
    {
        len = 0;
    }
    return len;
}

使用特权

评论回复
45
实际测量不符|  楼主 | 2024-1-31 22:57 | 只看该作者
将MBRTUMasterTimerISRCallback函数放置于定时器中断函数中,对于HAL库那就是这样的:

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    if(htim->Instance == htim3.Instance)
    {
        MBRTUMasterTimerISRCallback(&MBRTUHandle);
    }
}

使用特权

评论回复
46
实际测量不符|  楼主 | 2024-1-31 22:57 | 只看该作者
将MBRTUMasterRecvByteISRCallback函数放置于串口中断函数中,对于HAL库那就是这样的:
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
    if(huart->Instance == huart3.Instance)
    {
        MBRTUMasterRecvByteISRCallback(&MBRTUHandle, g_Uart3RxByte);
        HAL_UART_Receive_IT(&huart3, &g_Uart3RxByte, 1);  // 注册接收
    }
}

使用特权

评论回复
47
实际测量不符|  楼主 | 2024-1-31 22:57 | 只看该作者
将MBRTUMasterRecvByteISRCallback函数放置于串口中断函数中,对于HAL库那就是这样的:

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
    if(huart->Instance == huart3.Instance)
    {
        MBRTUMasterRecvByteISRCallback(&MBRTUHandle, g_Uart3RxByte);
        HAL_UART_Receive_IT(&huart3, &g_Uart3RxByte, 1);  // 注册接收
    }
}

使用特权

评论回复
48
实际测量不符|  楼主 | 2024-1-31 22:57 | 只看该作者
重定向printf到串口1:
int fputc(int ch, FILE* fp)
{
    HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 100);
    return ch;
}

使用特权

评论回复
49
实际测量不符|  楼主 | 2024-1-31 22:58 | 只看该作者
至此,就移植完毕了,测试函数如下:

int ret;
uint8_t ucBuf[10];
uint16_t usBuf[10];

int main_example(void)
{
    // 定时器初始化,设置为3.5个字符的超时时间
    // Timer_Init();

    // 串口初始化,初始化波特率等
    // UART_Init();

    // 写单个线圈
    ret = MBRTUMasterWriteSingleCoil(&MBRTUHandle, 1, 0, 1, 500);
    printf(" write single coil %s. \r\n", ret < 0 ? "failed" : "ok");
    HAL_Delay(100);

    // 写单个寄存器
    ret = MBRTUMasterWriteSingleRegister(&MBRTUHandle, 1, 0, 0XAABB, 500);
    printf(" write single reg %s. \r\n", ret < 0 ? "failed" : "ok");
    HAL_Delay(100);

    // 写多个线圈
    memset(ucBuf, 0X01, 10);
    ret = MBRTUMasterWriteMultipleCoils(&MBRTUHandle, 1, 0, 10, ucBuf, 500);
    printf(" write coils %s. \r\n", ret < 0 ? "failed" : "ok");
    HAL_Delay(100);

    // 写多个寄存器
    memset(usBuf, 0XFF, 20);
    ret = MBRTUMasterWriteMultipleRegisters(&MBRTUHandle, 1, 0, 10, usBuf, 500);
    printf(" write regs %s. \r\n", ret < 0 ? "failed" : "ok");
    HAL_Delay(100);

    // 读线圈
    MBRTUMasterReadCoils(&MBRTUHandle, 1, 0, 10, 500, ucBuf);
    printf(" read coils %s. \r\n", ret < 0 ? "failed" : "ok");
    HAL_Delay(100);

    // 读离散量输入
    MBRTUMasterReadDiscreteInputs(&MBRTUHandle, 1, 0, 10, 500, ucBuf);
    printf(" read discs %s. \r\n", ret < 0 ? "failed" : "ok");
    HAL_Delay(100);

    // 读保持寄存器
    MBRTUMasterReadHoldingRegisters(&MBRTUHandle, 1, 0, 10, 500, usBuf);
    printf(" read hold regs %s. \r\n", ret < 0 ? "failed" : "ok");
    HAL_Delay(100);

    // 读输入寄存器
    MBRTUMasterReadInputRegisters(&MBRTUHandle, 1, 0, 10, 500, usBuf);
    printf(" read input regs %s. \r\n", ret < 0 ? "failed" : "ok");
    HAL_Delay(100);

    return 0;
}

使用特权

评论回复
50
实际测量不符|  楼主 | 2024-1-31 22:58 | 只看该作者
5、移植测试验证
移植完毕了现在需要测试,测试你可以使用MobusSlave软件模拟测试,也可以选用选用FreeModbus作为从机

使用特权

评论回复
51
everyrobin| | 2024-2-2 21:20 | 只看该作者
在STM32CubeMX中,配置UART接口以与Modbus RTU从设备通信。设置正确的波特率、数据位、停止位和奇偶校验位。

使用特权

评论回复
52
macpherson| | 2024-2-3 11:02 | 只看该作者
优化Modbus RTU主机协议栈的性能。例如, 可以调整接收缓冲区大小、中断处理速度等。

使用特权

评论回复
53
uytyu| | 2024-2-3 11:17 | 只看该作者
在完成协议栈的移植和集成后,进行充分的测试,确保主机能够与不同厂商的Modbus从机设备兼容工作。

使用特权

评论回复
54
jtracy3| | 2024-2-3 16:48 | 只看该作者
合理管理内存,确保在Modbus RTU通信过程中不会出现内存溢出或地址错误。

使用特权

评论回复
55
olivem55arlowe| | 2024-2-4 22:36 | 只看该作者
所采用的Modbus RTU主机协议栈具有良好的移植性,能够方便地适配到STM32系列微控制器

使用特权

评论回复
56
averyleigh| | 2024-2-5 11:37 | 只看该作者
如果系统中同时与多个从站设备通信,要注意并发控制,避免多个Modbus请求冲突或串行化处理不当。

使用特权

评论回复
57
1988020566| | 2024-2-5 12:08 | 只看该作者
如果使用的是FreeRTOS等实时操作系统,需要正确地配置和管理任务,确保Modbus主机的任务具有合适的优先级,并在适当的时候启动和停止。此外,还应确保互斥量(用于保护共享资源)的正确使用,以避免竞态条件。

使用特权

评论回复
58
chenci2013| | 2024-2-6 13:06 | 只看该作者
实现完成后,应进行详细的测试以验证Modbus RTU主机的功能。这包括正常的数据读写测试,以及边界情况的测试,如超时处理、错误响应等。可以使用模拟从机或实际的从机设备进行通信测试。

使用特权

评论回复
59
51xlf| | 2024-2-6 13:37 | 只看该作者
需要处理UART接收中断。              

使用特权

评论回复
60
ingramward| | 2024-2-6 14:33 | 只看该作者
正确配置接收中断,当接收到完整的Modbus帧后触发中断,处理完数据后再重新开启接收中断等待下一次通信。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则