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
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
Generated on Tue Jul 12 2022 20:45:58 by 1.7.2