Crear el análisis del código fuente de la primera tarea

Después de las operaciones anteriores, podemos iniciar la primera tarea. La función prvStartFirstTask() se utiliza para iniciar la primera tarea. Esta es una función de ensamblaje. El código fuente de la función es el siguiente:

__asm void prvStartFirstTask( void )
{
PRESERVE8
ldr r0, =0xE000ED08 ;R0=0XE000ED08 (1)
ldr r0, [r0] ;取 R0 所保存的地址处的值赋给 R0 (2)
ldr r0, [r0] ;获取 MSP 初始值 (3)
msr msp, r0 ;复位 MSP (4)
cpsie I ;使能中断(清除 PRIMASK) (5)
cpsie f ;使能中断(清除 FAULTMASK) (6)
dsb ;数据同步屏障 (7)
isb ;指令同步屏障 (8)
svc 0 ;触发 SVC 中断(异常) (9)
nop
nop
}

(1) Guarde 0XE000ED08 en el registro R0. En términos generales, la tabla de vectores debe almacenarse desde la dirección de inicio (0X00000000). Sin embargo, algunas aplicaciones pueden necesitar modificar o redefinir la tabla de vectores en tiempo de ejecución. El procesador Cortex-M proporciona una función llamada reubicación de la tabla de vectores para este propósito. . La función de reubicación de la tabla de vectores proporciona un registro programable denominado registro de desplazamiento de la tabla de vectores (VTOR). La dirección del registro VTOR es 0XE000ED08. La tabla de vectores se puede redefinir a través de este registro. Por ejemplo, en la biblioteca oficial ST de STM32F103, el registro VTOR se establecerá a través de la función SystemInit(). El código es el siguiente: SCB ->VTOR = FLASH_BASE | VECT_TAB_OFFSET; //VTOR =0x08000000+0X00 A través de la línea de código anterior, la dirección de inicio de la tabla de vectores se redefine a 0X08000000, y la dirección de inicio de la tabla de vectores almacena el valor inicial de MSP.

(2) Lea los datos en la dirección almacenada en R0 y guárdelos en el registro R0, es decir, lea el valor en el registro VTOR y guárdelo en el registro R0. Después de ejecutar esta línea de código, el valor de R0 debe ser 0X08000000 . Esta es la dirección de partida de la redefinición de la tabla de vectores.

(3) Lea los datos en la dirección almacenada en R0 y guárdelos en el registro R0, es decir, lea los datos almacenados en la dirección 0X08000000 y guárdelos en el registro R0 (porque el valor de MSP se guarda en el primero de variable) . Sabemos que la dirección inicial de la tabla de vectores almacena el valor inicial del puntero de la pila principal MSP Después de ejecutar esta línea de código, el registro R0 almacena el valor inicial de MSP. Ahora veamos (1), (2), (3) ¡estos tres pasos son solo para obtener el valor inicial de MSP!

(4) Restablecer MSP, el valor inicial de MSP se guarda en R0, asignarlo a MSP equivale a restablecer MSP.

(5) y (6), habilitar interrupción, para obtener detalles sobre estas dos instrucciones, consulte la Sección 4.2.3 del "Capítulo 4 Arquitectura" en "La guía definitiva".

(7) y (8), sincronización de datos y barreras de sincronización de instrucciones, para obtener detalles de estas dos instrucciones, consulte la Sección 5.6.13 del "Capítulo 5 Conjunto de instrucciones" en "La guía definitiva".

(9) Llame a la instrucción SVC para activar la interrupción SVC , SVC también se denomina llamada de gestión de solicitudes, SVC y la excepción PendSV son muy importantes para el diseño del sistema operativo. Las excepciones SVC son desencadenadas por la instrucción SVC. Para obtener detalles sobre SVC, consulte la Sección 10.3 del "Capítulo 10 Funciones de soporte del sistema operativo" en "La guía definitiva". En FreeRTOS, solo se usa la excepción SVC para iniciar la primera tarea y SVC ya no se usa en programas posteriores.

8.2.4 Función de servicio de interrupción SVC

La interrupción de SVC se activa llamando a la instrucción SVC en la función prvStartFirstTask(), y el inicio de la primera tarea se completa en la función de servicio de interrupción de SVC. La función de servicio de interrupción de SVC debe ser SVC_Handler(), pero en FreeRTOSConfig.h hasta #define La forma de redefinir xPortPendSVHandler() es la siguiente:

