mbed library sources. Supersedes mbed-src.

Fork of mbed-dev by mbed official

Revision:
156:95d6b41a828b
Parent:
149:156823d33999
Child:
167:e84263d55307
--- 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) {