Table of contents:
Introduction 1. Specifications 2. Fundamental concepts
2.1. Generalities on multitasking 2.2. Stacks
2 2 3
3 3
4
4 4
7 9
9 10 10 11
6. Interrupt handling
6.1. Some useful registers 6.2. Context save
12
12 13
16
16 16
8. Example of use
8.1. Simple program 8.2. Use programs in multitasking
21
21 22
22 23
Introduction:
The project which is presented in this report is a description of a multitasking environment in an AT91SAM7X microcontroller. The main concepts of multitasking structure are task scheduling and stack management. This structure has to manage tasks in order to execute them together. The presentation is not only aimed at users, who want to use the multitasking application, but also contains the description of how the system and functions are build.
1. Specifications:
The task number that multitasking handle is to be defined by the user The use of a real time clock not required This project is aimed at providing a tool for second-year students to be used during practical work. They will therefore be able to compare multitasking and interrupts.
2. Fundamental concepts:
In a first part some fundamental concepts: how multitasking works, what a scheduler and a context are and how a stack works, are explained.
2.2. Stacks:
Let us move onto how a stack works. On figure 2, there is a stack (in the middle) and some data blocks on the left (a blue one, a red one a green one). To be stored in the stack, the data blocks are piled up in the stack, and to load the data blocks, they are removed backwards. The arrows identify two levels called the stack top (also called the stack pointer) and the stack bottom.
When the microprocessor is taken, multitasking was not available by default. So stacks must be created in order to store the context of each task.
stack_limit
The limit address delineating the STACK_SIZE bytes allocated to this task ;
In figure 4, you can see a diagram summarizing the links between these two tables.
The next part deals with a another task structure parameter: state. In a common working of multitasking, each task goes through different states depending on its life cycle. That is why there are four states:
UNUSED
(STARTING)
ACTIVE SLEEPING
When the task is not used. The first case is if the scheduler is not running and the task not launched. The second case is if the scheduler is running but the task not launched or over ; This state is important because of the interrupt working. It will be detailed why below. For the moment, just note that this state is not visible from the user point of view. It is just a transitional state between the UNUSED state and the ACTIVE state ; When the task is initialized, ready and running. It is the state following the UNUSED state or alternately with the SLEEPING state ; When the execution of the task must be stopped during a moment (to wait for data for instance). Typically, it is the user who sparks off this state.
In order to know continuously the state of each task, the task state are store in four tables: unused[], starting[], active[] and sleeping[]. One more time, these tables or all composed by N squares. We just deal with the first n squares, the other ones being unused. The working of every table is similar. Lets take the example of unused[] : the first square corresponds to task number zero, the second square to task number one... A 1 is stored in the first square if the task number zero is in an unused state. A 0 is stored if it is not. Figure 5 shows an example of this situation.
NB_TASK
used tasks
unused[]
1 0 0 0
0 1 0 0
0 1 0 0
0 0 1 0
0 1 0 0
0 0 0 1
1 0 0 0
0 1 0 0
1 0 0 0
1 0
starting[]
active[]
0 0 -
sleeping[]
Tasks 0 and 6 are unused (not launched or over) Tasks 8 are 9 are unused (not used by the user) Tasks 1, 2, 4 and 7 are starting Task 3 is active Task 5 is sleeping
4. User part:
This part is about the user space, that is to say all the functions he can execute in order to run this program.
int Init_Struct ()
This function initializes the data structure to implement N parallel tasks. Then, it is up to the user to use fewer tasks if he wants to. Each structure of the tasks table is initialized in the same way. Lets take the standard example of task number i: we put i in num, UNUSED in state, the name of the associated C function in add_fct and the corresponding addresses of tasks_RAM in stack_bottom, stack_top and stack_limit. As for the tables unused[], starting[], active[] and sleeping[], we feel unused[] with ones and the other tables with zeros. 0 E_RUNNING_SCHED
This function allows the user to add a task provided that there are less than N tasks created and that the scheduler is not already running. It just changes the parameter of the next free task_t square of the table tasks by fixing add_fct to the input parameter function. To do that, the data structure must already have been called but the scheduler must not be running. 0 E_RUNNING_SCHED E_NO_SPACE_LEFT E_NO_INIT
function
Create_Task
Input : Output :
The C function associated to this task 0 if successful E_RUNNING_SCHED if the scheduler is already started E_NO_SPACE_LEFT if all the N tasks have already been created E_NO_INIT if the data structure is not yet initialized
This function allows starting a task. Thus, the user can control the moment he wants to start a task and is not obliged to start all the tasks at the scheduler setting up. It replaces the parameter state of the corresponding task_t to STARTING. 0 E_UNINIT_TASK E_RUNNING_TASK
Launch_Task
Input : Output :
The number of the task we want to launch 0 if successful E_UNINIT_TASK if the task is not initialized E_RUNNING_TASK if the task is already launched
int Start_Scheduler ()
This function starts the scheduler. It is from this moment that the launched tasks are executed, so that the program runs. It defines the interrupt function. 0 E_NO_INIT E_NO_FUNCTION
No input 0 if successful E_NO_INIT if the data structure is not initialized E_NO_FUNCTION if no tasks are initialized
This function allows the user to stop the execution of a task by fixing his state to SLEEPING. It changes the parameter state to SLEEPING provided that the task is already running and not sleeping. 0 E_UNINIT_TASK E_SLEEPING_TASK
Scotch_Task
Input : Output :
The number of the task we want to stop 0 if successful E_UNINIT_TASK if the task is not initialized E_SLEEPING_TASK if the task is already sleeping
This function allows the user to wake a sleeping task by changing his state to ACTIVE. The task must be already initialized and sleeping. n Input : Output : Wake_Task 0 E_UNINIT_TASK E_ACTIVE_TASK
The number of the task we want to stop 0 if successful E_UNINIT_TASK if the task is not initialized E_ACTIVE_TASK if the task is already running
5. Interrupt sources:
5.1. Three possibilities of periodic interrupt:
In a multi tasks platform, an interrupt is necessary to plan a schedule for each task. When an interrupt occurs, the scheduler has to change the running task. In this project, a periodic interrupt is necessary; they have to be generated by the controller. In the AT91SAM7X microcontroller, there are 3 kinds of periodic interrupt: The PIT is inside the System Controller, it is used to generate periodic interrupt. This component is designed to offer maximum accuracy and efficient management, even for systems with long response time. The PIT has a 20 bits counter with a clock at MCK/16. Three Timer Counters are external peripherals. Timer Counter can be used to generate interrupt or a special clock. They have a lot of option: to control the input clock and registers values. A Timer Counter has a 12 bits counter with 5 possible input clock (MCK/2, MCK/8, MCK/32, MCK/128 and MCK/1024). The Real-time Timer is built around a 32-bit counter and used to count elapsed seconds. It generates a periodic interrupt. It is inside the System Controller too.
The comparison of this three interrupt generators give us a crucial point: The PIT and the RTT use the System Controller vector. In our case if we use this kind of interrupt, users of the multi tasks will not be allowed to use something in the System Controller, because it will cause confrontation between each declaration. Regarding this, we focused on the first Timer Counter (TC0). When an interrupt from this timer will occur, the scheduler has to change the active task.
10
11
6. Interrupt handling:
6.1. Some useful registers:
In the project, two different stacks are used: the IRQ stack and the Supervisor stack. They only are two stacks among the six existing. On the figure below, the six registers are displayed and the user stacks shows the different register succession.
There are one dedicated program counter ( PC ), one dedicated current program status register ( CPSR ), 5 dedicated saved program registers ( SPSR ) and 30 general purpose registers. However these are arranged into several banks, with the accessible bank being governed by the processor mode. Each mode can access: - a particular set of r0-r12 registers - a particular r13 ( the stack pointer ) and r14 ( link register ) which is used as the subroutine link register and stores the return address when Branch with Link operations are performed. Thus, to return from a branch the instruction below is used: MOV pc , lr. - r15 ( the program counter ). Depending on the details of the particular computer, the PC holds either the address of the instruction being executed, or the address of the next instruction to be executed. - cpsr ( the current program status register) . It contains the current value of the condition code bits ( N, Z, C and V) and 8 system status bits. and privileged modes can also access - a particular spsr ( saved program status register ) which stores the current value of CPSR when an exception occurs, so that it can be restored after handling the exception.
12
There are : Condition Code Flag which are copies from the ALU status flags Mode Bits , M[4:0] define the processor mode Interrupt Disable bits : I=1, disables the IRQ and F=1, disables the FIQ
The next step consists in indicating which interrupt has to be launched. The address of the latter is loaded. The entry point of the interrupt handling is set in r14. Subsequently the interrupt is enabled by switching in Supervisor Mode. Then the scratch/used registers and lr are saved in svc stack. Finally the interrupt function is launched by branching to the address holds by r0.
13
Enables Interrupt and switches in Supervisor Mode Saves registers and LR in SVC stack
Stores pc in r14
Executes r0
The figure below shows the context save sequence. Once the interrupt function ends, the registers are restored from supervisor stack and interrupts disabled. The mode chosen is the IRQ mode. EOICR means End of Interrupt Command Register and is the exit point of the interrupt handling. The AIC has to know when the current interrupt handling is over. This is done by writing in AIC_EOICR. The AIC restores the previous interrupt level if there was an ongoing one. An idle interrupt can then be handled. In our case, this ends the interrupt. All the registers stored in the IRQ stack can be restored: r14, SPSR and r0. The last instruction return to the program running before the interrupt occurred.
Restores registers and LR from SVC Stack
14
SVC MODE
IRQ MODE
INTERRUPT
CPSR
Return address calculated from SVC mode PC value and stored in IRQ mode LR
SPSR_IRQ
CPSR SPSR_IRQ
The figure above sums up the different steps concerning the registers during an interrupt.
However, this whole sequence only handles interrupts but not multitasking. That is why a work had to be done on the stacks.
15
In our case tasks are managed with an interrupt, that is why only two stacks are useful, the IRQ stack and the supervisor stack. In fact when an interrupt occurs the stack is automatically set in the IRQ mode then turn to the supervisor mode as it can be seen in the assembler file.
16
In this figure the two main steps can be seen. When task are starting, the IRQ stack increases. In fact during the scheduler interrupt a task is launched so the end of the interrupt does not occur consequently the IRQ stack does not fulfil. During the second step, tasks are running but the interrupt change the task but this time the end of interrupt occurs so the IRQ stack decreases at the end of the interrupt.
stack pointer
stack pointer
17
stack pointer
stack pointer
These figures show the three steps when a task runs for the first time and scheduler interrupt occurs. The two middle figures show the save of the context. The last one show when the copy of the IRQ stack is performed. Then the stack pointer is changed to the next task stack.
18
stack pointer
The new task can be launched. The same actions occur and the new context is created.
stack pointer
In this example only two tasks has been initialized so the stack pointer is not set to a free task but at the top of the task1 stack. Before launching task1 IRQ stack has to be change, so the task one irq stack is copied into the IRQ stack.
19
stack pointer
Then the task1 is launching. This operation is done at each stack change.
20
8. Example of use:
8.1. Simple program:
Two simple led function:
void led1 ( void ) { unsigned int waiting_time ; int flag =0; int i; while(1) { for(waiting_time = 0; waiting_time < LedSpeed;waiting_time++) ; if(flag == 0) { flag = 1; BASE_PIO_LED->PIO_SODR = LED1 ; } else if(flag == 1) { flag = 0; BASE_PIO_LED->PIO_CODR = LED1; } } } void led2 ( void ) { unsigned int waiting_time ; int flag =0; int i; while(1) { for(waiting_time = 0; waiting_time < LedSpeed;waiting_time++) ; if(flag == 0) { flag = 1; BASE_PIO_LED->PIO_SODR = LED1 ; } else if(flag == 1) { flag = 0; BASE_PIO_LED->PIO_CODR = LED1; } } }
21
int main() { Int task[2]; Init_Struct(2); Task[0] = Create_Task(led1); Task[1] = Create_Task(led2); Launch_Task(task[0]); Launch_Task(task[1]); Start_Scheduler(); // infinite loop for (;;) { } }
Conclusion:
The multitasking environment is now operational, some options have been added since the beginning of the project. It is now possible to finish a task and to start and restart tasks. But some correction must be added: there is a leak of memory in the IRQ stack. Another drawback is that a minimum of one task must be active when an interrupt occurs. The next step to lead an operating system is the management of shared resources (memory, pins ) between tasks. Semaphores can be added to conduct on this way.
22
23
64 // Relunch an existing unused task with an reinitializing a its own stack 65 // Return 0 if succes 66 // errors: 67 // -14 : Task doesn't exist (E_TASK_NO_EXISTINGG) 68 // -15 : Task not in unused state(E_TASK_RUNNING) 69
multitask.c
1 /* 2 filename : multitask.c 3 multitask environnement for AT91SAM7X controller 4 ENSEIRB-MATMECA 5 6 7 operationnal 8 version date who from 9 0.0 2010-11-12 (yb) from scratch 10 0.1 2010-11-19 rd from 0.0 11 0.2 2010-11-21 jsm from 0.1 12 0.3 2010-11-26 jsm from 0.2 13 0.4 2010-11-26 yb from 0.3 14 0.5 2010-12-09 rd from 0.4 15 0.6 2010-12-10 all from 0.5 16 final 2010-01-26 17 */ 18 19 //Include Standard files 20 #include <stdlib.h> 21 #include "include/AT91SAM7X-EK.h" 22 23 #include "Multi_task.h" 24 25 // task state definitions 26 #define UNUSED 0 27 #define SLEEPING 1 28 #define ACTIVE 2 29 #define STARTING 3 30 31 // Error Messages 32 #define E_RUNNING_SCHED -1 33 #define E_NO_SPACE_LEFT -2 34 #define E_NO_INIT -3 35 #define E_NO_FUNCTION -4 36 #define E_UNINIT_TASK -10 37 #define E_ACTIVE_TASK -11 38 #define E_SLEEPING_TASK -12 39 #define E_TASK_NO_EXISTING -14 40 #define E_TASK_RUNNING -15 41 42 // Constants 43 #define DATA_RAM 32000 // size of RAM used for tasks in bytes 44 #define TASK_SIZE (DATA_RAM/4)/NB_TASKS // amount of RAM per task in int (assuming that 1int=4bytes... ;-)) 45 46 47 typedef struct s_task_type // structure that defines a task 48 { 49 int num; // task number 50 int state; // task state 51 void (*add_fct)(void); // address of the associate function 52 void* stack_bottom; // address of the first (older) stack element 53 void* stack_top; // address of the first free stack element 54 void* stack_limit; // address of the end of stack section 55 } task_t; 56 57 58 AT91_REG save_sp; // stack pointer address
24
59 AT91_REG *psave_sp = &save_sp; 60 AT91_REG *pint_sp = NULL; 61 AT91_REG int_sp; 62 int nb_task_running = 0; 63 int actual_nb_tasks = 0; // number of initialized tasks 64 int sleeping[NB_TASKS]; // array of sleeping tasks 65 int active[NB_TASKS]; // array of active tasks 66 int unused[NB_TASKS]; // array of not initialized tasks 67 int starting[NB_TASKS]; // array of not sleeping tasks 68 task_t tasks[NB_TASKS]; // array of all manageable tasks 69 int tasks_RAM[NB_TASKS][TASK_SIZE]; // this table contains data area for tasks 70 int task_running = 0; // curently running task 71 int state_multitask = 0; // 0 when struct is not init, 1 when struct is init, 2 when scheduler is running 72 int scheduler_started; 73 char premier = 2; 74 75 void ITsched(); 76 77 78 int Init_Struct(int n) 79 // initializes data structures to implement n parrallel tasks 80 // must be the first function called. 81 // cannot be called from a task 82 // erases all previous data when called 83 // error messages 84 // -1 : called after scheduler start (E_RUNNING_SCHED) 85 { 86 int i = 0; 87 88 if (scheduler_started) 89 return E_RUNNING_SCHED; 90 91 for (i=0 ; i<NB_TASKS ; i++) 92 { 93 // initializes the array of all manageable tasks 94 tasks[i].num = i; 95 tasks[i].state = UNUSED; 96 tasks[i].add_fct = NULL; 97 tasks[i].stack_bottom = &(tasks_RAM[i][TASK_SIZE]); // bottom of stack is at higher addresses 98 tasks[i].stack_top = &(tasks_RAM[i][TASK_SIZE]); // stack is empty at first 99 tasks[i].stack_limit = &(tasks_RAM[i][0]); // lower address is the end of stack space 100 101 // initializes the array of sleeping, active, starting and not initialized tasks 102 sleeping[i] = 0; 103 active[i] = 0; 104 unused[i] = 1; 105 starting[i] = 0; 106 state_multitask = 1; 107 } 108 109 return 0; 110 } 111 112 113 114 int Create_Task(void (*task_func)(void)) 115 // initializes a task with the function given in argument 116 // returns the id number of the new task (positive value) or an error (negative value) 117 // the function given in argument must not return 118 // default state is STARTING 119 // error messages : 120 // -1 : called after scheduler start (E_RUNNING_SCHED) 121 // -2 : no room for new task (E_NO_SPACE_LEFT) 122 // -3 : data not initialized (E_NO_INIT) 123 { 124 // if struct is not init 125 if (state_multitask == 0) 126 return E_NO_INIT;
25
127 128 // if scheduler is already running 129 if (state_multitask == 2) 130 return E_RUNNING_SCHED; 131 132 // if all the tasks are initialized 133 if (actual_nb_tasks == NB_TASKS) 134 return E_NO_SPACE_LEFT; 135 136 tasks[actual_nb_tasks].state = UNUSED; 137 tasks[actual_nb_tasks].add_fct = task_func; 138 139 starting[actual_nb_tasks] = 1; 140 actual_nb_tasks++; 141 142 return (actual_nb_tasks-1); 143 } 144 145 146 147 int Start_Scheduler() 148 // actually starts the scheduler 149 // error messages : 150 // -3 : data not initialized (E_NO_INIT) 151 // -4 : no function to run (E_NO_FUNCTION) 152 { 153 // if struct is not init 154 if (state_multitask == 0) 155 return E_NO_INIT; 156 157 // if no functions are initialized 158 if (actual_nb_tasks == 0) 159 return E_NO_FUNCTION; 160 161 //TC0 Configuration 162 AT91C_BASE_PMC -> PMC_PCER = (1 << AT91C_ID_TC0); // Timer Counter 0 Power ON 163 AT91C_BASE_AIC -> AIC_IDCR = (1 << AT91C_ID_TC0); // Disable interrupt 164 AT91C_BASE_AIC -> AIC_SMR[AT91C_ID_TC0] = (AT91C_AIC_PRIOR_HIGHEST) | (AT91C_AIC_SRCTYPE_EXT_LOW_LEVEL); // AIC Configuration 165 AT91C_BASE_AIC -> AIC_SVR[AT91C_ID_TC0] = (unsigned int) ITsched; // Define interrupt function 166 AT91C_BASE_TC0 -> TC_CMR = AT91C_TC_CLKS_TIMER_DIV5_CLOCK | AT91C_TC_CPCTRG; // TC0 clock configure at 199691 Hz and auto reset of the counter 167 AT91C_BASE_TC0 -> TC_CCR = AT91C_TC_CLKEN; // TC0 clock enable 168 AT91C_BASE_TC0 -> TC_IER = AT91C_TC_CPCS; // TC0 interrupt type 169 AT91C_BASE_TC0 -> TC_RC = 2000; // 2000/199691=10ms between each interrupt 170 AT91C_BASE_TC0 -> TC_CCR = AT91C_TC_SWTRG; 171 AT91C_BASE_AIC -> AIC_IECR = (1 << AT91C_ID_TC0); 172 173 return 0; 174 } 175 176 177 178 int Wake_Task(int n) 179 // moves a task from sleeping to active state 180 // 0 means success 181 // errors : 182 // -10 : Task Not initialized (E_UNINIT_TASK) 183 // -11 : Active Task (E_ACTIVE_TASK) 184 { 185 if (sleeping[n] == 1) 186 { 187 tasks[n].state = ACTIVE; 188 sleeping[n] = 0; 189 active[n] = 1; 190 return 0; 191 } 192 193 if (active[n] == 1) 194 return E_ACTIVE_TASK;
26
195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233
return E_UNINIT_TASK; }
int Scotch_Task(int n) // moves a task from active to sleeping state // 0 means success // errors : // -10 : Task Not initialized (E_UNINIT_TASK) // -12 : Sleeping Task (E_SLEEPING_TASK) { if (active[n] == 1) { tasks[n].state = SLEEPING; sleeping[n] = 1; active[n] = 0; return 0; } if (sleeping[n] == 1) return E_SLEEPING_TASK; return E_UNINIT_TASK; }
int Launch_Task(int task_number) // Relunch an existing unused task with an reinitializing a its own stack // Return 0 if succes // errors: // -14 : Task doesn't exist (E_TASK_NO_EXISTINGG) // -15 : Task not in unused state(E_TASK_RUNNING) { if(task_number >= actual_nb_tasks) return E_TASK_NO_EXISTING; if(tasks[task_number].state == SLEEPING||tasks[task_number].state == ACTIVE || tasks[task_number].state == STARTING) return E_TASK_RUNNING; tasks[task_number].stack_top = tasks[task_number].stack_bottom; // reinitializing stack
234 235 236 237 tasks[task_number].state = STARTING; //Task ready to be start 238 239 sleeping[task_number] = 0; 240 active[task_number] = 0; 241 unused[task_number] = 0; 242 starting[task_number] = 1; 243 244 return 0; 245 246 } 247 248 249 250 void ITsched() 251 // this function is intented to be run as an interrupt 252 // and implements the scheduler 253 { 254 int i = task_running; 255 256 // Search for the next active or starting task 257 do 258 { 259 i++; 260 if (i > NB_TASKS-1) 261 i = 0; 262 } while ((active[i] != 1) && (starting[i] != 1)); 263 264 // Disable interrupt
27
265 AT91C_BASE_AIC->AIC_IDCR = 1 << AT91C_ID_TC0; // Timer Counter 0 Interrupt Disable 266 unsigned long int interrupt_sr = AT91C_BASE_AIC -> AIC_IMR; // Store Status of all interrupt 267 AT91C_BASE_AIC -> AIC_IDCR = 0xFFFFFFFF; // Disable all interrupt 268 AT91C_BASE_TC0 -> TC_SR; // Read the Status Register to empty the Interrupt Register 269 270 // if next task is "starting" 271 // change task to "active" 272 // remove IT mode 273 // launch function 274 // infinite loop 275 276 //SP modification 277 //commande asm 278 // if(j < NB_TASKS+1) 279 { 280 //asm("MOV save_sp, sp"); 281 if(premier <= 1 ) //Verify that's not the first launching of ITsched() 282 { 283 premier = 0; 284 asm( "mov r2, sp \n" //Save sp register in save_sp 285 "ldr r3, psave_sp \n" 286 "str r2, [r3] \n"); 287 save_sp -= 4; 288 int_sp = 0xFFFC -(nb_task_running)*12; //Store IRQ stack into our supervisor stack 289 asm( "ldr r3, int_sp \n" 290 "ldr r3, [r3] \n" 291 "ldr r2,save_sp \n" 292 "mov sp,r2 \n" 293 "str r3, [r2] \n"); 294 save_sp -= 4; 295 int_sp -= 4; 296 asm( "ldr r3, int_sp \n" 297 "ldr r3, [r3] \n" 298 "ldr r2,save_sp \n" 299 "mov sp,r2 \n" 300 "str r3, [r2] \n"); 301 save_sp -= 4; 302 int_sp -= 4; 303 asm( "ldr r3, int_sp \n" 304 "ldr r3, [r3] \n" 305 "ldr r2,save_sp \n" 306 "mov sp,r2 \n" 307 "str r3, [r2] \n"); 308 309 tasks[task_running].stack_top = (void*) save_sp; // in the current task 310 } 311 312 313 task_running = i; 314 save_sp = (AT91_REG) tasks[task_running].stack_top; // in the new task 315 //commande asm 316 317 if(premier < 1) // Check if it is the second (or more) lauch of ITsched() 318 { 319 if(premier <= 0) 320 premier = 1; 321 asm( "ldr r2, save_sp \n" //restore the IRQ stack from our supervisor stack 322 "ldr r3, int_sp \n" 323 "ldr r2, [r2] \n" 324 "str r2, [r3] \n"); 325 save_sp += 4; 326 int_sp += 4; 327 asm( "ldr r2, save_sp \n" 328 "ldr r3, int_sp \n" 329 "ldr r2, [r2] \n" 330 "str r2, [r3] \n"); 331 save_sp += 4; 332 int_sp += 4; 333 asm( "ldr r2, save_sp \n"
28
334 "ldr r3, int_sp \n" 335 "ldr r2, [r2] \n" 336 "str r2, [r3] \n"); 337 save_sp += 4; 338 int_sp += 4; 339 } 340 asm( "ldr r2,save_sp \n" // Change stack 341 "mov sp,r2 \n"); 342 premier--; 343 } 344 345 // Enable interrupt 346 AT91C_BASE_AIC -> AIC_IECR = interrupt_sr; // Restore status of all interrupt 347 AT91C_BASE_AIC -> AIC_IECR = 1 << AT91C_ID_TC0; // Timer Counter 0 Interrupt Enable 348 349 // First time execute of a task 350 if (tasks[task_running].state == STARTING) 351 { 352 tasks[task_running].state = ACTIVE; // Change task to active 353 starting[task_running] = 0; 354 active[task_running] = 1; 355 AT91C_BASE_AIC->AIC_EOICR = 0; // The function is not an interrupt function anymore 356 nb_task_running++; 357 //Launch_Function((tasks[task_running].add_fct)); // Go to the new function 358 (tasks[task_running].add_fct)(); 359 tasks[task_running].state = UNUSED; //In case of return from the task (STOP) 360 active[task_running] = 0; 361 unused[task_running] = 1; 362 while(1){} 363 } 364 } 365
29