tonyp12 26 Posted March 15, 2016 Share Posted March 15, 2016 Tested on G2553 Launchpad with IAR, I recommend G2955 with 1K RAM if you want more than 3 task #include "msp430.h" #include "common.h" //=========================(C) Tony Philipsson 2016 ======================= funcpnt const taskpnt[]={ task1, task2, task3, // <- PUT YOUR TASKS HERE }; const int stacksize[tasks] = {28}; // a blank value defaults to 24 stack words //========================================================================= int taskstackpnt[tasks]; unsigned int taskdelay[tasks]; char taskrun; int main( void ) { WDTCTL = WDTPW + WDTHOLD; // Stop watchdog timer if (CALBC1_8MHZ != 0xff){ // erased by mistake? BCSCTL1 = CALBC1_8MHZ; // Set DCO to factory calibrate 1MHz DCOCTL = CALDCO_8MHZ; } int* multistack = (int*) __get_SP_register(); int i=0; while(i<tasks-1){ int j = stacksize[i]; if (!j) j = 24; multistack -= j; *(multistack) = (int) taskpnt[++i]; // prefill in PC *(multistack-1) = GIE; // prefill in SR taskstackpnt[i] = (int) multistack-26; // needs 12 dummy push words } WDTCTL = WDTPW+WDTTMSEL+WDTCNTCL; // 4ms interval at 8MHz smclk IE1 |= WDTIE; __bis_SR_register(GIE); asm ("br &taskpnt"); // indirect jmp to first task } //============= TASK SWITCHER ISR ============= #pragma vector = WDT_VECTOR __raw __interrupt void taskswitcher(void) { asm ("push R15\n push R14\n push R13\n push R12\n" "push R11\n push R10\n push R9\n push R8\n" "push R7\n push R6\n push R5\n push R4"); taskstackpnt[taskrun] = __get_SP_register(); if (++taskrun == tasks) taskrun = 0; __set_SP_register(taskstackpnt[taskrun]); asm ("pop R4\n pop R5\n pop R6\n pop R7\n" "pop R8\n pop R9\n pop R10\n pop R11\n" "pop R12\n pop R13\n pop R14\n pop R15"); } #include "msp430.h" #include "common.h" __task void task1(void){ P1DIR |= BIT0; while(1){ __delay_cycles(800000); P1OUT |= BIT0; __delay_cycles(800000); P1OUT &=~BIT0; } } #include "msp430.h" #include "common.h" __task void task2(void){ P1DIR |= BIT6; while(1){ __delay_cycles(1200000); P1OUT |= BIT6; __delay_cycles(1200000); P1OUT &=~BIT6; } } #include "msp430.h" #include "common.h" unsigned int fibo(int); __task void task3(void){ int temp = 0; while(1){ fibo(++temp); } } unsigned int fibo(int n){ if (n < 2) return n; else return (fibo(n-1) + fibo(n-2)); } #ifndef COMMON_H_ #define COMMON_H_ #define tasks (sizeof(taskpnt)/2) __task void task1(void); __task void task2(void); __task void task3(void); typedef __task void (*funcpnt)(void); #endif Fmilburn, jazz, agaelema and 9 others 12 Quote Link to post Share on other sites
greeeg 460 Posted March 15, 2016 Share Posted March 15, 2016 Hey @@tonyp12 I love seeing people implement multi-tasking things. Many full implementations of RTOS's include so many extra things that it becomes difficult to actually see the the implementation of the task switching itself. Nice simple elegant approach. I like it. Quote Link to post Share on other sites
tonyp12 26 Posted March 15, 2016 Author Share Posted March 15, 2016 a ISR always have way to trick the RETI by modifying the SR values on stack. I trick the RETI with a different stack location , task 1 have a stack as normal as first entry in to ISR the PC,SR and R15-R4 are push'ed? But for the other tasks I have to preset the PC and SR in the "fake" stack for its first entry inand also offset the stack pointer as it will see 12 pop's the first time without seeing the 12 push'es yet. I plan to add system_delay() so task can asked to be put on hold, though it will be hard to calculate a actual time if other tasks also do that at the same time. maybe even enter LPM0 if all task are on timeout for this 4ms interval. gsutton 1 Quote Link to post Share on other sites
spirilis 1,265 Posted March 16, 2016 Share Posted March 16, 2016 G2955 has 4KB RAM btw, even better! Quote Link to post Share on other sites
tonyp12 26 Posted March 16, 2016 Author Share Posted March 16, 2016 You're right, that is room for 16 multi-stacks!! Run that msp at 16Mhz, intervals will now be 2ms and you have something powerful.?IRQ's can handle the real-time stuff and just make sure you always code with the thoughts that code can get on hold at any time (though temporary disable WDTIE will give you a lock) ?I just selected 1K+ and did not see that some had even more.http://www.ti.com/lsds/ti/microcontrollers_16-bit_32-bit/msp/ultra-low_power/msp430g2x_i2x/products.page#p1219=1;4 Quote Link to post Share on other sites
spirilis 1,265 Posted March 16, 2016 Share Posted March 16, 2016 That is beautiful too, just read the code -- super small and simple! Always wondered how those things worked... now this is a nice approach just using the __raw keyword to override the normal stuff. Quote Link to post Share on other sites
Rickta59 589 Posted March 16, 2016 Share Posted March 16, 2016 This is interesting stuff, nice post! I took a quick stab at getting this to compile on msp430-gcc. I replaced the __task and __raw with the gcc naked attribute. It gives you warnings but looking at the code it seems to do the right thing. Thanks, -rick $ diff common.txt common.h 3a4 > #define __task __attribute__((naked)) 8a10 > $ diff main.txt main.c 20c20 < int* multistack = (int*) __get_SP_register(); --- > int* multistack = (int*) __read_stack_pointer(); 35,36c35,36 < #pragma vector = WDT_VECTOR < __raw __interrupt void taskswitcher(void) --- > __attribute__((interrupt(WDT_VECTOR), naked)) > void taskswitcher(void) 42c42 < taskstackpnt[taskrun] = __get_SP_register(); --- > taskstackpnt[taskrun] = (int)__read_stack_pointer(); 44c44 < __set_SP_register(taskstackpnt[taskrun]); --- > __write_stack_pointer((void *)taskstackpnt[taskrun]); 49,50c49 < } < --- > } Quote Link to post Share on other sites
tonyp12 26 Posted March 16, 2016 Author Share Posted March 16, 2016 I may replace the c code in isr with asm if I can not make changes to get it optimized so it only uses 8 words,now it uses 19 words, but making stuff (int*) instead of array[char value] I think that will get it down to 12 at least. If all asm of course there is no need for __raw if you compiler don't support it. btw the asm ("br &taskpnt") can be done in C by: taskpnt[0](); but it does a call and that wastes a stackword for return address. So unless you want task1 to have an option to exit and return to that spot in main, a exit in task1 now would use the return address from the original call to main Quote Link to post Share on other sites
Rickta59 589 Posted March 17, 2016 Share Posted March 17, 2016 const int stacksize[tasks] = {28}; // a blank value defaults to 24 stack words ... int* multistack = (int*) __get_SP_register(); int i=0; while(i<tasks-1){ int j = stacksize[i]; if (!j) j = 24; multistack -= j; *(multistack) = (int) taskpnt[++i]; // prefill in PC *(multistack-1) = GIE; // prefill in SR taskstackpnt[i] = (int) multistack-26; // needs 12 dummy push words } Can you explain why you are using 28 and 24 for the task stack sizes? Why do you back up the magic # of -26 -rick Quote Link to post Share on other sites
tonyp12 26 Posted March 17, 2016 Author Share Posted March 17, 2016 ?{28}; is the same thing as {28,0,0} and 28 was just random number to show somethingso if you forget to fill in all stack sizes for each task I will give you 24 words (48bytes) A minimum should be 14 words if your task don't use the stack at all. -26, as I cast the address to (int) it's now a byte space referencedIf I wrapped it first(int) (multistack-13); it will be word referenced as it is a int pointer, probably should wrap it for next time, ?So I'm just adjusting the stack pointer to include the PC,SR and the 12 pop's it will see on first entry (only done for task2+) That is 14 words but Pop is post increment, it works if I do -13 wordsthe registers will have random data first time, but as task have not started doing anything yet it should not care.a POP R15 is actual a: MOV.W @R1+,R15 oPossum 1 Quote Link to post Share on other sites
Rickta59 589 Posted March 17, 2016 Share Posted March 17, 2016 Thanks, the stack size thing seemed arbitrary to me, and I was wondering why. task1 and task2 don't use any stack space. So I was wondering why you bothered to give them any stack beyond the 14 words. Quote Link to post Share on other sites
veryalive 49 Posted March 17, 2016 Share Posted March 17, 2016 Hi, and a great example of tight code. But ...... How is this pre-emptive? Why do I see this as round robin? What am I missing? Thanks and cheers. Quote Link to post Share on other sites
tonyp12 26 Posted March 17, 2016 Author Share Posted March 17, 2016 preemptive means that the code (e.g.each task that is pretty much its own main.c) does not know it is sharing a single mcu core.it does not need to say "i take a pause now so next task can go on", you can even run two instances of the same task (though not really useful) A higher power takes over and switch tasks.Sure there is stuff to add like system_sleep and priority and maybe even new task and end task etc. I plan to show how to use a 512Hz mems osc to NMI pin, so task switching can not be overridden by (maybe mistakenly) disabling GIE.?wiki: Preemptive multitasking wiki: Cooperative multitasking P.S I also have a Cooperative multitasking system that uses a Event Machine in main.c and each (up to 16) tasks are all State Machines (switch/case) as that is a good time slicer.?What I like about that is that I know I have exclusivity while I'm doing my thing so no race conditions.?And very little overhead so better for battery-operation as task that needs to use a time-constant can sleep from 1ms to 64sec independently keeping in LPM3 95% of the time spirilis and tripwire 2 Quote Link to post Share on other sites
Rickta59 589 Posted March 18, 2016 Share Posted March 18, 2016 @@tonyp12 what license are you releasing this under? Quote Link to post Share on other sites
Rickta59 589 Posted March 18, 2016 Share Posted March 18, 2016 I've been playing around with this under msp430-gcc. I've got it working pretty well however, I had to make a bunch of changes to make ms430-gcc happy. Unfortunately, I've not been successful getting this to work with msp430-elf-gcc. It seems that the way it uses the stack and ABI aren't playing nicely with this code. I added a systick and sys_delay feature. To make my life simpler, I have the WDT ticking every 1ms running at 8MHz. As soon as we here back from @@tonyp12 I'll post the code. -rick Quote Link to post Share on other sites
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.