Building embedded real-time operating system from scratch 3 -- task state switching

1. Preface

A walker asked the old Taoist priest, "what did you do before you got the Tao?" Old Taoist priest: "chop firewood, carry water and cook." The walker asked, "what about after gaining the Tao?" Old Taoist priest: "chop firewood, carry water and cook." The walker asked again, "what is Tao?" Old Taoist priest: "before the enlightenment, when cutting firewood, you think about carrying water, and when carrying water, you think about cooking; after the enlightenment, cutting firewood means cutting firewood, carrying water means carrying water, and cooking means cooking."

Isn't it a quick start? Many of the highest and deepest truths in life are often contained in some extremely simple thoughts, which is the so-called great road to simplicity. Perfect is often the simplest. Simplicity is intelligence. Simplicity is the complexity of advanced forms. Simplicity to the extreme is great wisdom. Powerful people tend to simplify complex problems. No matter how big and difficult things in the world, they can be broken down into many simple things as long as they are "divided into two".

The single principle in the five principles of software design coincides with the idea of simplicity. The core idea of the single principle is to make the software pattern structure simple, and each module only realizes one function.

2. Design background

In Enuo v0 02 version added task C and interface C two documents:
1,task.c contains functions related to task operation, such as task creation function task_create and system startup scheduling function enuo_schedule.
2,interface.c contains operations related to processor hardware, which enhances the portability of the system.
In Enuo v0 The task abstract object is added in version 02, and all task related data are encapsulated in the task abstract object. At the same time, in Enuo v0 The linked list data structure is added in version 02. The function of task linked list is to connect multiple tasks in series, which can efficiently realize task retrieval and operation.

3. Design objectives

At present, the enuo system can create tasks in the ready table, and the system timer can poll and schedule the tasks in the ready table. Obviously, after the task is created, in addition to being in the running state, the task also needs to support stopping. Therefore, this version adds a stop table for enuo.

The task has two states: ready state and stop state. The processor will execute the task in ready state in turn, and the task in stop state will not be run.

In the enuo system, both the ready list and the stop list need to carry out the linked list operation. Therefore, improve the linked list operation, separate the linked list into a C file and abide by the single principle.

4. Design environment

The hardware environment is a self-made development board with STM32F401RE as the core, and the software environment is keil V5 2 development tools.

5. Design process

5.1 linked list operation

The linked list needs to realize two basic operations:
1. Insert the linked list in the desired order
2. Remove a node from the linked list

The linked list needs to realize sorting operation, so a sorting value sort is added to the linked list structure_ Value, the new linked list and the new data structure are as follows:

struct list_node_def
{
	struct list_node_def *  next;		/* Point to the next list node */
	struct list_node_def *  previous;	/* Point to previous list node */
	void 	*owner;						/* Point to the owner of the linked list node */
	uint32_t sort_value;				/* List sorting value*/
};

The initialization, insertion and deletion functions of the linked list are as follows:

/*********************************************************************************************************
* @name 	:  list_initialise
* @describe 	:  List initialization
**********************************************************************************************************/ 
void list_initialise( list_t * const list )
{
	/* The sliding pointer points to the meter */
	list->sliding_pointer = &list->head ;			

	/* The front and back pointers of the header are set to NULL */
	list->head.next = &list->head;	
	list->head.previous = &list->head;

	/* The sorting value of the header is 0 */
	list->head.sort_value = MIN_VALUE;
}
/*********************************************************************************************************
* @name 	:  list_sort_insert
* @describe 	:  According to sort_value value is inserted into the list in ascending order
**********************************************************************************************************/ 
uint8_t list_sort_insert( list_t * const list  , list_node_t * const new_node)
{
	list_node_t *seek;
	const uint32_t current_value = new_node->sort_value;
	uint16_t i = 0;


	/*   According to sort_value value to find the corresponding position  */
	for( seek =  & list->head ; seek->next->sort_value <= current_value; seek = seek->next )  
	{
		/*   Limit the number of lookups  */
		if( (i++) > 255 )
			return 0;
	}
	/*   New node insertion list  */
	new_node->next = seek->next;
	new_node->next->previous = new_node;
	new_node->previous = seek;
	seek->next = new_node;

	return 1;	
}
/*********************************************************************************************************
* @name 	:  list_remove
* @describe 	:  Delete a node from the list
**********************************************************************************************************/ 
void list_remove( list_node_t  * const remove_node )
{
	/*Delete node */
	remove_node->next->previous = remove_node->previous;
	remove_node->previous->next = remove_node->next;	
}

list_sort_insert is a reference sort_value values are arranged in ascending order.

There is a sliding pointer sliding in the list_ Pointer, the sliding pointer points to the current node. We construct an operation: when inserting the linked list, insert the data into the end of the sliding pointer. This operation can simplify adding new tasks to the end of the linked list when polling tasks. The code is as follows:

/*********************************************************************************************************
* @name 	:  list_insert_sliding_pointer_end
* @describe 	:  After inserting the node pointed by the sliding pointer
**********************************************************************************************************/ 
void list_insert_sliding_pointer_end( list_t * const list , list_node_t  * const new_node )
{
	/* Save slide pointer position */
	list_node_t * const seek = list->sliding_pointer;

	/* After inserting the node pointed by the sliding pointer */
	new_node->next = seek;
	new_node->previous = seek->previous;

	seek->previous->next = new_node;
	seek->previous = new_node;

}

Use list_ insert_ sliding_ pointer_ The linked list diagram after inserting a node into the end function is as follows:

