STM32 program architecture kernel porting

catalogue

1, Why use program architecture

2, Transplantation practice

3, Explanation of system execution process

4, Explanation of queue algorithm

1, Why use program architecture

1. Improve the stability of the program and avoid code conflict.

2. Solve common pain points in product development.

For example, short press, short press release, long press, Chang'an release and other functions. For example, LED should achieve a variety of flashing effects, flashing once every 2 seconds, once every 5 seconds, etc. For example, when sending and receiving a large number of serial port data, how to realize without losing a byte.

In short, writing complex products must be supported by a good program architecture! Just like using ucos and rtos system, it is actually to solve the problem of program architecture.

2, Transplantation practice

1. Migration process

Step 1: create OS_System.c and OS_ Put the system. H file into the project directory and add it to the Keil project

Step 2: set void OS_ClockInterruptHandle(void) is placed in the systick 10ms interrupt function of stem32 to provide a clock beat reference for the system.

/****************************** OS_System.c *******************************************
* @Function name OS_ClcokInterruptHandle
* @Describe system task scheduling function
* @Parameter none
* @Return value none
* @Note that in order to ensure the real-time performance of the task, this must be put into the 10ms timer or system clock or clock interrupt function
*************************************************************************/
volatile OS_TaskTypeDef OS_Task[OS_Task_SUM]; % Define a task array

void OS_ClockInterruptHandle(void)
{
	unsigned char i;
	for(i=0;i<OS_Task_SUM;i++) // Traverse all tasks
	{
		if(OS_Task[i].task) // Judge whether the task is created by pointing to the task function pointer that is not equal to 0
		{
			OS_Task[i].Runtime++;
			if(OS_Task[i].Runtime >= OS_Task[i].RunPeriod) // Determine whether the time required for task execution has been reached
			{
				OS_Task[i].Runtime = 0;  
				OS_Task[i].RunFlag = OS_RUN; // Set the task status to execute, and the task scheduling function will always judge / / the value of this variable if the OS_RUN will execute the function pointed to by task 
			}
		}
	}
}

/********************************* OS_System.h******************************************/

// System task ID
typedef enum{
	OS_Task1,
	
	OS_Task_SUM
}OS_TaskIDTypeDef;

// System task running status
typedef enum
{
	OS_SLEEP,
	OS_RUN=!OS_SLEEP
}OS_TaskStatusTypeDef;

// System task structure
typedef struct
{
	void (*task)(void);   // Task function pointer
	OS_TaskStatusTypeDef RunFlag;
	unsigned short RunPeriod; // Task cycle
	unsigned short Runtime;   
}OS_TaskTypeDef;

/********************************* cpu.c ******************************************/

static void hal_CoreClockInit(void)
{	
	SysTick_Config(SystemCoreClock / 100);			
//If 48M is used as the system clock, the counter minus 1 equals 1/48M(ms), (1/48000000hz)*(48000000/100)=0.01S=10ms
}

void SysTick_Handler(void)
{
	OS_ClockInterruptHandle();  // System task scheduling is interrupted every 10ms
}

Step 3: call OS_ Cpuinteruptcbregister (cpuinterupt_callback_pcuinterupttrlcbs) function, which passes in the switch general interrupt function.

/******************************* OS_System.c ****************************************/

//  Declare a function pointer through the macro cpuinteruptctrlcbs
CPUInterrupt_CallBack_t	CPUInterruptCtrlCBS;

/*************************************************************************
* @Function name OS_CPUInterruptCBSRegister
* @Describes registering CPU interrupt control functions
* @Valuepoint pccuinteruptctrlcbs interrupt callback function address
* @Return value none
* @be careful 	  nothing
*************************************************************************/
void OS_CPUInterruptCBSRegister(CPUInterrupt_CallBack_t	pCPUInterruptCtrlCBS)
{
	if(CPUInterruptCtrlCBS == 0)
	{
		CPUInterruptCtrlCBS = pCPUInterruptCtrlCBS;
	}
}

/******************************* OS_System.h ****************************************/

