A simple CAN adapter that supports two channels of CAN on the mbed. Configurable speed, monitor mode, statistics and send/receive via the USB serial port to a PC (or terminal program set for 921.6 kbaud)

Dependencies:   CommandProcessor Watchdog mbed

CANadapter.cpp

Committer:
WiredHome
Date:
2011-04-11
Revision:
1:6b831d0c058c
Parent:
0:ea85c59ec672

File content as of revision 1:6b831d0c058c:

/// CANadapter is a simple program that permits monitoring as well as transmitting
/// on both CAN buses that the mbed supports. It communicates to either the user or
/// a PC hosted program via the USB Serial port.
///
/// For robustness, there is a small CommandProcessor, which permits setting
/// CAN interface metrics, reviewing statistics, and sending messages.
///
/// There is also a watchdog, which will keep the system running and recover
/// if there was a problem.
///
/// @note Copyright &copr; 2011 by Smartware Computing, all rights reserved.
///     Individuals may use this application for evaluation or non-commercial
///     purposes. Within this restriction, changes may be made to this application
///     as long as this copyright notice is retained. The user shall make
///     clear that their work is a derived work, and not the original.
///     Users of this application and sources accept this application "as is" and
///     shall hold harmless Smartware Computing, for any undesired results while
///     using this application - whether real or imagined.
/// @author David Smart, Smartware Computing
///
#include "mbed.h"
#include "Watchdog.h"
#include "CommandProcessor.h"
#include "CANUtilities.h"
#include "CANQueue.h"
#include "vs_string.h"      // helpers that normalize between compilers for string functions

extern "C" void mbed_reset();

Serial pc(USBTX, USBRX);    ///!< Used as the console for interactively reporting progress
DigitalOut myled(LED4);     /// LED sign of life

Watchdog wd;

//Ticker ticker;          // for automated sending of messages during development

// Services for each of the two CAN channels
CAN can1(p9, p10);      // bind CAN1 to the hardware
CAN can2(p30, p29);     // bind CAN2 to the hardware
Timeout t1;             // create a timeout mechanism for can1
Timeout t2;             // create a timeout mechanism for can2
void c1off();           // extinquish the can1 activity indicator
void c2off();           // extinquish the can2 activity indicator

/// binding the services together for a simpler interface
struct {
    CAN *can;
    Timeout *flash;
    DigitalOut led;
    void (*off)(void);
    bool active;
    int bitrate;
    uint32_t txCounter;
    uint32_t rxCounter;
} can[] = {
    {&can1, &t1, LED1, c1off, false, 250000, 0, 0},
    {&can2, &t2, LED2, c2off, false, 250000, 0, 0}
};

// Support up to this many inbound CAN messages from the attached callbacks
CANQueue inQueue(10);

// A method to transmit a CAN message of the derived type
bool CANTransmitMsg(CANmsg msg);


// The included command processor supports interactive sessions, with these
// and the built-in commands.
RUNRESULT_T CANconfig(char *p);
const CMD_T CANconfigCmd = {"CANconfig", "Configure [channel mode speed], ? for more", CANconfig, visible};
RUNRESULT_T CANmessage(char *p);
const CMD_T CANmessageCmd = {"CANmessage", "Shows the CAN message format", CANmessage, visible};
RUNRESULT_T CANtransmit(char *p);
const CMD_T CANtransmitCmd = {"t", "transmit a CAN message (see CANmessage format)", CANtransmit, visible};
RUNRESULT_T CANstatistics(char *p);
const CMD_T CANstatisticsCmd = {"CANstats", "Shows the CAN statistics", CANstatistics, visible};
RUNRESULT_T CANreset(char *p);
const CMD_T CANresetCmd = {"CANreset", "Reset CAN channel [0|1|*]", CANreset, visible};
RUNRESULT_T Free(char *p);
const CMD_T FreeCmd = {"Free", "Shows the free memory in bytes", Free, visible};
RUNRESULT_T Reboot(char *p);
const CMD_T RebootCmd = {"Reboot", "Causes a near immediate reboot", Reboot, visible};

// Implementation of Command Processor accessed commands

