Monday, January 25, 2016

Understanding Blinky - part 1

Ok, all compiled, all working, but nothing was truly understood. Let's dig through Blinky's code and study a bit about how does it work. It probably won't make much difference, but I'll be using the Eclipse + MinGW version to explain. Just for me to remember, this project creates two tasks and a queue. One task is the Sender (it posts a message on the queue) and the other is the Receiver (it receives the message from the queue and prints another message on the screen).

main_blinky()

We'll start checking main_blinky() function (main_blinky.c, line 162), since I believe it's a little more practical than going through FreeRTOS' main() function. The first thing it does is to create a queue (xQueueCreate() [1]), with mainQUEUE_LENGTH positions (1 position only), each of them with sizeof( unsigned long ) bytes (in my case, 4 bytes). A queue is a simple way to send messages from one task to the other, usually used as a FIFO. In this case, the queue identifier (xQueue) is a global variable that can be used by both tasks.

Moving on, the tasks are created with xTaskCreate() and the parameters are [2]:

  • A pointer to the function that implements the task (prvQueueReceiveTask for the Receiver and prvQueueSendTask for the Sender)
  • The name of the task, only used for easier debug ("RX" and "TX")
  • The size of the stack, in Words (configMINIMAL_STACK_SIZE for both, which is 50 Words, or 200 bytes, since each Word is 4 bytes wide)
  • A pointer to the parameter(s) sent to the task (mainQUEUE_RECEIVE_PARAMETER and mainQUEUE_SEND_PARAMETER, used just to show how the functionality works; it is important to notice that the content must exist during the entire time the task is running, thus declaring it static is a good idea, otherwise, the task could access some address that no longer exists)
  • The priority of the task (mainQUEUE_RECEIVE_TASK_PRIORITY for the receiver and mainQUEUE_SEND_TASK_PRIORITY for the sender; in this case, the receiver has a higher priority and both the task have priorities higher than the Idle Task, which has the lowest possible priority)
  • A handler to the task, which can be used to check if the task was created, to delete the task, usw. (this is not used on Blinky).
Finally, the scheduler is started and the main_blinky ends on an endless loop.

prvQueueSendTask

The Sender uses a few local variables: xNextWakeTime, which holds the moment, in ticks, when the task will wake up; ulValueToSend, which holds the value to be stored on the queue; and xBlockTime, which stored the amount of time, in ticks, that the task will be sleeping (blocked state - 200 ms). The task starts by checking whether the parameters are right using the configASSERT() function, and if they're not, it will stop the execution. The xNextWakeTime is initialized with the amount of ticks since the scheduler started (xTaskGetTickCount()). The endless loop contains a delay function (vTaskDelayUntil()), which receives as parameters the previous wake time (currently stored on xNextWakeTime) and the amount of ticks the task will remain in blocked stated (xBlockTime), and the post-to-queue funtion (xQueueSend(), which is equivalent to xQueueSendToBack(), thus using the queue as a FIFO [3]), which receives as parameters the handler/identifier of the queue (xQueue, the global variable), the value to be sent (ulValueToSend - the value will be copied to the queue) and the amount of time, in ticks, that the function should wait if the queue is full (for Blinky, the queue should always be empty by the time this function is called, so the wait period is zero).

prvQueueReceiveTask

The Receiver only uses one local variable: ulReceivedValue, which will be filled with the valued stored on the queue. Similar to the Sender, the first action is to check whether the parameters received by the task are right and stop the execution in case they're not. The endless loop will receive the value from the queue and, if the value is right, will print a message. The function used to receive is xQueueReceive() [4], which receives as parameters the handler/identifier of the queue (xQueue), a place to store the value read (ulReceivedValue) and the amount of time, in ticks, it will wait in blocked mode until there is something on the queue (portMAX_DELAY, which is the maximum amount of time possible; and, as INCLUDE_vTaskSuspend is set to 1 on FreeRTOSConfig.h, the task may wait forever, or actually until the Sender posts a messsage). The rest of the task is pretty straight forward.


Idle Task? Scheduler? Blocked mode? Scenes for our next episode...

No comments:

Post a Comment