![](/media/cache/img/default_profile.jpg.50x50_q85.jpg)
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; }