Now let's take a closer look at how the entire lighting method is implemented.
I. hardware initialization.
In this case, the GPIO is initialized.
LED_GPIO_Config (); // initialize the LED Port
The specific configuration is not detailed.
Ii. operating system initialization
OSInit();
Next, let's take a look at what the mask has done:
void OSInit (void){ OSInitHookBegin(); /* Call port specific initialization code */ OS_InitMisc(); /* Initialize miscellaneous variables */ OS_InitRdyList(); /* Initialize the Ready List */ OS_InitTCBList(); /* Initialize the free list of OS_TCBs */ OS_InitEventList(); /* Initialize the free list of OS_EVENTs */ OS_InitTaskIdle(); /* Create the Idle Task */ OS_InitTaskStat(); /* Create the Statistic Task */ OSInitHookEnd(); /* Call port specific init. code */}
The above is the simplified code for removing some macro switches:
1. The first is the hook function. Next I will talk about this hook function in combination with the official documentation and my own understanding.
I understand that for our beginners, hook functions do not need to be taken care of. They directly turn off the macro switch because of the extended functions of the operating system.
This hook function is used to expand the functions of the operating system. The hook function is added
Prevent us from directly modifying the source code. This damages the source code!
That is to say, we can comment out the two hook functions directly. Try it by yourself. I have tried it!
2. The OS _InitMisc () is followed. The comments are used to initialize various variables. Let's take a look at the Code as follows:
static void OS_InitMisc (void){#if OS_TIME_GET_SET_EN > 0 OSTime = 0L; /* Clear the 32-bit system clock */#endif OSIntNesting = 0; /* Clear the interrupt nesting counter */ OSLockNesting = 0; /* Clear the scheduling lock counter */ OSTaskCtr = 0; /* Clear the number of tasks */ OSRunning = OS_FALSE; /* Indicate that multitasking not started */ OSCtxSwCtr = 0; /* Clear the context switch counter */ OSIdleCtr = 0L; /* Clear the 32-bit idle counter */#if OS_TASK_STAT_EN > 0 OSIdleCtrRun = 0L; OSIdleCtrMax = 0L; OSStatRdy = OS_FALSE; /* Statistic task is not ready */#endif}
From the above code, we can obtain the following information:
These lines of code are used to clear the initial values of the system timer, the interrupt counter, and the number of current tasks.
3. OS _InitRdyList indicates the initialization ready state list.
In this case, the sequence table refers to the ready state task of the task in the three States. For details about the three states, refer to Baidu!
So we can follow up to see what the code has done,
static void OS_InitRdyList (void){ INT8U i;#if OS_LOWEST_PRIO <= 63 INT8U *prdytbl;#else INT16U *prdytbl;#endif OSRdyGrp = 0; /* Clear the ready list */ prdytbl = &OSRdyTbl[0]; for (i = 0; i < OS_RDY_TBL_SIZE; i++) { *prdytbl++ = 0; } OSPrioCur = 0; OSPrioHighRdy = 0; OSTCBHighRdy = (OS_TCB *)0; OSTCBCur = (OS_TCB *)0;}
Again, we can get the following information from the code:
Clears the priority of the current task, the highest priority of the ready state task, and the control block.
4. Next, let's look at an important function. OS _InitTCBList
static void OS_InitTCBList (void){ INT8U i; OS_TCB *ptcb1; OS_TCB *ptcb2; OS_MemClr((INT8U *)&OSTCBTbl[0], sizeof(OSTCBTbl)); /* Clear all the TCBs */ OS_MemClr((INT8U *)&OSTCBPrioTbl[0], sizeof(OSTCBPrioTbl)); /* Clear the priority table */ ptcb1 = &OSTCBTbl[0]; ptcb2 = &OSTCBTbl[1]; for (i = 0; i < (OS_MAX_TASKS + OS_N_SYS_TASKS - 1); i++) { /* Init. list of free TCBs */ ptcb1->OSTCBNext = ptcb2;#if OS_TASK_NAME_SIZE > 1 ptcb1->OSTCBTaskName[0] = '?'; /* Unknown name */ ptcb1->OSTCBTaskName[1] = OS_ASCII_NUL;#endif ptcb1++; ptcb2++; } ptcb1->OSTCBNext = (OS_TCB *)0; /* Last OS_TCB */#if OS_TASK_NAME_SIZE > 1 ptcb1->OSTCBTaskName[0] = '?'; /* Unknown name */ ptcb1->OSTCBTaskName[1] = OS_ASCII_NUL;#endif OSTCBList = (OS_TCB *)0; /* TCB lists initializations */ OSTCBFreeList = &OSTCBTbl[0];}
The code here involves some complicated data structures, which are not detailed at the moment. Find a time for special research, which is to complete the initialization of some linked lists.
5. The OS _InitEventList function is similar to step 1.
At this point, I have probably understood what has been done in initialization, and the next step is this
Iii. Task creation code
OSTaskCreate(Task_LED,(void *)0, &startup_task_stk[STARTUP_TASK_STK_SIZE-1], STARTUP_TASK_PRIO);
Let's take a good look at how task creation works.
1. Analysis of function prototype
INT8U OSTaskCreate (void (*task)(void *p_arg), void *p_arg, OS_STK *ptos, INT8U prio)
The above is the function prototype.
The first parameter is the name of the task to be created. The parameter is the pointer function pointer, so when we assign a value, we can directly assign a function name to it.
The second parameter is the parameter passed by the task. A void pointer. The value assignment can be null.
Third parameter: stack space allocated for the task. The stack address is passed, so we need to allocate an address to the stack in advance, and then pass the address in
The fourth parameter is the priority of the task we created.
OK function prototype description completed
2. Follow the task creation function to see what has been done?
INT8U OSTaskCreate (void (*task)(void *p_arg), void *p_arg, OS_STK *ptos, INT8U prio){ OS_STK *psp; INT8U err;#if OS_CRITICAL_METHOD == 3 /* Allocate storage for CPU status register */ OS_CPU_SR cpu_sr = 0;#endif#if OS_ARG_CHK_EN > 0 if (prio > OS_LOWEST_PRIO) { /* Make sure priority is within allowable range */ return (OS_ERR_PRIO_INVALID); }#endif OS_ENTER_CRITICAL(); if (OSIntNesting > 0) { /* Make sure we don't create the task from within an ISR */ OS_EXIT_CRITICAL(); return (OS_ERR_TASK_CREATE_ISR); } if (OSTCBPrioTbl[prio] == (OS_TCB *)0) { /* Make sure task doesn't already exist at this priority */ OSTCBPrioTbl[prio] = OS_TCB_RESERVED;/* Reserve the priority to prevent others from doing ... */ /* ... the same thing until task is created. */ OS_EXIT_CRITICAL(); psp = OSTaskStkInit(task, p_arg, ptos, 0); /* Initialize the task's stack */ err = OS_TCBInit(prio, psp, (OS_STK *)0, 0, 0, (void *)0, 0); if (err == OS_ERR_NONE) { if (OSRunning == OS_TRUE) { /* Find highest priority task if multitasking has started */ OS_Sched(); } } else { OS_ENTER_CRITICAL(); OSTCBPrioTbl[prio] = (OS_TCB *)0;/* Make this priority available to others */ OS_EXIT_CRITICAL(); } return (err); } OS_EXIT_CRITICAL(); return (OS_ERR_PRIO_EXIST);}
You can see that you have done something.
1. Clear the Status Register and check whether the priority is valid.
2. OS _ENTER_CRITICAL (); indicates that the code below the CPU cannot be interrupted. That is to say, after entering the critical section, the interrupt is disabled in this function. This is mentioned in the previous blog.
3. The code below is to ensure that there is no task created above the defined priority. If this task is not created, we will mark it as a task.
4.
psp = OSTaskStkInit(task, p_arg, ptos, 0); /* Initialize the task's stack */ err = OS_TCBInit(prio, psp, (OS_STK *)0, 0, 0, (void *)0, 0);
Initialize a function of the task stack. Analyze it carefully.
OS_STK *OSTaskStkInit (void (*task)(void *p_arg), void *p_arg, OS_STK *ptos, INT16U opt){ OS_STK *stk; (void)opt; /* 'opt' is not used, prevent warning */ stk = ptos; /* Load stack pointer */ /* Registers stacked as if auto-saved on exception */ *(stk) = (INT32U)0x01000000L; /* xPSR */ *(--stk) = (INT32U)task; /* Entry Point */ *(--stk) = (INT32U)0xFFFFFFFEL; /* R14 (LR) (init value will cause fault if ever used)*/ *(--stk) = (INT32U)0x12121212L; /* R12 */ *(--stk) = (INT32U)0x03030303L; /* R3 */ *(--stk) = (INT32U)0x02020202L; /* R2 */ *(--stk) = (INT32U)0x01010101L; /* R1 */ *(--stk) = (INT32U)p_arg; /* R0 : argument */ /* Remaining registers saved on process stack */ *(--stk) = (INT32U)0x11111111L; /* R11 */ *(--stk) = (INT32U)0x10101010L; /* R10 */ *(--stk) = (INT32U)0x09090909L; /* R9 */ *(--stk) = (INT32U)0x08080808L; /* R8 */ *(--stk) = (INT32U)0x07070707L; /* R7 */ *(--stk) = (INT32U)0x06060606L; /* R6 */ *(--stk) = (INT32U)0x05050505L; /* R5 */ *(--stk) = (INT32U)0x04040404L; /* R4 */ return (stk);}
Compare the above Code with the official comments to explain it
A is a stack initialization function, simulating the inbound stack operation.
The growth direction of stack B is downward, and the top address of stack is returned.
C each register into the stack order is determined, because our above code is to simulate R0-R12 + spSR these registers into the stack, which is based on the authoritative manual above to simulate
For more information, see the authoritative manual.
D. This function does not need to return values, so LR (R14) is an invalid value.
E xPSR Status Register should be set to thread mode, T location 1
Another important function is to initialize the function of the task control block. It passes in the previous parameters such as the stack top pointer and does not detail them.
Once the OSTaskStkInit () function completes the stack creation task, OSTaskCreate () calls OSTCBInit () to obtain and initialize an OS _TCB from the idle OS _TCB pool.
If the OS _TCB pool has an idle OS _TCB, It is initialized. Note that once the OS _TCB is assigned, the task creator has it, even if the kernel is created again
These new tasks cannot perform any operation on the assigned OS _TCB. Therefore, OSTCBInit () allows interruption and continues to initialize the data unit of OS _TCB.
Now that the task is created, let's start running the task.
Iv. OSStart code analysis
void OSStart (void){ if (OSRunning == OS_FALSE) { OS_SchedNew(); /* Find highest priority's task priority number */ OSPrioCur = OSPrioHighRdy; OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy]; /* Point to highest priority task ready to run */ OSTCBCur = OSTCBHighRdy; OSStartHighRdy(); /* Execute target specific code to start task */ }}
Note that when no task is running, a high-priority task is selected for execution, and the single task system is implemented, the next step is to see how to implement the first task system.