RT-Thread Community Developer Chuchu has organized a very detailed doc that explains many of the issues that new RT-Thread beginners may encounter during the very first development experience on RT-Thread. If you happen to have some technical issues and looking for someone to support you, this post could be a support foundation for you!
This post keeps updating.
If you're on the Keil + RT-Thread Env environment, the first thing you should do after downloading the source code is to
If you're using the RT-Studio IDE, you should open
Settings before creating the project; Go through all the configuration items, cancel the irrelevant ones, and only leave the kernel.
First, ensure the minimum system can run, and use the lighting program to verify that the minimum system is running normally. Then add the features you need and the underlying peripherals, or something else.
Same operations as above
Same operations as above
In principle, the little size of the
RT_NAME_MAX, the less memory will be used. Take 100 kernel objects as an example, an object name occupies 8 bytes, a total of 800 bytes. The struct rt_object is the struct definition, it is followed by two
RT_NAME_MAX can be defined as 2n + 2
Not turn on kernel debugging unless it's necessary for your situation.
It has a lot to do with applications, and if it is only a minimal kernel system, idle threads, without enabling the interrupts and running the programs, 256 will be enough. If you're adding the program code, along with breaks and message mechanisms, it's better to put 1024.
We know that the GPIO grouping of chips tends to start with PA, followed by PB PC PD PE... PZ. Usually, each set of ports is either 16bit or 8bit (corresponding to 16 IO and 8 IO, respectively). The simplified formula for
GET_PIN is given below:
(X - A) * 16 + n
A10 is 10.
C9 is 2*16+9=41.
H1 is 7*16+1=113.
(X - A) * 8 + n
The RT-Thread kernel defines a software timer, and unlike hardware timers, hardware timers require a timer peripheral, as well as various comparison, capture, and other functions. The software timer simply sets a time, and it executes the callback function that we set when it's timeout.
The software timer defined by RT-Thread is also subdivided into two types, "hard timer" and "soft timer", the former is to execute callback functions in SysTick interrupts, most of which are used for thread built-in timers, and the application layer can also be used, but always keep in mind that its callback functions are executed in interrupts.
The latter, which runs in a thread, can be used when the application layer does not have very high requirements for timing precision, but it should also be noted that the thread that defines the timer and executes the timer callback function are two different threads.
1rt_err_t rt_mq_init(rt_mq_t mq, const char *name, void *msgpool, 2rt_size_t msg_size, rt_size_t pool_size, rt_uint8_t flag); 3rt_mq_t rt_mq_create(const char *name, rt_size_t msg_size, rt_size_t max_msgs, rt_uint8_t flag)
If you create a message queue with
rt_mq_create, the message queue pool is automatically calculated based on the message body size
msg_size and the maximum number that the message queue can hold
If you use
rt_mq_init to initialize the message queue, the memory
msgpool of the message queue pool needs to be provided by the user, in this case, you should pay attention to the message pool memory size
pool_size. It can be calculated according to this formula:
(RT_ALIGN(msg_size, RT_ALIGN_SIZE) + sizeof(struct rt_mq_message*)) * max_msgs
msg_size is the message body size and
max_msgs is the maximum message capacity in the message queue.
Although several APIs like
rt_mq_recv have size parameters, please strictly pass equal argument values according to the msg_size parameter values in
rt_mq_create. You should not change the value of the size parameter.
That's to say, don't use a message queue to send longer data directly.
The first thing that RT-Thread impressed me is it has no initial configuration in the main function, it is a separate thread. Other threads are automatically started via INIT_APP_EXPORT.
RT-Thread defines a total of 6 startup processes,
1/* board init routines will be called in board_init() function */ 2#define INIT_BOARD_EXPORT(fn) INIT_EXPORT(fn, "1") 3 4/* pre/device/component/env/app init routines will be called in init_thread */ 5/* components pre-initialization (pure software initilization) */ 6#define INIT_PREV_EXPORT(fn) INIT_EXPORT(fn, "2") 7/* device initialization */ 8#define INIT_DEVICE_EXPORT(fn) INIT_EXPORT(fn, "3") 9/* components initialization (dfs, lwip, ...) */ 10#define INIT_COMPONENT_EXPORT(fn) INIT_EXPORT(fn, "4") 11/* environment initialization (mount disk, ...) */ 12#define INIT_ENV_EXPORT(fn) INIT_EXPORT(fn, "5") 13/* appliation initialization (rtgui application etc ...) */ 14#define INIT_APP_EXPORT(fn) INIT_EXPORT(fn, "6")
INIT_BOARD_EXPORT runs before the task scheduler starts, and the only task scheduler is executed before it runs. Here is the peripheral initialization configuration process.
The remaining processes are executed by the main thread (on RT-Thread standard version, if the main thread is used) after the task scheduler starts.
These processes are not completely fixed, and some can be adjusted, for example, I used to advance the initialization of the LCD from DEVICE to BOARD, and make the initialization of emwin to PREV. Some message queues are also initialized during the ENV process.
In most cases, all of the defined initialization can be done in the above processes. However, it is also inevitable that there will be a possibility of conflict.
Check PR #5194 on RT-Thread Github Repository, it includes some links that also reported this issue, and many other developers ping their solutions there, it can be of your reference.
Personally, I think the startup orders are at the same level, and they have dependency and mutex between. In this case, you should adjust the code execution order, write the initialization process of dependency and mutex into the same function, and maintain its dependency on your own.
To be honest, I don't suggest you do this! On the RT-Thread, there are two possibilities that will happen if a thread jumps into the suspend state, one is that the time slice exhausts automatically give up CPU; One is to wait for resources to block and give up CPU. There is no complete and transparent way for two threads to know each other's current state.
Suppose thread A wants to explicitly suspend thread B, but A does not know whether B is currently giving up CPU while running, or waiting for resources to be suspended, or if resources are available while being woken up from the suspended state. Apparently, it is dangerous to hang other threads without knowing it.
The only thing that I can think of is that the thread B perform more tasks, and it will not take the initiative to give up CPU. Moreover, its thread priority is relatively low, and a high-priority thread A suspends B under certain conditions. But in this way thread B is bound to affect the idle thread.
In fact, this kind of scenario can be achieved by using the inter-thread synchronization mechanism, and thread B suspends itself by signaling to A; Thread A then wakes up thread B with another signal.
I feel I could find a way to use these two APIs directly, but one day, I notice that the RT-Thread's IPC was targeted. If it is suspended because of semaphores then it won't be woken up because of the mailbox. Because the time slice is exhausted, the suspended thread also does not need to think about what resources will be woken up. Suspend and wake are unique.
rt_thread_mdelayit turns to -2. However, it does not mean that there is an error.
As mentioned above, there are three kinds of timers in RT-Thread, each with its own characteristics
Hardware timer: The callback function is in the interrupt, and it is not recommended to directly perform long-term operations.
Hard timer: Also executing callback functions in interrupts, it is not recommended to perform long-term operations directly.
Soft Timer: A soft timer that is executed by a timer thread that calls callback functions and has a theoretical basis for performing long-term operations. The timer thread is also a thread, it also has its own thread stack, priority, etc. If some operations are independent, putting them in a particular thread is the same as running on a timer thread.
However, the way the current timer thread handles soft timers is not suitable for performing long-running operations. Modifications are required to do so.
Note: The priority of the timer thread needs to be adjusted as needed; If there are multiple soft timers, the execution of the callback function will be relatively long, and there must be a possibility that a callback will be delayed, which is unavoidable.