From c7faa5d5e0c3cdad06d390d417867761fbc89d54 Mon Sep 17 00:00:00 2001 From: Collin Kidder Date: Sun, 27 Sep 2015 19:38:19 -0400 Subject: [PATCH 1/3] Fix warnings that this library redefines some things that the Atmel hardware headers also define. --- due_can.h | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/due_can.h b/due_can.h index e3da0fe..4604e03 100644 --- a/due_can.h +++ b/due_can.h @@ -46,6 +46,19 @@ #define CAN_DISABLE_ALL_INTERRUPT_MASK 0xffffffff /** Define the typical baudrate for CAN communication in KHz. */ +#ifdef CAN_BPS_500K +#undef CAN_BPS_1000K +#undef CAN_BPS_800K +#undef CAN_BPS_500K +#undef CAN_BPS_250K +#undef CAN_BPS_125K +#undef CAN_BPS_50K +#undef CAN_BPS_33333 +#undef CAN_BPS_25K +#undef CAN_BPS_10K +#undef CAN_BPS_5K +#endif + #define CAN_BPS_1000K 1000000 #define CAN_BPS_800K 800000 #define CAN_BPS_500K 500000 From 1cbe03825c8c88b7091e49f2cc300b2861a90145 Mon Sep 17 00:00:00 2001 From: Collin Kidder Date: Thu, 8 Oct 2015 19:05:52 -0400 Subject: [PATCH 2/3] Added a convenient way to have object oriented callbacks in this library. Deleted the old trampoline example and added a new example that shows how to use the object oriented approach to allow a class to receive CAN frames. --- due_can.cpp | 46 ++++++- due_can.h | 59 +++++--- .../CAN_SnoopCB_Trampoline.ino | 126 ------------------ .../CAN_SnoopCB_WithObjects.ino | 91 +++++++++++++ howtouse.txt | 6 +- 5 files changed, 178 insertions(+), 150 deletions(-) delete mode 100644 examples/CAN_SnoopCB_Trampoline/CAN_SnoopCB_Trampoline.ino create mode 100644 examples/CAN_SnoopCB_WithObjects/CAN_SnoopCB_WithObjects.ino diff --git a/due_can.cpp b/due_can.cpp index b75c4ad..f4cbd18 100644 --- a/due_can.cpp +++ b/due_can.cpp @@ -31,6 +31,8 @@ CANRaw::CANRaw(Can* pCan, uint32_t En ) { m_pCan = pCan; enablePin = En; bigEndian = false; + listener = NULL; + callbacksActive = 0; //none. Bitfield were bits 0-7 are the mailboxes and bit 8 is the general callback } /** @@ -255,7 +257,38 @@ void CANRaw::detachCANInterrupt(uint8_t mailBox) cbCANFrame[mailBox] = 0; } +void CANRaw::attachObj(CANListener *listener) +{ + this->listener = listener; + callbacksActive = 0; +} + +void CANRaw::attachMBHandler(uint8_t mailBox) +{ + if (mailBox >= 0 && mailBox < 8) + { + callbacksActive |= (1<= 0 && mailBox < 8) + { + callbacksActive &= ~(1<gotFrame(&tempFrame, mb); + } + } + else if (callbacksActive & 256) //general callback for object oriented callbacks + { + listener->gotFrame(&tempFrame, -1); + } + else //if none of the callback types caught this frame then queue it in the buffer { uint8_t temp = (rx_buffer_head + 1) % SIZE_RX_BUFFER; if (temp != rx_buffer_tail) diff --git a/due_can.h b/due_can.h index 4604e03..f8b8a0d 100644 --- a/due_can.h +++ b/due_can.h @@ -167,6 +167,12 @@ typedef struct BytesUnion data; // 64 bits - lots of ways to access it. } CAN_FRAME; +class CANListener +{ +public: + virtual void gotFrame(CAN_FRAME *frame, int mailbox); +}; + class CANRaw { protected: @@ -188,6 +194,8 @@ class CANRaw bool bigEndian; void (*cbCANFrame[9])(CAN_FRAME *); //8 mailboxes plus an optional catch all + CANListener *listener; + int callbacksActive; //bitfield letting the code know which callbacks to actually try to use (for object oriented callbacks only) public: @@ -215,6 +223,37 @@ class CANRaw void enable(); void disable(); + + bool sendFrame(CAN_FRAME& txFrame); + void setWriteID(uint32_t id); + template void write(t inputValue); //write a variable # of bytes out in a frame. Uses id as the ID. + void setBigEndian(bool); + + void setCallback(int mailbox, void (*cb)(CAN_FRAME *)); + void setGeneralCallback(void (*cb)(CAN_FRAME *)); + //note that these below versions still use mailbox number. There isn't a good way around this. + void attachCANInterrupt(void (*cb)(CAN_FRAME *)); //alternative callname for setGeneralCallback + void attachCANInterrupt(uint8_t mailBox, void (*cb)(CAN_FRAME *)); + void detachCANInterrupt(uint8_t mailBox); + + //now, object oriented versions to make OO projects easier + void attachObj(CANListener *listener); + void attachMBHandler(uint8_t mailBox); + void detachMBHandler(uint8_t mailBox); + void attachGeneralHandler(); + void detachGeneralHandler(); + + void reset_all_mailbox(); + void interruptHandler(); + bool rx_avail(); + int available(); //like rx_avail but returns the number of waiting frames + + uint32_t get_rx_buff(CAN_FRAME &); + uint32_t read(CAN_FRAME &); + + //misc old cruft kept around just in case anyone actually used any of it in older code. + //some are used within the functions above. Unless you really know of a good reason to use + //any of these you probably should steer clear of them. void disable_low_power_mode(); void enable_low_power_mode(); void disable_autobaud_listen_mode(); @@ -255,26 +294,6 @@ class CANRaw void mailbox_set_datalen(uint8_t uc_index, uint8_t dlen); void mailbox_set_datal(uint8_t uc_index, uint32_t val); void mailbox_set_datah(uint8_t uc_index, uint32_t val); - - bool sendFrame(CAN_FRAME& txFrame); - void setWriteID(uint32_t id); - template void write(t inputValue); //write a variable # of bytes out in a frame. Uses id as the ID. - void setBigEndian(bool); - - void setCallback(int mailbox, void (*cb)(CAN_FRAME *)); - void setGeneralCallback(void (*cb)(CAN_FRAME *)); - //note that these below versions still use mailbox number. There isn't a good way around this. - void attachCANInterrupt(void (*cb)(CAN_FRAME *)); //alternative callname for setGeneralCallback - void attachCANInterrupt(uint8_t mailBox, void (*cb)(CAN_FRAME *)); - void detachCANInterrupt(uint8_t mailBox); - - void reset_all_mailbox(); - void interruptHandler(); - bool rx_avail(); - int available(); //like rx_avail but returns the number of waiting frames - - uint32_t get_rx_buff(CAN_FRAME &); - uint32_t read(CAN_FRAME &); }; extern CANRaw Can0; diff --git a/examples/CAN_SnoopCB_Trampoline/CAN_SnoopCB_Trampoline.ino b/examples/CAN_SnoopCB_Trampoline/CAN_SnoopCB_Trampoline.ino deleted file mode 100644 index 597b5ed..0000000 --- a/examples/CAN_SnoopCB_Trampoline/CAN_SnoopCB_Trampoline.ino +++ /dev/null @@ -1,126 +0,0 @@ -// Arduino Due - Displays all traffic found on either canbus port -//Modified version of the SnooperCallback sketch. This one illustrates -//how to fix up the issue of not being able to do a callback to a class method function - -// By Thibaut Viard/Wilfredo Molina/Collin Kidder 2013-2014 - -// Required libraries -#include "variant.h" -#include - -//Leave defined if you use native port, comment if using programming port -//This sketch could provide a lot of traffic so it might be best to use the -//native port -#define Serial SerialUSB - -class ExampleClass -{ - public: - void printFrame(CAN_FRAME *frame); -}; - -//Prints everything out in decimal instead of hex to show it is different -void ExampleClass::printFrame(CAN_FRAME *frame) -{ - Serial.print("Special OOP print frame: "); - Serial.print(" ID: "); - Serial.print(frame->id); - Serial.print(" Len: "); - Serial.print(frame->length); - Serial.print(" Data: "); - for (int count = 0; count < frame->length; count++) { - Serial.print(frame->data.bytes[count]); - Serial.print(" "); - } - Serial.print("\r\n"); -} - -ExampleClass myClass; //initialize the class global so the reference to it can be picked up anywhere - -void printFrame(CAN_FRAME *frame, int filter) { - Serial.print("Fltr: "); - if (filter > -1) Serial.print(filter); - else Serial.print("???"); - Serial.print(" ID: 0x"); - Serial.print(frame->id, HEX); - Serial.print(" Len: "); - Serial.print(frame->length); - Serial.print(" Data: 0x"); - for (int count = 0; count < frame->length; count++) { - Serial.print(frame->data.bytes[count], HEX); - Serial.print(" "); - } - Serial.print("\r\n"); -} - -void gotFrameMB0(CAN_FRAME *frame) -{ - printFrame(frame, 0); -} - -void gotFrameMB1(CAN_FRAME *frame) -{ - printFrame(frame, 1); -} - -void gotFrameMB3(CAN_FRAME *frame) -{ - printFrame(frame, 3); -} - -void gotFrameMB4(CAN_FRAME *frame) -{ - printFrame(frame, 4); -} - -void gotFrameMB5(CAN_FRAME *frame) -{ - printFrame(frame, 5); -} - -/*This is a trampoline function essentially. What that means is that it fixes up the calling convention - and then passes (bounces) to the proper function. Since the object was registered global above it - is possible to reference it here. Feel free to use more advanced schemes (such as a static reference and getReference) in actual code. -*/ -void gotFrameClass(CAN_FRAME *frame) -{ - myClass.printFrame(frame); //bounce! -} - -void setup() -{ - - Serial.begin(115200); - - // Initialize CAN0, Set the proper baud rates here - CAN.init(CAN_BPS_250K); - - //By default there are 7 RX mailboxes for each device - //extended - //syntax is mailbox, ID, mask, extended - CAN.setRXFilter(0, 0x2FF00, 0x1FF2FF00, true); - CAN.setRXFilter(1, 0x1F0000, 0x1F1F0000, true); - CAN.setRXFilter(2, 0, 0, true); //catch all mailbox - - //standard - CAN.setRXFilter(3, 0x40F, 0x7FF, false); - CAN.setRXFilter(4, 0x310, 0x7F0, false); - CAN.setRXFilter(5, 0x200, 0x700, false); - CAN.setRXFilter(6, 0, 0, false); //catch all mailbox - - //now register all of the callback functions. - CAN.setCallback(0, gotFrameMB0); - CAN.setCallback(1, gotFrameMB1); - CAN.setCallback(3, gotFrameMB3); - CAN.setCallback(4, gotFrameMB4); - CAN.setCallback(5, gotFrameMB5); - - //this function will get a callback for any mailbox that doesn't have a registered callback from above -> 2 and 6 - //CAN.setGeneralCallback(myClass.printFrame); //NO!! You can't do C-style callbacks to a member function - CAN.setGeneralCallback(gotFrameClass); //Better! You can call a C function that bounces to the C++ member function -} - -void loop(){ //note the empty loop here. All work is done via callback as frames come in - no need to poll for them -} - - diff --git a/examples/CAN_SnoopCB_WithObjects/CAN_SnoopCB_WithObjects.ino b/examples/CAN_SnoopCB_WithObjects/CAN_SnoopCB_WithObjects.ino new file mode 100644 index 0000000..871681b --- /dev/null +++ b/examples/CAN_SnoopCB_WithObjects/CAN_SnoopCB_WithObjects.ino @@ -0,0 +1,91 @@ +// Arduino Due - Displays all traffic found on either canbus port +//Modified version of the SnooperCallback sketch. This one illustrates +//how to use a class object to receive callbacks. It's a little different +//from the non-OO approach. + +// By Thibaut Viard/Wilfredo Molina/Collin Kidder 2013-2015 + +// Required libraries +#include "variant.h" +#include + +//Leave defined if you use native port, comment if using programming port +//This sketch could provide a lot of traffic so it might be best to use the +//native port +#define Serial SerialUSB + +class ExampleClass : public CANListener //CANListener provides an interface to get callbacks on this class +{ + public: + void printFrame(CAN_FRAME *frame, int mailbox); + void gotFrame(CAN_FRAME *frame, int mailbox); //overrides the parent version so we can actually do something +}; + +//Prints out the most useful information about the incoming frame. +void ExampleClass::printFrame(CAN_FRAME *frame, int mailbox) +{ + Serial.print("MB: "); + if (mailbox > -1) Serial.print(mailbox); + else Serial.print("???"); + Serial.print(" ID: 0x"); + Serial.print(frame->id, HEX); + Serial.print(" Len: "); + Serial.print(frame->length); + Serial.print(" Data: 0x"); + for (int count = 0; count < frame->length; count++) { + Serial.print(frame->data.bytes[count], HEX); + Serial.print(" "); + } + Serial.print("\r\n"); +} + +//Classes register just one method that receives all callbacks. If a frame didn't match any specific mailbox +//callback but the general callback was registered then the mailbox will be set as -1. Otherwise it is the mailbox +//with the matching filter for this frame. +void ExampleClass::gotFrame(CAN_FRAME* frame, int mailbox) +{ + this->printFrame(frame, mailbox); +} + +ExampleClass myClass; //initialize the class global so the reference to it can be picked up anywhere + +void setup() +{ + + Serial.begin(115200); + + // Initialize CAN0, Set the proper baud rates here + // Pass the proper pin for enabling your transceiver or 255 if you don't need an enable pin to be toggled. + Can0.begin(CAN_BPS_500K, 50); + + //By default there are 7 RX mailboxes for each device + //extended + //syntax is mailbox, ID, mask, extended + Can0.setRXFilter(0, 0x2FF00, 0x1FF2FF00, true); + Can0.setRXFilter(1, 0x1F0000, 0x1F1F0000, true); + Can0.setRXFilter(2, 0, 0, true); //catch all mailbox + + //standard + Can0.setRXFilter(3, 0x40F, 0x7FF, false); + Can0.setRXFilter(4, 0x310, 0x7F0, false); + Can0.setRXFilter(5, 0x200, 0x700, false); + Can0.setRXFilter(6, 0, 0, false); //catch all mailbox + + Can0.attachObj(&myClass); + //let library know we want to receive callbacks for the following mailboxes + Can0.attachMBHandler(0); + Can0.attachMBHandler(1); + Can0.attachMBHandler(3); + Can0.attachMBHandler(4); + Can0.attachMBHandler(5); + + //set to get a callback for any other mailboxes not already covered above + Can0.attachGeneralHandler(); +} + +void loop(){ //All work is done via callback as frames come in - no need to poll for them. SO, just print periodic message to show we're alive + delay(5000); + Serial.println("Still listening"); +} + + diff --git a/howtouse.txt b/howtouse.txt index 5194d79..51b2e47 100644 --- a/howtouse.txt +++ b/howtouse.txt @@ -63,11 +63,11 @@ typedef union { uint32_t high; }; struct { - uint16_t s0; + uint16_t s0; uint16_t s1; uint16_t s2; uint16_t s3; - }; + }; uint8_t bytes[8]; uint8_t byte[8]; //alternate name so you can omit the s if you feel it makes more sense } BytesUnion; @@ -192,4 +192,4 @@ the ID from the setWriteID function. Immediately tries to send the frame. This f for simple tests as it does not support sending multiple values in the same frame. Example: int SomeValue = 23423; -Can0.write(SomeValue); \ No newline at end of file +Can0.write(SomeValue); From 070851c09bac0d0d23b88e9be28f6e88c3247335 Mon Sep 17 00:00:00 2001 From: Collin Kidder Date: Sat, 10 Oct 2015 10:48:23 -0400 Subject: [PATCH 3/3] Added the ability to have multiple classes register as listeners. Updated the example sketch to show the new, slightly different syntax. Currently the limit is four listeners. Also, the begin() set of functions will only really initialize the bus if it wasn't already. It will no longer allow you to change the baud rate of an already initialized bus. For that use set_baudrate. --- due_can.cpp | 146 +++++++++++++----- due_can.h | 36 +++-- .../CAN_SnoopCB_WithObjects.ino | 14 +- 3 files changed, 141 insertions(+), 55 deletions(-) diff --git a/due_can.cpp b/due_can.cpp index f4cbd18..d47272e 100644 --- a/due_can.cpp +++ b/due_can.cpp @@ -31,8 +31,9 @@ CANRaw::CANRaw(Can* pCan, uint32_t En ) { m_pCan = pCan; enablePin = En; bigEndian = false; - listener = NULL; - callbacksActive = 0; //none. Bitfield were bits 0-7 are the mailboxes and bit 8 is the general callback + busSpeed = 0; + + for (int i = 0; i < 4; i++) listener[i] = NULL; } /** @@ -119,6 +120,10 @@ uint32_t CANRaw::begin(uint32_t baudrate, uint8_t enablePin) return init(baudrate); } +uint32_t CANRaw::getBusSpeed() +{ + return busSpeed; +} /** * \brief Initialize CAN controller. @@ -134,6 +139,10 @@ uint32_t CANRaw::init(uint32_t ul_baudrate) { uint32_t ul_flag; uint32_t ul_tick; + + if (busSpeed == ul_baudrate) return ul_baudrate; //no need to reinitialize so just return that it was a success + else if (busSpeed != 0) return 0xFFFFFFFF; //ERROR! The bus was already initialized and you're trying to change it! Use set_baudrate if you really want to do that. + //initialize all function pointers to null for (int i = 0; i < 9; i++) cbCANFrame[i] = 0; @@ -178,14 +187,22 @@ uint32_t CANRaw::init(uint32_t ul_baudrate) ul_tick++; } - NVIC_SetPriority(m_pCan == CAN0 ? CAN0_IRQn : CAN1_IRQn, 12); //set a fairly low priority so almost anything can preempt + //set a fairly low priority so almost anything can preempt. + //this has the effect that most anything can interrupt our interrupt handler + //that's a good thing because the interrupt handler is long and complicated + //and can send callbacks into user code which could also be long and complicated. + //But, keep in mind that user code in callbacks runs in interrupt context + //but can still be preempted at any time. + NVIC_SetPriority(m_pCan == CAN0 ? CAN0_IRQn : CAN1_IRQn, 12); + NVIC_EnableIRQ(m_pCan == CAN0 ? CAN0_IRQn : CAN1_IRQn); //tell the nested interrupt controller to turn on our interrupt /* Timeout or the CAN module has been synchronized with the bus. */ if (CAN_TIMEOUT == ul_tick) { return 0; } else { - return 1; + busSpeed = ul_baudrate; + return busSpeed; } } @@ -257,38 +274,33 @@ void CANRaw::detachCANInterrupt(uint8_t mailBox) cbCANFrame[mailBox] = 0; } -void CANRaw::attachObj(CANListener *listener) -{ - this->listener = listener; - callbacksActive = 0; -} - -void CANRaw::attachMBHandler(uint8_t mailBox) +boolean CANRaw::attachObj(CANListener *listener) { - if (mailBox >= 0 && mailBox < 8) + for (int i = 0; i < SIZE_LISTENERS; i++) { - callbacksActive |= (1<listener[i] == NULL) + { + this->listener[i] = listener; + listener->callbacksActive = 0; + return true; + } } + return false; } -void CANRaw::detachMBHandler(uint8_t mailBox) +boolean CANRaw::detachObj(CANListener *listener) { - if (mailBox >= 0 && mailBox < 8) + for (int i = 0; i < SIZE_LISTENERS; i++) { - callbacksActive &= ~(1<listener[i] == listener) + { + this->listener[i] = NULL; + return true; + } + } + return false; } -void CANRaw::detachGeneralHandler() -{ - callbacksActive &= ~256; -} - /** * \brief Enable CAN Controller. * @@ -1167,6 +1179,8 @@ int CANRaw::watchFor(uint32_t id, uint32_t mask) //A bit more complicated. Makes sure that the range from id1 to id2 is let through. This might open //the floodgates if you aren't careful. +//There are undoubtedly better ways to calculate the proper values for the filter but this way seems to work. +//It'll be kind of slow if you try to let a huge span through though. int CANRaw::watchForRange(uint32_t id1, uint32_t id2) { int id = 0; @@ -1240,9 +1254,12 @@ uint32_t CANRaw::getMailboxIer(int8_t mailbox) { * \brief Handle a mailbox interrupt event * \param mb which mailbox generated this event * \param ul_status The status register of the canbus hardware +* */ void CANRaw::mailbox_int_handler(uint8_t mb, uint32_t ul_status) { CAN_FRAME tempFrame; + boolean caughtFrame = false; + CANListener *thisListener; if (mb > 7) mb = 7; if (m_pCan->CAN_MB[mb].CAN_MSR & CAN_MSR_MRDY) { //mailbox signals it is ready switch(((m_pCan->CAN_MB[mb].CAN_MMR >> 24) & 7)) { //what sort of mailbox is it? @@ -1251,20 +1268,37 @@ void CANRaw::mailbox_int_handler(uint8_t mb, uint32_t ul_status) { case 4: //consumer - technically still a receive buffer mailbox_read(mb, &tempFrame); //First, try to send a callback. If no callback registered then buffer the frame. - if (cbCANFrame[mb]) (*cbCANFrame[mb])(&tempFrame); - else if (cbCANFrame[8]) (*cbCANFrame[8])(&tempFrame); - else if (callbacksActive & (1<gotFrame(&tempFrame, mb); - } + caughtFrame = true; + (*cbCANFrame[mb])(&tempFrame); + } + else if (cbCANFrame[8]) + { + caughtFrame = true; + (*cbCANFrame[8])(&tempFrame); } - else if (callbacksActive & 256) //general callback for object oriented callbacks + else { - listener->gotFrame(&tempFrame, -1); + for (int listenerPos = 0; listenerPos < SIZE_LISTENERS; listenerPos++) + { + thisListener = listener[listenerPos]; + if (thisListener != NULL) + { + if (thisListener->callbacksActive & (1 << mb)) + { + caughtFrame = true; + thisListener->gotFrame(&tempFrame, mb); + } + else if (thisListener->callbacksActive & 256) + { + caughtFrame = true; + thisListener->gotFrame(&tempFrame, -1); + } + } + } } - else //if none of the callback types caught this frame then queue it in the buffer + if (!caughtFrame) //if none of the callback types caught this frame then queue it in the buffer { uint8_t temp = (rx_buffer_head + 1) % SIZE_RX_BUFFER; if (temp != rx_buffer_tail) @@ -1295,6 +1329,44 @@ void CANRaw::mailbox_int_handler(uint8_t mb, uint32_t ul_status) { } } +CANListener::CANListener() +{ + callbacksActive = 0; //none. Bitfield were bits 0-7 are the mailboxes and bit 8 is the general callback +} + +//an empty version so that the linker doesn't complain that no implementation exists. +void CANListener::gotFrame(CAN_FRAME *frame, int mailbox) +{ + +} + +void CANListener::attachMBHandler(uint8_t mailBox) +{ + if (mailBox >= 0 && mailBox < 8) + { + callbacksActive |= (1<= 0 && mailBox < 8) + { + callbacksActive &= ~(1<