From 63b2b271d1654bf7fbc961f062fb17c465f43ae6 Mon Sep 17 00:00:00 2001 From: Joris Aerts Date: Wed, 31 Jul 2013 07:23:21 -0700 Subject: [PATCH] Refactor CAN interrupt handling to LPC176X HAL implementation Add handlers for other CAN interrupt events Changed CAN private entities to protected --- libraries/mbed/api/CAN.h | 19 ++-- libraries/mbed/common/CAN.cpp | 65 +++---------- libraries/mbed/hal/can_api.h | 19 ++++ .../hal/TARGET_NXP/TARGET_LPC176X/can_api.c | 91 +++++++++++++++++++ 4 files changed, 130 insertions(+), 64 deletions(-) diff --git a/libraries/mbed/api/CAN.h b/libraries/mbed/api/CAN.h index af5501a2ff..1a0bc7861b 100644 --- a/libraries/mbed/api/CAN.h +++ b/libraries/mbed/api/CAN.h @@ -163,7 +163,7 @@ public: * * @param fptr A pointer to a void function, or 0 to set as none */ - void attach(void (*fptr)(void)); + void attach(void (*fptr)(void), can_irq_event event=IRQ_RX); /** Attach a member function to call whenever a CAN frame received interrupt * is generated. @@ -172,21 +172,18 @@ public: * @param mptr pointer to the member function to be called */ template - void attach(T* tptr, void (T::*mptr)(void)) { + void attach(T* tptr, void (T::*mptr)(void), can_irq_event event=IRQ_RX) { if((mptr != NULL) && (tptr != NULL)) { - _rxirq.attach(tptr, mptr); - setup_interrupt(); - } else { - remove_interrupt(); + _irq[type].attach(tptr, mptr); + can_irq_set(&_can, event, 1); } } -private: - can_t _can; - FunctionPointer _rxirq; + static void _irq_handler(uint32_t id, can_irq_event event); - void setup_interrupt(void); - void remove_interrupt(void); +protected: + can_t _can; + FunctionPointer _irq[9]; }; } // namespace mbed diff --git a/libraries/mbed/common/CAN.cpp b/libraries/mbed/common/CAN.cpp index ef6ebd87df..936400309b 100644 --- a/libraries/mbed/common/CAN.cpp +++ b/libraries/mbed/common/CAN.cpp @@ -23,10 +23,12 @@ namespace mbed { CAN::CAN(PinName rd, PinName td) { can_init(&_can, rd, td); + can_irq_init(&_can, (&CAN::_irq_handler), (uint32_t)this); } CAN::~CAN() { can_free(&_can); + can_irq_free(&_can); } int CAN::frequency(int f) { @@ -57,63 +59,20 @@ void CAN::monitor(bool silent) { can_monitor(&_can, (silent) ? 1 : 0); } -static FunctionPointer* can_obj[2] = { NULL }; - -// Have to check that the CAN block is active before reading the Interrupt -// Control Register, or the mbed hangs -void can_irq(void) { - uint32_t icr; - - if(LPC_SC->PCONP & (1 << 13)) { - icr = LPC_CAN1->ICR; - - if(icr && (can_obj[0] != NULL)) { - can_obj[0]->call(); - } - } - - if(LPC_SC->PCONP & (1 << 14)) { - icr = LPC_CAN2->ICR; - if(icr && (can_obj[1] != NULL)) { - can_obj[1]->call(); - } - } - -} - -void CAN::setup_interrupt(void) { - switch ((int)_can.dev) { - case CAN_1: can_obj[0] = &_rxirq; break; - case CAN_2: can_obj[1] = &_rxirq; break; - } - _can.dev->MOD |= 1; - _can.dev->IER |= 1; - _can.dev->MOD &= ~1; - NVIC_SetVector(CAN_IRQn, (uint32_t) &can_irq); - NVIC_EnableIRQ(CAN_IRQn); -} - -void CAN::remove_interrupt(void) { - switch ((int)_can.dev) { - case CAN_1: can_obj[0] = NULL; break; - case CAN_2: can_obj[1] = NULL; break; - } - - _can.dev->IER &= ~(1); - if ((can_obj[0] == NULL) && (can_obj[1] == NULL)) { - NVIC_DisableIRQ(CAN_IRQn); - } -} - -void CAN::attach(void (*fptr)(void)) { - if (fptr != NULL) { - _rxirq.attach(fptr); - setup_interrupt(); +void CAN::attach(void (*fptr)(void), can_irq_event event) { + if (fptr) { + _irq[event].attach(fptr); + can_irq_set(&_can, event, 1); } else { - remove_interrupt(); + can_irq_set(&_can, event, 0); } } +void CAN::_irq_handler(uint32_t id, can_irq_event event) { + CAN *handler = (CAN*)id; + handler->_irq[event].call(); +} + } // namespace mbed #endif diff --git a/libraries/mbed/hal/can_api.h b/libraries/mbed/hal/can_api.h index b681cae90c..ed9bf90648 100644 --- a/libraries/mbed/hal/can_api.h +++ b/libraries/mbed/hal/can_api.h @@ -28,11 +28,30 @@ extern "C" { #endif +typedef enum { + IRQ_RX, + IRQ_TX, + IRQ_ERROR, + IRQ_OVERRUN, + IRQ_WAKEUP, + IRQ_PASSIVE, + IRQ_ARB, + IRQ_BUS, + IRQ_READY +} can_irq_event; + +typedef void (*can_irq_handler)(uint32_t id, can_irq_event event); + typedef struct can_s can_t; void can_init (can_t *obj, PinName rd, PinName td); void can_free (can_t *obj); int can_frequency(can_t *obj, int hz); + +void can_irq_init (can_t *obj, can_irq_handler handler, uint32_t id); +void can_irq_free (can_t *obj); +void can_irq_set (can_t *obj, can_irq_event irq, uint32_t enable); + int can_write (can_t *obj, CAN_Message, int cc); int can_read (can_t *obj, CAN_Message *msg); void can_reset (can_t *obj); diff --git a/libraries/mbed/targets/hal/TARGET_NXP/TARGET_LPC176X/can_api.c b/libraries/mbed/targets/hal/TARGET_NXP/TARGET_LPC176X/can_api.c index 9e62b65207..a6771d417d 100644 --- a/libraries/mbed/targets/hal/TARGET_NXP/TARGET_LPC176X/can_api.c +++ b/libraries/mbed/targets/hal/TARGET_NXP/TARGET_LPC176X/can_api.c @@ -63,6 +63,9 @@ struct CANMsg { }; typedef struct CANMsg CANMsg; +static uint32_t can_irq_ids[CAN_NUM] = {0}; +static can_irq_handler irq_handler; + static uint32_t can_disable(can_t *obj) { uint32_t sm = obj->dev->MOD; obj->dev->MOD |= 1; @@ -75,6 +78,94 @@ static inline void can_enable(can_t *obj) { } } +static inline void can_irq(uint32_t icr, uint32_t index) { + #warning TODO(@jorisa) Check that events not happen at same time + can_irq_event event; + switch (icr) { + case (1 << 0): event = IRQ_RX; break; + case (1 << 1): event = IRQ_TX; break; + case (1 << 2): event = IRQ_ERROR; break; + case (1 << 3): event = IRQ_OVERRUN; break; + case (1 << 4): event = IRQ_WAKEUP; break; + case (1 << 5): event = IRQ_PASSIVE; break; + case (1 << 6): event = IRQ_ARB; break; + case (1 << 7): event = IRQ_BUS; break; + case (1 << 8): event = IRQ_READY; break; + default: return; + } + + if (can_irq_ids[index] != 0) + irq_handler(can_irq_ids[index], event); +} + +// Have to check that the CAN block is active before reading the Interrupt +// Control Register, or the mbed hangs +void can_irq_n() { + uint32_t icr; + + if(LPC_SC->PCONP & (1 << 13)) { + icr = LPC_CAN1->ICR & 0x1FF; + can_irq(icr, 0); + } + + if(LPC_SC->PCONP & (1 << 14)) { + icr = LPC_CAN2->ICR & 0x1FF; + can_irq(icr, 1); + } +} + +// Register CAN object's irq handler +void can_irq_init(can_t *obj, can_irq_handler handler, uint32_t id) { + irq_handler = handler; + can_irq_ids[obj->index] = id; +} + +// Unregister CAN object's irq handler +void can_irq_free(can_t *obj) { + obj->dev->IER &= ~(1); + can_irq_ids[obj->index] = 0; + + if ((can_irq_ids[0] == 0) && (can_irq_ids[1] == 0)) { + NVIC_DisableIRQ(CAN_IRQn); + } +} + +// Clear or set a irq +void can_irq_set(can_t *obj, can_irq_event event, uint32_t enable) { + uint32_t ier; + + switch (event) { + case IRQ_RX: ier = (1 << 0); break; + case IRQ_TX: ier = (1 << 1); break; + case IRQ_ERROR: ier = (1 << 2); break; + case IRQ_OVERRUN: ier = (1 << 3); break; + case IRQ_WAKEUP: ier = (1 << 4); break; + case IRQ_PASSIVE: ier = (1 << 5); break; + case IRQ_ARB: ier = (1 << 6); break; + case IRQ_BUS: ier = (1 << 7); break; + case IRQ_READY: ier = (1 << 8); break; + default: return; + } + + obj->dev->MOD |= 1; + if(enable == 0) { + obj->dev->IER &= ~ier; + } + else { + obj->dev->IER |= ier; + } + obj->dev->MOD &= ~(1); + + // Enable NVIC if at least 1 interrupt is active + if(LPC_CAN1->IER | LPC_CAN2->IER != 0) { + NVIC_SetVector(CAN_IRQn, (uint32_t) &can_irq_n); + NVIC_EnableIRQ(CAN_IRQn); + } + else { + NVIC_DisableIRQ(CAN_IRQn); + } +} + static int can_pclk(can_t *obj) { int value = 0; switch ((int)obj->dev) {