Skip to content

Commit

Permalink
STM32 CAN Error Handling (#644)
Browse files Browse the repository at this point in the history
* Add ioctl() for getting the CAN state.

* Properly handle bus error/off states.

* Change execution priority.

* Support Arduino.

* Add SCE interrupt handler call to Common CAN IRQ handler for Arduino.

* Reverse error handling order and make clearing the flags non-exclusive.

* Fix review comments.
  • Loading branch information
bakerstu authored Aug 17, 2022
1 parent 49dbfc2 commit 1fa064a
Show file tree
Hide file tree
Showing 2 changed files with 97 additions and 25 deletions.
111 changes: 86 additions & 25 deletions src/freertos_drivers/st/Stm32Can.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@

#include <stdint.h>

#include "can_ioctl.h"

#include "stm32f_hal_conf.hxx"

#if defined (STM32F072xB) || defined (STM32F091xC)
Expand All @@ -53,6 +55,7 @@
#define CAN_TX_IRQN USB_HP_CAN1_TX_IRQn
#define CAN_IRQN CAN_TX_IRQN
#define CAN_SECOND_IRQN USB_LP_CAN1_RX0_IRQn
#define CAN_THIRD_IRQN CAN1_SCE_IRQn
#define CAN CAN1
#define CAN_CLOCK (cm3_cpu_clock_hz >> 1)

Expand All @@ -63,6 +66,7 @@
#define CAN_TX_IRQN USB_HP_CAN_TX_IRQn
#define CAN_IRQN CAN_TX_IRQN
#define CAN_SECOND_IRQN USB_LP_CAN_RX0_IRQn
#define CAN_THIRD_IRQN CAN1_SCE_IRQn
#define CAN_CLOCK (cm3_cpu_clock_hz >> 1)

#elif defined (STM32L431xx) || defined (STM32L432xx)
Expand All @@ -72,6 +76,7 @@
#define CAN_TX_IRQN CAN1_TX_IRQn
#define CAN_IRQN CAN_TX_IRQN
#define CAN_SECOND_IRQN CAN1_RX0_IRQn
#define CAN_THIRD_IRQN CAN1_SCE_IRQn
#define CAN_CLOCK (cm3_cpu_clock_hz)

#elif defined (STM32F767xx)
Expand All @@ -82,6 +87,7 @@
#define CAN_TX_IRQN CAN1_TX_IRQn
#define CAN_IRQN CAN_TX_IRQN
#define CAN_SECOND_IRQN CAN1_RX0_IRQn
#define CAN_THIRD_IRQN CAN1_SCE_IRQn
#define CAN_CLOCK (cm3_cpu_clock_hz >> 2) // 54 MHz, sysclk/4

#else
Expand All @@ -95,6 +101,7 @@ Stm32Can *Stm32Can::instances[1] = {NULL};
*/
Stm32Can::Stm32Can(const char *name)
: Can(name)
, state_(CAN_STATE_STOPPED)
{
/* only one instance allowed */
HASSERT(instances[0] == NULL);
Expand All @@ -119,10 +126,25 @@ Stm32Can::Stm32Can(const char *name)
#ifdef SPLIT_INT
HAL_NVIC_DisableIRQ(CAN_SECOND_IRQN);
SetInterruptPriority(CAN_SECOND_IRQN, configKERNEL_INTERRUPT_PRIORITY);
HAL_NVIC_DisableIRQ(CAN_THIRD_IRQN);
SetInterruptPriority(CAN_THIRD_IRQN, configKERNEL_INTERRUPT_PRIORITY);
#endif
#endif
}

//
// Stm32Can::ioctl()
//
int Stm32Can::ioctl(File *file, unsigned long int key, unsigned long data)
{
if (key == SIOCGCANSTATE)
{
*((can_state_t*)data) = state_;
return 0;
}
return -EINVAL;
}

/** Enable use of the device.
*/
void Stm32Can::enable()
Expand Down Expand Up @@ -162,11 +184,15 @@ void Stm32Can::enable()
CAN->FA1R = 0x000000001;
CAN->FMR &= ~CAN_FMR_FINIT;

state_ = CAN_STATE_ACTIVE;

/* enable interrupts */
CAN->IER = (/*CAN_IER_ERRIE |*/ CAN_IER_BOFIE | CAN_IER_FMPIE0);
CAN->IER = (CAN_IER_BOFIE | CAN_IER_EPVIE | CAN_IER_EWGIE); // errors
CAN->IER |= (CAN_IER_ERRIE | CAN_IER_FMPIE0); // error + receive
HAL_NVIC_EnableIRQ(CAN_IRQN);
#ifdef SPLIT_INT
HAL_NVIC_EnableIRQ(CAN_SECOND_IRQN);
HAL_NVIC_EnableIRQ(CAN_THIRD_IRQN);
#endif
}

Expand All @@ -177,9 +203,12 @@ void Stm32Can::disable()
HAL_NVIC_DisableIRQ(CAN_IRQN);
#ifdef SPLIT_INT
HAL_NVIC_DisableIRQ(CAN_SECOND_IRQN);
HAL_NVIC_DisableIRQ(CAN_THIRD_IRQN);
#endif
CAN->IER = 0;

state_ = CAN_STATE_STOPPED;

