


Operation Video
3>Set up the Logic Analyzer: In the logic analyzer window, click the “Setup” button. In the pop-up dialog, enter the port you want to observe, for example, PORTA.0 (indicating the 0th pin of GPIOA) and ensure “Display Type” is set to “Bit”. If needed, set the “Shift right” value to specify the specific pin number.
Benchmark Code
void SystemClock_Config(void);/* USER CODE BEGIN PFP */
/* USER CODE BEGIN PFP */#include <stdio.h>#include <string.h>
void SysTick_Disable() { // Disable SysTick timer SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;}
void SysTick_Enable() { // Enable SysTick timer SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk;}
#if 0//IARint putchar(int ch){ HAL_UART_Transmit(&huart2 , (uint8_t *)&ch, 1 , 0xffff); while (huart2.gState != HAL_UART_STATE_READY);//end of transmission return ch;}
#elseint fputc( int ch, FILE *f ) { HAL_UART_Transmit(&huart2 , (uint8_t *)&ch, 1 , 0xffff); while (huart2.gState != HAL_UART_STATE_READY);//end of transmission return ch;}
#endifint TASK1DATA = 9;int TASK2DATA = 6;void TaskSwitch(void);typedef void (*TaskFunction_t)( void * );static void taskfun1(void *A){ int* data=(int*)A; while(1){ HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_RESET); };}static void taskfun2(void *A){ int* data=(int*)A; while(1){ HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_SET); };}//TCBtypedef struct tskTaskControlBlock{ uint32_t *pxTopOfStack; /*< Points to the location of the last item placed on the tasks stack. THIS MUST BE THE FIRST MEMBER OF THE TCB STRUCT. */ uint32_t *pxStack; /*< Points to the start of the stack. */} tskTCB;typedef tskTCB TCB_t;TCB_t TCB[2]={0};//Point to the current working TCBTCB_t * volatile pxCurrentTCB = NULL;//Array#define ARRLEN 512uint32_t arrForStack[ARRLEN]={0};uint32_t arrForStack2[ARRLEN]={0};//Initialize a stackstatic void prvTaskExitError( void ){ for( ;; );}/*******************************************************************************/uint32_t *pxPortInitialiseStack( uint32_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters ){ /* Simulate the stack frame as it would be created by a context switch interrupt. */ pxTopOfStack--; /* Offset added to account for the way the MCU uses the stack on entry/exit of interrupts. */ *pxTopOfStack = 0x01000000; /* xPSR *///https://blog.csdn.net/k666499436/article/details/127072835 α??xPSP????? pxTopOfStack--; *pxTopOfStack = ( ( uint32_t ) pxCode ) & 0xfffffffeUL; /* PC *///α??PC????? https://blog.csdn.net/k666499436/article/details/127072835 pxTopOfStack--; *pxTopOfStack = ( uint32_t ) prvTaskExitError; /* LR */ pxTopOfStack -= 5; /* R12, R3, R2 and R1. */ *pxTopOfStack = ( uint32_t ) pvParameters; /* R0 */ pxTopOfStack -= 8; /* R11, R10, R9, R8, R7, R6, R5 and R4. */ return pxTopOfStack;}void prvInitialiseNewTask(const uint32_t ulStackDepth,TCB_t *pxNewTCB){ uint32_t *pxTopOfStack; //portSTACK_GROWTH < 0 pxTopOfStack = pxNewTCB->pxStack + ( ulStackDepth - ( uint32_t ) 1 ); printf("%p\r\n",pxTopOfStack);//[This is the end of the array, the address of the last member is 0X20000084, which is arrForStack[23]] pxTopOfStack = ( uint32_t * ) ( ( ( uint32_t ) pxTopOfStack ) & ( ~( ( uint32_t ) 0x0007 ) ) ); printf("%p\r\n",pxTopOfStack);//[After the above, ensure this address ends in 000 which is 8, so the address is 0X20000080, which is arrForStack[22]] pxNewTCB->pxTopOfStack = pxTopOfStack;}void xTaskCreateStatic(void){ uint8_t index=0; TCB[index].pxStack=arrForStack; prvInitialiseNewTask(ARRLEN,&TCB[index]); TCB[index].pxTopOfStack=pxPortInitialiseStack(TCB[index].pxTopOfStack,taskfun1,&TASK1DATA); pxCurrentTCB = &TCB[index]; index=1; TCB[index].pxStack=arrForStack2; prvInitialiseNewTask(ARRLEN,&TCB[index]); TCB[index].pxTopOfStack=pxPortInitialiseStack(TCB[index].pxTopOfStack,taskfun2,&TASK2DATA);}/*******************************************************************************/__asm void SVC_Handler( void ){//it.c Please remove the same name function, after execution jump to task 1 extern pxCurrentTCB; PRESERVE8 ldr r3, =pxCurrentTCB /* Restore the context. */ ldr r1, [r3] /* Use pxCurrentTCBConst to get the pxCurrentTCB address. */ ldr r0, [r1] /* The first item in pxCurrentTCB is the task top of stack. */ ldmia r0!, {r4-r11} /* Pop the registers that are not automatically saved on exception entry and the critical nesting count. */ msr psp, r0 /* Restore the task stack pointer. */ isb mov r0, #0 msr basepri, r0 orr r14, #0xd bx r14}
__asm void prvStartFirstTask( void ){//Manually trigger SVC interrupt to automatically jump to function SVC_Handler PRESERVE8
/* Use the NVIC offset register to locate the stack. */ ldr r0, =0xE000ED08 ldr r0, [r0] ldr r0, [r0]
/* Set the msp back to the start of the stack. */ msr msp, r0 /* Globally enable interrupts. */ cpsie i cpsie f dsb isb /* Call SVC to start the first task. */ svc 0 nop nop}
void xPortStartScheduler(void){ HAL_NVIC_SetPriority(SysTick_IRQn, 0X0F, 0X00); HAL_NVIC_SetPriority(PendSV_IRQn, 0X0F, 0X00); SysTick_Enable(); prvStartFirstTask();}
void setupMyOs(void){ xTaskCreateStatic();//Create two task stacks xPortStartScheduler();//On power-up, the original plan is to go to main and then to the while loop, now intercept before the while loop to go to the agreed task stack 1
}/*********************************************************************/
/*******************************Timing**********************************/
//Enable mask interrupts#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5#define configMAX_SYSCALL_INTERRUPT_PRIORITY ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << 4 )
//Manually trigger PendSV interrupt to automatically jump to function PendSV_Handlervoid xPortSysTickHandler( void ){ __set_BASEPRI(0X50);//Enable mask interrupts ( * ( ( volatile uint32_t * ) 0xE000ED04 ) ) = ( 1UL << 28UL ); __set_BASEPRI(0X00);//Disable mask interrupts}
void vTaskSwitchContext( void );__asm void PendSV_Handler( void ){//it.c Please remove the same name function extern pxCurrentTCB; extern vTaskSwitchContext;
PRESERVE8
mrs r0, psp isb
ldr r3, =pxCurrentTCB /* Get the location of the current TCB. */ ldr r2, [r3]
stmdb r0!, {r4-r11} /* Save the remaining registers. */ str r0, [r2] /* Save the new top of stack into the first member of the TCB. */
stmdb sp!, {r3, r14} mov r0, #configMAX_SYSCALL_INTERRUPT_PRIORITY msr basepri, r0 dsb isb bl vTaskSwitchContext mov r0, #0 msr basepri, r0 ldmia sp!, {r3, r14}
ldr r1, [r3] ldr r0, [r1] /* The first item in pxCurrentTCB is the task top of stack. */ ldmia r0!, {r4-r11} /* Pop the registers and the critical nesting count. */ msr psp, r0 isb bx r14 nop}
void TaskSwitch(void){ xPortSysTickHandler();}
uint8_t volatile index = 0;void vTaskSwitchContext( void ){ //static uint8_t index=0; pxCurrentTCB = &TCB[index]; index = (index+1)%2;//There are a total of 2 tasks, so it's 0 1 0 1 loop}
Other Code
void SysTick_Handler(void){ /* USER CODE BEGIN SysTick_IRQn 0 */extern void TaskSwitch(void); /* USER CODE END SysTick_IRQn 0 */ HAL_IncTick(); /* USER CODE BEGIN SysTick_IRQn 1 */TaskSwitch(); /* USER CODE END SysTick_IRQn 1 */}
SysTick_Disable();setupMyOs();//SysTick_Enable();//Note: The statements after setupMyOs cannot be executed! So this statement needs to be placed in front.