Quality RTOS & Embedded Software

 Real time embedded FreeRTOS RSS feed 
Quick Start Supported MCUs PDF Books Trace Tools Ecosystem TCP & FAT




Loading

xTaskCreate() question

Posted by lcj on February 17, 2009
Can multiple tasks be created all pointing to the same function? Creating tasks seems simple enough:

xTaskCreate( vGenericTask,
(const portCHAR * const) "Gen1",
configMINIMAL_STACK_SIZE,
NULL,
TASK_PRIORITY,
NULL);

Can I simply create it again with a different name:

xTaskCreate( vGenericTask,
(const portCHAR * const) "Gen2",
configMINIMAL_STACK_SIZE,
NULL,
TASK_PRIORITY,
NULL);

I have one function that receives data on a pair of port pins (PIC18F4620) and posts it to a message queue. There are four different port pairs on which data will arrive, and each packet will be posted to the same queue. Since other than the pin being examined, the functions would be identical, is it possible to write a generic function and create four tasks pointing to it, each time hooking a different input port pair to the code? Do I create four function pointers, point them all at the same function, then call xTaskCreate passing a different function pointer each time? Examples? If this is in the documentation, please let me know where I should look.

RE: xTaskCreate() question

Posted by MEdwards on February 17, 2009
If the tasks are reentrant (don't access static or global variables without some protection) then your code is fine.

RE: xTaskCreate() question

Posted by Richard Damon on February 17, 2009
You can create as many tasks as you want off of one function, the trick is that if you want them to actually do something a bit different (like use different pins), you need to tell the function how to know which version it is running. The parameter to the function is a good way to do this, typically I create a struct with the needed information and pass the address of it as that parameter. The function can then cast it back to the struct type and get the info it needs.

RE: xTaskCreate() question

Posted by lcj on February 26, 2009
Hmm, no forthcoming examples, so I thought I'd experiment a little. I have a PICDEM2 with a PIC18F4620 in it, running at 40MHz (10MIPS). Here's what I tried:

struct taskParms {
volatile UCHAR *portreg;
UCHAR portpin;
int period;
};


void main(void)
{
struct taskParms led[4];

led[0].period = 200;
led[1].period = led[0].period * 2;
led[2].period = led[1].period * 2;
led[3].period = led[2].period * 2;
led[0].portreg = led[1].portreg = led[2].portreg = led[3].portreg = &LATB;
led[0].portpin = 0;
led[1].portpin = 1;
led[2].portpin = 2;
led[3].portpin = 3;

app_init();// Initialise hardware.

// Create dasblinkentasks, all using the same function
xTaskCreate( vDasBlinkenTask,
(const portCHAR * const) "LED0",
configMINIMAL_STACK_SIZE,
&led[0],
tskIDLE_PRIORITY + 1,
NULL);

xTaskCreate( vDasBlinkenTask,
(const portCHAR * const) "LED1",
configMINIMAL_STACK_SIZE,
&led[1],
tskIDLE_PRIORITY + 1,
NULL);

xTaskCreate( vDasBlinkenTask,
(const portCHAR * const) "LED2",
configMINIMAL_STACK_SIZE,
&led[2],
tskIDLE_PRIORITY + 1,
NULL);

xTaskCreate( vDasBlinkenTask,
(const portCHAR * const) "LED3",
configMINIMAL_STACK_SIZE,
&led[3],
tskIDLE_PRIORITY + 1,
NULL);


vTaskStartScheduler();// Start the scheduler. Will never return.
while(1);
}

void vDasBlinkenTask(void *pvParameters)
{
struct taskParms *led;
portTickType xLastWakeTime;

led = (struct taskParms *) pvParameters;

while(1) {
xLastWakeTime = xTaskGetTickCount();
*led->portreg ^= (1 << led->portpin);
vTaskDelayUntil(&xLastWakeTime,led->period);
}
}

It's a simple down-counter, and it works. There are four tasks, each using the same function, each with a different set of port pins and running times. The practical function is nothing more than a down-counter flashing four LEDs on PortB pins 0-3.

Here's the question:

At the start of main(), I declare:

struct taskParms led[4];

If I declare the above static, or move it above main, making it a global definition, it breaks. The code seems to make it through three of the four tasks once, and then reset the processor with a hardware stack overflow. Since it looks like a pointer / memory issue, here's what's in the linker script file for heap space:

DATABANK NAME=BIG_BLOCK START=0x800 END=0xEF3

...and here's what's in freertosconfig.h for heap & stack declaration:

#define configMINIMAL_STACK_SIZE( 128 )
#define configTOTAL_HEAP_SIZE( ( size_t ) 1776 )

The P18F4620 has 64Kbytes of instruction space (32K single word instructions) and 3968 bytes of RAM.

Can anybody give me some suggestions as to why the code fails with such a minor change?

RE: xTaskCreate() question

Posted by dave m on February 26, 2009
I've never used FreeRTOS on a PIC (or, really, done any significant amount of work on a PIC) but on some of the ARM ports, when you call vTaskStartScheduler(), it clobbers the main() stack. So it's probably safer to have your taskParams structures declared globally or static. As for why doing that causes problems...

Well, 3968 bytes seems like an awfully small amount of RAM. Check your link map to see whether you're (getting close to) filling it up. (Maybe the scheduler interrupt stack usage is clobbering one of the task stacks?) Try making taskParams global/static, but only start two or three tasks. If that works, then my money would be on "out of RAM."

RE: xTaskCreate() question

Posted by lcj on February 26, 2009
I should probably say that I'm using the heap_1 scheme: create all tasks before starting the RTOS, and never kill a task or release any memory after.

If I remove one task, and change the declaration to static, all is well. If I look at the memory usage from within the debugger, I get 8181/32768 locations of code space used up, and 2057/3968 locations of ram space used up. Oddly enough, if I add the extra task back into the mix, I end up with 8213 locations of code space, and the same 2057 locations of ram space. I went back and played with the heap and the number of tasks. Here's what I got:

Heap | # of tasks
------+-------------
496 | 2
752 | 3
1008 | 3
1776 | 3
...bla bla bla...
3312 | 3

So, I suspect the heap's not the problem. I can't see how I'd need an order of magnitude more heap space to pull this off.

The next thing I tried was to leave the heap at 3312, but increase the stack for each task to 512 bytes. It works with three instantiations of vDasBlinkenTask, not four.

If I dump the ram into the debugger, all allocated space from the heap lives below 0x530, or 1328 (stack set to 128 bytes/task). Also, since the unused stack space is padded with 0xa5 I can see where each unused portion of stack lives. They all look like there's a lot of space available, both in unused stack and in overall heap space. There are five visible blocks of space filled with 0xa5, some are smaller than others, but they are all there. I expect five because there's an Idle task too. Increasing configMINIMAL_STACK_SIZE, doesn't seem to matter. I have lots of space in RAM.

If I set a break point at the first instruction in main(), the processor hits the breakpoint after the rtos is started, meaning something has gone terribly wrong. It can't get to this point unless it was reset. When this happens, some of the statically assigned variables have been overwritten. Specifically the pointer assignment for led[0].portreg is pointing to the wrong address, and led[0].period is corrupt. The rest of the table is fine. Something is overwriting these variables after the RTOS begins executing. If I move the call to app_init() above the led[x] assignments, it all works. It would be great news, if I knew why.

The app_init() function is pitifully simple:

void app_init(void)
{
// Set up the ports
PORTA = PORTAINI;
PORTB = PORTBINI;
PORTC = PORTCINI;
PORTD = PORTDINI;
PORTE = PORTEINI;

TRISA = TRISAINI;
TRISB = TRISBINI;
TRISC = TRISCINI;
TRISD = TRISDINI;
TRISE = TRISEINI;

ADCON0 = 0;// Kill ADC so port A can be digital inputs.
ADCON1 = 0x0f;

CMCON = 0x07;// Ensure comparator is off

#ifdef WATCHDOG
watchdog();
WDTCONbits.SWDTEN = 1;// Turn on watchdog timer.
#endif

STKPTRbits.STKFUL = 0;// Clear any stack over/underflow errors.
STKPTRbits.STKUNF = 0;
}

...so I wouldn't expect this to make any difference at all. For completeness, WATCHDOG is undefined, so that's not it. Besides, if it were, moving the function wouldn't matter.

Lastly, since it works with app_init() called prior to the variable assignments in main(), I tried shrinking the heap and stack sizes down to a ridiculous level, and it still works. Currently it works with:

#define configMINIMAL_STACK_SIZE 76
#define configTOTAL_HEAP_SIZE (size_t) 752

It's out of memory. Why would the function location matter?

RE: xTaskCreate() question

Posted by lcj on February 26, 2009
Hmm, that last line should have read "It's *not* out of memory"...

RE: xTaskCreate() question

Posted by Richard Damon on February 26, 2009
It you are running out of task memory when using heap_1.c you need to increase the value of configTOTAL_HEAP_SIZE to allocate more memory to freeRTOS. Note that this is different then what the linker will call its "heap", that is what is available to be used by malloc and free. That is only used by freeRTOS if you use heap_3.c

RE: xTaskCreate() question

Posted by lcj on February 26, 2009
I was afraid I might cause confusion becase the last line of my previous post is wrong. It says "It's out of memory" and should read "It's *not* out of memory." I posted a note to that affect but for some reason the correction appears before the errant message, even though the correction is time-stamped 3 minutes after the message it's correcting.

I understand your comment about the RTOS heap vs. the compiler heap, and the reason I reduced the stack and heap down to such tiny sizes was to illustrate that this is not a memory problem. With the stack at 76 bytes and the heap at 752 bytes, the test program works perfectly.

At one point, I had the stack at 512 bytes per task, the heap at 3312 bytes, and the application would not run. Once I got it running, I reduced the stack and heap spaces to 76 & 712 bytes respectively, and it *still* worked. My point was, this had little or nothing to do with the problem.

The question was, why did moving the app_init() function above the led[x] assignments fix it? I hate suggesting it, but is this a compiler or an RTOS bug? It's usually neither, but this seems awfully obscure.

I'm using mcc18 v3.30, and freertos v5.1.0.

RE: xTaskCreate() question

Posted by Oleg Mazurov on February 28, 2009
> DATABANK NAME=BIG_BLOCK START=0x800 END=0xEF3

this could be the problem. Your globals may get allocated to the heap and since combining pages is against C18 rules, your code will break when you do ramdom changes. What you need to do is to read this:

https://sourceforge.net/forum/message.php?msg_id=5872795

mark your big block as protected and assign heap to big block manually.



RE: xTaskCreate() question

Posted by lcj on March 1, 2009
Thanks for the link, but I actually started that thread, and when our debate wrapped up, the conclusion was that:

1. It's not a violation of the compiler's addressing rules provided that you use 16-bit address pointers for everything within the block of combined sections.

2. You don't need to protect the space. Using the PROTECT keyword just tells the linker not to allocate anything into the space. You, therefore, have to do it all manually. This means that once you use PROTECT on a section in your linker command file, you must wrap #pragma udata/idata directives around anything you want located in that space. Nothing about how variables are addressed in large spaces changes when you do this. If you create a large combined section in the linker command file, the heap is so big that it can't go anywhere else. When the linker tries to fit the heap somewhere, it looks for a section big enough to hold it, and will find the big one you created is the only place it can go. The compiler won't allocate anything else into the space because the heap must go there, and will use it all. I prefer not to bother with the PROTECT keyword and #pragma directives because I'd rather not have to hunt through the RTOS files everytime there's a new release.

I should note that heap addressing is a dicey subject, as I'm still a bit fuzzy on how a running task gets at locally allocated variables, which may cross a 256 byte memory boundary. I ASSUME that because each task has its own stack (allocated from the heap) and you compiled with the large stack model (-ls option, right?) and all local variables are allocated off of the stack, that all local variables must be addressed using 16-bit pointers, which can cross page boundaries, so all is well.

That all said, I noticed something unrelated that seems to be the real culprit. I'm using an idle task hook, and I mistakenly thought it had to be written like a task: while(1) { ... } so it would never return. This is wrong, so I can't figure out why it ever worked. Luck, maybe? I re-wrote the idle task hook function to be able to return, and so far, so good.


[ Back to the top ]    [ About FreeRTOS ]    [ Sitemap ]    [ ]




Copyright (C) Amazon Web Services, Inc. or its affiliates. All rights reserved.

Latest News

FreeRTOS kernel V10 is available for immediate download. Now MIT licensed.


FreeRTOS Partners

ARM Connected RTOS partner for all ARM microcontroller cores

IAR Partner

Microchip Premier RTOS Partner

RTOS partner of NXP for all NXP ARM microcontrollers

STMicro RTOS partner supporting ARM7, ARM Cortex-M3, ARM Cortex-M4 and ARM Cortex-M0

Texas Instruments MCU Developer Network RTOS partner for ARM and MSP430 microcontrollers

OpenRTOS and SafeRTOS