uC/OS-III Implementación del kernel y desarrollo de aplicaciones Aprendizaje práctico (1)

Tutoría de referencia

[Wildfire] Placa de desarrollo "Guía práctica de desarrollo de aplicaciones e implementación del kernel de uCOS-III"
---->Wildfire MINI_STM32F103RCT6
inserte la descripción de la imagen aquí
1. Sistema de sondeo
2. Sistema frontal y posterior
3. Sistema multitarea

Respuesta y manejo de incidentes
sistema de votación Responder y procesar en el programa principal
sistema delantero y trasero Respuesta a la interrupción, manejada en el programa principal
sistema multitarea Respuesta de interrupción, manejada en la tarea

conciencia de la pila

La pila de la que estamos hablando es en realidad dos cosas, a saber, montón y pila. Por supuesto, si alguien te menciona el término pila, lo que quiere expresar debe ser el concepto de pila . Además de colocar las variables globales en el área del montón, otras funciones, como las variables locales y los parámetros de función, se asignan en el área de la pila, y la pila crece de direcciones altas a direcciones bajas. Esto es diferente del montón, que crece de direcciones más bajas a direcciones más altas.
Método de asignación: el montón se asigna dinámicamente y no hay un montón asignado estáticamente.

programa cpu.h

/*cpu.h头文件内容*/
#ifndef CPU_H
#define CPU_H

typedef unsigned short CPU_INT16U;
typedef unsigned int   CPU_INT32U;
typedef unsigned char  CPU_INT08U;

typedef CPU_INT32U CPU_ADDR;

/*堆栈数据类型重定义*/

typedef CPU_INT32U CPU_STK;
typedef CPU_ADDR   CPU_STK_SIZE;
typedef volatile CPU_INT32U CPU_REG32;

#endif

¿Se puede pasar una estructura como parámetro a una función?

programa os_task.c

/*实现任务创建函数*/

void OSTaskCreate(OS_TCB       *p_tcb,
                  OS_TASK_PTR   p_task,
				  void         *p_arg,
				  CPU_STK      *p_stk_base,
				  CPU_STK_SIZE  stk_size,
				  OS_ERR       *p_err)
{
    
    
   CPU_STK  *p_sp;
   p_sp = OSTaskStkInit( p_task, 
                         p_arg,
						 p_stk_base,
						 stk_size);
				
    p_tcb->StkPtr = p_sp;
	p_tcb->StkSize = stk_size;
	
	*p_err = OS_ERR_NONE;//执行到这一步,表示没有错误!
 
}
/*(1)p_tcb是任务控制块TCB指针
  (2)p_task是任务函数名,类型为OS_TASK_PTR,原型声明
  在os.h  typedef void  (*OS_TASK_PTR)(void *p_arg);
  (3)p_arg是任务形参,用于传递任务参数
  (4)p_stk_base 用于指向任务堆栈的起始地址
  (5)stk_size 表示任务堆栈的大小
  (6)p_err用于存放错误码,这个错误码是uC/OS-III已经
  预定义好的,错误码是枚举类型
*/

/*错误码枚举类型*/
 typedef enum os_err {
    
    
 OS_ERR_NONE = 0u,
 
 OS_ERR_A = 10000u,
 OS_ERR_ACCEPT_ISR = 10001u,
 
 OS_ERR_B = 11000u,
 
 OS_ERR_C = 12000u,
 OS_ERR_CREATE_ISR = 12001u,
 
 /* 篇幅限制,中间部分删除,具体的可查看本章配套的例程 */
 
 OS_ERR_X = 33000u,
 
 OS_ERR_Y = 34000u,
 OS_ERR_YIELD_ISR = 34001u,
 
 OS_ERR_Z = 35000u
} OS_ERR;

------------------------------- Escrito a las 12:25 el 10.1.2022, sigue leyendo mañana

13:10 19:10 ¡Hola, hermanos, sigan picando!
En la función OSTaskCreate() llamamos a la función OSTaskStkInit(), echemos un vistazo al prototipo de función de OSTaskStkInit():

CPU_STK *OSTaskStkInit(OS_TASK_PTR   p_task,)
                       void         *p_arg,
					   CPU_STK      *p_stk_base,
					   CPU_STK_SIZE  stk_size)
{
    
    
    CPU_STK *p_stk;
	
	p_stk = &p_stk_base[stk_size];
	
	//异常发生时自动保存的寄存器
	
	*--p_stk = (CPU_STK)0x01000000u;  //xPSR的bit24必须置1
	*--p_stk = (CPU_STK)p_task;       //R15(PC)任务的入口地址
	*--p_stk = (CPU_STK)0x14141414u;  //R14(LR)              	
    *--p_stk = (CPU_STK)0x12121212u;  //R12
	*--p_stk = (CPU_STK)0x03030303u;  //R3
	*--p_stk = (CPU_STK)0x02020202u;  //R2
	*--p_stk = (CPU_STK)0x01010101u;  //R1
	*--p_stk = (CPU_STK)p_arg;        //R0:任务参数
	
	//异常发生时需手动保存的寄存器R4~R11
	*--p_stk = (CPU_STK)0x11111111u;   //R11
	*--p_stk = (CPU_STK)0x10101010u;   //R10
	*--p_stk = (CPU_STK)0x09090909u;   //R9
	*--p_stk = (CPU_STK)0x08080808u;   //R8
	*--p_stk = (CPU_STK)0x07070707u;   //R7
	*--p_stk = (CPU_STK)0x06060606u;   //R6
	*--p_stk = (CPU_STK)0x05050505u;   //R5
	*--p_stk = (CPU_STK)0x04040404u;   //R4
	
	return (p_stk);

}	

