Provides a CanPipe class that will associate callback methods with filter handles and pipe CAN messages through them.

Dependents:   CanPipe_Example

CanPipe Library

Introduction


CanPipe is a library to make it simple to implement more complex CAN protocols, such as CANOpen. You can easily create message filters and attach callbacks associated with those filters. Callbacks for a given filter can be chained together to create complex responses to any given message.

Message Handling


The CanPipe class stores incoming and outgoing messages in buffers so that the handling of complex and timing sensitive routines and be executed outside of the interrupt context. The user must call CanPipe::HandleMessages on a regular basis to handle all incoming messages as well as actually write posted messages to the CAN bus. This call should not be made from inside of an interrupt routine.

Received Messages

When a message is received, the CanPipe class automatically adds the message to a buffer. User callbacks will be executed the next time HandleMessages is called.

Outgoing Messages

Users can use CanPipe::PostMessage to add new messages to a buffer. They will be written to the bus the next time HandleMessages is called.

Because messages are handled outside of the interrupt, it is safe to handle and post messages in response to others however you wish without threat of blocking additional messages.

Filters


To create a filter use the CanPipe::RegisterFilter method. This method gets passes an id, which is the 11- or 29-bit message id to filter for and a 32-bit mask. The mask is applied to both the incoming message ID and the filter id, which are then compared for a match. If no handle is specified, or is zero, then the next available filter handle will be used. If handle is specified as a value valid for your device, then it will override any existing filter at that handle. Filters are triggered in the order in which they are registered (In the future, it is desirable to sort software filters so that filters with the lowest handle are always triggered first). The first filter that passes is the one that is used.

An optional FilterMode can be passed during CanPipe construction.

  • CanPipe::kFilterAuto: Hardware filter will be attempted if possible, and software filter will be created (Default).
  • CanPipe::kFilterHardwareOnly: Hardware filter will be attempted, and do nothing if it fails.
  • CanPipe::kFilterSoftwareOnly: Software filter will be created.

If a hardware filter succeeds, it will prevent any unmatched messages from triggering an interrupt. A software copy of the filter is saved in order to sort messages when they are handled. Specifying kFilterHardwareOnly will turn off software filter sorting, and all callback messages should be attached to handle 0. If kFilterSoftwareOnly is specified, then all messages will be let through and cause an interrupt. The software filter will then sort the message when handled.

NOTE: Hardware filters are currently available only to LPC11Cxx (handles 1 to 31), LPC15xx (handles 1 to 31), and mbed Renesas (handle 1 only) devices.

NOTE: The filter "handle" specified here is similar to "mailbox" or "message object" used in some device user manuals.

Callbacks


Users can attach a callback of the form int(CANMessage&) to a filter in order to handle received messages. For each pending received message, during CanPipe::HandleMessages call:

  • A filter handle is obtained, either from hardware when message was received, or by executing software filters
  • A list callbacks is obtained based on the message's filter handle.
  • Each callback in the list is called in the order in which it is attached.

If a callback returns anything other than 0 (CanPipe::kOkay), the chain of callbacks will be broken. The user can use this feature to end execution in the event of an error or simply claim that the message is fully handled (i.e. returned CanPipe::kDone).

Callbacks can be attached to handle 0 in order to handle messages that failed to match a filter or in a case where no filters were created.

Known Issues


The mbed CAN api implementation for LPC15xx does not currently execute attached interrupts correctly. This library is being developed with LPC15xx hardware, so several lines of LPC15xx specific code have been added to the CanPipe::ReadIrq method to get it to work for development hardware.

Example


https://developer.mbed.org/users/ptpaterson/code/CanPipe_Example/

Example demonstrates how to create a message filter and use the returned handle to associate callback functions with messages that pass that filter.

Committer:
Paul Paterson
Date:
Tue Jan 24 19:39:06 2017 -0500
Revision:
7:bf0682e6b1c8
Parent:
6:2ee06a07e10e
commit latest changes

Who changed what in which revision?

