-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathscheduler.c
285 lines (226 loc) · 8.11 KB
/
scheduler.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
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
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
/* Copyright (c) 2014, Jeffrey Antony */
/* Read license file for more information */
#include <msp430.h>
#include <stdio.h>
#define BACKUP_REGS 12 /* r4 to r15*/
#define STACK_SIZE 1024
#define STACK_TOP STACK_SIZE - 1
#define TOTAL_TASKS 3
/*Enable GIE in SR so that the WDT never stops when we go to user task*/
/*Enable SCG0 for 25MHZ CPU execution*/
#define DEFAULT_SR ((uint16_t)0x0048)
#define SAVE_CONTEXT() \
asm volatile ( "push r4 \n\t" \
"push r5 \n\t" \
"push r6 \n\t" \
"push r7 \n\t" \
"push r8 \n\t" \
"push r9 \n\t" \
"push r10 \n\t" \
"push r11 \n\t" \
"push r12 \n\t" \
"push r13 \n\t" \
"push r14 \n\t" \
"push r15 \n\t" \
);
#define RESTORE_CONTEXT() \
asm volatile ( "pop r15 \n\t" \
"pop r14 \n\t" \
"pop r13 \n\t" \
"pop r12 \n\t" \
"pop r11 \n\t" \
"pop r10 \n\t" \
"pop r9 \n\t" \
"pop r8 \n\t" \
"pop r7 \n\t" \
"pop r6 \n\t" \
"pop r5 \n\t" \
"pop r4 \n\t" \
"reti \n\t" \
);
/*stack for each task - 1024*16 = 2KB RAM*/
uint16_t task1ram[STACK_SIZE];
uint16_t task2ram[STACK_SIZE];
uint16_t task3ram[STACK_SIZE];
volatile uint8_t task_id; /*has the current running task*/
volatile uint16_t *stack_pointer[TOTAL_TASKS]; /*address of stack pointer for each task*/
/*****************************************************/
volatile uint8_t button1 = 0x1, button2=0x1; /*volatile since its a shared resource between tasks*/
/*****************************************************/
void task1(void)
{
volatile unsigned int i;
P1DIR |= BIT0; /*for LED P1.0*/
while(1)
{
if(button1)
{
for (i=0; i < 65535; i++);
for (i=0; i < 65535; i++);
for (i=0; i < 65535; i++);
P1OUT ^= BIT0; // Toggle P1.0 (LED)
}
}
}
void task2(void)
{
volatile unsigned int i;
P4DIR |= BIT7; /*for LED P4.7*/
while(1)
{
if(button2)
{
for (i=0; i < 65535; i++);
P4OUT ^= BIT7;
}
}
}
void task3(void)
{
/*get both button states and exchange data */
uint8_t b1,b2;
volatile unsigned int i;
P2REN |= BIT1;
P2OUT |= BIT1;
P1REN |= BIT1;
P1OUT |= BIT1;
while(1)
{
b1 = (P2IN & BIT1);
b2 = (P1IN & BIT1);
if(b1!=BIT1)
{
button1 ^= 0x1;
}
if(b2!=BIT1)
{
button2 ^= 0x1;
}
for (i=0; i < 20000; i++); //software debouncing
}
}
/*****************************************************/
/*
* This function will initialise stack for each task. Following are filled into the stack
* 1) Store the PC first since it has to be poped back and loaded at end
* 2) then store SR register. This determines the CPU condition at which your task should execute
* 3) leave the space for the registers r4 to r15 since the scheduler will pop these when a task switching occurs
* 3) return the address of stack which will contain all informations for the task to execute after task switching
* 4) TODO: fill the end of stack with known values to detect buffer overflows.
*/
uint16_t *initialise_stack(void (* func)(void), uint16_t *stack_location)
{
uint8_t i;
/*MSP430F5529 has a 20bit PC register*/
*stack_location = (uint16_t)func; //last 16 bits will only stored. Pending 4bits will be stored with SR
stack_location--;
/*refer datasheet to see how 20bit PC is stored along with SR*/
*stack_location = (((uint16_t)((uint32_t)(0xf0000 & (uint32_t)func) >> 4))| DEFAULT_SR); //TODO:fix compiler warning
/*leave space in stack for r4 to r15*/
for(i= 0; i< BACKUP_REGS; i++)
{
stack_location--;
}
/*TODO: fill bottom of stack to detect buffer overflow*/
return stack_location;
}
void SetVcoreUp (unsigned int level)
{
// Open PMM registers for write
PMMCTL0_H = PMMPW_H;
// Set SVS/SVM high side new level
SVSMHCTL = SVSHE + SVSHRVL0 * level + SVMHE + SVSMHRRL0 * level;
// Set SVM low side to new level
SVSMLCTL = SVSLE + SVMLE + SVSMLRRL0 * level;
// Wait till SVM is settled
while ((PMMIFG & SVSMLDLYIFG) == 0);
// Clear already set flags
PMMIFG &= ~(SVMLVLRIFG + SVMLIFG);
// Set VCore to new level
PMMCTL0_L = PMMCOREV0 * level;
// Wait till new level reached
if ((PMMIFG & SVMLIFG))
while ((PMMIFG & SVMLVLRIFG) == 0);
// Set SVS/SVM low side to new level
SVSMLCTL = SVSLE + SVSLRVL0 * level + SVMLE + SVSMLRRL0 * level;
// Lock PMM registers for write access
PMMCTL0_H = 0x00;
}
void InitClock(void)
{
UCSCTL3 = SELREF_2; // Set DCO FLL reference = REFO
UCSCTL4 |= SELA_2; // Set ACLK = REFO
__bis_SR_register(SCG0); // Disable the FLL control loop
UCSCTL0 = 0x0000; // Set lowest possible DCOx, MODx
UCSCTL1 = DCORSEL_7; // Select DCO range 50MHz operation
UCSCTL2 = FLLD_0 + 762; // Set DCO Multiplier for 25MHz
// (N + 1) * FLLRef = Fdco
// (762 + 1) * 32768 = 25MHz
// Set FLL Div = fDCOCLK/2
__bic_SR_register(SCG0); // Enable the FLL control loop
// Worst-case settling time for the DCO when the DCO range bits have been
// changed is n x 32 x 32 x f_MCLK / f_FLL_reference. See UCS chapter in 5xx
// UG for optimization.
// 32 x 32 x 25 MHz / 32,768 Hz ~ 780k MCLK cycles for DCO to settle
__delay_cycles(782000);
// Loop until XT1,XT2 & DCO stabilizes - In this case only DCO has to stabilize
do
{
UCSCTL7 &= ~(XT2OFFG + XT1LFOFFG + DCOFFG);
// Clear XT2,XT1,DCO fault flags
SFRIFG1 &= ~OFIFG; // Clear fault flags
}while (SFRIFG1&OFIFG); // Test oscillator fault flag
}
volatile uint16_t *temp;
void main(void)
{
//Stop the watchdog timer until we configure our scheduler
WDTCTL = WDTPW + WDTHOLD;
// Increase Vcore setting to level3 to support fsystem=25MHz
// NOTE: Change core voltage one level at a time..
SetVcoreUp (0x01);
SetVcoreUp (0x02);
SetVcoreUp (0x03);
InitClock();
/*initialise stack for each task*/
stack_pointer[0] = initialise_stack(task1, &task1ram[STACK_TOP]);
stack_pointer[1] = initialise_stack(task2, &task2ram[STACK_TOP]);
stack_pointer[2] = initialise_stack(task3, &task3ram[STACK_TOP]);
//configure WDT for cyclically calling the scheduler
//WDTCTL = WDT_MDLY_32; // WDT 32ms, SMCLK, interval timer
WDTCTL = WDTPW + WDTSSEL1 +WDTTMSEL + WDTCNTCL + WDTIS_7;
SFRIE1 |= WDTIE; // Enable WDT interrupt
// WDT won't work until we enable GIE in SR. But this is enabled via our SR's #define
// when the first task is poped out, automatically GEI is enabled
/*initialise to first task*/
task_id =0;
//set the stack pointer to task1's RAM location
temp = stack_pointer[task_id];
asm volatile ("mov.w temp, r1 \n\t");
// lets pop our first task out!
RESTORE_CONTEXT();
}
__attribute__( ( __interrupt__( WDT_VECTOR ), __naked__ ) )
void scheduler(void)
{
SAVE_CONTEXT(); /*save current registers into stack. PC and SR will be saved automatically when an interrupt occurs*/
//if required, we can stop the WDT here until we are done with all the works for context switching.
//then start the WDT before restoring the context (this step is not required since our SR is by default is enabled with global interrupts)
//we can do a stack overflow check here
/*backup the stack pointer*/
asm volatile ("mov.w r1, temp \n\t");
stack_pointer[task_id] = temp;
//round robin scheduling
if(task_id < (TOTAL_TASKS-1))
{
task_id++;
}
else
{
task_id = 0;
}
/*check for stack overflow*/
temp = stack_pointer[task_id];
asm ( "mov.w temp, r1 \n\t");
RESTORE_CONTEXT();
}