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