Previously omitted a variable in Ucos multi-task switching,
OSCTXSWCTR Identification System task switching times
It should be used primarily in debugging functions.
The Ucos system initialization function is Osinit (), which mainly accomplishes the following functions
Global variable Initialization
Ready Task Table Initialization
Empty task control block initialization
Event control block List initialization
Semaphore Set Initialization
Memory Management Initialization
QS Queue Control Initialization
System Idle Task Initialization
System Statistics Task Initialization
Some functions need to rely on macro definition open in addition to note that a variable ostaskctr identifies the total number of system tasks, after the initialization is complete, you can create the task, and after the creation of the task is completed, the system starts using the Osstart function, the code is as follows
void Osstart (void)
{
if (osrunning = = Os_false) {
Os_schednew ();
Ospriocur = Ospriohighrdy;
Ostcbhighrdy = Ostcbpriotbl[ospriohighrdy];
Ostcbcur = Ostcbhighrdy;
Osstarthighrdy ();
}
}
The start stage does not call Os_schednew directly, because at this time the system is no task in the execution, there is no interruption lock, so it is important to note that before the system starts, it is best to leave only the system interruption, start and then open the interrupt, otherwise it may cause problems
Then still get the priority, get the TCB, and finally call the Osstarthighrdy function according to the current highest priority task, the function is a function that requires assembly language porting, in Os_cpu_a.asm, the code is as follows
Osstarthighrdy
LDR R4, =nvic_syspri2; Set the PENDSV exception priority
LDR R5, =nvic_pendsv_pri
STR R5, [R4]
MOV R4, #0; Set the PSP to 0 for initial context switch call
MSR PSP, R4
LDR R4, =osrunning; osrunning = TRUE
MOV R5, #1
STRB R5, [R4]
; Switch to the highest priority task
LDR R4, =nvic_int_ctrl; rigger the PENDSV exception (causes context switch)
LDR R5, =nvic_pendsvset
STR R5, [R4]
Cpsie I; Enable interrupts at processor level
Osstarthang
B Osstarthang; should never get here dead loop
The stack is zeroed, then osrunning is set to true, and finally the interrupt is triggered because the previous os_schednew has been given the highest priority task, so after the interrupt is triggered, the interrupt can be switched directly to the highest priority task we want to switch, enabling the system to start
Also need to note that if the use of statistical tasks, then the system must start the statistical task, otherwise open the macro does not initialize the statistical task will be in a certain exception
About System Critical segments
In the system running process, sometimes a piece of code can not be interrupted, and the time of interruption is often more random, in order to solve this problem, the concept of critical section, Ucos general use three ways to deal with the critical section
- How the switch is interrupted
- By saving the program status Word and closing the interrupt mode
- Save the program status Word to the CPU_SR variable
The first method is simple and rough, the second method is a little more complicated, but it allows the processor to interrupt the token before and after the interrupt, but the third way the state is pressed into the stack uses local variables to hold the interrupt state word, without using the stack, more flexible
When porting, you can choose one of the options, generally choosing a third way, such as the following
Os_cpu_sr_save
MRS R0, Primask; read Primask to R0,R0 for return value
Cpsid I; Primask=1, off interrupt (NMI and hardware fault can respond)
BX LR; back
Os_cpu_sr_restore
MSR Primask, R0; read R0 to Primask, R0 as parameter
BX LR; back
#if Os_critical_method = = 3
#define Os_enter_critical () {cpu_sr = Os_cpu_sr_save ();}
#define Os_exit_critical () {os_cpu_sr_restore (CPU_SR);}
#endif
Ucos Clock
Ucos in order to deal with waiting, delay and other time-related time, the introduction of a periodic signal, that is, the Ucos clock, the clock depends on the hardware environment, we need to determine the timing of the hardware processor, with the relevant macro has a, as follows
OS_TICKS_PER_SEC, the macro defines the number of interrupts in the system class 1s, for example, we define the macro as 1000, then we need to ensure that the system interrupts every 1ms, and call the following code in the interrupt handler
Osintenter (); Enter interrupt
Ostimetick (); Call Ucos's Clock service program
Osintexit (); Trigger Task Toggle Soft Interrupt
Osintenter and Osintexit have said before, look at the composition of Ostimetick, as follows
while (Ptcb->ostcbprio! = Os_task_idle_prio) {
Os_enter_critical ();
if (ptcb->ostcbdly! = 0u) {
ptcb->ostcbdly--;
if (ptcb->ostcbdly = = 0u) {
if ((Ptcb->ostcbstat & os_stat_pend_any)! = Os_stat_rdy) {
Ptcb->ostcbstat &= (int8u) ~ (int8u) Os_stat_pend_any;
Ptcb->ostcbstatpend = os_stat_pend_to;
} else {
Ptcb->ostcbstatpend = OS_STAT_PEND_OK;
}
if ((Ptcb->ostcbstat & os_stat_suspend) = = Os_stat_rdy) {
Osrdygrp |= ptcb->ostcbbity;
OSRDYTBL[PTCB->OSTCBY] |= ptcb->ostcbbitx;
}
}
}
PTCB = ptcb->ostcbnext;
Os_exit_critical ();
}
As you can see, in this function, the system iterates over the entire TCB control structure, decrements the ostcbdly element in each element, and when the delay time of a task control block is ostcbdly to 0, the state of the task is checked for the suspended state, if not the suspended state, To modify the system Readiness table to set the current task to Reday, waiting for the system to perform task scheduling, which is the last sentence in the previous three sentences osintexit () completed, which is why the design of a break in the function of the task to switch the reason, This ensures that a task does not release the processor at the time of the machine and can switch to the task immediately when a higher-priority task occurs.
In order not to allow the highest priority task in the system to monopolize the processor, Ucos designed a delay function for the high priority task to actively release the CPU ownership, in the actual embedded system, this release is also very common, such as waiting for the device response, human eye vision residue, and related to the important function is
ostimedly parameter is the number of delay beats
OSTIMEDLYHMSM long time delay, the parameters are delay hours, seconds, minutes, milliseconds, respectively
Ostimedlyresume delay of a task that cancels a specific priority
Ostimeget get current system beats
Ostimeset setting current system beats
Basically, we just have to focus on ostimedly and Ostimedlyresume, first look at ostimedly.
if (osintnesting > 0u) {
Return
}
if (oslocknesting > 0u) {
Return
}
if (Ticks > 0u) {
Os_enter_critical ();
y = ostcbcur->ostcby;
Osrdytbl[y] &= (Os_prio) ~ostcbcur->ostcbbitx;
if (osrdytbl[y] = = 0u) {
Osrdygrp &= (Os_prio) ~ostcbcur->ostcbbity;
}
ostcbcur->ostcbdly = ticks;
Os_exit_critical ();
Os_sched ();
When the delay time of setting is greater than 0, the ready flag of the current task in the System Readiness table is canceled first, then the ostcbdly parameter of the system control block is set to the number of Beats set, and finally the system task scheduling function is called to complete the system task scheduling. And the ostcbdly is set up and the previous interrupt processing Ostimetick function is correlated, thus realizing the system delay
The delay function for canceling the task is ostimedlyresume, and the useful code is
PTCB = Ostcbpriotbl[prio];
*******
ptcb->ostcbdly = 0u;
if ((Ptcb->ostcbstat & os_stat_pend_any)! = Os_stat_rdy) {
Ptcb->ostcbstat &= ~os_stat_pend_any;
Ptcb->ostcbstatpend = os_stat_pend_to;
} else {
Ptcb->ostcbstatpend = OS_STAT_PEND_OK;
}
if ((Ptcb->ostcbstat & os_stat_suspend) = = Os_stat_rdy) {
Osrdygrp |= ptcb->ostcbbity;
OSRDYTBL[PTCB->OSTCBY] |= ptcb->ostcbbitx;
Os_exit_critical ();
Os_sched ();
} else {
Os_exit_critical ();
}
First get the task TCB that wants to cancel the delay (based on the priority), then see if the task is suspended, if it is not suspended and the task is ready, set the task Readiness table to be Raday, and call os_sched for task switching, but not that the cancellation will run. It depends on the priority of the task.
Here we can say that the scheduling time of the task is the time when the system is interrupted and the system calls the time delay function. (There are other time delays later).
Ucos system initialization and startup process