UserRevisionLine numberNew contents of line
ptpaterson 0:26f78923d093 1
ptpaterson 0:26f78923d093 2
ptpaterson 0:26f78923d093 3 #ifndef CANPIPE_H
ptpaterson 0:26f78923d093 4 #define CANPIPE_H
ptpaterson 0:26f78923d093 5
ptpaterson 0:26f78923d093 6 #include "mbed.h"
ptpaterson 0:26f78923d093 7 #include "CircularBuffer.h"
ptpaterson 0:26f78923d093 8
ptpaterson 0:26f78923d093 9 /** Function pointer type for user to attach and handle CAN messages
ptpaterson 0:26f78923d093 10 */
ptpaterson 6:2ee06a07e10e 11 typedef Callback<int(CANMessage&)> CanMessageCallback;
ptpaterson 0:26f78923d093 12
ptpaterson 1:ebd382c0a2b8 13 /** A stack on top of mbed CAN class to handle complex filtered callbacks
ptpaterson 0:26f78923d093 14 */
ptpaterson 1:ebd382c0a2b8 15 class CanPipe
ptpaterson 1:ebd382c0a2b8 16 {
ptpaterson 1:ebd382c0a2b8 17 public:
ptpaterson 6:2ee06a07e10e 18 int temp_counter;
ptpaterson 0:26f78923d093 19 /* Public Type definitions and forward declarations ***********************/
ptpaterson 0:26f78923d093 20 /** Enumeration for method return values */
ptpaterson 0:26f78923d093 21 enum CanPipeResult {
ptpaterson 0:26f78923d093 22 kOkay = 0,
ptpaterson 0:26f78923d093 23 kErrorCbNodeMemory,
ptpaterson 0:26f78923d093 24 kErrorCbListMemory,
ptpaterson 0:26f78923d093 25 kDone
ptpaterson 0:26f78923d093 26 };
ptpaterson 1:ebd382c0a2b8 27
ptpaterson 0:26f78923d093 28 /** Enumeration for how filters should be handled */
ptpaterson 0:26f78923d093 29 enum FilterMode {
ptpaterson 0:26f78923d093 30 kFilterAuto = 0,
ptpaterson 0:26f78923d093 31 kFilterHardwareOnly,
ptpaterson 1:ebd382c0a2b8 32 kFilterSoftwareOnly
ptpaterson 0:26f78923d093 33 };
ptpaterson 1:ebd382c0a2b8 34
ptpaterson 0:26f78923d093 35 /* Public Methods *********************************************************/
ptpaterson 0:26f78923d093 36 /** Creates message handler linked with the CAN device
ptpaterson 0:26f78923d093 37 *
ptpaterson 0:26f78923d093 38 * @param p_can reference to CAN device
ptpaterson 5:4f73b59cfe7b 39 * @param filter_mode hardware or software filtering only (Optional)
ptpaterson 0:26f78923d093 40 *
ptpaterson 0:26f78923d093 41 * Example:
ptpaterson 0:26f78923d093 42 * @code
ptpaterson 1:ebd382c0a2b8 43 * #include "mbed.h"
ptpaterson 1:ebd382c0a2b8 44 * #include "CanPipe.h"
ptpaterson 1:ebd382c0a2b8 45 *
ptpaterson 1:ebd382c0a2b8 46 * Ticker ticker;
ptpaterson 1:ebd382c0a2b8 47 * DigitalOut led1(LED1);
ptpaterson 1:ebd382c0a2b8 48 *
ptpaterson 1:ebd382c0a2b8 49 * CAN m_can(P0_11, P0_31);
ptpaterson 1:ebd382c0a2b8 50 * CanPipe m_can_pipe(&m_can);
ptpaterson 1:ebd382c0a2b8 51 *
ptpaterson 1:ebd382c0a2b8 52 * char counter = 0;
ptpaterson 1:ebd382c0a2b8 53 *
ptpaterson 1:ebd382c0a2b8 54 * void send() {
ptpaterson 1:ebd382c0a2b8 55 * led1 = !led1;
ptpaterson 1:ebd382c0a2b8 56 * m_can_pipe.PostMessage(CANMessage(1337, &counter, 1));
ptpaterson 1:ebd382c0a2b8 57 * }
ptpaterson 1:ebd382c0a2b8 58 *
ptpaterson 3:40648bfee1bc 59 * int callback1(CANMessage &msg) { return CanPipe::kOkay; }
ptpaterson 5:4f73b59cfe7b 60 * int callback2(CANMessage &msg) { return CanPipe::kOkay; }
ptpaterson 5:4f73b59cfe7b 61 * int callback3(CANMessage &msg) { return CanPipe::kOkay; }
ptpaterson 1:ebd382c0a2b8 62 *
ptpaterson 1:ebd382c0a2b8 63 * int main() {
ptpaterson 1:ebd382c0a2b8 64 * int handle;
ptpaterson 1:ebd382c0a2b8 65 *
ptpaterson 2:388d2c07299e 66 * handle = m_can_pipe.RegisterFilter(0x200, 0x780);
ptpaterson 1:ebd382c0a2b8 67 * m_can_pipe.RegisterCallback(callback1, handle);
ptpaterson 1:ebd382c0a2b8 68 *
ptpaterson 1:ebd382c0a2b8 69 * handle = m_can_pipe.RegisterFilter(0x281, 0);
ptpaterson 1:ebd382c0a2b8 70 * m_can_pipe.RegisterCallback(callback2, handle);
ptpaterson 1:ebd382c0a2b8 71 * m_can_pipe.RegisterCallback(callback3, handle);
ptpaterson 1:ebd382c0a2b8 72 *
ptpaterson 1:ebd382c0a2b8 73 * ticker.attach(send, 1);
ptpaterson 1:ebd382c0a2b8 74 *
ptpaterson 1:ebd382c0a2b8 75 * while (1) {
ptpaterson 1:ebd382c0a2b8 76 * __WFI(); //sleep();
ptpaterson 1:ebd382c0a2b8 77 * m_can_pipe.HandleMessages();
ptpaterson 1:ebd382c0a2b8 78 * }
ptpaterson 1:ebd382c0a2b8 79 * }
ptpaterson 0:26f78923d093 80 * @endcode
ptpaterson 0:26f78923d093 81 */
ptpaterson 0:26f78923d093 82 CanPipe(CAN *p_can, FilterMode filter_mode = kFilterAuto);
ptpaterson 0:26f78923d093 83
Paul Paterson 7:bf0682e6b1c8 84 /** Assigns a filter to apply to CAN messages.
ptpaterson 0:26f78923d093 85 * @param id 29 bit identifier to base filter on
ptpaterson 0:26f78923d093 86 * @param mask Bit mask applied to the id
ptpaterson 0:26f78923d093 87 * @param format CAN message format (Default CANAny)
ptpaterson 0:26f78923d093 88 * @param handle Number to associate with message when passing this filter (Optional)
Paul Paterson 7:bf0682e6b1c8 89 *
Paul Paterson 7:bf0682e6b1c8 90 * Can create software filters if device fails to create filters in hardware
ptpaterson 0:26f78923d093 91 */
ptpaterson 0:26f78923d093 92 int RegisterFilter(unsigned int id, unsigned int mask, CANFormat format = CANAny, int handle = 0);
ptpaterson 1:ebd382c0a2b8 93
ptpaterson 0:26f78923d093 94 /** Assigns a callback to apply to CAN messages associated with a given filter.
ptpaterson 0:26f78923d093 95 * @param callback 29 bit identifier to base filter on
ptpaterson 0:26f78923d093 96 * @param handle Filter handle to associate with this callback (Optional)
Paul Paterson 7:bf0682e6b1c8 97 *
Paul Paterson 7:bf0682e6b1c8 98 * Member functions can be added using the right callback Constructor
Paul Paterson 7:bf0682e6b1c8 99 * @code
Paul Paterson 7:bf0682e6b1c8 100 * canopen_class.RegisterCallback(
Paul Paterson 7:bf0682e6b1c8 101 * CanMessageCallback(&object_instance, &ObjectClass::HandleMessage),
Paul Paterson 7:bf0682e6b1c8 102 * handle);
Paul Paterson 7:bf0682e6b1c8 103 * @endcode
ptpaterson 0:26f78923d093 104 */
ptpaterson 0:26f78923d093 105 int RegisterCallback(CanMessageCallback callback, int handle);
Paul Paterson 7:bf0682e6b1c8 106
ptpaterson 0:26f78923d093 107 /** Stage a message to be written to the bus.
ptpaterson 0:26f78923d093 108 *
ptpaterson 0:26f78923d093 109 * @param msg message to write
ptpaterson 0:26f78923d093 110 */
ptpaterson 0:26f78923d093 111 void PostMessage(CANMessage msg);
ptpaterson 1:ebd382c0a2b8 112
ptpaterson 5:4f73b59cfe7b 113 /** Passes all received messages through the software-filters, if any, and
Paul Paterson 7:bf0682e6b1c8 114 * writes any messages that have been posted.
Paul Paterson 7:bf0682e6b1c8 115 * @return Boolean 1 if a message was handled (post or receive). 0 otherwise.
ptpaterson 0:26f78923d093 116 */
Paul Paterson 7:bf0682e6b1c8 117 int HandleMessages();
ptpaterson 0:26f78923d093 118
ptpaterson 0:26f78923d093 119 private:
ptpaterson 0:26f78923d093 120 /* Private Type definitions and forward declarations **********************/
ptpaterson 0:26f78923d093 121 static const int kMaxHandles = 32;
ptpaterson 0:26f78923d093 122 static const int kMaxCallbacks = 64;
ptpaterson 1:ebd382c0a2b8 123
ptpaterson 0:26f78923d093 124 struct SoftwareFilter {
ptpaterson 0:26f78923d093 125 unsigned int id;
ptpaterson 0:26f78923d093 126 unsigned int mask;
ptpaterson 0:26f78923d093 127 CANFormat format;
ptpaterson 0:26f78923d093 128 int handle;
ptpaterson 0:26f78923d093 129 };
ptpaterson 1:ebd382c0a2b8 130
ptpaterson 0:26f78923d093 131 struct CallbackNode {
ptpaterson 0:26f78923d093 132 CanMessageCallback callback;
ptpaterson 0:26f78923d093 133 CallbackNode *next_node;
ptpaterson 0:26f78923d093 134 };
ptpaterson 1:ebd382c0a2b8 135
ptpaterson 0:26f78923d093 136 struct CallbackList {
ptpaterson 0:26f78923d093 137 int handle;
ptpaterson 0:26f78923d093 138 CallbackNode *begin;
ptpaterson 0:26f78923d093 139 };
ptpaterson 0:26f78923d093 140
ptpaterson 0:26f78923d093 141 /* Private Members ********************************************************/
ptpaterson 0:26f78923d093 142 CAN *p_can_;
ptpaterson 0:26f78923d093 143
ptpaterson 0:26f78923d093 144 FilterMode filter_mode_;
ptpaterson 0:26f78923d093 145
ptpaterson 1:ebd382c0a2b8 146 SoftwareFilter software_filters_[kMaxHandles];
ptpaterson 0:26f78923d093 147 CallbackNode callback_node_pool_[kMaxCallbacks];
ptpaterson 0:26f78923d093 148 CallbackList callback_list_map_[kMaxHandles];
ptpaterson 1:ebd382c0a2b8 149
ptpaterson 0:26f78923d093 150 int num_software_filters_;
ptpaterson 0:26f78923d093 151 int num_nodes_;
ptpaterson 0:26f78923d093 152 int num_lists_;
ptpaterson 1:ebd382c0a2b8 153
ptpaterson 6:2ee06a07e10e 154 CircularBuffer<CANMessage, 16> read_buffer;
ptpaterson 6:2ee06a07e10e 155 CircularBuffer<CANMessage, 16> write_buffer;
ptpaterson 1:ebd382c0a2b8 156
ptpaterson 0:26f78923d093 157 /* Private Methods ********************************************************/
ptpaterson 0:26f78923d093 158 void ReadIrq(void);
ptpaterson 1:ebd382c0a2b8 159
ptpaterson 0:26f78923d093 160 int FilterMessage();
ptpaterson 1:ebd382c0a2b8 161
ptpaterson 0:26f78923d093 162 CallbackNode* AllocateNode(CanMessageCallback callback);
ptpaterson 0:26f78923d093 163 CallbackList* AllocateList(int handle);
ptpaterson 1:ebd382c0a2b8 164
ptpaterson 0:26f78923d093 165 CallbackList* FindListWithHandle(int handle);
ptpaterson 0:26f78923d093 166 };
ptpaterson 0:26f78923d093 167
ptpaterson 0:26f78923d093 168 #endif /* CANPIPE_H */