typedef enum
{
	CPU_ENTER_CRITICAL, // CPU enters critical
	CPU_EXIT_CRITICAL, // CPU exit critical
}CPU_EA_TYPEDEF;

// Macro defines a CPU interrupt control callback function pointer type
typedef void (*CPUInterrupt_CallBack_t)(CPU_EA_TYPEDEF cmd,unsigned char *pSta);

/******************************* cpu.c ****************************************/

// CPU initialization
void hal_CPUInit(void)
{
	hal_CoreClockInit();
	OS_CPUInterruptCBSRegister(hal_CPU_Critical_Control);
}

/********************************************************************************************************

/********************************************************************************************************
*  @Function name hal_getprimask						                                                           
*  @Description get CPU total interrupt status							                                     
*  @Parameter none
*  @Return value 0 - Total interrupt off 1 - Total interrupt on
*  @Attention none
********************************************************************************************************/
static unsigned char hal_getprimask(void)
{
	return (!__get_PRIMASK());		//0 is interrupt on and 1 is interrupt off, so it should be reversed
}

*  @Function name   hal_CPU_Critical_Control						                                                           
*  @describe     CPU Critical processing control						                                     
*  @parameter     cmd-control command  *pSta-Total interrupt status
*  @Return value none
*  @be careful     nothing
********************************************************************************************************/
static void hal_CPU_Critical_Control(CPU_EA_TYPEDEF cmd,unsigned char *pSta)
{
	if(cmd == CPU_ENTER_CRITICAL) 
	{
		*pSta = hal_getprimask(); // Save interrupt status
		__disable_irq();		//Turn off CPU total interrupt
	}
	else if(cmd == CPU_EXIT_CRITICAL)
	{
		if(*pSta)
		{
			__enable_irq();		//Open interrupt
		}
		else
		{
			__disable_irq();	//Close interrupt
		}
	}
}

Note to write the switch total interrupt function.

Switch interrupt is generally called critical in the operating system. Its function is that when placing operation data, the data is accidentally changed, resulting in unpredictable bugs. When the MCU is running, it may be interrupted, and then execute the program in the interrupt first. When executing the interrupt program, it may be interrupted by the interrupt with higher priority. At this time, it is time to execute the interrupt program with higher priority. If these three places operate on the same pointer or variable, unpredictable problems may occur. After the program is large, this situation is inevitable.

Step 4: create a task to test whether the migration is successful

/*********************************** main.c *************************/

int main(void)
{
	hal_CPUInit(); 
	hal_TimeInit();
    OS_TaskInit();
	
	hal_LedInit();
	OS_CreatTask(OS_Task1,hal_LedProc,1,OS_RUN); //Create a task and let the LED flash for 1 second
	
	OS_Start();
}

/***********************************************************************/

/*********************************** hal_led.c *************************/

void hal_LedInit(void)
{
	hal_ledConfig();	
}

void hal_LedProc(void)
{
	
	static unsigned short i = 0;
	i++;
	if(i > 100)
	{
		i=0;
		hal_LedTurn();
	}
}

void hal_LedTurn(void)
{
	GPIO_WriteBit(GPIOA,GPIO_Pin_1,(BitAction)(1-GPIO_ReadOutputDataBit(GPIOA,GPIO_Pin_1)));
}

3, Explanation of system execution process

1. System task structure OS_TaskTypeDef

// System task ID
typedef enum
{
	OS_Task1,  // Task 1. When multiple tasks are needed, you can add them in turn
	
	OS_Task_SUM
}OS_TaskIDTypeDef;

// System task running status
typedef enum
{
	OS_SLEEP,     // Sleep state
	OS_RUN=!OS_SLEEP // Execution status
}OS_TaskStatusTypeDef;

// System task structure
typedef struct
{
	void (*task)(void);   // Task function pointer
	OS_TaskStatusTypeDef RunFlag; // Task function run flag
	unsigned short RunPeriod; // Task function cycle
	unsigned short Runtime;   // Task function timer
}OS_TaskTypeDef;

The structure is used to define the common characteristic parameters of all task functions, including a task function pointer, operation flag, operation cycle and operation timer. Use enumeration to define task ID and running state.

