rt-thread 使用宝典(2022-0516更新)

本文是 rt-thread 用户在论坛上分享的使用经验总结,涵盖了从入门到深入的常见问题,包括 SDK 初始化、内核配置、线程管理、消息队列、定时器、外设驱动和文件系统等多个方面。作者强调了正确配置 SDK 的重要性,解释了如何避免程序运行异常,并提供了处理线程挂起、定时器使用和消息队列等问题的建议。此外,还涉及了开发环境的设置和外设驱动的适配,为 rt-thread 的实际应用提供了实用的指导。
摘要由CSDN通过智能技术生成

本文由RT-Thread论坛用户@出出啊原创发布:https://club.rt-thread.org/ask/article/2460fcd7db4821ae.html

前言

接触 rt-thread 已有半年,混论坛也5个半月了,期间遇到过各种奇奇怪怪的棘手问题,有过尴尬,也自信曾经提供过比较妙的应对方案。所以产生了将一些典型的使用技巧汇总分享出来的想法,遂有此篇。

入门篇

Q1. 刚下载的 SDK 啥也没干,编译没错,为啥程序跑不起来?

如果使用 keil + env 环境,下载源码后的第一件事就是 menuconfig
如果使用 RT-Studio ,创建项目后的第一件事就是打开 Settings
把其中所有配置页面所有配置项全浏览一遍,取消掉所有不相干的配置,最后只留一个内核。

先保证最小系统跑起来,用点灯程序验证最小系统运行正常。然后再添加自己需要用到的功能和底层外设等等。

Q2. 刚下载的 SDK 啥也没干,编译没错,为啥程序跑起来 hard fault on thread?

同上

Q3. 刚下载的 SDK 啥也没干,编译为啥报错了?

同上

内核篇

Q1. RT_NAME_MAX 定义多少合适

原则上越少越省内存,以内核对象 100 个为例,一个对象名占用 8 字节,总共是 800 字节。但是考虑到 struct rt_object 结构体定义,后面跟了两个 rt_uint8_t 型变量。

RT_NAME_MAX 可以定义成 2n + 2

Q2. RT_DEBUG

如非必要,不要开启内核调试。除非,你真的想学习内核,或者调试内核的问题。

Q3. 线程栈大小定义多少合适?

这个问题和应用有很大关系,如果仅仅是一个最小内核系统,除了 idle 线程,没有使用其它中断和应用,256 也将将够。如果添加了应用代码,还有中断和消息机制。建议 1024 起步。

Q4. 怎么快速计算 GET_PIN 返回的编号?

我们知道,芯片的 GPIO 分组往往是从 PA 开始,往后依次是 PB PC PD PE … PZ。往往的,每组端口或者是 16bit 或者是 8bit (分别对应 16 个 IO 和 8 个 IO)。下面给出 GET_PIN 的简化公式:

16bit 是 (X - A) * 16 + n

A10 就是 10.
C9 就是 2*16+9=41.
H1 就是 7*16+1=113.

8bit 是 (X - A) * 8 + n

这个公式别忘啊,别忘了!

PS: 有种,他们的引脚号编码很奇特,比如 RA6M4 ,见【开发板评测】Renesas RA6M4开发板之GPIO、IIC(模拟) 第二节部分。

Q5. 硬定时器、软定时器、硬件定时器,傻傻分不清楚

rt-thread 内核定义了软件定时器,和硬件定时器不同,硬件定时器需要占用一个定时器外设,还有各种比较、捕获等功能。软件定时器仅仅是简单的设定一个时间,时间 timeout 的时候执行我们设定的回调函数。

rt-thread 定义的软件定时器还细分两种,“硬定时器” “软定时器”,前一种是在 SysTick 中断中执行回调函数的,多数用于线程内置定时器,应用层也可以用,但是要时刻谨记它的回调函数是在中断中执行的。
后一种,是在一个线程中运行的,应用层对定时精度要求不是很高的可以用这种,但是也要注意“定义定时器和执行定时器回调函数的线程是两个不同的线程!”

Q6. 消息队列池申请多少内存合适?
rt_err_t rt_mq_init(rt_mq_t mq, const char *name, void *msgpool, rt_size_t msg_size, rt_size_t pool_size, rt_uint8_t flag);
rt_mq_t rt_mq_create(const char *name, rt_size_t msg_size, rt_size_t max_msgs, rt_uint8_t flag)

如果使用 rt_mq_create 创建消息队列,消息队列池自动根据消息体大小 msg_size 和消息队列最多容纳的消息数量 max_msgs 计算。

但如果使用 rt_mq_init 初始化消息队列,消息队列池的内存 msgpool 需要用户提供,这个时候,需要注意消息池内存大小 pool_size。根据下面的公式计算得出:

(RT_ALIGN(msg_size, RT_ALIGN_SIZE) + sizeof(struct rt_mq_message*)) * max_msgs

其中,msg_size 是消息体大小,max_msgs 是消息队列中最多消息容量。

Q7. 使用消息队列注意

虽然 rt_mq_send rt_mq_send_wait rt_mq_urgent rt_mq_recv 几个 api 有 size 参数,但是请严格按照 rt_mq_init rt_mq_create 中的 msg_size 参数值传递相等的实参值。千万不要随意改变 size 参数的数值

换种说法,别用消息队列直接发变长数据。

Q8. INIT_xxx_EXPORT 宏详解

当初接触 rt-thread 第一个让我感触的地方就是,main 函数里没有初始化配置,上来直接就是一个单独的线程。而,其它线程都通过 INIT_APP_EXPORT 自动启动了。
rt-thread 一共定义了 6 个启动阶段,

/* board init routines will be called in board_init() function */
#define INIT_BOARD_EXPORT(fn)           INIT_EXPORT(fn, "1")

/* pre/device/component/env/app init routines will be called in init_thread */
/* components pre-initialization (pure software initilization) */
#define INIT_PREV_EXPORT(fn)            INIT_EXPORT(fn, "2")
/* device initialization */
#define INIT_DEVICE_EXPORT(fn)          INIT_EXPORT(fn, "3")
/* components initialization (dfs, lwip, ...) */
#define INIT_COMPONENT_EXPORT(fn)       INIT_EXPORT(fn, "4")
/* environment initialization (mount disk, ...) */
#define INIT_ENV_EXPORT(fn)             INIT_EXPORT(fn, "5")
/* appliation initialization (rtgui application etc ...) */
#define INIT_APP_EXPORT(fn)             INIT_EXPORT(fn, "6")

其中, INIT_BOARD_EXPORT 运行在任务调度器启动前,也是唯一任务调度器运行前被执行的。这里是外设初始化配置阶段。
其余几个阶段都是任务调度器启动以后,由 main 线程(标准版,如果使用了 main 线程)负责执行。

这些阶段并不是完全固定,有些是可以调整的,例如,我曾经把 lcd 的初始化从 DEVICE 提前到 BOARD ,而把 emwin 的初始化放到 PREV 。还在 ENV 阶段初始化了一些消息队列等等。

大部分情况下,以上几个阶段可以完成所有定义的初始化工作。但是,也难免出现冲突的可能。

例如,github

  • 7
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值