williamdavy wrote on Monday, May 23, 2011:
The easy one first. The Timer needs to count as many ticks as necessary to generate an interrupt at the desired rate. For example, if your Timer was being driven by a 32768Hz clock, then counting to 32768 would generate an interrupt every second. Typically, for FreeRTOS you want your interrupt rate to be somewhere between 1 Hz and 1000Hz translating (1 second to 1 ms range).
First figure out what clock is driving your Timer and change the #define configCPU_CLOCK_HZ in FreeRTOSConfig.h to match. Then decide how often you want the interrupt to fire, I suggest 100Hz or every 10 ms, set the configTICK_RATE_HZ definition to 100. Then program the Timer Load register to configCPU_CLOCK_HZ / configTICK_RATE_HZ.
Next, I suggest that you enable the vApplicationTickHook() and define that function to toggle an LED or other GPIO pin every time it is called. Then you can attach a scope and verify your tick rate is correct. Alternatively, if you don’t have a scope, toggle the LED every 100 calls to the function and count how many times it toggles in a minute which will give you a good enough idea. Take a look into the partest.c file in the demo project for code to drive the GPIO.
With respect to your other problem with returning to main(), it sounds like the context switch is not quite right. I would imagine that the OS will end up somewhere strange if there are no available tasks to run. Can you confirm that the IDLE task is successfully being created (if it fails because you have run out of heap RAM, the FreeRTOS scheduler won’t start and vTaskStartScheduler() will return to main()) and that you are never calling a blocking API function from vApplicationIdleHook().
What Stack space are you using to execute the interrupt handler? I think that the context switch in the demo project will switch the stack pointer to a dedicated interrupt handler stack space, do you replicate this? Are you confident that you change to the Task’s stack pointer before doing the return from interrupt instruction, so jumping back to the correct place in the code.
Have you tried disabling vTaskSwitchContext() in the vTickISR()? Disabling the context switch in the Tick interrupt will make time progress (from the RTOS point of view because you call vTaskIncrementTick()) but your tasks will still run co-operatively because the context will only change when they call taskYIELD().
Have you included the various NOP (technically they are ‘OR r0, r0, r0’ instructions) instructions in your assembly. Sometimes the effects of instructions take a little while to go through the instruction pipeline so these can be important, especially at the end of the portRESTORE_CONTEXT macro.
Make sure that you aren’t modifying the return address when using the interrupt handler. When you yield, you need to modify the return address so that you jump over the instruction that caused the yield but when you are interrupted, you want to return back to exactly where you were.
Hopefully one of the above suggestions will help you. Generally speaking, vTaskStartScheduler() returns because either you have run out of RAM, accidentally called vTaskEndScheduler(), somehow returned to the stack of main() just after calling vStartFirstTask() and you have hit a return call or ended up in somewhere random that just happens to take you back to main().