#define xPortPendSVHandler PendSV_Handler La función vPortSVCHandler() está definida en el archivo port.c. Esta función también está escrita en ensamblador. El código fuente de la función es el siguiente:

__asm void vPortSVCHandler( void )
{
PRESERVE8
ldr r3, =pxCurrentTCB ;R3=pxCurrentTCB 的地址 (1)
ldr r1, [r3] ;取 R3 所保存的地址处的值赋给 R1 (2)
ldr r0, [r1] ;取 R1 所保存的地址处的值赋给 R0 (3)
ldmia r0!, {r4-r11, r14} ;出栈 ,R4~R11 和 R14 (4)
msr psp, r0 ;进程栈指针 PSP 设置为任务的堆栈 (5)
isb ;指令同步屏障
mov r0, #0 ;R0=0 (6)
msr basepri, r0 ;寄存器 basepri=0,开启中断 (7)
orr r14, #0xd ; (8)
bx r14 (9)
}

(1) Obtenga la dirección de almacenamiento del puntero pxCurrentTCB, pxCurrentTCB es un puntero a TCB_t, este puntero siempre apunta a la tarea en ejecución. Aquí primero obtenga la dirección donde se almacena el puntero

(2) Obtenga la dirección del primer miembro a través de la dirección del bloque de control de tareas

(3) Obtenga el valor del primer miembro a través de la dirección del primer miembro, y el valor del primer miembro es el valor en la parte superior de la pila cuando se crea la función. Este valor es la dirección de la parte superior de la pila en este momento.

 (4), R4~R11, R14 Estos registros se extraen de la pila. La instrucción LDMIA se usa aquí. La instrucción LDMIA es una instrucción de almacenamiento/carga múltiple, pero aquí hay una instrucción de acceso de almacenamiento/carga múltiple con reescritura. El uso es el siguiente: LDMIA Rn!, {lista de registros} significa lectura desde la ubicación de memoria especificada por Rn Se obtienen varias palabras, la dirección se incrementa (IA) después de cada lectura y Rn se vuelve a escribir después de que se completa la transferencia. Para STM32, la dirección aumenta en 4 bytes a la vez, como el siguiente código: LDR R0, =0X800 LDMIA R0!, {R2~R4} Las dos líneas de código anteriores son para asignar los datos en la dirección 0X800 al registro R2 , y asigne los datos de la dirección 0X804 al registro R3, asigne los datos de la dirección 0X8008 al registro R4, y luego, ¡viene el punto importante! ¡En este momento R0 es 800A! A través de este paso, restauramos los valores de los registros R4~R11 de la pila de tareas. Algunos amigos aquí preguntarán, ¿por qué no se restauran los registros R0~R3, R12, PC y xPSR? Esto se debe a que la MCU extraerá (restaurará) automáticamente estos registros al salir de la interrupción, mientras que el usuario debe abrir manualmente R4~R11. Hablaremos de esto cuando analicemos la función de servicio de interrupción de PendSV. Después de este paso, echemos un vistazo a dónde apunta el puntero superior de la pila. Como se muestra en la Figura 8.2.4.5:

 En la Figura 8.3.4.5 se puede ver que después de restaurar R4~R11 y R14, el puntero superior de la pila debe apuntar a la dirección 0X20000EB8, que es la dirección de almacenamiento para guardar el valor del registro R0. Después de salir de la función de servicio de interrupción, el puntero de la pila de procesos PSP debería restaurar otros valores de registro desde esta dirección.

(5) Establezca el puntero de pila de procesos PSP, PSP=R0=0X20000EB8, como se muestra en la Figura 8.2.4.6

(6) Establezca el registro R0 en 0.

(7) Establezca el registro BASEPRI en R0, que es 0, ¡y habilite la interrupción!

(8) El valor del registro R14 se combina con 0X0D y el resultado es el nuevo valor del registro R14. ¡Significa que la CPU ingresa al modo de subprocesos y usa la pila de procesos después de salir de la excepción!

 (9) Después de ejecutar esta línea de código, el hardware restaura automáticamente los valores de los registros R0~R3, R12, LR, PC y xPSR, la pila usa la pila de proceso PSP y luego ejecuta la función de tarea guardada en el registrar PC. ¡Hasta ahora, el programador de tareas de FreeRTOS ha comenzado a ejecutarse oficialmente!

Supongo que te gusta

Origin blog.csdn.net/qq_51519091/article/details/131627449
Recomendado
Clasificación