2. Task initialization function OS_TaskInit

/********************************************************************************************************
*  @Function name OS_TaskInit					                                                           
*  @Describe system task initialization							                                     
*  @Parameter none
*  @Return value none
*  @Attention none
********************************************************************************************************/
void OS_TaskInit(void)
{
	unsigned char i;
	for(i=0; i<OS_Task_SUM; i++)
	{
		OS_Task[i].task = 0;
		OS_Task[i].RunFlag = OS_SLEEP;
		OS_Task[i].RunPeriod = 0;
		OS_Task[i].Runtime = 0;
	}	
}

Initialize the task function, traverse all task functions, and set the address pointed to by the function pointer to 0, that is, it does not point to any task function. The system sleeps, and the running cycle is 0. At this time, the timing is not counted.

3. Task creation function OS_CreatTask

/*************************************************************************
* @Function name OS_CreatTask(unsigned cahr ID,void (*proc)(void),unsigned short Period,OS_TaskStatusTypeDef flag)
* @Description create task function
* @Parameter - ID: task ID
*					- (*proc)() User function entry address 
*					- TimeDly Task execution frequency, unit: ms
* 					- flag Task ready status OS_SLEEP sleep OS_RUN run 
* @Return value none
* @Attention none
*************************************************************************/
void OS_CreatTask(unsigned char ID,void (*proc)(void),unsigned short Period,OS_TaskStatusTypeDef flag)
 {
	 if(!OS_Task[ID].task) // If it does not point to a task function
	 {
			OS_Task[ID].task = proc;   // Use a void function pointer to point to the task function
			OS_Task[ID].RunFlag = OS_SLEEP;
			OS_Task[ID].RunPeriod = Period; 
			OS_Task[ID].Runtime = 0; 
	 }
 }

First judge whether other task functions are being executed. If not, execute them in sequence according to the task enumeration list.

4. Task scheduling function OS_ClockInterruptHandle

/*************************************************************************
* @Function name OS_ClcokInterruptHandle
* @Describe system task scheduling function
* @Parameter none
* @Return value none
* @Note that in order to ensure the real-time performance of the task, this must be put into the 10ms timer or system clock or clock interrupt function
*************************************************************************/
void OS_ClockInterruptHandle(void)
{
	unsigned char i;
	for(i=0;i<OS_Task_SUM;i++) // Traverse all tasks
	{
		if(OS_Task[i].task) // Judge whether the task is created by pointing to the task function pointer that is not equal to 0
		{
			OS_Task[i].Runtime++;
			if(OS_Task[i].Runtime >= OS_Task[i].RunPeriod) // Determine whether the time required for task execution has been reached
			{
				OS_Task[i].Runtime = 0;  
				OS_Task[i].RunFlag = OS_RUN; // If the status of the task is set to execute, the task scheduling function will always judge the value of this variable. If the OS_RUN will execute the function pointed to by task 
			}
		}
	}
}

5. Start task function OS_Start

/*************************************************************************
* @Function name OS_Start
* @Describe start task
* @Parameter none
* @Return value none
* @Attention none
*************************************************************************/
void OS_Start(void)
{
	unsigned char i;
	while(1)
	{
		for(i=0;i<OS_Task_SUM;i++)  // Cycle tasks
		{
			if(OS_Task[i].RunFlag == OS_RUN)  
			{
				OS_Task[i].RunFlag = OS_SLEEP;
				
				(*(OS_Task[i].task))();  // Execute the function pointed to by task
			}
		}
	}
}

The greatest function of this program architecture:

1. Each task can be assigned a separate execution frequency, generally 10ms, and the minimum is 10ms. Of course, it can be modified according to actual needs. In this way, for some tasks that do not need to be executed frequently, the scheduling time can be increased to release CPU resources. The tasks here refer to functions, such as LED execution functions, key processing functions, etc

2. Provide common algorithms in product development: queue

Follow up is not finished.

Keywords: C Algorithm data structure architecture stm32

Added by Aretai on Tue, 12 Oct 2021 04:49:05 +0300