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

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers CANadapter.cpp Source File

CANadapter.cpp

00001 /// CANadapter is a simple program that permits monitoring as well as transmitting
00002 /// on both CAN buses that the mbed supports. It communicates to either the user or
00003 /// a PC hosted program via the USB Serial port.
00004 ///
00005 /// For robustness, there is a small CommandProcessor, which permits setting
00006 /// CAN interface metrics, reviewing statistics, and sending messages.
00007 ///
00008 /// There is also a watchdog, which will keep the system running and recover
00009 /// if there was a problem.
00010 ///
00011 /// @note Copyright &copr; 2011 by Smartware Computing, all rights reserved.
00012 ///     Individuals may use this application for evaluation or non-commercial
00013 ///     purposes. Within this restriction, changes may be made to this application
00014 ///     as long as this copyright notice is retained. The user shall make
00015 ///     clear that their work is a derived work, and not the original.
00016 ///     Users of this application and sources accept this application "as is" and
00017 ///     shall hold harmless Smartware Computing, for any undesired results while
00018 ///     using this application - whether real or imagined.
00019 /// @author David Smart, Smartware Computing
00020 ///
00021 #include "mbed.h"
00022 #include "Watchdog.h"
00023 #include "CommandProcessor.h"
00024 #include "CANUtilities.h"
00025 #include "CANQueue.h"
00026 #include "vs_string.h"      // helpers that normalize between compilers for string functions
00027 
00028 extern "C" void mbed_reset();
00029 
00030 Serial pc(USBTX, USBRX);    ///!< Used as the console for interactively reporting progress
00031 DigitalOut myled(LED4);     /// LED sign of life
00032 
00033 Watchdog wd;
00034 
00035 //Ticker ticker;          // for automated sending of messages during development
00036 
00037 // Services for each of the two CAN channels
00038 CAN can1(p9, p10);      // bind CAN1 to the hardware
00039 CAN can2(p30, p29);     // bind CAN2 to the hardware
00040 Timeout t1;             // create a timeout mechanism for can1
00041 Timeout t2;             // create a timeout mechanism for can2
00042 void c1off();           // extinquish the can1 activity indicator
00043 void c2off();           // extinquish the can2 activity indicator
00044 
00045 /// binding the services together for a simpler interface
00046 struct {
00047     CAN *can;
00048     Timeout *flash;
00049     DigitalOut led;
00050     void (*off)(void);
00051     bool active;
00052     int bitrate;
00053     uint32_t txCounter;
00054     uint32_t rxCounter;
00055 } can[] = {
00056     {&can1, &t1, LED1, c1off, false, 250000, 0, 0},
00057     {&can2, &t2, LED2, c2off, false, 250000, 0, 0}
00058 };
00059 
00060 // Support up to this many inbound CAN messages from the attached callbacks
00061 CANQueue inQueue(10);
00062 
00063 // A method to transmit a CAN message of the derived type
00064 bool CANTransmitMsg(CANmsg msg);
00065 
00066 
00067 // The included command processor supports interactive sessions, with these
00068 // and the built-in commands.
00069 RUNRESULT_T CANconfig(char *p);
00070 const CMD_T CANconfigCmd = {"CANconfig", "Configure [channel mode speed], ? for more", CANconfig, visible};
00071 RUNRESULT_T CANmessage(char *p);
00072 const CMD_T CANmessageCmd = {"CANmessage", "Shows the CAN message format", CANmessage, visible};
00073 RUNRESULT_T CANtransmit(char *p);
00074 const CMD_T CANtransmitCmd = {"t", "transmit a CAN message (see CANmessage format)", CANtransmit, visible};
00075 RUNRESULT_T CANstatistics(char *p);
00076 const CMD_T CANstatisticsCmd = {"CANstats", "Shows the CAN statistics", CANstatistics, visible};
00077 RUNRESULT_T CANreset(char *p);
00078 const CMD_T CANresetCmd = {"CANreset", "Reset CAN channel [0|1|*]", CANreset, visible};
00079 RUNRESULT_T Free(char *p);
00080 const CMD_T FreeCmd = {"Free", "Shows the free memory in bytes", Free, visible};
00081 RUNRESULT_T Reboot(char *p);
00082 const CMD_T RebootCmd = {"Reboot", "Causes a near immediate reboot", Reboot, visible};
00083 
00084 // Implementation of Command Processor accessed commands
00085 
00086 RUNRESULT_T Reboot(char *p) {
00087     (void)p;
00088     pc.printf(" now...\r\n");
00089     wait(0.5);
00090     mbed_reset();
00091     return runok;
00092 }
00093 
00094 RUNRESULT_T Free(char *p) {
00095     (void)p;
00096     uint32_t max = 100000;
00097     uint32_t x = max / 2;
00098     uint32_t min = 0;
00099 
00100     while (min < max-1) {
00101         void * p = malloc(x);
00102         if (p) {
00103             free(p);
00104             min = x;
00105         } else {
00106             max = x;
00107         }
00108         x = (max + min)/2;
00109     }
00110     pc.printf("\r\n%u bytes free\r\n", x);
00111     return runok;
00112 }
00113 
00114 RUNRESULT_T CANconfig(char *p) {
00115     int ch, mode, bitrate;
00116     char *token;
00117     char *search = " ,\t";
00118 
00119     token = strtok(p, search);
00120     ch = atoi(token);
00121     token = strtok(NULL, search);
00122     if (mystrnicmp(token, "monitor", 7) == 0)
00123         mode = 0;
00124     else if (mystrnicmp(token, "0", 1) == 0)
00125         mode = 0;
00126     else if (mystrnicmp(token, "active", 7) == 0)
00127         mode = 1;
00128     else if (mystrnicmp(token, "1", 1) == 0)
00129         mode = 1;
00130     else
00131         mode = -1;
00132     token = strtok(NULL, search);
00133     bitrate = atoi(token);
00134 
00135     if (ch >=1 && ch <= 2 && mode != -1 && bitrate > 1000 && bitrate <= 1000000) {
00136         can[ch-1].can->monitor(mode);
00137         can[ch-1].can->frequency(bitrate);
00138         pc.printf("\r\n");
00139     } else {
00140         pc.printf("\r\n CANconfig [channel mode bits/sec]\r\n"
00141                   "     channel = 1 or 2\r\n"
00142                   "     mode    = 0|monitor|1|active\r\n"
00143                   "     speed   = baud rate (e.g. 10000, 250000, 500000, etc.)\r\n"
00144                   "");
00145     }
00146     return runok;
00147 }
00148 
00149 RUNRESULT_T CANreset(char *p) {
00150     if (*p == '1' || *p == '*')
00151         can[CH1].can->reset();
00152     if (*p == '2' || *p == '*')
00153         can[CH2].can->reset();
00154     pc.printf("\r\n");
00155     return runok;
00156 }
00157 
00158 RUNRESULT_T CANmessage(char *p) {
00159     pc.printf( "\r\n// CAN Message Format\r\n"
00160                "//\r\n"
00161                "// +--- 'r'eceive or 't'ransmit\r\n"
00162                "// |  +--- 'nrm' 11 bit identifier, 'xtd' 29 bit identifier\r\n"
00163                "// |  |   +--- channel '1' to '2'\r\n"
00164                "// |  |   |     +--- identifier in hex\r\n"
00165                "// |  |   |     |     +--- dlc is data length control from 0 to 8\r\n"
00166                "// |  |   |     |     |            +---  data bytes 1 to 8\r\n"
00167                "// |  |   |     |     |            |            [Below not required to send\r\n"
00168                "// |  |   |     |     |            |            +--- fixed zero\r\n"
00169                "// |  |   |     |     |            |            |   +--- err count\r\n"
00170                "// |  |   |     |     |            |            |   |    +--- timestamp\r\n"
00171                "// |  |   |     |     |            |            |   |    |\r\n"
00172                "// _ ___ __ ________ __ _______________________ _ ___ ___________\r\n"
00173                "// r xtd 02 1CF00400 08 11 22 33 44 55 66 77 88 0   0 1234.567890\r\n"
00174                "// t xtd 01 18EAFF03 03 EE EE 00                0   0 1235.654321\r\n"
00175                "// 12345678901234567890123456789012345678901234567890123456789012\r\n");
00176     return runok;
00177 }
00178 
00179 RUNRESULT_T CANtransmit(char *p) {
00180     if (*p) {
00181         CANmsg msg(p);
00182         if (msg.dir == xmt)         // silent failure if they try to transmit a receive msg
00183             CANTransmitMsg(msg);
00184         pc.printf("\r\n");
00185     } else {
00186         pc.printf( "\r\n't'ransmit a CAN message in the message format\r\n");
00187     }
00188     return runok;
00189 }
00190 
00191 RUNRESULT_T CANstatistics(char *p) {
00192     pc.printf("\r\n  ch    mode  bitrate rxCount rxErrors txCount txErrors\r\n");
00193     for (int i=0; i<CANCHANNELS; i++)
00194         pc.printf("  %2u %7s %8u %7u %8u %7u %8u\r\n",
00195                   i+1,
00196                   can[i].active ? "active" : "monitor",
00197                   can[i].bitrate,
00198                   can[i].rxCounter,
00199                   can[i].can->rderror(),
00200                   can[i].txCounter,
00201                   can[i].can->tderror()
00202                  );
00203     return runok;
00204 }
00205 
00206 
00207 
00208 // Helper functions provided to the command processor as a means of I/O
00209 int mReadable() {
00210     return pc.readable();
00211 }
00212 int mGetCh() {
00213     return pc.getc();
00214 }
00215 int mPutCh(int a) {
00216     return pc.putc(a);
00217 }
00218 int mPutS(const char * s) {
00219     return pc.printf("%s\r\n", s);
00220 }
00221 
00222 
00223 // LED activity indicators need to extinguish on timeout
00224 void c1off() {
00225     can[CH1].led = false;
00226 }
00227 void c2off() {
00228     can[CH2].led = false;
00229 }
00230 
00231 // When a CAN message is received, it is placed in queue
00232 void canreceive(CANCHANNEL_T ch) {
00233     CANMessage msg;
00234 
00235     if (can[ch].can->read(msg)) {
00236         CANmsg _msg(ch, rcv, msg);
00237 
00238         inQueue.Enqueue(_msg);
00239         can[ch].rxCounter++;
00240         can[ch].led = true;
00241         can[ch].flash->attach(can[ch].off, 0.02);
00242     }
00243 }
00244 
00245 // received bound to each CAN channel
00246 void can1rcv() {
00247     canreceive(CH1);
00248 }
00249 void can2rcv() {
00250     canreceive(CH2);
00251 }
00252 
00253 // method to transmit a CAN message of type CANmsg
00254 bool CANTransmitMsg(CANmsg msg) {
00255     if (msg.dir == xmt) {
00256         if (can[msg.ch].can->write(CANMessage(msg.id, (char *)&msg.data, msg.len, CANData, msg.format)))
00257             return true;
00258     }
00259     return false;
00260 }
00261 
00262 // quick transmitter for testing only
00263 void cantransmit(int ch) {
00264     char byte = (char)can[ch].txCounter;
00265 
00266     if (can[ch].can->write(CANMessage(1337, &byte, 1))) {
00267         can[ch].txCounter++;
00268     }
00269 }
00270 
00271 void can1send() {
00272     cantransmit(1);
00273 }
00274 void can2send() {
00275     cantransmit(2);
00276 }
00277 
00278 
00279 
00280 int main(int argc, char* argv[]) {
00281     CMDP_T * cp = GetCommandProcessor();
00282     RUNRESULT_T cp_state;
00283 
00284     pc.baud(921600);
00285     if (wd.WatchdogCausedReset())
00286         pc.printf("Watchdog caused reset. WD is now rearmed\r\n");
00287     wd.Configure(2.0);  // sets the timeout interval pretty short
00288 
00289     // Set up the Command Processor interface and commands.
00290     cp->Init(
00291         0xFFFF,     // Everything is enabled
00292         TRUE,       // Case Insensitive
00293         TRUE,       // Echo on
00294         50,         // Command Buffer length
00295         mReadable,  // User provided API (kbhit())
00296         mGetCh,     // User provided API
00297         mPutCh,     // User provided API
00298         mPutS);     // User provided API
00299     cp->Add(&CANconfigCmd);
00300     cp->Add(&CANmessageCmd);
00301     cp->Add(&CANtransmitCmd);
00302     cp->Add(&CANstatisticsCmd);
00303     cp->Add(&CANresetCmd);
00304     cp->Add(&FreeCmd);
00305     cp->Add(&RebootCmd);
00306 
00307     can2.attach(can2rcv);
00308     can1.attach(can1rcv);
00309     can[CH1].can->monitor(false);       // make them active on the network or tx errors result
00310     can[CH2].can->monitor(false);
00311 
00312     // This just sends a message every now and again
00313     //ticker.attach(&can1send, 1);
00314 
00315     // Do nothing to waste time in here... (e.g. do not "wait(1.2);")
00316     do {
00317         myled = !myled;         // activity indicator
00318         
00319         wd.Service();           // service the dog
00320         cp_state = cp->Run();   // user interactions on the console interface
00321 
00322         while (inQueue.QueueCount()) {  // If we handle messages badly, could watchdog in here
00323             CANmsg msg;
00324 
00325             if (inQueue.Dequeue(&msg)) {
00326                 char buf[100];
00327                 msg.FormatCANMessage(buf, sizeof(buf));
00328                 pc.printf("%s\r\n", buf);
00329                 // To test, just enable the following, which tosses the ball back and forth
00330                 //wait(0.2);
00331                 //msg.dir = xmt;      // What we received, we reflect back
00332                 //CANTransmitMsg(msg);
00333             }
00334         }
00335     } while (cp_state == runok);
00336     cp->End();
00337     return 0;
00338 }
00339 
00340