Al principio encontré que el número de registro y el número hexadecimal “en realidad son iguales”, me pregunté, ¿no será tal coincidencia? Después de ver que es conveniente para la depuración, deliberadamente hago esto para
dar una explicación del programa:
(1) p_task es el nombre de la tarea, que indica la dirección de entrada de la tarea. Cuando se cambia la tarea,
debe cargarse en R15, es decir, el registro de PC, para que la CPU encuentre la tarea para ejecutar.
(2) p_arg es el parámetro formal de la tarea, que se utiliza para transferir el parámetro.Cuando se cambia la tarea, debe
cargarse en el registro R0. El registro R0 generalmente se usa para pasar parámetros.
(3) p_stk_base indica la dirección inicial de la pila de tareas.
(4) stk_size representa el tamaño de la pila de tareas, el tipo de datos es CPU_STK_SIZE,
que equivale a 4 bytes, es decir, una palabra en el procesador del núcleo Cortex-M3.

typedef unsigned int      CPU_INT32U;
typedef CPU_INT32U        CPU_ADDR;
typedef CPU_ADDR          CPU_STK_SIZE;

(5) Obtenga la dirección superior de la pila. Anteriormente dijimos que la pila tiene un lugar especial, es decir, crece de una dirección alta a una dirección baja.
Consulte el blog: Tear startup_stm32f10x_hd.s código fuente del archivo de inicio
( 6) La tarea se ejecuta por primera vez y se carga en Necesitamos inicializar los parámetros de entorno de los registros de la CPU por adelantado. El orden de inicialización es fijo (¿por qué? Creo que el orden fijo de xPSR, PC, LR está bien. Otros son innecesarios)
8 registros que se guardan automáticamente cuando ocurre una excepción, a saber, xPSR, PC, LR, R12, R3 , R2 , R1, R0 (¿por qué esto se guarda automáticamente? ¿Por qué los demás se guardan manualmente?)
¿Por qué el bit 24 del registro xPSR debe ser 1?
(7) Los parámetros de registro R11~R4CPU cargados manualmente
(8) return devuelven el puntero superior de la pila, lo asignan a p_sp y luego p_sp se almacena en el primer miembro StkPtr de la estructura TCB.
El tamaño de la pila de tareas stk_size se almacena en el segundo miembro de la TCB, StkSize

//在函数OSTaskStkInit中
return (p_stk);
//在函数OSTaskCreate中
p_sp = OSTaskStkInit( p_task, p_arg, p_stk_base,stk_size);
p_tcb->StkPtr = p_sp;
p_tcb->StkSize = stk_size;

--------------------------------------Línea divisoria---------- --------------------------------------------
1. Da este paso, Fui a verificar la información nuevamente, consulte este blog ARM Cortex -M Architecture ————— ARM Microcontroller and Embedded System
Descubrí que esto provino de Joseph Yiu, traducido por Song Yan "Cortex M3 Authoritative Guide" , jaja, casi ¡Olvidé este libro de referencia clásico! Como se describe en este libro:
inserte la descripción de la imagen aquí
(1) R0-R7 también se conocen como registros bancarios bajos. Todos los comandos pueden acceder a ellos . Sus longitudes de palabra son todas de 32 bits, y el valor inicial después del restablecimiento es impredecible
(2) R8-R12 también se denominan registros de grupo alto. Esto se debe a que solo unas pocas instrucciones Thumb de 16 bits pueden acceder a ellas y las instrucciones thumb-2 de 32 bits no están restringidas . También tienen una longitud de palabras de 32 bits y el valor inicial después del reinicio es impredecible.
inserte la descripción de la imagen aquí
Respuesta al registro xPSR:
la x en este xPSR significa "estado indeterminado arbitrario", que se puede dividir en tres registros de subestado APSR, IPSR y EPSR.
inserte la descripción de la imagen aquí
¿Por qué el bit 24 del registro xPSR debe ser 1?
—> Estado del pulgar, siempre 1, borrar este bit provocará una excepción de error.
La siguiente imagen no está en la "Guía autorizada de Cortex M3", ¿por qué no lo está? En cambio, encontré la respuesta en un blog: "Embedded - Embedded Hodgepodge" comprensión profunda de los registros ARM
inserte la descripción de la imagen aquí

--------------------------------------Línea divisoria---------- --------------------------------------------

Supongo que te gusta

Origin blog.csdn.net/Eterlove/article/details/122398896
Recomendado
Clasificación