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:
Mon Nov 07 02:08:21 2016 +0000
Revision:
6:2ee06a07e10e
Parent:
3:40648bfee1bc
Child:
7:bf0682e6b1c8
implement lpc15xx specific interrupt bug fix.  Software filters need to be made and checked even if hardware filters are made.

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 6:2ee06a07e10e 21 //printf("creating hardware filter\r\n");
ptpaterson 2:388d2c07299e 22 retval = p_can_->filter(id, mask, format, handle);
ptpaterson 2:388d2c07299e 23 break;
ptpaterson 2:388d2c07299e 24 case kFilterAuto:
ptpaterson 6:2ee06a07e10e 25 //printf("creating hardware filter\r\n");
ptpaterson 2:388d2c07299e 26 retval = p_can_->filter(id, mask, format, handle);
ptpaterson 2:388d2c07299e 27 case kFilterSoftwareOnly:
ptpaterson 6:2ee06a07e10e 28 //printf(" creating software filter\r\n");
ptpaterson 2:388d2c07299e 29 if (num_software_filters_ < kMaxHandles) {
ptpaterson 3:40648bfee1bc 30 SoftwareFilter new_filter = {id, mask, format, handle};
ptpaterson 3:40648bfee1bc 31 software_filters_[num_software_filters_++] = new_filter;
ptpaterson 2:388d2c07299e 32 retval = handle;
ptpaterson 2:388d2c07299e 33 }
ptpaterson 2:388d2c07299e 34 break;
ptpaterson 2:388d2c07299e 35 default:
ptpaterson 2:388d2c07299e 36 break;
ptpaterson 0:26f78923d093 37 }
ptpaterson 6:2ee06a07e10e 38 //printf("\r\n");
ptpaterson 6:2ee06a07e10e 39
ptpaterson 6:2ee06a07e10e 40 return retval;
ptpaterson 0:26f78923d093 41 }
ptpaterson 0:26f78923d093 42
ptpaterson 2:388d2c07299e 43 int CanPipe::RegisterCallback(CanMessageCallback callback, int handle) {
ptpaterson 6:2ee06a07e10e 44
ptpaterson 0:26f78923d093 45 /* get new node from the pool */
ptpaterson 2:388d2c07299e 46 CallbackNode *new_node = AllocateNode(callback);
ptpaterson 0:26f78923d093 47 if (!new_node)
ptpaterson 0:26f78923d093 48 return kErrorCbNodeMemory;
ptpaterson 2:388d2c07299e 49
ptpaterson 0:26f78923d093 50 /* get the appropriate list */
ptpaterson 0:26f78923d093 51 CallbackList *list = FindListWithHandle(handle);
ptpaterson 0:26f78923d093 52 if (!list) {
ptpaterson 0:26f78923d093 53 /* no list for handle yet. Allocate it now */
ptpaterson 2:388d2c07299e 54 list = AllocateList(handle);
ptpaterson 0:26f78923d093 55 }
ptpaterson 0:26f78923d093 56 if (!list)
ptpaterson 0:26f78923d093 57 return kErrorCbListMemory;
ptpaterson 2:388d2c07299e 58
ptpaterson 0:26f78923d093 59 /* add new node to list */
ptpaterson 0:26f78923d093 60 if (list->begin) {
ptpaterson 0:26f78923d093 61 /* append to the end */
ptpaterson 0:26f78923d093 62 CallbackNode *node_iterator = list->begin;
ptpaterson 0:26f78923d093 63 while (node_iterator->next_node) {
ptpaterson 0:26f78923d093 64 node_iterator = node_iterator->next_node;
ptpaterson 0:26f78923d093 65 }
ptpaterson 0:26f78923d093 66 node_iterator->next_node = new_node;
ptpaterson 0:26f78923d093 67 } else {
ptpaterson 0:26f78923d093 68 /* this is the first node */
ptpaterson 0:26f78923d093 69 list->begin = new_node;
ptpaterson 0:26f78923d093 70 }
ptpaterson 0:26f78923d093 71 return kOkay;
ptpaterson 0:26f78923d093 72 }
ptpaterson 0:26f78923d093 73
ptpaterson 0:26f78923d093 74
ptpaterson 0:26f78923d093 75 void CanPipe::PostMessage(CANMessage msg) {
ptpaterson 0:26f78923d093 76 write_buffer.push(msg);
ptpaterson 0:26f78923d093 77 }
ptpaterson 0:26f78923d093 78
ptpaterson 0:26f78923d093 79 void CanPipe::HandleMessages(void) {
ptpaterson 0:26f78923d093 80 /* Perform callbacks for all received messages */
ptpaterson 6:2ee06a07e10e 81 CANMessage msg;
ptpaterson 6:2ee06a07e10e 82 while (read_buffer.pop(msg)) {
ptpaterson 6:2ee06a07e10e 83 printf("* Reading message 0x%03X\r\n", msg.id);
ptpaterson 6:2ee06a07e10e 84 int handle;
ptpaterson 6:2ee06a07e10e 85
ptpaterson 2:388d2c07299e 86 // software filters override any hardware filtering
ptpaterson 6:2ee06a07e10e 87 if (filter_mode_ == kFilterAuto || filter_mode_ == kFilterSoftwareOnly) {
ptpaterson 2:388d2c07299e 88 SoftwareFilter filter;
ptpaterson 2:388d2c07299e 89 for (int f = 0; f < num_software_filters_; ++f) {
ptpaterson 2:388d2c07299e 90 filter = software_filters_[f];
ptpaterson 2:388d2c07299e 91 if (filter.format == CANAny || msg.format == filter.format) {
ptpaterson 2:388d2c07299e 92 if ((msg.id & filter.mask) == (filter.id & filter.mask)) {
ptpaterson 2:388d2c07299e 93 handle = filter.handle;
ptpaterson 6:2ee06a07e10e 94 break;
ptpaterson 2:388d2c07299e 95 }
ptpaterson 2:388d2c07299e 96 }
ptpaterson 2:388d2c07299e 97 }
ptpaterson 2:388d2c07299e 98 }
ptpaterson 2:388d2c07299e 99
ptpaterson 0:26f78923d093 100 CallbackList *callback_list = FindListWithHandle(handle);
ptpaterson 0:26f78923d093 101 if (!callback_list)
ptpaterson 0:26f78923d093 102 return;
ptpaterson 6:2ee06a07e10e 103
ptpaterson 0:26f78923d093 104 CallbackNode *node_iterator = callback_list->begin;
ptpaterson 0:26f78923d093 105 while (node_iterator) {
ptpaterson 1:ebd382c0a2b8 106 int result = node_iterator->callback(msg);
ptpaterson 2:388d2c07299e 107
ptpaterson 2:388d2c07299e 108 /* if an error occurred or callback claims to have completed message
ptpaterson 0:26f78923d093 109 * handling, then leave the message chain.
ptpaterson 0:26f78923d093 110 */
ptpaterson 0:26f78923d093 111 if (result) {
ptpaterson 0:26f78923d093 112 break;
ptpaterson 0:26f78923d093 113 } else {
ptpaterson 0:26f78923d093 114 node_iterator = node_iterator->next_node;
ptpaterson 2:388d2c07299e 115 }
ptpaterson 0:26f78923d093 116 }
ptpaterson 0:26f78923d093 117 }
ptpaterson 2:388d2c07299e 118
ptpaterson 0:26f78923d093 119 /* Write all posted messages to the bus */
ptpaterson 0:26f78923d093 120 CANMessage write_msg;
ptpaterson 0:26f78923d093 121 while (write_buffer.pop(write_msg)) {
ptpaterson 6:2ee06a07e10e 122 if (p_can_->write(write_msg)) {
ptpaterson 6:2ee06a07e10e 123 /* Do something */
ptpaterson 6:2ee06a07e10e 124 } else {
ptpaterson 6:2ee06a07e10e 125 /* Do something */
ptpaterson 6:2ee06a07e10e 126 }
ptpaterson 0:26f78923d093 127 }
ptpaterson 0:26f78923d093 128 }
ptpaterson 0:26f78923d093 129
ptpaterson 0:26f78923d093 130 /* Private Methods ************************************************************/
ptpaterson 0:26f78923d093 131 void CanPipe::ReadIrq(void) {
ptpaterson 6:2ee06a07e10e 132
ptpaterson 6:2ee06a07e10e 133 // LPC15xx specific
ptpaterson 6:2ee06a07e10e 134 uint32_t can_int = LPC_C_CAN0->CANINT;
ptpaterson 6:2ee06a07e10e 135 uint32_t can_status = LPC_C_CAN0->CANSTAT;
ptpaterson 6:2ee06a07e10e 136 if (can_int & 0x8000) {
ptpaterson 6:2ee06a07e10e 137 // TODO: React to status changes
ptpaterson 6:2ee06a07e10e 138 }
ptpaterson 6:2ee06a07e10e 139 // END LPC15xx specific
ptpaterson 2:388d2c07299e 140
ptpaterson 6:2ee06a07e10e 141 CANMessage msg;
ptpaterson 6:2ee06a07e10e 142 if (p_can_->read(msg)){
ptpaterson 6:2ee06a07e10e 143 // TODO: raise flag if buffer is full
ptpaterson 6:2ee06a07e10e 144 read_buffer.push(msg);
ptpaterson 6:2ee06a07e10e 145 }
ptpaterson 0:26f78923d093 146 }
ptpaterson 0:26f78923d093 147
ptpaterson 0:26f78923d093 148 CanPipe::CallbackNode* CanPipe::AllocateNode(CanMessageCallback callback) {
ptpaterson 0:26f78923d093 149 CallbackNode* result = 0;
ptpaterson 0:26f78923d093 150 if (num_nodes_ < kMaxCallbacks) {
ptpaterson 0:26f78923d093 151 result = &callback_node_pool_[num_nodes_++];
ptpaterson 0:26f78923d093 152 result->callback = callback;
ptpaterson 2:388d2c07299e 153 }
ptpaterson 0:26f78923d093 154 return result;
ptpaterson 0:26f78923d093 155 }
ptpaterson 0:26f78923d093 156
ptpaterson 0:26f78923d093 157 CanPipe::CallbackList* CanPipe::AllocateList(int handle) {
ptpaterson 0:26f78923d093 158 CallbackList* result = 0;
ptpaterson 0:26f78923d093 159 if (num_lists_ < kMaxHandles) {
ptpaterson 0:26f78923d093 160 result = &callback_list_map_[num_lists_++];
ptpaterson 0:26f78923d093 161 result->handle = handle;
ptpaterson 2:388d2c07299e 162 }
ptpaterson 0:26f78923d093 163 return result;
ptpaterson 0:26f78923d093 164 }
ptpaterson 0:26f78923d093 165
ptpaterson 0:26f78923d093 166 CanPipe::CallbackList* CanPipe::FindListWithHandle(int handle) {
ptpaterson 0:26f78923d093 167 CallbackList* result = 0;
ptpaterson 0:26f78923d093 168 /* iterate over all of the lists */
ptpaterson 0:26f78923d093 169 for (int i = 0; i < num_lists_; ++i) {
ptpaterson 0:26f78923d093 170 if (callback_list_map_[i].handle == handle) {
ptpaterson 0:26f78923d093 171 result = &callback_list_map_[i];
ptpaterson 0:26f78923d093 172 break;
ptpaterson 0:26f78923d093 173 }
ptpaterson 2:388d2c07299e 174 }
ptpaterson 0:26f78923d093 175 return result;
ptpaterson 0:26f78923d093 176 }