ME507 Utility Library  0.2.1
Mechatronics Course Support Software for ARM/Arduino/FreeRTOS
taskshare.h
Go to the documentation of this file.
1 /** @file taskshare.h
2  * @brief Data which can be shared between tasks in a thread-safe manner.
3  * @details This file contains a template class for data which is to be shared
4  * between tasks. The data must be protected against damage due to
5  * context switches, so it is protected by a mutex or by causing
6  * transfers to take place inside critical sections of code which are
7  * not interrupted. This version is known to work on STM32's and
8  * ESP32's only because the insertion and extraction operators @c <<
9  * and @c >> need specific functions to determine if they're running
10  * in ISR's or not.
11  *
12  * @date 2012-Oct-29 JRR Original file
13  * @date 2014-Aug-26 JRR Changed file names, class name to @c TaskShare,
14  * removed unused version that uses semaphores, renamed @c put() and
15  * @c get()
16  * @date 2014-Oct-18 JRR Added linked list of all shares for tracking and
17  * debugging
18  * @date 2020-Oct-10 JRR Made compatible with Arduino, class name to @c Share
19  * @date 2020-Nov-14 JRR Added new-ESP32 compatible @c SHARE_..._CRITICAL(x);
20  * added @c << and @c >> operators
21  * @date 2020-Nov-18 JRR Critical sections not reliable; changed to a queue
22  * @date 2021-Sep-17 JRR Changed some @c put params from references to copies
23  * @date 2021-Sep-19 JRR Added overloads for @c get() which return values
24  *
25  * @copyright This file is copyright 2014 -- 2021 by JR Ridgely and released
26  * under the Lesser GNU Public License, version 2. It intended for
27  * educational use only, but its use is not limited thereto. */
28 /* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
29  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
30  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
31  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIB-
32  * UTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
33  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
34  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
35  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
36  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
37  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
38  * THE POSSIBILITY OF SUCH DAMAGE. */
39 
40 // This define prevents this .h file from being included more than once
41 #ifndef _TASKSHARE_H_
42 #define _TASKSHARE_H_
43 
44 #include "baseshare.h" // Base class for shared data items
45 #include "FreeRTOS.h" // Main header for FreeRTOS
46 
47 /** @brief Class for data to be shared in a thread-safe manner between tasks.
48  * @details This class implements an item of data which can be shared between
49  * tasks without the risk of data corruption associated with global
50  * variables. Unlike queues, shares do not use a buffer for many data
51  * items; there is only a one-item buffer in which the most recent
52  * value of the data is kept. Shares therefore do not provide the
53  * task synchronization or incur the overhead associated with queues.
54  *
55  * The data is protected by using critical code sections (see the
56  * FreeRTOS documentation of @c portENTER_CRITICAL() ) so that tasks
57  * can't interrupt each other when reading or writing the data is
58  * taking place. This prevents data corruption due to thread
59  * switching. The C++ template mechanism is used to ensure that only
60  * data of the correct type is put into or taken from a shared data
61  * item. A @c TaskShare<DataType> object keeps its own separate copy
62  * of the data. This uses some memory, but it is necessary to
63  * reliably prevent data corruption; it prevents possible side
64  * effects from causing the sender's copy of the data from being
65  * inadvertently changed.
66  *
67  * @section usage_share Usage
68  * The following bits of code show how to set up and use a share to
69  * transfer data of type @c uint16_t from one hypothetical task
70  * called @c task_A to another called @c task_B.
71  *
72  * In the file which contains @c setup() we create a shared data
73  * object. The constructor of the @c Share<uint16_t> class is given
74  * a name for the share; the name will be shown on system diagnostic
75  * printouts. If it is desired to print diagnostic information to a
76  * serial monitor, a pointer to an output stream may also be given:
77  * @code{.cpp}
78  * #include "taskshare.h"
79  * ...
80  * /// Data from sensor number 3 on the moose's right antler
81  * Share<uint16_t> my_share ("Data_3", &Serial);
82  * @endcode
83  * If there are any tasks which use this share in other source files,
84  * we must re-declare this share with the keyword @c extern near the
85  * top of those files to make it accessible to those task(s). This
86  * copy of the share does not need a Doxygen comment:
87  * @code
88  * // Sensor 3 (right antler) data
89  * extern Share<uint16_t> my_share;
90  * @endcode
91  * In the sending task, data is put into the share:
92  * @code
93  * uint16_t a_data_item = 42; ///< Holds antler data
94  * ...
95  * a_data_item = antler3 (); // Get the data
96  * my_share.put (a_data_item); // Put data into share
97  * @endcode
98  * In the receiving task, data is read from the share:
99  * @code
100  * uint16_t got_data; ///< Holds received data
101  * ...
102  * my_share.get (got_data); // Get local copy of shared data
103  * @endcode
104  *
105  * @b The @b Easy @b Way
106  * It's also possible to use overloaded stream insertion and
107  * extraction operators to interact with the share. These operators
108  * contain code which checks if they're running in an interrupt
109  * service routine (ISR) or not and use the appropriate method to
110  * interact with the internal queue which holds the data, so they're
111  * a little slower than @c put() and @c get(). Usage is as follows,
112  * assuming that the share and local variable have been set up above:
113  * @code
114  * my_share << a_data_item; // In sending task
115  * ...
116  * my_share >> got_data; // In receiving task
117  * @endcode
118  */
119 template <class DataType> class Share : public BaseShare
120 {
121 protected:
122  /// A queue is used to hold the data, as it's portable to different CPU's
123  QueueHandle_t queue;
124 
125 public:
126  /** @brief Construct a shared data item.
127  * @details This constructor for a shared data item creates a queue in
128  * which to hold one item of data. Note that the data is @b not
129  * initialized.
130  * @param p_name A name to be shown in the list of task shares
131  * (default @c NULL)
132  */
133  Share<DataType> (const char* p_name = NULL) : BaseShare (p_name)
134  {
135  queue = xQueueCreate (1, sizeof (DataType));
136  }
137 
138  /** @brief Put data into the shared data item.
139  * @details This method is used to write data into the shared data item.
140  * @param new_data The data which is to be written
141  */
142  void put (DataType new_data)
143  {
144  xQueueOverwrite (queue, &new_data);
145  }
146 
147  /** @brief Put data into the shared data item from within an ISR.
148  * @details This method writes data from an ISR into the shared data item.
149  * It must only be called from within an interrupt service
150  * routine, not a normal task.
151  * @param new_data The data to be written into the shared data item
152  */
153  void ISR_put (DataType new_data)
154  {
155  BaseType_t wake_up;
156  xQueueOverwriteFromISR (queue, &new_data, &wake_up);
157  }
158 
159  /** @brief Operator which inserts data into the share.
160  * @details This convenient operator puts data into the share, protecting
161  * the data from corruption by thread switching. It checks if the
162  * processor is currently in an interupt service routine (ISR);
163  * if so, it calls ISR specific functions to prevent corruption,
164  * so this function may be used within an ISR or outside one. It
165  * runs a little more slowly than the @c put() method.
166  * @param new_data The data which is to be put into the share
167  */
168  void operator << (DataType new_data)
169  {
170  if (CHECK_IF_IN_ISR ())
171  {
172  BaseType_t wake_up;
173  xQueueOverwriteFromISR (queue, &new_data, &wake_up);
174  }
175  else
176  {
177  xQueueOverwrite (queue, &new_data);
178  }
179  }
180 
181  /** @brief Read data from the shared data item.
182  * @details This method is used to read data from the shared data item
183  * with critical section protection to ensure that the data
184  * cannot be corrupted by a task switch. The shared data is
185  * copied into the variable which is given as this method's
186  * parameter, replacing the previous contents. This method checks
187  * if the processor is currently in an interupt service routine
188  * (ISR) and if so, it calls ISR specific functions to prevent
189  * corruption, so this function may be used within an ISR or
190  * outside one. It runs a little more slowly than the @c get()
191  * method because of the run-time ISR check.
192  * @param put_here A reference to the variable in which to put received
193  * data
194  */
195  void operator >> (DataType put_here)
196  {
197  if (CHECK_IF_IN_ISR ())
198  {
199  // Copy the data from the queue into the receiving variable
200  xQueuePeekFromISR (queue, &put_here);
201  }
202  else
203  {
204  xQueuePeek (queue, &put_here, portMAX_DELAY);
205  }
206  }
207 
208  /** @brief Read data from the shared data item into a variable.
209  * @details This method is used to read data from the shared data item
210  * with protection to ensure that the data cannot be corrupted by
211  * a task switch. The shared data is copied into the variable
212  * which is given as this method's parameter, replacing the
213  * previous contents of that variable.
214  * @param recv_data A reference to the variable in which to put received
215  * data
216  */
217  void get (DataType& recv_data)
218  {
219  // Copy the data from the queue into the receiving variable
220  xQueuePeek (queue, &recv_data, portMAX_DELAY);
221  }
222 
223  /** @brief Read and return data from the shared data item.
224  * @details This method is used to read data from the shared data item
225  * with protection to ensure that the data cannot be corrupted by
226  * a task switch. A copy of the shared data is returned.
227  * @returns A copy of the data found in the queue, or a default value of
228  * the given data type if no data was found in the queue
229  */
230  DataType get (void)
231  {
232  DataType return_this;
233 
234  // Copy the data from the queue into the receiving variable
235  xQueuePeek (queue, &return_this, portMAX_DELAY);
236 
237  return return_this;
238  }
239 
240  /** @brief Read data from the shared data item, from within an ISR.
241  * @details This method is used to enable code within an ISR to read data
242  * from the shared data item. It must only be called from within
243  * an interrupt service routine, not a normal task.
244  * @param recv_data A reference to the variable in which to put received
245  * data
246  */
247  void ISR_get (DataType& recv_data)
248  {
249  xQueuePeekFromISR (queue, &recv_data);
250  }
251 
252  /** @brief Read and return data from the shared data item, from within an
253  * ISR.
254  * @details This method is used to enable code within an ISR to read data
255  * from the shared data item. It must only be called from within
256  * an interrupt service routine, not a normal task.
257  * @returns A copy of the data found in the queue, or a default value of
258  * the given data type if no data was found in the queue
259  */
260  DataType ISR_get (void)
261  {
262  DataType return_this;
263  xQueuePeekFromISR (queue, &return_this);
264  return return_this;
265  }
266 
267  // Print the share's status within a list of all shares' statuses
268  void print_in_list (Print& printer);
269 
270 }; // class TaskShare<DataType>
271 
272 
273 /** @brief Print the name and type (share) of this data item.
274  * @details This method prints the share's name and a word indicating that it
275  * is a shared data item, as opposed to a queue, formatted to match
276  * similar printouts from other task shares such as queues. After
277  * printing this share's information, it looks in the linked list of
278  * shares for the next one and asks it to print its information too.
279  * @param printer Reference to a serial device on which to print the status
280  */
281 template <class DataType>
282 void Share<DataType>::print_in_list (Print& printer)
283 {
284  // Print this task's name and pad it to 16 characters
285  printer.printf ("%-16sshare\t", name);
286 
287  // End the line
288  printer << endl;
289 
290  // Call the next item
291  if (p_next != NULL)
292  {
293  p_next->print_in_list (printer);
294  }
295 }
296 
297 #endif // _TASKSHARE_H_
Share::print_in_list
void print_in_list(Print &printer)
Print the name and type (share) of this data item.
Definition: taskshare.h:282
Share::ISR_put
void ISR_put(DataType new_data)
Put data into the shared data item from within an ISR.
Definition: taskshare.h:153
Share::operator<<
void operator<<(DataType new_data)
Operator which inserts data into the share.
Definition: taskshare.h:168
Share::operator>>
void operator>>(DataType put_here)
Read data from the shared data item.
Definition: taskshare.h:195
Share::get
void get(DataType &recv_data)
Read data from the shared data item into a variable.
Definition: taskshare.h:217
Share
Class for data to be shared in a thread-safe manner between tasks.
Definition: taskshare.h:119
Share::queue
QueueHandle_t queue
A queue is used to hold the data, as it's portable to different CPU's.
Definition: taskshare.h:123
baseshare.h
Headers for a base class for type-safe, thread-safe task data exchange classes.
Share::ISR_get
void ISR_get(DataType &recv_data)
Read data from the shared data item, from within an ISR.
Definition: taskshare.h:247
Share::get
DataType get(void)
Read and return data from the shared data item.
Definition: taskshare.h:230
Share::put
void put(DataType new_data)
Put data into the shared data item.
Definition: taskshare.h:142
BaseShare
Base class for classes that share data in a thread-safe manner between tasks.
Definition: baseshare.h:54
Share::ISR_get
DataType ISR_get(void)
Read and return data from the shared data item, from within an ISR.
Definition: taskshare.h:260