preface
The primary role of RTOS is to switch threads, just like interrupt processing logic. After interrupt, handle other things. After processing, return to normal and continue execution.
Thread switching
RT thread kernel code is used here to verify
The main functions of thread switching are written in assembly language. I think we will continue to deeply study how to implement each function later.
Suppose we have a thread switching function, how to switch a thread? If you call other functions directly in a [stack space], it is also switching.
Here, the management of thread stack is used to realize the switching between several threads, and the priority scheduling (SWITCHING) logic is not realized for the time being. That is, simple implementation: [switch to which thread you want to switch to]
Engineering construction
Here you need to implement the RTOS kernel related code
For the cpuport part, you can directly use the RT thread kernel source code. I use the STM32F103 platform here, so
rt-thread\libcpu\arm\cortex-m3
Directory, copy the whole to your own test project, and manually add Keil MDK5 project.
Here you only need to comment out: context_ rvds. In S: rt_hw_hard_fault_exception, i.e. not used temporarily: hardfault_ Override of handler.
The following kernel files can be compiled normally without all copies.
Definition of type and data structure: rtdef h
/* rtdef.h Type, data structure definition */ #ifndef __RTDEF_H__ #define __RTDEF_H__ typedef signed char rt_int8_t; typedef signed short rt_int16_t; typedef signed int rt_int32_t; typedef unsigned char rt_uint8_t; typedef unsigned short rt_uint16_t; typedef unsigned int rt_uint32_t; typedef int rt_bool_t; typedef long rt_base_t; typedef unsigned long rt_ubase_t; typedef rt_base_t rt_err_t; typedef rt_uint32_t rt_tick_t; #define RT_TRUE 1 #define RT_FALSE 0 #define RT_EOK 0 #define RT_ERROR 1 #define ALIGN(n) __attribute__((aligned(n))) #define RT_ALIGN(size, align) (((size) + (align) - 1) & ~ ((align) - 1)) #define RT_ALIGN_DOWN(size, align) ((size) & ~ ((align) - 1)) #define RT_NULL (0) struct rt_thread { /* stack point and entry */ void *sp; void *entry; void *parameter; void *stack_addr; rt_uint32_t stack_size; }; #endif
rthw.h
Interface switches related to hardware migration, interrupt stack initialization, etc. this part of the interface mainly comes from the migration file, such as context_rvds.S assembly file.
/* rthw.h Hardware transplantation related interface switch, interrupt stack initialization, etc */ #ifndef __RT_HW_H__ #define __RT_HW_H__ #include <rtthread.h> rt_base_t rt_hw_interrupt_disable(void); void rt_hw_interrupt_enable(rt_base_t level); void rt_hw_context_switch_to(rt_uint32_t to); void rt_hw_context_switch(rt_uint32_t from, rt_uint32_t to); rt_uint8_t *rt_hw_stack_init(void *tentry, void *parameter, rt_uint8_t *stack_addr, void *texit); #endif
rtthread.h file
For the main header file, you only need to implement the inclusion of other header files and the interface between thread initialization and scheduling.
/* rtthread.h Main header file */ #ifndef __RTTHREAD_H__ #define __RTTHREAD_H__ #include <string.h> #include <rtdef.h> #include <rtconfig.h> #include <rthw.h> void rt_system_scheduler_start(struct rt_thread *to_thread); void rt_thread_init(struct rt_thread *thread, void (*entry)(void *param), void *param, void *stack_start, rt_uint32_t stack_size); #endif
thread.c
Function implementation: thread initialization
/* thread.c Thread related functions are implemented, such as initialization */ #include <rtthread.h> void rt_thread_init(struct rt_thread *thread, void (*entry)(void *param), void *param, void *stack_start, rt_uint32_t stack_size) { thread->entry = (void *)entry; thread->parameter = param; thread->stack_addr = stack_start; thread->stack_size = stack_size; memset(thread->stack_addr, '#', thread->stack_size); thread->sp = (void *)rt_hw_stack_init(thread->entry, thread->parameter, (rt_uint8_t *)((char *)thread->stack_addr + thread->stack_size - sizeof(rt_uint32_t)), RT_NULL); }
Scheduler: scheduler c
Only the initial enabling thread is implemented here.
/* scheduler.c Manual scheduling is implemented here */ #include <rtthread.h> void rt_system_scheduler_start(struct rt_thread *to_thread) { rt_hw_context_switch_to((rt_ubase_t)&to_thread->sp); }
User test program
task_test.c. It is mainly used to test the definition and initialization of threads and the implementation of thread switching logic
/* task_test.c */ #include <rtthread.h> #include <board.h> #include <string.h> #include <rthw.h> //#include "printk.h" #include "task_test.h" #define TASK1_STACK_SIZE 1024 #define TASK2_STACK_SIZE 1024 #define TASK3_STACK_SIZE 1024 #define TASK4_STACK_SIZE 1024 ALIGN(RT_ALIGN_SIZE) struct rt_thread t4; struct rt_thread t3; struct rt_thread t2; struct rt_thread t1; static uint8_t task4_stack[TASK3_STACK_SIZE]; static uint8_t task3_stack[TASK3_STACK_SIZE]; static uint8_t task2_stack[TASK2_STACK_SIZE]; static uint8_t task1_stack[TASK1_STACK_SIZE]; void task1_entry(void *param) { rt_ubase_t level; static rt_ubase_t cnt = 0x00; while (1) { //printk("task1 run.\r\n"); HAL_Delay(3000); level = rt_hw_interrupt_disable(); cnt++; rt_hw_interrupt_enable(level); if (cnt >= 3) { cnt = 0; //printk("1->2\r\n"); task1_to_task2(); } } } void task2_entry(void *param) { rt_ubase_t level; static rt_ubase_t cnt = 0x00; while (1) { //printk("task2 run.\r\n"); HAL_Delay(3000); level = rt_hw_interrupt_disable(); cnt++; rt_hw_interrupt_enable(level); if (cnt >= 3) { cnt = 0; //printk("2->3\r\n"); task2_to_task3(); } } } void task3_entry(void *param) { rt_ubase_t level; static rt_ubase_t cnt = 0x00; while (1) { //printk("task3 run.\r\n"); HAL_Delay(3000); level = rt_hw_interrupt_disable(); cnt++; rt_hw_interrupt_enable(level); if (cnt >= 3) { cnt = 0; //printk("3->1\r\n"); task3_to_task1(); } } } void task4_entry(void *param) { rt_ubase_t level; static rt_ubase_t cnt = 0x00; while (1) { //printk("task4 run.\r\n"); HAL_Delay(3000); level = rt_hw_interrupt_disable(); cnt++; rt_hw_interrupt_enable(level); if (cnt >= 3) { cnt = 0; //printk("4->1\r\n"); task4_to_task1(); } } } void task1_init(void) { rt_thread_init(&t1, task1_entry, RT_NULL, task1_stack, TASK1_STACK_SIZE); } void task2_init(void) { rt_thread_init(&t2, task2_entry, RT_NULL, task2_stack, TASK2_STACK_SIZE); } void task3_init(void) { rt_thread_init(&t3, task3_entry, RT_NULL, task3_stack, TASK3_STACK_SIZE); } void task4_init(void) { rt_thread_init(&t4, task4_entry, RT_NULL, task4_stack, TASK4_STACK_SIZE); } void task1_run(void) { rt_system_scheduler_start(&t1); } void task2_run(void) { rt_system_scheduler_start(&t2); } void task3_run(void) { rt_system_scheduler_start(&t3); } void task4_run(void) { rt_system_scheduler_start(&t4); } void task1_to_task2(void) { rt_hw_context_switch((rt_ubase_t)&t1.sp, (rt_ubase_t)&t2.sp); } void task2_to_task3(void) { rt_hw_context_switch((rt_ubase_t)&t2.sp, (rt_ubase_t)&t3.sp); } void task3_to_task1(void) { rt_hw_context_switch((rt_ubase_t)&t3.sp, (rt_ubase_t)&t1.sp); } void task4_to_task1(void) { rt_hw_context_switch((rt_ubase_t)&t4.sp, (rt_ubase_t)&t1.sp); }
task_test.h
/* task_test.h */ #ifndef __TASK_TEST_H__ #define __TASK_TEST_H__ void task1_init(void); void task2_init(void); void task3_init(void); void task4_init(void); void task1_run(void); void task2_run(void); void task3_run(void); void task4_run(void); void task1_to_task2(void); void task2_to_task3(void); void task3_to_task1(void); void task4_to_task1(void); #endif
Open test main
#include <board.h> #include <drv_common.h> #include "test.h" #include "task_test.h" /** * @brief The application entry point. * @retval int */ int main(void) { hw_board_init(); //stmfd_test(); /* Thread initialization */ task1_init(); task2_init(); task3_init(); task4_init(); task1_run(); /* Run the first thread */ task1_to_task2(); /* Switch thread Task1 - > task2*/ while (1) { HAL_GPIO_WritePin(GPIOE, GPIO_PIN_4, GPIO_PIN_RESET); HAL_Delay(1000); HAL_GPIO_WritePin(GPIOE, GPIO_PIN_4, GPIO_PIN_SET); HAL_Delay(1000); } } /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
Compilation, download and debugging:
Comment out irrelevant code and set the path. It should be able to compile and download normally.
Software debugging: it is found that the thread can be initialized normally and the thread can be started (do not continue to execute while (1) of main)
Each thread can be flexibly manually switched and executed.
Run task1
Switch task2 and run
Summary
The preliminary switching idea has been clear
Continue to drill down into thread switching (task context switching)
Understand the scheduler, such as priority scheduling, for thread switching logic implementation