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.

CanPipe.h

Committer:
Paul Paterson
Date:
2017-01-24
Revision:
7:bf0682e6b1c8
Parent:
6:2ee06a07e10e

File content as of revision 7:bf0682e6b1c8:



#ifndef CANPIPE_H
#define CANPIPE_H

#include "mbed.h"
#include "CircularBuffer.h"

/** Function pointer type for user to attach and handle CAN messages
 */
typedef Callback<int(CANMessage&)> CanMessageCallback;

/** A stack on top of mbed CAN class to handle complex filtered callbacks
 */
class CanPipe
{
public:
    int temp_counter;
    /* Public Type definitions and forward declarations ***********************/
    /** Enumeration for method return values */
    enum CanPipeResult {
        kOkay = 0,
        kErrorCbNodeMemory,
        kErrorCbListMemory,
        kDone
    };

    /** Enumeration for how filters should be handled */
    enum FilterMode {
        kFilterAuto = 0,
        kFilterHardwareOnly,
        kFilterSoftwareOnly
    };

    /* Public Methods *********************************************************/
    /** Creates message handler linked with the CAN device
     *
     * @param p_can reference to CAN device
     * @param filter_mode hardware or software filtering only (Optional)
     *
     * Example:
     * @code
     * #include "mbed.h"
     * #include "CanPipe.h"
     *
     * Ticker ticker;
     * DigitalOut led1(LED1);
     *
     * CAN m_can(P0_11, P0_31);
     * CanPipe m_can_pipe(&m_can);
     *
     * char counter = 0;
     *
     * void send() {
     *     led1 = !led1;
     *     m_can_pipe.PostMessage(CANMessage(1337, &counter, 1));
     * }
     *
     * int callback1(CANMessage &msg) { return CanPipe::kOkay; }
     * int callback2(CANMessage &msg) { return CanPipe::kOkay; }
     * int callback3(CANMessage &msg) { return CanPipe::kOkay; }
     *
     * int main() {
     *     int handle;
     *
     *     handle = m_can_pipe.RegisterFilter(0x200, 0x780);
     *     m_can_pipe.RegisterCallback(callback1, handle);
     *
     *     handle = m_can_pipe.RegisterFilter(0x281, 0);
     *     m_can_pipe.RegisterCallback(callback2, handle);
     *     m_can_pipe.RegisterCallback(callback3, handle);
     *
     *     ticker.attach(send, 1);
     *
     *     while (1) {
     *         __WFI(); //sleep();
     *         m_can_pipe.HandleMessages();
     *     }
     * }
     * @endcode
     */
    CanPipe(CAN *p_can, FilterMode filter_mode = kFilterAuto);

    /** Assigns a filter to apply to CAN messages.
     * @param id 29 bit identifier to base filter on
     * @param mask Bit mask applied to the id
     * @param format CAN message format (Default CANAny)
     * @param handle Number to associate with message when passing this filter (Optional)
     *
     * Can create software filters if device fails to create filters in hardware
     */
    int RegisterFilter(unsigned int id, unsigned int mask, CANFormat format = CANAny, int handle = 0);

    /** Assigns a callback to apply to CAN messages associated with a given filter.
     * @param callback 29 bit identifier to base filter on
     * @param handle Filter handle to associate with this callback (Optional)
     *
     * Member functions can be added using the right callback Constructor
     * @code
     * canopen_class.RegisterCallback(
     *         CanMessageCallback(&object_instance, &ObjectClass::HandleMessage),
     *         handle);
     * @endcode
     */
    int RegisterCallback(CanMessageCallback callback, int handle);

    /** Stage a message to be written to the bus.
     *
     * @param msg message to write
     */
    void PostMessage(CANMessage msg);

    /** Passes all received messages through the software-filters, if any, and
     *  writes any messages that have been posted.
     *  @return Boolean 1 if a message was handled (post or receive).  0 otherwise.
     */
    int HandleMessages();

private:
    /* Private Type definitions and forward declarations **********************/
    static const int kMaxHandles = 32;
    static const int kMaxCallbacks = 64;

    struct SoftwareFilter {
        unsigned int id;
        unsigned int mask;
        CANFormat format;
        int handle;
    };

    struct CallbackNode {
        CanMessageCallback callback;
        CallbackNode *next_node;
    };

    struct CallbackList {
        int handle;
        CallbackNode *begin;
    };

    /* Private Members ********************************************************/
    CAN *p_can_;

    FilterMode filter_mode_;

    SoftwareFilter software_filters_[kMaxHandles];
    CallbackNode callback_node_pool_[kMaxCallbacks];
    CallbackList callback_list_map_[kMaxHandles];

    int num_software_filters_;
    int num_nodes_;
    int num_lists_;

    CircularBuffer<CANMessage, 16> read_buffer;
    CircularBuffer<CANMessage, 16> write_buffer;

    /* Private Methods ********************************************************/
    void ReadIrq(void);

    int FilterMessage();

    CallbackNode* AllocateNode(CanMessageCallback callback);
    CallbackList* AllocateList(int handle);

    CallbackList* FindListWithHandle(int handle);
};

#endif /* CANPIPE_H */