Skip to content

Commit

Permalink
Added the ability to have multiple classes register as listeners.
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
collin80 committed Oct 10, 2015
1 parent 1cbe038 commit 070851c
Show file tree
Hide file tree
Showing 3 changed files with 141 additions and 55 deletions.
146 changes: 109 additions & 37 deletions due_can.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

/**
Expand Down Expand Up @@ -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.
Expand All @@ -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;
Expand Down Expand Up @@ -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;
}
}

Expand Down Expand Up @@ -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<<mailBox);
if (this->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<<mailBox);
}
}

void CANRaw::attachGeneralHandler()
{
callbacksActive |= 256;
if (this->listener[i] == listener)
{
this->listener[i] = NULL;
return true;
}
}
return false;
}

void CANRaw::detachGeneralHandler()
{
callbacksActive &= ~256;
}

/**
* \brief Enable CAN Controller.
*
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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?
Expand All @@ -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<<mb))
if (cbCANFrame[mb])
{
if (listener)
{
listener->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)
Expand Down Expand Up @@ -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<<mailBox);
}
}

void CANListener::detachMBHandler(uint8_t mailBox)
{
if (mailBox >= 0 && mailBox < 8)
{
callbacksActive &= ~(1<<mailBox);
}
}

void CANListener::attachGeneralHandler()
{
callbacksActive |= 256;
}

void CANListener::detachGeneralHandler()
{
callbacksActive &= ~256;
}


/**
* \brief Interrupt dispatchers - Never directly call these
*
Expand Down
36 changes: 24 additions & 12 deletions due_can.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,11 @@
#define ARDUINO152
#endif

#define CAN Can0
#define CAN Can0
#define CAN2 Can1

#define CAN0_EN 62
#define CAN1_EN 65
#define CAN0_EN 50 //these enable pins match most all recent EVTV boards (EVTVDue, CAN Due 2.0)
#define CAN1_EN 48 //they're only defaults, you can set whichever pin you need when calling begin()

/** Define the Mailbox mask for eight mailboxes. */
#define GLOBAL_MAILBOX_MASK 0x000000ff
Expand Down Expand Up @@ -65,7 +65,7 @@
#define CAN_BPS_250K 250000
#define CAN_BPS_125K 125000
#define CAN_BPS_50K 50000
#define CAN_BPS_33333 33333
#define CAN_BPS_33333 33333
#define CAN_BPS_25K 25000
#define CAN_BPS_10K 10000
#define CAN_BPS_5K 5000
Expand All @@ -88,6 +88,7 @@

#define SIZE_RX_BUFFER 32 //RX incoming ring buffer is this big
#define SIZE_TX_BUFFER 16 //TX ring buffer is this big
#define SIZE_LISTENERS 4 //number of classes that can register as listeners with this class

/** Define the timemark mask. */
#define TIMEMARK_MASK 0x0000ffff
Expand Down Expand Up @@ -147,7 +148,7 @@ typedef union {
uint32_t high;
};
struct {
uint16_t s0;
uint16_t s0;
uint16_t s1;
uint16_t s2;
uint16_t s3;
Expand All @@ -170,7 +171,19 @@ typedef struct
class CANListener
{
public:
CANListener();

virtual void gotFrame(CAN_FRAME *frame, int mailbox);

void attachMBHandler(uint8_t mailBox);
void detachMBHandler(uint8_t mailBox);
void attachGeneralHandler();
void detachGeneralHandler();

private:
int callbacksActive; //bitfield letting the code know which callbacks to actually try to use (for object oriented callbacks only)

friend class CANRaw; //class has to have access to the the guts of this one
};

class CANRaw
Expand All @@ -190,12 +203,13 @@ class CANRaw
void mailbox_int_handler(uint8_t mb, uint32_t ul_status);

uint8_t enablePin;
uint32_t busSpeed; //what speed is the bus currently initialized at? 0 if it is off right now

uint32_t write_id; //public storage for an id. Will be used by the write function to set which ID to send to.
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)
CANListener *listener[SIZE_LISTENERS];

public:

Expand All @@ -220,6 +234,7 @@ class CANRaw
uint32_t begin();
uint32_t begin(uint32_t baudrate);
uint32_t begin(uint32_t baudrate, uint8_t enablePin);
uint32_t getBusSpeed();

void enable();
void disable();
Expand All @@ -237,11 +252,8 @@ class CANRaw
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();
boolean attachObj(CANListener *listener);
boolean detachObj(CANListener *listener);

void reset_all_mailbox();
void interruptHandler();
Expand Down
14 changes: 8 additions & 6 deletions examples/CAN_SnoopCB_WithObjects/CAN_SnoopCB_WithObjects.ino
Original file line number Diff line number Diff line change
Expand Up @@ -73,14 +73,16 @@ void setup()

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);
//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
Can0.attachGeneralHandler();
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
Expand Down

5 comments on commit 070851c

@eerimoq
Copy link

@eerimoq eerimoq commented on 070851c Nov 3, 2015

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for posting this question here. It's not related to the commit at all, but I couldn't come up with a better idea of how to contact you.

I downloaded this library and tried to run the examples on my Arduino Due board. It failed, probably because I connected the pins wrong. Could you please explain how to connect the pins to transmit data between can0 and can1? I connected can0 tx to can1 rx and vice versa. Is a tranceiver needed?

@collin80
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, you need transceivers. People sometimes try to cheat and not use them. If you insist on trying you might be able to use google to figure it out but it isn't a good idea. Instead, you need two transceivers - one for CAN0 and one for CAN1. Then you connect CAN0 Low to CAN1 Low and CAN0 High to CAN1 High.

@eerimoq
Copy link

@eerimoq eerimoq commented on 070851c Nov 4, 2015

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay thanks. I ordered 3.3V tranceivers a few days ago and I'll give it another try when they have arrived.

I have colleagues that use CAN quite frequently, and one of them said that it works to connect two can controllers without tranceivers, just as I tried. I guess he was wrong, or that something else affected the result.

Thanks for the quick reply!

@RasmusB
Copy link

@RasmusB RasmusB commented on 070851c Nov 4, 2015

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For quick test purposes with very short cable lengths, you can get away with a few diodes and a pull-up resistor. http://www.mikrocontroller.net/attachment/28831/siemens_AP2921.pdf

But like Colin said, it is not the proper way to do it. Might be fun to try out while you are waiting for your transceivers though. :)

@eerimoq
Copy link

@eerimoq eerimoq commented on 070851c Nov 4, 2015

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ohhh I can't wait to test this :P

Since the arduino due runs ön 3.3V I'll add a pullup to that voltage instead of 5V as the document said.

Thanks.

Please sign in to comment.