RUNRESULT_T Reboot(char *p) {
    (void)p;
    pc.printf(" now...\r\n");
    wait(0.5);
    mbed_reset();
    return runok;
}

RUNRESULT_T Free(char *p) {
    (void)p;
    uint32_t max = 100000;
    uint32_t x = max / 2;
    uint32_t min = 0;

    while (min < max-1) {
        void * p = malloc(x);
        if (p) {
            free(p);
            min = x;
        } else {
            max = x;
        }
        x = (max + min)/2;
    }
    pc.printf("\r\n%u bytes free\r\n", x);
    return runok;
}

RUNRESULT_T CANconfig(char *p) {
    int ch, mode, bitrate;
    char *token;
    char *search = " ,\t";

    token = strtok(p, search);
    ch = atoi(token);
    token = strtok(NULL, search);
    if (mystrnicmp(token, "monitor", 7) == 0)
        mode = 0;
    else if (mystrnicmp(token, "0", 1) == 0)
        mode = 0;
    else if (mystrnicmp(token, "active", 7) == 0)
        mode = 1;
    else if (mystrnicmp(token, "1", 1) == 0)
        mode = 1;
    else
        mode = -1;
    token = strtok(NULL, search);
    bitrate = atoi(token);

    if (ch >=1 && ch <= 2 && mode != -1 && bitrate > 1000 && bitrate <= 1000000) {
        can[ch-1].can->monitor(mode);
        can[ch-1].can->frequency(bitrate);
        pc.printf("\r\n");
    } else {
        pc.printf("\r\n CANconfig [channel mode bits/sec]\r\n"
                  "     channel = 1 or 2\r\n"
                  "     mode    = 0|monitor|1|active\r\n"
                  "     speed   = baud rate (e.g. 10000, 250000, 500000, etc.)\r\n"
                  "");
    }
    return runok;
}

RUNRESULT_T CANreset(char *p) {
    if (*p == '1' || *p == '*')
        can[CH1].can->reset();
    if (*p == '2' || *p == '*')
        can[CH2].can->reset();
    pc.printf("\r\n");
    return runok;
}

RUNRESULT_T CANmessage(char *p) {
    pc.printf( "\r\n// CAN Message Format\r\n"
               "//\r\n"
               "// +--- 'r'eceive or 't'ransmit\r\n"
               "// |  +--- 'nrm' 11 bit identifier, 'xtd' 29 bit identifier\r\n"
               "// |  |   +--- channel '1' to '2'\r\n"
               "// |  |   |     +--- identifier in hex\r\n"
               "// |  |   |     |     +--- dlc is data length control from 0 to 8\r\n"
               "// |  |   |     |     |            +---  data bytes 1 to 8\r\n"
               "// |  |   |     |     |            |            [Below not required to send\r\n"
               "// |  |   |     |     |            |            +--- fixed zero\r\n"
               "// |  |   |     |     |            |            |   +--- err count\r\n"
               "// |  |   |     |     |            |            |   |    +--- timestamp\r\n"
               "// |  |   |     |     |            |            |   |    |\r\n"
               "// _ ___ __ ________ __ _______________________ _ ___ ___________\r\n"
               "// r xtd 02 1CF00400 08 11 22 33 44 55 66 77 88 0   0 1234.567890\r\n"
               "// t xtd 01 18EAFF03 03 EE EE 00                0   0 1235.654321\r\n"
               "// 12345678901234567890123456789012345678901234567890123456789012\r\n");
    return runok;
}

RUNRESULT_T CANtransmit(char *p) {
    if (*p) {
        CANmsg msg(p);
        if (msg.dir == xmt)         // silent failure if they try to transmit a receive msg
            CANTransmitMsg(msg);
        pc.printf("\r\n");
    } else {
        pc.printf( "\r\n't'ransmit a CAN message in the message format\r\n");
    }
    return runok;
}