Use list_ insert_ sliding_ pointer_ The linked list diagram after inserting the node into the end function is as follows:

Use list_ The linked list diagram after the remove function removes a node is as follows:

5.2 task operation

The task has the following three basic operations:
1. Create a task
2. Stop a specified task
3. Restore a specified task

In order to stop tasks, you need to create a delay waiting list, and the tasks that need to be stopped are placed in the delay waiting table. So far, enuo will have two lists:

The ready table stores the tasks in the ready state and the delay waiting table stores the tasks to be stopped. The processor will execute the tasks in the ready table in turn, and the tasks in the delay waiting table will not be run.
The operation functions of the task are as follows:

/*********************************************************************************************************
* @name 	:  task_create
* @describe 	:  Create task
**********************************************************************************************************/ 
void task_create( task_tcb_t *task , task_function_t function , uint32_t *stack_space , uint32_t stack_number )
{
	/* Save slide pointer position */
	list_node_t * const new_node = &task->link;
	/* The linked list user points to the task */	
	task->link.owner = task;
	/* Insert end of slide pointer */	
	list_insert_sliding_pointer_end( &ready_list , new_node);
	/* Initialize task stack  */	
	task_stack_init( (uint32_t *)task, function , stack_space , stack_number );	
}
/*********************************************************************************************************
* @name 	:  task_stop
* @describe 	:  Suspend task
**********************************************************************************************************/ 
void task_stop( task_tcb_t *task  )
{
	/* Save slide pointer position */
	list_node_t * const new_node = &task->link;
	/* Remove the original linked list relationship */
	list_remove(new_node);
	/* Insert end of slide pointer */	
	list_insert_sliding_pointer_end( &delay_list , new_node);
}
/*********************************************************************************************************
* @name 	:  task_resume
* @describe 	:  Recovery task
**********************************************************************************************************/ 
void task_resume( task_tcb_t *task  )
{
	/* Save slide pointer position */
	list_node_t * const new_node = &task->link;
	/* Remove the original linked list relationship */
	list_remove(new_node);
	/* Insert end of slide pointer */	
	list_insert_sliding_pointer_end( &ready_list , new_node);
}

Using task_ The relationship between the ready table and the delay table after the create function creates the task0 task is as follows:

Using task_ The relationship between the ready table and the delay table after the create function creates the task1 task is as follows:

Using task_ The stop function stops the task0 task. The relationship between the ready table and the delay table after the task is stopped is as follows:

Using task_ The resume function restores the task0 task. The relationship between the ready table and the delay table after the task is restored is as follows:

5.3 task scheduling

Task scheduling uses the polling method, and then reads the next task from the linked list, and uses the sliding pointer sliding_ The pointer points to the next node. The task scheduling function is as follows:

/*********************************************************************************************************
* @name 	:  SysTick_Handler
* @describe 	:  System interrupt service program
**********************************************************************************************************/
void SysTick_Handler(void)
{	
	list_t * const const_list = &ready_list ;													
	/* sliding_pointer The implementation of the sliding node in the loop points to the next node in the list */										
	( const_list )->sliding_pointer = ( const_list )->sliding_pointer->next;
	
	if( const_list ->sliding_pointer == & const_list ->head  )	
	{																						
		 const_list->sliding_pointer = const_list->sliding_pointer->next;						
			/* If the list is ListEnd, pointing to the next is the first implementation loop*/							
	}
	next_task = const_list->sliding_pointer->owner;
	
	/* Request switching tasks */
	TSAK_SWITCH_REQUEST;
	return;
}

Tsak is used for task switching_ SWITCH_ Request macro, macro definition uses the do while form commonly used by linux macros, Tsak_ SWITCH_ The request macro is as follows:

5.4 function test

enuo system adds the functions of task stop and task recovery. A task control logic is designed in task 0 to periodically stop and recover task1 and task2. The task0 task is designed as follows:

void task0(void)
{
	static uint16_t clk = 0;
	static uint16_t state = 0;
	while(1)
	{
		if( ( ( clk++ )%9999 ) == 0 ) 
		{
			task_debug_num0++;	/* Test tracking */
			test_function();
			/* Control the running status of task 1 and task 2*/
			switch( state )
			{
				case 0:/* Suspend task 1 task 2 */
					task_stop( &my_task1 );
					task_stop( &my_task2 );
				break;
				case 50:/* Restore task 1 */
					task_resume( &my_task1 );
				break;
				case 75:/* Restore task 2 */
					task_resume( &my_task2 );
				break;				
				default:
				break;					
			}
			/* Cycle timing */
			if( state++ > 100 )
				state = 0;		
		}
	}
}

Design a state machine using state:
When state is 0, stop task1 and task2;
When the state is 50, restore task1;
Restore task2 when state is 75.
If the function runs normally and the result is task0, the running times of task1 and task2 are monitored_ debug_ The ratio of num values is about 4:2:1.

6. Operation results

The results of code simulation are as follows:

task0, task1 and task2 run task_ debug_ The ratio of num values is about 4:2:1
Prove that the stop task and resume task of enuo system function normally.

Friends who want to get the source code leave messages in the comment area.

To be continued
The real-time operating system family will be continuously updated
Creation is not easy. I hope friends like, forward, comment and pay attention.
Your likes, forwarding, comments and attention will be the driving force of my continuous update
Author: Li Wei
Github: liyinuoman2017
CSDN: liyinuo2017
Today's headline: program ape Li Wei

Keywords: C Linux Single-Chip Microcomputer IoT ARM

Added by gli on Sun, 06 Mar 2022 09:26:20 +0200