mbed library sources. Supersedes mbed-src.
Fork of mbed-dev by
Diff: targets/TARGET_NXP/TARGET_LPC15XX/can_api.c
- Revision:
- 156:95d6b41a828b
- Parent:
- 149:156823d33999
--- a/targets/TARGET_NXP/TARGET_LPC15XX/can_api.c Thu Jan 05 10:51:54 2017 +0000 +++ b/targets/TARGET_NXP/TARGET_LPC15XX/can_api.c Mon Jan 16 15:03:32 2017 +0000 @@ -23,7 +23,8 @@ #include <string.h> /* Handy defines */ -#define MSG_OBJ_MAX 32 +#define RX_MSG_OBJ_COUNT 31 +#define TX_MSG_OBJ_COUNT 1 #define DLC_MAX 8 #define ID_STD_MASK 0x07FF @@ -56,6 +57,12 @@ #define CANIFn_CMDMSK_RD (0UL << 7) #define CANIFn_CMDREQ_BUSY (1UL << 15) +#define CANSTAT_TXOK (1 << 3) // Transmitted a message successfully This bit must be reset by the CPU. It is never reset by the CAN controller. +#define CANSTAT_RXOK (1 << 4) // Received a message successfully This bit must be reset by the CPU. It is never reset by the CAN controller. +#define CANSTAT_EPASS (1 << 5) // Error passive +#define CANSTAT_EWARN (1 << 6) // Warning status +#define CANSTAT_BOFF (1 << 7) // Busoff status + #define CANCNTL_INIT (1 << 0) // Initialization #define CANCNTL_IE (1 << 1) // Module interrupt enable #define CANCNTL_SIE (1 << 2) // Status change interrupt enable @@ -74,6 +81,16 @@ static uint32_t can_irq_id = 0; static can_irq_handler irq_handler; +#define IRQ_ENABLE_TX (1 << 0) +#define IRQ_ENABLE_RX (1 << 1) +#define IRQ_ENABLE_EW (1 << 2) +#define IRQ_ENABLE_EP (1 << 3) +#define IRQ_ENABLE_BE (1 << 4) +#define IRQ_ENABLE_STATUS (IRQ_ENABLE_TX | IRQ_ENABLE_RX) +#define IRQ_ENABLE_ERROR (IRQ_ENABLE_EW | IRQ_ENABLE_EP | IRQ_ENABLE_BE) +#define IRQ_ENABLE_ANY (IRQ_ENABLE_STATUS | IRQ_ENABLE_ERROR) +static uint32_t enabled_irqs = 0; + static inline void can_disable(can_t *obj) { LPC_C_CAN0->CANCNTL |= 0x1; } @@ -139,7 +156,7 @@ } } - if (handle > 0 && handle < 32) { + if (handle > 0 && handle <= 32) { if (format == CANExtended) { // Mark message valid, Direction = TX, Extended Frame, Set Identifier and mask everything LPC_C_CAN0->CANIF1_ARB1 = (id & 0xFFFF); @@ -153,7 +170,7 @@ } // Use mask, single message object and set DLC - LPC_C_CAN0->CANIF1_MCTRL = CANIFn_MCTRL_UMASK | CANIFn_MCTRL_EOB | CANIFn_MCTRL_RXIE | (DLC_MAX & 0xF); + LPC_C_CAN0->CANIF1_MCTRL = CANIFn_MCTRL_UMASK | CANIFn_MCTRL_EOB | (DLC_MAX & 0xF); // Transfer all fields to message object LPC_C_CAN0->CANIF1_CMDMSK_W = CANIFn_CMDMSK_WR | CANIFn_CMDMSK_MASK | CANIFn_CMDMSK_ARB | CANIFn_CMDMSK_CTRL; @@ -169,7 +186,41 @@ } static inline void can_irq() { - irq_handler(can_irq_id, IRQ_RX); + uint32_t intid = LPC_C_CAN0->CANINT & 0xFFFF; + + if (intid == 0x8000) { + uint32_t status = LPC_C_CAN0->CANSTAT; + // Note that since it's impossible to tell which specific status caused + // the interrupt to fire, this just fires them all. + // In particular, EWARN is not mutually exclusive with the others and + // may fire multiple times with other status transitions, including + // transmit and receive completion (if enabled). Ignoring EWARN with a + // priority system (i.e. blocking EWARN interrupts if EPASS or BOFF is + // set) may discard some EWARN interrupts. + if (status & CANSTAT_BOFF) { + if (enabled_irqs & IRQ_ENABLE_BE) { + irq_handler(can_irq_id, IRQ_BUS); + } + } + if (status & CANSTAT_EPASS) { + if (enabled_irqs & IRQ_ENABLE_EP) { + irq_handler(can_irq_id, IRQ_PASSIVE); + } + } + if (status & CANSTAT_EWARN) { + if (enabled_irqs & IRQ_ENABLE_EW) { + irq_handler(can_irq_id, IRQ_ERROR); + } + } + if ((status & CANSTAT_RXOK) != 0) { + LPC_C_CAN0->CANSTAT &= ~CANSTAT_RXOK; + irq_handler(can_irq_id, IRQ_RX); + } + if ((status & CANSTAT_TXOK) != 0) { + LPC_C_CAN0->CANSTAT &= ~CANSTAT_TXOK; + irq_handler(can_irq_id, IRQ_TX); + } + } } // Register CAN object's irq handler @@ -187,13 +238,53 @@ // Clear or set a irq void can_irq_set(can_t *obj, CanIrqType type, uint32_t enable) { + uint32_t mask_enable; + switch (type) { + case IRQ_RX: + mask_enable = IRQ_ENABLE_RX; + break; + case IRQ_TX: + mask_enable = IRQ_ENABLE_TX; + break; + case IRQ_BUS: + mask_enable = IRQ_ENABLE_BE; + break; + case IRQ_PASSIVE: + mask_enable = IRQ_ENABLE_EP; + break; + case IRQ_ERROR: + mask_enable = IRQ_ENABLE_EW; + break; + default: + return; + } + + if (enable) { + enabled_irqs = enabled_irqs | mask_enable; + } else { + enabled_irqs = enabled_irqs & ~mask_enable; + } + // Put CAN in Reset Mode and enable interrupt can_disable(obj); - if (enable == 0) { - LPC_C_CAN0->CANCNTL &= ~(1UL << 1 | 1UL << 2); + if (!(enabled_irqs & IRQ_ENABLE_ANY)) { + LPC_C_CAN0->CANCNTL &= ~(1UL << 1 | 1UL << 2 | 1UL << 3); } else { - LPC_C_CAN0->CANCNTL |= 1UL << 1 | 1UL << 2; + LPC_C_CAN0->CANCNTL |= 1UL << 1; + // Use status interrupts instead of message interrupts to avoid + // stomping over potential filter configurations. + if (enabled_irqs & IRQ_ENABLE_STATUS) { + LPC_C_CAN0->CANCNTL |= 1UL << 2; + } else { + LPC_C_CAN0->CANCNTL &= ~(1UL << 2); + } + if (enabled_irqs & IRQ_ENABLE_ERROR) { + LPC_C_CAN0->CANCNTL |= 1UL << 3; + } else { + LPC_C_CAN0->CANCNTL &= ~(1UL << 3); + } } + // Take it out of reset... can_enable(obj); @@ -280,9 +371,9 @@ LPC_C_CAN0->CANIF1_ARB2 = 0; LPC_C_CAN0->CANIF1_MCTRL = 0; - for ( i = 0; i < MSG_OBJ_MAX; i++ ) { + for ( i = 1; i <= RX_MSG_OBJ_COUNT; i++ ) { // Transfer arb and control fields to message object - LPC_C_CAN0->CANIF1_CMDMSK_W = CANIFn_CMDMSK_WR | CANIFn_CMDMSK_ARB | CANIFn_CMDMSK_CTRL | CANIFn_CMDMSK_TXRQST; + LPC_C_CAN0->CANIF1_CMDMSK_W = CANIFn_CMDMSK_WR | CANIFn_CMDMSK_ARB | CANIFn_CMDMSK_CTRL; // Start Transfer to given message number LPC_C_CAN0->CANIF1_CMDREQ = (i & 0x3F); @@ -297,6 +388,33 @@ return 1; } +int can_config_txmsgobj(can_t *obj) { + uint16_t i = 0; + + // Make sure the interface is available + while ( LPC_C_CAN0->CANIF1_CMDREQ & CANIFn_CMDREQ_BUSY ); + + // Mark message valid, Direction = TX, Don't care about anything else + LPC_C_CAN0->CANIF1_ARB1 = 0; + LPC_C_CAN0->CANIF1_ARB2 = CANIFn_ARB2_DIR; + LPC_C_CAN0->CANIF1_MCTRL = 0; + + for ( i = RX_MSG_OBJ_COUNT + 1; i <= (TX_MSG_OBJ_COUNT + RX_MSG_OBJ_COUNT); i++ ) + { + // Transfer arb and control fields to message object + LPC_C_CAN0->CANIF1_CMDMSK_W = CANIFn_CMDMSK_WR | CANIFn_CMDMSK_ARB | CANIFn_CMDMSK_CTRL; + // In a union with CANIF1_CMDMSK_R + + // Start Transfer to given message number + LPC_C_CAN0->CANIF1_CMDREQ = i & 0x3F; + + // Wait until transfer to message ram complete - TODO: maybe not block?? + while( LPC_C_CAN0->CANIF1_CMDREQ & CANIFn_CMDREQ_BUSY ); + } + + return 1; +} + void can_init(can_t *obj, PinName rd, PinName td) { // Enable power and clock @@ -320,6 +438,8 @@ // Initialize RX message object can_config_rxmsgobj(obj); + // Initialize TX message object + can_config_txmsgobj(obj); } void can_free(can_t *obj) { @@ -345,11 +465,26 @@ } int can_write(can_t *obj, CAN_Message msg, int cc) { - uint16_t msgnum = 0; // Make sure controller is enabled can_enable(obj); + // Find first message object that isn't pending to send + uint16_t msgnum = 0; + uint32_t txPending = (LPC_C_CAN0->CANTXREQ1 & 0xFF) | (LPC_C_CAN0->CANTXREQ2 << 16); + uint16_t i = 0; + for(i = RX_MSG_OBJ_COUNT; i < 32; i++) { + if ((txPending & (1 << i)) == 0) { + msgnum = i+1; + break; + } + } + + // If no messageboxes are available, stop and return failure + if (msgnum == 0) { + return 0; + } + // Make sure the interface is available while ( LPC_C_CAN0->CANIF1_CMDREQ & CANIFn_CMDREQ_BUSY ); @@ -405,7 +540,7 @@ if (handle == 0) { uint32_t newdata = LPC_C_CAN0->CANND1 | (LPC_C_CAN0->CANND2 << 16); // Find first free messagebox - for (i = 0; i < 32; i++) { + for (i = 0; i < RX_MSG_OBJ_COUNT; i++) { if (newdata & (1 << i)) { handle = i+1; break; @@ -413,7 +548,7 @@ } } - if (handle > 0 && handle < 32) { + if (handle > 0 && handle <= 32) { // Wait until message interface is free while ( LPC_C_CAN0->CANIF2_CMDREQ & CANIFn_CMDREQ_BUSY ); @@ -462,6 +597,9 @@ LPC_SYSCON->PRESETCTRL1 &= ~(1UL << 7); LPC_C_CAN0->CANSTAT = 0; can_config_rxmsgobj(obj); + can_config_txmsgobj(obj); + + can_enable(obj); // clears a bus-off condition if necessary } unsigned char can_rderror(can_t *obj) {