Skip to content

[LPC15xx] CAN implementation improvements #3504

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jan 9, 2017
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
162 changes: 150 additions & 12 deletions targets/TARGET_NXP/TARGET_LPC15XX/can_api.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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;
}
Expand Down Expand Up @@ -139,7 +156,7 @@ int can_filter(can_t *obj, uint32_t id, uint32_t mask, CANFormat format, int32_t
}
}

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);
Expand All @@ -153,7 +170,7 @@ int can_filter(can_t *obj, uint32_t id, uint32_t mask, CANFormat format, int32_t
}

// 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;
Expand All @@ -169,7 +186,41 @@ int can_filter(can_t *obj, uint32_t id, uint32_t mask, CANFormat format, int32_t
}

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
Expand All @@ -187,13 +238,53 @@ void can_irq_free(can_t *obj) {

// 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);

Expand Down Expand Up @@ -280,9 +371,9 @@ int can_config_rxmsgobj(can_t *obj) {
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);
Expand All @@ -297,6 +388,33 @@ int can_config_rxmsgobj(can_t *obj) {
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
Expand All @@ -320,6 +438,8 @@ void can_init(can_t *obj, PinName rd, PinName td) {

// Initialize RX message object
can_config_rxmsgobj(obj);
// Initialize TX message object
can_config_txmsgobj(obj);
}

void can_free(can_t *obj) {
Expand All @@ -345,11 +465,26 @@ int can_frequency(can_t *obj, int f) {
}

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 );

Expand Down Expand Up @@ -405,15 +540,15 @@ int can_read(can_t *obj, CAN_Message *msg, int handle) {
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;
}
}
}

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 );

Expand Down Expand Up @@ -462,6 +597,9 @@ void can_reset(can_t *obj) {
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) {
Expand Down