RT-Thread论坛
直播中

胡秋阳

14年用户 1664经验值
私信 关注
[问答]

STM32已经用标准库写好的代码,怎么导入RT THREAD?

之前已经在裸机上实现了SAE J1939协议,因为要添加IOT 功能,现在想添加RT THREAD.因为之前写裸机程序的时候用的是标准库。而RT THREAD STUDIO自动生成的是HAL库,请问有什么好方法在我原来的代码上添加RT THREAD吗?

回帖(1)

周臻庸

2025-9-23 17:01:23

将基于STM32标准库的裸机代码(特别是SAE J1939协议)移植到RT-Thread操作系统,同时解决标准库与HAL库的兼容问题,可以按以下步骤进行。核心思路是保留原有标准库代码,通过RT-Thread设备驱动框架统一硬件操作,避免直接修改HAL库相关部分。


整体移植步骤


graph TD
    A[创建RT-Thread工程] --> B[保留标准库硬件初始化]
    B --> C[适配RT-Thread设备驱动]
    C --> D[重构J1939协议栈线程]
    D --> E[替换硬件操作接口]
    E --> F[优化中断与系统时钟]

详细步骤与代码示例


1. 创建RT-Thread工程



  • 使用RT-Thread Studio创建基于HAL库的工程(选择对应开发板BSP)。

  • 关键配置:在rtconfig.h中启用CAN设备、软件定时器、信号量等必要组件:
     #define RT_USING_CAN
    #define RT_USING_TIMER_SOFT
    #define RT_USING_SEMAPHORE


2. 保留标准库硬件初始化



  • 将原有标准库的初始化代码(如stm32f10x_conf.hsystem_stm32f10x.c及启动文件)复制到工程Drivers目录。


  • board.c替换HAL初始化为你的标准库初始化:


     // 注释掉HAL_Init()和SystemClock_Config()
    // HAL_Init();
    // SystemClock_Config();

    // 添加标准库初始化
    extern void SystemInit(void); // 声明标准库初始化函数
    SystemInit(); // 在RT-Thread启动前调用



3. 适配CAN设备驱动



  • 目标:让RT-Thread使用标准库的CAN控制器。


  • 创建自定义CAN驱动文件(如drv_can.c):


     #include "bsp_can.h" // 你的标准库CAN头文件
    #include

    static rt_err_t _can_configure(struct rt_can_device *can, ...) {
         // 调用标准库的CAN初始化函数
         CAN_Config(CAN1, &your_can_init_struct);
         return RT_EOK;
    }

    static rt_err_t _can_sendmsg(struct rt_can_device *can, ...) {
         CAN_TxMessage(CAN1, &tx_msg); // 调用标准库发送函数
         return RT_EOK;
    }

    // 注册CAN设备到RT-Thread
    static struct rt_can_ops _can_ops = {
         .configure = _can_configure,
         .sendmsg = _can_sendmsg,
    };

    int rt_hw_can_init(void) {
         rt_can_device_register(&can_dev, "can1", &_can_ops);
         return 0;
    }
    INIT_DEVICE_EXPORT(rt_hw_can_init);



4. 重构J1939协议栈线程




  • 将裸机主循环中的协议处理移到独立线程:


     void j1939_thread_entry(void *param) {
         while (1) {
             // 从CAN接收数据(使用RT-Thread API)
             rt_device_read(can_dev, 0, &msg, sizeof(msg));

             // 调用原J1939协议处理函数
             J1939_ProcessMessage(&msg);

             rt_thread_mdelay(10);
         }
    }

    int main(void) {
         rt_thread_t tid = rt_thread_create("j1939", j1939_thread_entry, RT_NULL, 2048, 10, 10);
         rt_thread_startup(tid);
    }



5. 替换硬件操作接口




  • 中断处理:在RT-Thread中接管CAN中断,使用信号量同步:


     // 原标准库中断函数改为弱定义
    void CAN1_RX0_IRQHandler(void) __attribute__((weak));

    // 实际中断处理(在drv_can.c中实现)
    void CAN1_RX0_IRQHandler(void) {
         rt_interrupt_enter();
         rt_hw_can_isr(&can_dev, RT_CAN_EVENT_RX_IND); // 触发接收事件
         rt_interrupt_leave();
    }


  • 延时函数:将裸机delay_ms()替换为rt_thread_mdelay()


6. 系统时钟配置



  • 确保SysTick中断由RT-Thread管理:
     // 在board.c中注释HAL的SysTick_Handler
    // 改为使用RT-Thread的rt_tick_increase()
    void SysTick_Handler(void) {
         rt_tick_increase();
    }


关键注意事项




  1. 库冲突解决



    • 在工程设置中移除HAL库的CAN驱动文件(如stm32f1xx_hal_can.c)。

    • 确保链接时优先使用你的标准库文件。




  2. 内存管理



    • 若原裸机使用动态内存,替换为RT-Thread的rt_malloc/rt_free




  3. 优先级调整



    • CAN中断优先级需高于J1939线程(例如:中断优先级5,线程优先级10)。




  4. 调试技巧



    • 使用ulog模块输出日志,通过finsh命令行动态查看线程状态。




移植前后的代码对比
































功能模块 裸机代码 RT-Thread适配后
CAN初始化 CAN_Init(CAN1, &init) rt_device_register() + 驱动封装
报文发送 CAN_Transmit(CAN1, ...) rt_device_write(can_dev, ...)
延时 delay_ms(100) rt_thread_mdelay(100)
中断处理 直接写在IRQHandler中 触发RT-Thread事件/信号量

验证流程



  1. 编译下载后,通过finsh输入list_device确认CAN设备注册成功。

  2. 使用逻辑分析仪或CAN分析仪检查报文收发是否正常。

  3. 监控J1939线程堆栈使用情况(free命令),避免溢出。


通过以上步骤,你可以在保留90%以上原有标准库代码的基础上,将J1939协议栈快速集成到RT-Thread中,后续添加IoT功能只需在系统中创建新的线程(如MQTT上传线程)。

举报

更多回帖

发帖
×
20
完善资料,
赚取积分