Chris O'Byrne - YAVRTOS |
|||||||||||
|
Using mailboxesMailboxes are used to send data between tasks or between ISRs and tasks.Example 1 - distributing data throughout the applicationIn this example, we have a task that generates information to be used by other parts of the application
mailbox_t data_mbox; void get_and_post_data(); void data_consumer_1_task(void *p) { int16_t version; int *data; ... version = get_current_mbox_version(&data_mbox); while (1) { if ((data = (int *)read_mbox_min_version(&data_mbox, &version)) != 0) { do_something_with_the_data(*data); } release_mbox_read(); version++; } } void data_consumer_2_task(void *p) { int16_t version; int *data; ... version = get_current_mbox_version(&data_mbox); while (1) { if ((data = (int *)read_mbox_min_version(&data_mbox, &version)) != 0) { do_something_else_with_the_data(*data); } release_mbox_read(); version++; } } void data_producer_task(void *p) { ... while (1) { ... get_and_post_data(); ... } } void get_and_post_data() { int the_data = get_data_from_some_microcontroller_system(); write_mbox(&data_mbox, &the_data, 0, 2); // See below... } int main() { ... initialise_mbox(&data_mbox, 0, 0); ... } So the big question is - what do the two final parameters of write_mbox() mean? The first parameter is the number of reading tasks to wait for. In this case, we decide that we do not wish to wait for any reading task - if there are no tasks waiting to read from the mailbox (i.e. no tasks have suspended themselves on read_mbox_min_version()), then the data will end up being "lost". It is up to the application developer to decide how many tasks, if any, must receive the data.
The second parameter means that, once the data has been put into the mailbox, the Example 2 - sending commands to a taskIn this example, we will send commands to a task that controls an LED display.
// This structure contains an instruction for our lcd display driver task typedef struct lcd_command_struct { uint8_t command; // e.g. cursor on, clear display char string[21]; // e.g. a string to display ... } lcd_command_t; // The mailbox that will be used to send instructions to the lcd display driver task mailbox_t lcd_commands_mbox; // The lcd display driver task void lcd_display_driver_task(void *p) { lcd_command_t *cmd; int16_t version; initialise_lcd_display(); version = get_current_mbox_version(&lcd_commands_mbox); while (1) { // When we set up the mailbox with initialise_mbox(), we set it up with null data, so our first read is going to return null if ((cmd = (lcd_command_t *)read_mbox_min_version(&lcd_commands_mbox, &version)) != 0) { process_lcd_command(cmd); } release_mbox_read(); version++; } } // A task that wants to send something to the LCD void data_provider_task(void *p) { lcd_command_t cmd; while (1) { get_information_for_lcd(&cmd); write_mbox(&lcd_commands_mbox, &cmd, 1, 1); // see below... } } int main() { ... initialise_mbox(&lcd_commands_mbox, 0, 0); ... } lcd_display_driver_task is of lower priority, we need to ensure that it has a chance to initialise the LCD display before we start sending commands to it - so we want it to get to the stage of being suspended on the read_mailbox_min_version() call. The way to do this is by using a value of 1 for the wait_for_receivers argument of write_mbox().
Also, we don't want the In this case, there is no need to "nullify" the mailbox after it has been read, because we know that no other task is going to attempt to read it again.
Of course, there is nothing to stop other tasks from using the Example 3 - sending data from an ISR to a taskSending information from ISRs to tasks is slightly complicated by the fact that ISRs cannot be suspended, hence they must use the write_mbox_now() call, which will fail if the mailbox is in use by a task. Hence all tasks that read from the mailbox should probably be of high priority, and should call release_mbox_read() before the next interrupt.
// We cannot use a local variable to hold the ISR data, as the data still needs to exist when the ISR exits int isr_data; mailbox_t isr_data_mbox; uint8_t isr() { isr_data = read_data_from_some_microcontroller_system(); write_mbox_now(&isr_data_mbox, &isr_data); return 1; // We have (probably) affected a mailbox, so the task switcher needs to run } TASK_ISR(xxxx_vect, isr()) void data_reading_task(void *p) { int isr_data_local_copy; int *i; int16_t version = get_current_mbox_version(&isr_data_mbox); while (1) { if ((i = (int *)read_mbox_min_version(&isr_data_mbox, &version)) != 0) { isr_data_local_copy = *i; // Quickly retrieve the data! release_mbox_read(); // The mailbox is now available to the ISR again process_data(isr_data_local_copy); } else { release_mbox_read(); // A good idea to always call this after any attempt to read a mailbox... } version++; } } int main() { ... initialise_mbox(&isr_data_mbox, 0, 0); ... } |
||||||||||
YAVRTOS and YAVRTOS documentation Copyright © 2007-2008 Chris O'Byrne. Email - chris <at> obyrne <dot> com