diff --git a/due_can.cpp b/due_can.cpp index b75c4ad..d47272e 100644 --- a/due_can.cpp +++ b/due_can.cpp @@ -31,6 +31,9 @@ CANRaw::CANRaw(Can* pCan, uint32_t En ) { m_pCan = pCan; enablePin = En; bigEndian = false; + busSpeed = 0; + + for (int i = 0; i < 4; i++) listener[i] = NULL; } /** @@ -117,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. @@ -132,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; @@ -176,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; } } @@ -255,6 +274,32 @@ void CANRaw::detachCANInterrupt(uint8_t mailBox) cbCANFrame[mailBox] = 0; } +boolean CANRaw::attachObj(CANListener *listener) +{ + for (int i = 0; i < SIZE_LISTENERS; i++) + { + if (this->listener[i] == NULL) + { + this->listener[i] = listener; + listener->callbacksActive = 0; + return true; + } + } + return false; +} + +boolean CANRaw::detachObj(CANListener *listener) +{ + for (int i = 0; i < SIZE_LISTENERS; i++) + { + if (this->listener[i] == listener) + { + this->listener[i] = NULL; + return true; + } + } + return false; +} /** * \brief Enable CAN Controller. @@ -1134,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; @@ -1207,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? @@ -1218,9 +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 (cbCANFrame[mb]) + { + caughtFrame = true; + (*cbCANFrame[mb])(&tempFrame); + } + else if (cbCANFrame[8]) + { + caughtFrame = true; + (*cbCANFrame[8])(&tempFrame); + } + else + { + 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); + } + } + } + } + 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) @@ -1251,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< 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 + boolean attachObj(CANListener *listener); + boolean detachObj(CANListener *listener); + + 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(); @@ -254,26 +306,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..2fc663c --- /dev/null +++ b/examples/CAN_SnoopCB_WithObjects/CAN_SnoopCB_WithObjects.ino @@ -0,0 +1,93 @@ +// 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 + //once we attach above the canbus object knows about us. The actual functions + //to attach are members of CANListener so use your class name + myClass.attachMBHandler(0); + myClass.attachMBHandler(1); + myClass.attachMBHandler(3); + myClass.attachMBHandler(4); + myClass.attachMBHandler(5); + + //set to get a callback for any other mailboxes not already covered above + myClass.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);