/* disable sleep, enter init mode */
CAN->MCR = CAN_MCR_INRQ;
}
Expand Down Expand Up @@ -261,30 +290,6 @@ void Stm32Can::rx_interrupt_handler()
{
unsigned msg_receive_count = 0;

if (CAN->ESR & CAN_ESR_BOFF)
{
/* bus off error condition */
CAN->TSR |= CAN_TSR_ABRQ2;
CAN->TSR |= CAN_TSR_ABRQ1;
CAN->TSR |= CAN_TSR_ABRQ0;
CAN->IER &= ~CAN_IER_TMEIE;
txBuf->flush();
txBuf->signal_condition_from_isr();
}
#if 0
if (CAN->MSR & CAN_MSR_ERRI)
{
/* error condition */
CAN->TSR |= CAN_TSR_ABRQ2;
CAN->TSR |= CAN_TSR_ABRQ1;
CAN->TSR |= CAN_TSR_ABRQ0;
CAN->IER &= ~CAN_IER_TMEIE;
CAN->MSR &= ~CAN_MSR_ERRI;
txBuf->flush();
txBuf->signal_condition_from_isr();
++softErrorCount;
}
#endif
while (CAN->RF0R & CAN_RF0R_FMP0)
{
/* rx data received */
Expand Down Expand Up @@ -339,6 +344,7 @@ void Stm32Can::rx_interrupt_handler()
/* advance the "zero copy" buffer by the number of messages received */
rxBuf->advance(msg_receive_count);
rxBuf->signal_condition_from_isr();
state_ = CAN_STATE_ACTIVE;
}
}

Expand Down Expand Up @@ -413,9 +419,48 @@ void Stm32Can::tx_interrupt_handler()
CAN->IER &= ~CAN_IER_TMEIE;
}
txBuf->signal_condition_from_isr();
state_ = CAN_STATE_ACTIVE;
}
}

/*
* Stm32Can::sce_interrupt_handler()
*/
void Stm32Can::sce_interrupt_handler()
{
if (CAN->MSR & CAN_MSR_ERRI)
{
/* error interrupt has occured */
CAN->MSR |= CAN_MSR_ERRI; // clear flag

if (CAN->ESR & CAN_ESR_EWGF)
{
/* error warning condition */
state_ = CAN_STATE_BUS_WARNING;
CAN->ESR &= ~CAN_ESR_EWGF;
}
if (CAN->ESR & CAN_ESR_EPVF)
{
/* error passive condition */
++softErrorCount;
state_ = CAN_STATE_BUS_PASSIVE;
CAN->ESR &= ~CAN_ESR_EPVF;
}
if (CAN->ESR & CAN_ESR_BOFF)
{
/* bus off error condition */
CAN->TSR |= CAN_TSR_ABRQ2;
CAN->TSR |= CAN_TSR_ABRQ1;
CAN->TSR |= CAN_TSR_ABRQ0;
CAN->IER &= ~CAN_IER_TMEIE;
txBuf->flush();
txBuf->signal_condition_from_isr();
++busOffCount;
state_ = CAN_STATE_BUS_OFF;
CAN->ESR &= ~CAN_ESR_BOFF;
}
}
}

extern "C" {
/** This is the interrupt handler for the can device.
Expand All @@ -426,6 +471,7 @@ void cec_can_interrupt_handler(void)
{
Stm32Can::instances[0]->rx_interrupt_handler();
Stm32Can::instances[0]->tx_interrupt_handler();
Stm32Can::instances[0]->sce_interrupt_handler();
}
#elif defined (STM32F103xB) || defined (STM32F303xC) || defined (STM32F303xE)

Expand All @@ -439,6 +485,11 @@ void usb_lp_can1_rx0_interrupt_handler(void)
Stm32Can::instances[0]->rx_interrupt_handler();
}

void can1_sce_interrupt_handler(void)
{
Stm32Can::instances[0]->sce_interrupt_handler();
}

#elif defined(STM32F767xx) || defined(STM32L431xx) || defined(STM32L432xx)

void can1_tx_interrupt_handler(void)
Expand All @@ -451,6 +502,11 @@ void can1_rx0_interrupt_handler(void)
Stm32Can::instances[0]->rx_interrupt_handler();
}

void can1_sce_interrupt_handler(void)
{
Stm32Can::instances[0]->sce_interrupt_handler();
}

#else
#error Dont know what STM32 chip you have.
#endif
Expand Down Expand Up @@ -515,6 +571,7 @@ void CEC_CAN_IRQHandler(void)
{
Stm32Can::instances[0]->rx_interrupt_handler();
Stm32Can::instances[0]->tx_interrupt_handler();
Stm32Can::instances[0]->sce_interrupt_handler();
}
void CAN1_TX_IRQHandler(void)
{
Expand All @@ -524,6 +581,10 @@ void CAN1_RX0_IRQHandler(void)
{
Stm32Can::instances[0]->rx_interrupt_handler();
}
void CAN1_SCE_IRQHandler(void)
{
Stm32Can::instances[0]->sce_interrupt_handler();
}
} // extern "C"

#endif
Expand Down
11 changes: 11 additions & 0 deletions src/freertos_drivers/st/Stm32Can.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,20 @@ public:
void rx_interrupt_handler();
/** Handle an interrupt. */
void tx_interrupt_handler();
/** Handle an interrupt. */
void sce_interrupt_handler();

/** Instance pointers help us get context from the interrupt handler(s) */
static Stm32Can *instances[1];

private:
/// Request an ioctl transaction.
/// @param file file reference for this device
/// @param key ioctl key
/// @param data key data
/// @return >= 0 upon success, -errno upon failure
int ioctl(File *file, unsigned long int key, unsigned long data) override;

void enable() override; /**< function to enable device */
void disable() override; /**< function to disable device */
void tx_msg() override; /**< function to try and transmit a message */
Expand All @@ -79,6 +88,8 @@ private:
*/
static unsigned int intCount;

uint8_t state_; ///< present bus state

/** Default constructor.
*/
Stm32Can();
Expand Down

0 comments on commit 1fa064a

Please sign in to comment.