📄 usage-mbox.html
字号:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"><title>YAVRTOS: Using mailboxes</title><link href="doxygen.css" rel="stylesheet" type="text/css"><link href="tabs.css" rel="stylesheet" type="text/css"></head><body><!-- Generated by Doxygen 1.5.4 --><div class="tabs"> <ul> <li><a href="index.html"><span>Main Page</span></a></li> <li><a href="modules.html"><span>Modules</span></a></li> <li><a href="annotated.html"><span>Data Structures</span></a></li> <li><a href="files.html"><span>Files</span></a></li> <li><a href="pages.html"><span>Related Pages</span></a></li> </ul></div><div class="nav"><a class="el" href="usage.html">Using YAVRTOS</a></div><h1><a class="anchor" name="usage-mbox">Using mailboxes </a></h1>Mailboxes are used to send data between tasks or between ISRs and tasks.<h2><a class="anchor" name="usage-mbox-1">Example 1 - distributing data throughout the application</a></h2>In this example, we have a task that generates information to be used by other parts of the application<p><div class="fragment"><pre class="fragment"><a class="code" href="structmailbox__t.html" title="Structure describing mailboxes.">mailbox_t</a> data_mbox;<span class="keywordtype">void</span> get_and_post_data();<span class="keywordtype">void</span> data_consumer_1_task(<span class="keywordtype">void</span> *p) { int16_t version; <span class="keywordtype">int</span> *data; ... version = <a class="code" href="group__mailbox.html#g9b7e62ecd1a61eda8cfa1229df0728ef" title="Get the current version of a mailbox.">get_current_mbox_version</a>(&data_mbox); <span class="keywordflow">while</span> (1) { <span class="keywordflow">if</span> ((data = (<span class="keywordtype">int</span> *)<a class="code" href="group__mailbox.html#g5dbb6937f6f93f6a1c14dec5b1c197c3" title="Wait for a mailbox to reach at least a certain version, and then start reading from...">read_mbox_min_version</a>(&data_mbox, &version)) != 0) { do_something_with_the_data(*data); } <a class="code" href="group__mailbox.html#g31b8ee5f4cd547c128143d99ea1c4a96" title="Function to call when finished reading from a mailbox.">release_mbox_read</a>(); version++; }}<span class="keywordtype">void</span> data_consumer_2_task(<span class="keywordtype">void</span> *p) { int16_t version; <span class="keywordtype">int</span> *data; ... version = <a class="code" href="group__mailbox.html#g9b7e62ecd1a61eda8cfa1229df0728ef" title="Get the current version of a mailbox.">get_current_mbox_version</a>(&data_mbox); <span class="keywordflow">while</span> (1) { <span class="keywordflow">if</span> ((data = (<span class="keywordtype">int</span> *)<a class="code" href="group__mailbox.html#g5dbb6937f6f93f6a1c14dec5b1c197c3" title="Wait for a mailbox to reach at least a certain version, and then start reading from...">read_mbox_min_version</a>(&data_mbox, &version)) != 0) { do_something_else_with_the_data(*data); } <a class="code" href="group__mailbox.html#g31b8ee5f4cd547c128143d99ea1c4a96" title="Function to call when finished reading from a mailbox.">release_mbox_read</a>(); version++; }}<span class="keywordtype">void</span> data_producer_task(<span class="keywordtype">void</span> *p) { ... <span class="keywordflow">while</span> (1) { ... get_and_post_data(); ... }}<span class="keywordtype">void</span> get_and_post_data() { <span class="keywordtype">int</span> the_data = get_data_from_some_microcontroller_system(); <a class="code" href="group__mailbox.html#gc75646248d03b1413198765b07ba54ad" title="Write to a mailbox.">write_mbox</a>(&data_mbox, &the_data, 0, 2); <span class="comment">// See below...</span>}<span class="keywordtype">int</span> main() { ... <a class="code" href="group__mailbox.html#gd4cde27abfbedc7c8e854e76d71612dd" title="Initialise a mailbox - this must be called on every mailbox before it is used.">initialise_mbox</a>(&data_mbox, 0, 0); ...}</pre></div><p>So the big question is - what do the two final parameters of <a class="el" href="group__mailbox.html#gc75646248d03b1413198765b07ba54ad" title="Write to a mailbox.">write_mbox()</a> mean?<p>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 <a class="el" href="group__mailbox.html#g5dbb6937f6f93f6a1c14dec5b1c197c3" title="Wait for a mailbox to reach at least a certain version, and then start reading from...">read_mbox_min_version()</a>), then the data will end up being "lost". It is up to the application developer to decide how many tasks, if any, <b>must</b> receive the data.<p>The second parameter means that, once the data has been put into the mailbox, the <code>data_producer_task</code> will be suspended until all of the tasks that were suspended on <a class="el" href="group__mailbox.html#g5dbb6937f6f93f6a1c14dec5b1c197c3" title="Wait for a mailbox to reach at least a certain version, and then start reading from...">read_mbox_min_version()</a> have received the data and have called <a class="el" href="group__mailbox.html#g31b8ee5f4cd547c128143d99ea1c4a96" title="Function to call when finished reading from a mailbox.">release_mbox_read()</a>. It also means that, once all the receiving tasks have called <a class="el" href="group__mailbox.html#g31b8ee5f4cd547c128143d99ea1c4a96" title="Function to call when finished reading from a mailbox.">release_mbox_read()</a>, the mailbox will contain a new version with a null pointer. This means that the <code>data_producer_task</code> is guaranteed that no-one is going to subsequently try and read the <code>the_data</code> which is important, as <code>the_data</code> is a local variable and hence ceases to exist once <code>get_and_post_data()</code> returns.<h2><a class="anchor" name="usage-mbox-2">Example 2 - sending commands to a task</a></h2>In this example, we will send commands to a task that controls an LED display.<p><div class="fragment"><pre class="fragment"><span class="comment">// This structure contains an instruction for our lcd display driver task</span><span class="keyword">typedef</span> <span class="keyword">struct </span>lcd_command_struct { uint8_t command; <span class="comment">// e.g. cursor on, clear display</span> <span class="keywordtype">char</span> <span class="keywordtype">string</span>[21]; <span class="comment">// e.g. a string to display</span> ...} lcd_command_t;<span class="comment">// The mailbox that will be used to send instructions to the lcd display driver task</span><a class="code" href="structmailbox__t.html" title="Structure describing mailboxes.">mailbox_t</a> lcd_commands_mbox;<span class="comment">// The lcd display driver task</span><span class="keywordtype">void</span> lcd_display_driver_task(<span class="keywordtype">void</span> *p) { lcd_command_t *cmd; int16_t version; initialise_lcd_display(); version = <a class="code" href="group__mailbox.html#g9b7e62ecd1a61eda8cfa1229df0728ef" title="Get the current version of a mailbox.">get_current_mbox_version</a>(&lcd_commands_mbox); <span class="keywordflow">while</span> (1) { <span class="comment">// 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</span> <span class="keywordflow">if</span> ((cmd = (lcd_command_t *)<a class="code" href="group__mailbox.html#g5dbb6937f6f93f6a1c14dec5b1c197c3" title="Wait for a mailbox to reach at least a certain version, and then start reading from...">read_mbox_min_version</a>(&lcd_commands_mbox, &version)) != 0) { process_lcd_command(cmd); } <a class="code" href="group__mailbox.html#g31b8ee5f4cd547c128143d99ea1c4a96" title="Function to call when finished reading from a mailbox.">release_mbox_read</a>(); version++; }}<span class="comment">// A task that wants to send something to the LCD</span><span class="keywordtype">void</span> data_provider_task(<span class="keywordtype">void</span> *p) { lcd_command_t cmd; <span class="keywordflow">while</span> (1) { get_information_for_lcd(&cmd); <a class="code" href="group__mailbox.html#gc75646248d03b1413198765b07ba54ad" title="Write to a mailbox.">write_mbox</a>(&lcd_commands_mbox, &cmd, 1, 1); <span class="comment">// see below...</span> }}<span class="keywordtype">int</span> main() { ... <a class="code" href="group__mailbox.html#gd4cde27abfbedc7c8e854e76d71612dd" title="Initialise a mailbox - this must be called on every mailbox before it is used.">initialise_mbox</a>(&lcd_commands_mbox, 0, 0); ...}</pre></div> So how do we choose the value for the last two arguments in <a class="el" href="group__mailbox.html#gc75646248d03b1413198765b07ba54ad" title="Write to a mailbox.">write_mbox()</a>? Assuming that the <code>lcd_display_driver_task</code> 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 <code>wait_for_receivers</code> argument of <a class="el" href="group__mailbox.html#gc75646248d03b1413198765b07ba54ad" title="Write to a mailbox.">write_mbox()</a>.<p>Also, we don't want the <code>data_provider_task</code> to make another call to get_information_for_lcd() until the <code>lcd_display_driver_task</code> has finished reading the command we have just sent it (otherwise, get_information_for_lcd() could over-write the information held in the <code>lcd_command</code> before the <code>lcd_display_driver_task</code> has finished with it). By providing a value of one for the <code>wait_for_empty_nullify</code> argument of <a class="el" href="group__mailbox.html#gc75646248d03b1413198765b07ba54ad" title="Write to a mailbox.">write_mbox()</a>, we ensure that <a class="el" href="group__mailbox.html#gc75646248d03b1413198765b07ba54ad" title="Write to a mailbox.">write_mbox()</a> won't return until the <code>lcd_display_driver_task</code> has executed <a class="el" href="group__mailbox.html#g31b8ee5f4cd547c128143d99ea1c4a96" title="Function to call when finished reading from a mailbox.">release_mbox_read()</a>.<p>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.<p>Of course, there is nothing to stop other tasks from using the <code>lcd_commands_mbox</code> - the mailbox is, in effect, a software interface to your LCD that any task can call upon at any time.<h2><a class="anchor" name="usage-mbox-3">Example 3 - sending data from an ISR to a task</a></h2>Sending information from ISRs to tasks is slightly complicated by the fact that ISRs cannot be suspended, hence they must use the <a class="el" href="group__mailbox.html#g87f02ab5b46654ae26b06851b2390132" title="Attempt to write to a mailbox.">write_mbox_now()</a> 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 <a class="el" href="group__mailbox.html#g31b8ee5f4cd547c128143d99ea1c4a96" title="Function to call when finished reading from a mailbox.">release_mbox_read()</a> before the next interrupt.<p><div class="fragment"><pre class="fragment"><span class="comment">// We cannot use a local variable to hold the ISR data, as the data still needs to exist when the ISR exits</span><span class="keywordtype">int</span> isr_data;<a class="code" href="structmailbox__t.html" title="Structure describing mailboxes.">mailbox_t</a> isr_data_mbox;uint8_t isr() { isr_data = read_data_from_some_microcontroller_system(); <a class="code" href="group__mailbox.html#g87f02ab5b46654ae26b06851b2390132" title="Attempt to write to a mailbox.">write_mbox_now</a>(&isr_data_mbox, &isr_data); <span class="keywordflow">return</span> 1; <span class="comment">// We have (probably) affected a mailbox, so the task switcher needs to run</span>}<a class="code" href="group__isr.html#g3d04938242a5060aac8a64b72c055eb0" title="The macro for ISRs.">TASK_ISR</a>(xxxx_vect, isr())void data_reading_task(<span class="keywordtype">void</span> *p) { <span class="keywordtype">int</span> isr_data_local_copy; <span class="keywordtype">int</span> *i; int16_t version = <a class="code" href="group__mailbox.html#g9b7e62ecd1a61eda8cfa1229df0728ef" title="Get the current version of a mailbox.">get_current_mbox_version</a>(&isr_data_mbox); <span class="keywordflow">while</span> (1) { <span class="keywordflow">if</span> ((i = (<span class="keywordtype">int</span> *)<a class="code" href="group__mailbox.html#g5dbb6937f6f93f6a1c14dec5b1c197c3" title="Wait for a mailbox to reach at least a certain version, and then start reading from...">read_mbox_min_version</a>(&isr_data_mbox, &version)) != 0) { isr_data_local_copy = *i; <span class="comment">// Quickly retrieve the data!</span> <a class="code" href="group__mailbox.html#g31b8ee5f4cd547c128143d99ea1c4a96" title="Function to call when finished reading from a mailbox.">release_mbox_read</a>(); <span class="comment">// The mailbox is now available to the ISR again</span> process_data(isr_data_local_copy); } <span class="keywordflow">else</span> { <a class="code" href="group__mailbox.html#g31b8ee5f4cd547c128143d99ea1c4a96" title="Function to call when finished reading from a mailbox.">release_mbox_read</a>(); <span class="comment">// A good idea to always call this after any attempt to read a mailbox...</span> } version++; }}<span class="keywordtype">int</span> main() { ... <a class="code" href="group__mailbox.html#gd4cde27abfbedc7c8e854e76d71612dd" title="Initialise a mailbox - this must be called on every mailbox before it is used.">initialise_mbox</a>(&isr_data_mbox, 0, 0); ...}</pre></div> <hr><p align="center"><font size="-1">YAVRTOS and YAVRTOS documentation Copyright © 2007-2008 Chris O'Byrne. Email - chris <at> obyrne <dot> com</font></p></body></html>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -