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:
ptpaterson
Date:
Fri Nov 04 02:42:14 2016 +0000
Revision:
2:388d2c07299e
Parent:
1:ebd382c0a2b8
Child:
3:40648bfee1bc
added software filter support.  software filter automatically created for failed hardware filters if FilterAuto mode enabled.

Who changed what in which revision?

UserRevisionLine numberNew contents of line
ptpaterson 0:26f78923d093 1 #include "CanPipe.h"
ptpaterson 0:26f78923d093 2
ptpaterson 0:26f78923d093 3 /* Public Methods ************************************************************/
ptpaterson 2:388d2c07299e 4 CanPipe::CanPipe(CAN *p_can, FilterMode filter_mode):
ptpaterson 0:26f78923d093 5 p_can_(p_can),
ptpaterson 0:26f78923d093 6 filter_mode_(filter_mode)
ptpaterson 0:26f78923d093 7 {
ptpaterson 0:26f78923d093 8 num_software_filters_ = 0;
ptpaterson 0:26f78923d093 9 p_can_->attach<CanPipe>(this, &CanPipe::ReadIrq);
ptpaterson 0:26f78923d093 10 }
ptpaterson 0:26f78923d093 11
ptpaterson 0:26f78923d093 12 int CanPipe::RegisterFilter(unsigned int id,
ptpaterson 0:26f78923d093 13 unsigned int mask,
ptpaterson 0:26f78923d093 14 CANFormat format,
ptpaterson 0:26f78923d093 15 int handle)
ptpaterson 0:26f78923d093 16 {
ptpaterson 2:388d2c07299e 17 int retval = 0;
ptpaterson 2:388d2c07299e 18
ptpaterson 2:388d2c07299e 19 switch (filter_mode_) {
ptpaterson 2:388d2c07299e 20 case kFilterHardwareOnly:
ptpaterson 2:388d2c07299e 21 retval = p_can_->filter(id, mask, format, handle);
ptpaterson 2:388d2c07299e 22 break;
ptpaterson 2:388d2c07299e 23 case kFilterAuto:
ptpaterson 2:388d2c07299e 24 retval = p_can_->filter(id, mask, format, handle);
ptpaterson 2:388d2c07299e 25 if (retval) break;
ptpaterson 2:388d2c07299e 26 case kFilterSoftwareOnly:
ptpaterson 2:388d2c07299e 27 if (num_software_filters_ < kMaxHandles) {
ptpaterson 2:388d2c07299e 28 software_filters_[num_software_filters_++] =
ptpaterson 2:388d2c07299e 29 {id, mask, format, handle};
ptpaterson 2:388d2c07299e 30 retval = handle;
ptpaterson 2:388d2c07299e 31 }
ptpaterson 2:388d2c07299e 32 break;
ptpaterson 2:388d2c07299e 33 default:
ptpaterson 2:388d2c07299e 34 break;
ptpaterson 0:26f78923d093 35 }
ptpaterson 2:388d2c07299e 36
ptpaterson 2:388d2c07299e 37 return retval; //TODO: IMPLEMENT
ptpaterson 0:26f78923d093 38 }
ptpaterson 0:26f78923d093 39
ptpaterson 2:388d2c07299e 40 int CanPipe::RegisterCallback(CanMessageCallback callback, int handle) {
ptpaterson 0:26f78923d093 41 /* get new node from the pool */
ptpaterson 2:388d2c07299e 42 CallbackNode *new_node = AllocateNode(callback);
ptpaterson 0:26f78923d093 43 if (!new_node)
ptpaterson 0:26f78923d093 44 return kErrorCbNodeMemory;
ptpaterson 2:388d2c07299e 45
ptpaterson 0:26f78923d093 46 /* get the appropriate list */
ptpaterson 0:26f78923d093 47 CallbackList *list = FindListWithHandle(handle);
ptpaterson 0:26f78923d093 48 if (!list) {
ptpaterson 0:26f78923d093 49 /* no list for handle yet. Allocate it now */
ptpaterson 2:388d2c07299e 50 list = AllocateList(handle);
ptpaterson 0:26f78923d093 51 }
ptpaterson 0:26f78923d093 52 if (!list)
ptpaterson 0:26f78923d093 53 return kErrorCbListMemory;
ptpaterson 2:388d2c07299e 54
ptpaterson 0:26f78923d093 55 /* add new node to list */
ptpaterson 0:26f78923d093 56 if (list->begin) {
ptpaterson 0:26f78923d093 57 /* append to the end */
ptpaterson 0:26f78923d093 58 CallbackNode *node_iterator = list->begin;
ptpaterson 0:26f78923d093 59 while (node_iterator->next_node) {
ptpaterson 0:26f78923d093 60 node_iterator = node_iterator->next_node;
ptpaterson 0:26f78923d093 61 }
ptpaterson 0:26f78923d093 62 node_iterator->next_node = new_node;
ptpaterson 0:26f78923d093 63 } else {
ptpaterson 0:26f78923d093 64 /* this is the first node */
ptpaterson 0:26f78923d093 65 list->begin = new_node;
ptpaterson 0:26f78923d093 66 }
ptpaterson 0:26f78923d093 67 return kOkay;
ptpaterson 0:26f78923d093 68 }
ptpaterson 0:26f78923d093 69
ptpaterson 0:26f78923d093 70
ptpaterson 0:26f78923d093 71 void CanPipe::PostMessage(CANMessage msg) {
ptpaterson 0:26f78923d093 72 write_buffer.push(msg);
ptpaterson 0:26f78923d093 73 }
ptpaterson 0:26f78923d093 74
ptpaterson 0:26f78923d093 75 void CanPipe::HandleMessages(void) {
ptpaterson 0:26f78923d093 76 /* Perform callbacks for all received messages */
ptpaterson 0:26f78923d093 77 FilteredMessage filtered_msg;
ptpaterson 0:26f78923d093 78 while (read_buffer.pop(filtered_msg)) {
ptpaterson 0:26f78923d093 79 CANMessage msg = filtered_msg.msg;
ptpaterson 0:26f78923d093 80 int handle = filtered_msg.handle;
ptpaterson 2:388d2c07299e 81
ptpaterson 2:388d2c07299e 82 // software filters override any hardware filtering
ptpaterson 2:388d2c07299e 83 if (filter_mode_ == kFilterAuto || filter_mode_ == kFilterSoftwareOnly) {
ptpaterson 2:388d2c07299e 84 SoftwareFilter filter;
ptpaterson 2:388d2c07299e 85 for (int f = 0; f < num_software_filters_; ++f) {
ptpaterson 2:388d2c07299e 86 filter = software_filters_[f];
ptpaterson 2:388d2c07299e 87 if (filter.format == CANAny || msg.format == filter.format) {
ptpaterson 2:388d2c07299e 88 if ((msg.id & filter.mask) == (filter.id & filter.mask)) {
ptpaterson 2:388d2c07299e 89 handle = filter.handle;
ptpaterson 2:388d2c07299e 90 }
ptpaterson 2:388d2c07299e 91 }
ptpaterson 2:388d2c07299e 92 }
ptpaterson 2:388d2c07299e 93 }
ptpaterson 2:388d2c07299e 94
ptpaterson 0:26f78923d093 95 CallbackList *callback_list = FindListWithHandle(handle);
ptpaterson 0:26f78923d093 96 if (!callback_list)
ptpaterson 0:26f78923d093 97 return;
ptpaterson 2:388d2c07299e 98
ptpaterson 0:26f78923d093 99 CallbackNode *node_iterator = callback_list->begin;
ptpaterson 0:26f78923d093 100 while (node_iterator) {
ptpaterson 1:ebd382c0a2b8 101 int result = node_iterator->callback(msg);
ptpaterson 2:388d2c07299e 102
ptpaterson 2:388d2c07299e 103 /* if an error occurred or callback claims to have completed message
ptpaterson 0:26f78923d093 104 * handling, then leave the message chain.
ptpaterson 0:26f78923d093 105 */
ptpaterson 0:26f78923d093 106 if (result) {
ptpaterson 0:26f78923d093 107 break;
ptpaterson 0:26f78923d093 108 } else {
ptpaterson 0:26f78923d093 109 node_iterator = node_iterator->next_node;
ptpaterson 2:388d2c07299e 110 }
ptpaterson 0:26f78923d093 111 }
ptpaterson 0:26f78923d093 112 }
ptpaterson 2:388d2c07299e 113
ptpaterson 0:26f78923d093 114 /* Write all posted messages to the bus */
ptpaterson 0:26f78923d093 115 CANMessage write_msg;
ptpaterson 0:26f78923d093 116 while (write_buffer.pop(write_msg)) {
ptpaterson 0:26f78923d093 117 p_can_->write(write_msg);
ptpaterson 0:26f78923d093 118 }
ptpaterson 0:26f78923d093 119 }
ptpaterson 0:26f78923d093 120
ptpaterson 0:26f78923d093 121 /* Private Methods ************************************************************/
ptpaterson 0:26f78923d093 122 void CanPipe::ReadIrq(void) {
ptpaterson 0:26f78923d093 123 CANMessage msg;
ptpaterson 0:26f78923d093 124 int handle = p_can_->read(msg, 0);
ptpaterson 2:388d2c07299e 125
ptpaterson 0:26f78923d093 126 // TODO: raise flag if buffer is full
ptpaterson 2:388d2c07299e 127
ptpaterson 0:26f78923d093 128 FilteredMessage filtered_msg = {msg, handle};
ptpaterson 0:26f78923d093 129 read_buffer.push(filtered_msg);
ptpaterson 0:26f78923d093 130 }
ptpaterson 0:26f78923d093 131
ptpaterson 0:26f78923d093 132 CanPipe::CallbackNode* CanPipe::AllocateNode(CanMessageCallback callback) {
ptpaterson 0:26f78923d093 133 CallbackNode* result = 0;
ptpaterson 0:26f78923d093 134 if (num_nodes_ < kMaxCallbacks) {
ptpaterson 0:26f78923d093 135 result = &callback_node_pool_[num_nodes_++];
ptpaterson 0:26f78923d093 136 result->callback = callback;
ptpaterson 2:388d2c07299e 137 }
ptpaterson 0:26f78923d093 138 return result;
ptpaterson 0:26f78923d093 139 }
ptpaterson 0:26f78923d093 140
ptpaterson 0:26f78923d093 141 CanPipe::CallbackList* CanPipe::AllocateList(int handle) {
ptpaterson 0:26f78923d093 142 CallbackList* result = 0;
ptpaterson 0:26f78923d093 143 if (num_lists_ < kMaxHandles) {
ptpaterson 0:26f78923d093 144 result = &callback_list_map_[num_lists_++];
ptpaterson 0:26f78923d093 145 result->handle = handle;
ptpaterson 2:388d2c07299e 146 }
ptpaterson 0:26f78923d093 147 return result;
ptpaterson 0:26f78923d093 148 }
ptpaterson 0:26f78923d093 149
ptpaterson 0:26f78923d093 150 CanPipe::CallbackList* CanPipe::FindListWithHandle(int handle) {
ptpaterson 0:26f78923d093 151 CallbackList* result = 0;
ptpaterson 0:26f78923d093 152 /* iterate over all of the lists */
ptpaterson 0:26f78923d093 153 for (int i = 0; i < num_lists_; ++i) {
ptpaterson 0:26f78923d093 154 if (callback_list_map_[i].handle == handle) {
ptpaterson 0:26f78923d093 155 result = &callback_list_map_[i];
ptpaterson 0:26f78923d093 156 break;
ptpaterson 0:26f78923d093 157 }
ptpaterson 2:388d2c07299e 158 }
ptpaterson 0:26f78923d093 159 return result;
ptpaterson 0:26f78923d093 160 }