RUNRESULT_T CANstatistics(char *p) {
    pc.printf("\r\n  ch    mode  bitrate rxCount rxErrors txCount txErrors\r\n");
    for (int i=0; i<CANCHANNELS; i++)
        pc.printf("  %2u %7s %8u %7u %8u %7u %8u\r\n",
                  i+1,
                  can[i].active ? "active" : "monitor",
                  can[i].bitrate,
                  can[i].rxCounter,
                  can[i].can->rderror(),
                  can[i].txCounter,
                  can[i].can->tderror()
                 );
    return runok;
}



// Helper functions provided to the command processor as a means of I/O
int mReadable() {
    return pc.readable();
}
int mGetCh() {
    return pc.getc();
}
int mPutCh(int a) {
    return pc.putc(a);
}
int mPutS(const char * s) {
    return pc.printf("%s\r\n", s);
}


// LED activity indicators need to extinguish on timeout
void c1off() {
    can[CH1].led = false;
}
void c2off() {
    can[CH2].led = false;
}

// When a CAN message is received, it is placed in queue
void canreceive(CANCHANNEL_T ch) {
    CANMessage msg;

    if (can[ch].can->read(msg)) {
        CANmsg _msg(ch, rcv, msg);

        inQueue.Enqueue(_msg);
        can[ch].rxCounter++;
        can[ch].led = true;
        can[ch].flash->attach(can[ch].off, 0.02);
    }
}

// received bound to each CAN channel
void can1rcv() {
    canreceive(CH1);
}
void can2rcv() {
    canreceive(CH2);
}

// method to transmit a CAN message of type CANmsg
bool CANTransmitMsg(CANmsg msg) {
    if (msg.dir == xmt) {
        if (can[msg.ch].can->write(CANMessage(msg.id, (char *)&msg.data, msg.len, CANData, msg.format)))
            return true;
    }
    return false;
}

// quick transmitter for testing only
void cantransmit(int ch) {
    char byte = (char)can[ch].txCounter;

    if (can[ch].can->write(CANMessage(1337, &byte, 1))) {
        can[ch].txCounter++;
    }
}

void can1send() {
    cantransmit(1);
}
void can2send() {
    cantransmit(2);
}



int main(int argc, char* argv[]) {
    CMDP_T * cp = GetCommandProcessor();
    RUNRESULT_T cp_state;

    pc.baud(921600);
    if (wd.WatchdogCausedReset())
        pc.printf("Watchdog caused reset. WD is now rearmed\r\n");
    wd.Configure(2.0);  // sets the timeout interval pretty short

    // Set up the Command Processor interface and commands.
    cp->Init(
        0xFFFF,     // Everything is enabled
        TRUE,       // Case Insensitive
        TRUE,       // Echo on
        50,         // Command Buffer length
        mReadable,  // User provided API (kbhit())
        mGetCh,     // User provided API
        mPutCh,     // User provided API
        mPutS);     // User provided API
    cp->Add(&CANconfigCmd);
    cp->Add(&CANmessageCmd);
    cp->Add(&CANtransmitCmd);
    cp->Add(&CANstatisticsCmd);
    cp->Add(&CANresetCmd);
    cp->Add(&FreeCmd);
    cp->Add(&RebootCmd);

    can2.attach(can2rcv);
    can1.attach(can1rcv);
    can[CH1].can->monitor(false);       // make them active on the network or tx errors result
    can[CH2].can->monitor(false);

    // This just sends a message every now and again
    //ticker.attach(&can1send, 1);

    // Do nothing to waste time in here... (e.g. do not "wait(1.2);")
    do {
        myled = !myled;         // activity indicator
        
        wd.Service();           // service the dog
        cp_state = cp->Run();   // user interactions on the console interface

        while (inQueue.QueueCount()) {  // If we handle messages badly, could watchdog in here
            CANmsg msg;

            if (inQueue.Dequeue(&msg)) {
                char buf[100];
                msg.FormatCANMessage(buf, sizeof(buf));
                pc.printf("%s\r\n", buf);
                // To test, just enable the following, which tosses the ball back and forth
                //wait(0.2);
                //msg.dir = xmt;      // What we received, we reflect back
                //CANTransmitMsg(msg);
            }
        }
    } while (cp_state == runok);
    cp->End();
    return 0;
}