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
Diff: CANadapter.cpp
- Revision:
- 0:ea85c59ec672
- Child:
- 1:6b831d0c058c
diff -r 000000000000 -r ea85c59ec672 CANadapter.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/CANadapter.cpp Mon Apr 11 11:32:05 2011 +0000 @@ -0,0 +1,329 @@ +/// 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 + +//Timer timestamp; +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 + +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} +}; + +CANQueue inQueue(10); + +bool CANTransmitMsg(CANmsg msg); + +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}; + +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) + 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; +} + + + + +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); +} + +void c1off() { + can[CH1].led = false; +} +void c2off() { + can[CH2].led = false; +} + +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); + } +} + + +void can1rcv() { + canreceive(CH1); +} +void can2rcv() { + canreceive(CH2); +} + +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; +} + +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 + 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... + 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; +} + +