stm32: add CAN error support
This commit is contained in:
parent
a8ca1ec026
commit
5b9b3814f8
@ -3388,6 +3388,7 @@ config STM32_I2C
|
||||
|
||||
config STM32_CAN
|
||||
bool
|
||||
select ARCH_HAVE_CAN_ERRORS
|
||||
|
||||
config STM32_TIM
|
||||
bool
|
||||
|
@ -67,6 +67,14 @@
|
||||
# undef CONFIG_STM32_CAN_REGDEBUG
|
||||
#endif
|
||||
|
||||
/* CAN error interrupts */
|
||||
|
||||
#ifdef CONFIG_CAN_ERRORS
|
||||
# define STM32_CAN_ERRINT (CAN_IER_LECIE | CAN_IER_ERRIE | \
|
||||
CAN_IER_BOFIE | CAN_IER_EPVIE | \
|
||||
CAN_IER_EWGIE)
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Private Types
|
||||
****************************************************************************/
|
||||
@ -76,6 +84,9 @@ struct stm32_can_s
|
||||
uint8_t port; /* CAN port number (1 or 2) */
|
||||
uint8_t canrx[2]; /* CAN RX FIFO 0/1 IRQ number */
|
||||
uint8_t cantx; /* CAN TX IRQ number */
|
||||
#ifdef CONFIG_CAN_ERRORS
|
||||
uint8_t cansce; /* CAN SCE IRQ number */
|
||||
#endif
|
||||
uint8_t filter; /* Filter number */
|
||||
uint32_t base; /* Base address of the CAN control registers */
|
||||
uint32_t fbase; /* Base address of the CAN filter registers */
|
||||
@ -138,12 +149,19 @@ static int stm32can_send(FAR struct can_dev_s *dev,
|
||||
static bool stm32can_txready(FAR struct can_dev_s *dev);
|
||||
static bool stm32can_txempty(FAR struct can_dev_s *dev);
|
||||
|
||||
#ifdef CONFIG_CAN_ERRORS
|
||||
static void stm32can_errint(FAR struct can_dev_s *dev, bool enable);
|
||||
#endif
|
||||
|
||||
/* CAN interrupt handling */
|
||||
|
||||
static int stm32can_rxinterrupt(FAR struct can_dev_s *dev, int rxmb);
|
||||
static int stm32can_rx0interrupt(int irq, FAR void *context, FAR void *arg);
|
||||
static int stm32can_rx1interrupt(int irq, FAR void *context, FAR void *arg);
|
||||
static int stm32can_txinterrupt(int irq, FAR void *context, FAR void *arg);
|
||||
#ifdef CONFIG_CAN_ERRORS
|
||||
static int stm32can_sceinterrupt(int irq, FAR void *context, FAR void *arg);
|
||||
#endif
|
||||
|
||||
/* Initialization */
|
||||
|
||||
@ -187,6 +205,9 @@ static struct stm32_can_s g_can1priv =
|
||||
STM32_IRQ_CAN1RX1,
|
||||
},
|
||||
.cantx = STM32_IRQ_CAN1TX,
|
||||
#ifdef CONFIG_CAN_ERRORS
|
||||
.cansce = STM32_IRQ_CAN1SCE,
|
||||
#endif
|
||||
.filter = 0,
|
||||
.base = STM32_CAN1_BASE,
|
||||
.fbase = STM32_CAN1_BASE,
|
||||
@ -210,6 +231,9 @@ static struct stm32_can_s g_can2priv =
|
||||
STM32_IRQ_CAN2RX1,
|
||||
},
|
||||
.cantx = STM32_IRQ_CAN2TX,
|
||||
#ifdef CONFIG_CAN_ERRORS
|
||||
.cansce = STM32_IRQ_CAN2SCE,
|
||||
#endif
|
||||
.filter = CAN_NFILTERS / 2,
|
||||
.base = STM32_CAN2_BASE,
|
||||
.fbase = STM32_CAN1_BASE,
|
||||
@ -615,9 +639,16 @@ static int stm32can_setup(FAR struct can_dev_s *dev)
|
||||
FAR struct stm32_can_s *priv = dev->cd_priv;
|
||||
int ret;
|
||||
|
||||
caninfo("CAN%" PRIu8 " RX0 irq: %" PRIu8 " RX1 irq: %" PRIu8
|
||||
" TX irq: %" PRIu8 "\n",
|
||||
priv->port, priv->canrx[0], priv->canrx[1], priv->cantx);
|
||||
#ifdef CONFIG_CAN_ERRORS
|
||||
ninfo("CAN%" PRIu8 " RX0 irq: %" PRIu8 " RX1 irq: %" PRIu8
|
||||
" TX irq: %" PRIu8 " SCE irq: %" PRIu8 "\n",
|
||||
priv->port, priv->canrx[0], priv->canrx[1], priv->cantx,
|
||||
priv->cansce);
|
||||
#else
|
||||
ninfo("CAN%" PRIu8 " RX0 irq: %" PRIu8 " RX1 irq: %" PRIu8
|
||||
" TX irq: %" PRIu8 "\n",
|
||||
priv->port, priv->canrx[0], priv->canrx[1], priv->cantx);
|
||||
#endif
|
||||
|
||||
/* CAN cell initialization */
|
||||
|
||||
@ -672,6 +703,20 @@ static int stm32can_setup(FAR struct can_dev_s *dev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_CAN_ERRORS
|
||||
ret = irq_attach(priv->cansce, stm32can_sceinterrupt, dev);
|
||||
if (ret < 0)
|
||||
{
|
||||
nerr("ERROR: Failed to attach CAN%" PRIu8 " SCE IRQ (%" PRIu8 ")",
|
||||
priv->port, priv->cansce);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Enable CAN error interrupts */
|
||||
|
||||
stm32can_errint(dev, true);
|
||||
#endif
|
||||
|
||||
/* Enable the interrupts at the NVIC. Interrupts are still disabled in
|
||||
* the CAN module. Since we coming out of reset here, there should be
|
||||
* no pending interrupts.
|
||||
@ -680,6 +725,9 @@ static int stm32can_setup(FAR struct can_dev_s *dev)
|
||||
up_enable_irq(priv->canrx[0]);
|
||||
up_enable_irq(priv->canrx[1]);
|
||||
up_enable_irq(priv->cantx);
|
||||
#ifdef CONFIG_CAN_ERRORS
|
||||
up_enable_irq(priv->cansce);
|
||||
#endif
|
||||
return OK;
|
||||
}
|
||||
|
||||
@ -704,17 +752,23 @@ static void stm32can_shutdown(FAR struct can_dev_s *dev)
|
||||
|
||||
caninfo("CAN%" PRIu8 "\n", priv->port);
|
||||
|
||||
/* Disable the RX FIFO 0/1 and TX interrupts */
|
||||
/* Disable the RX FIFO 0/1, TX and SCE interrupts */
|
||||
|
||||
up_disable_irq(priv->canrx[0]);
|
||||
up_disable_irq(priv->canrx[1]);
|
||||
up_disable_irq(priv->cantx);
|
||||
#ifdef CONFIG_CAN_ERRORS
|
||||
up_disable_irq(priv->cansce);
|
||||
#endif
|
||||
|
||||
/* Detach the RX FIFO 0/1 and TX interrupts */
|
||||
/* Detach the RX FIFO 0/1, TX and SCE interrupts */
|
||||
|
||||
irq_detach(priv->canrx[0]);
|
||||
irq_detach(priv->canrx[1]);
|
||||
irq_detach(priv->cantx);
|
||||
#ifdef CONFIG_CAN_ERRORS
|
||||
irq_detach(priv->cansce);
|
||||
#endif
|
||||
|
||||
/* And reset the hardware */
|
||||
|
||||
@ -740,7 +794,7 @@ static void stm32can_rxint(FAR struct can_dev_s *dev, bool enable)
|
||||
FAR struct stm32_can_s *priv = dev->cd_priv;
|
||||
uint32_t regval;
|
||||
|
||||
caninfo("CAN%" PRIu8 " enable: %d\n", priv->port, enable);
|
||||
caninfo("CAN%" PRIu8 " rxint enable: %d\n", priv->port, enable);
|
||||
|
||||
/* Enable/disable the FIFO 0/1 message pending interrupt */
|
||||
|
||||
@ -776,7 +830,7 @@ static void stm32can_txint(FAR struct can_dev_s *dev, bool enable)
|
||||
FAR struct stm32_can_s *priv = dev->cd_priv;
|
||||
uint32_t regval;
|
||||
|
||||
caninfo("CAN%" PRIu8 " enable: %d\n", priv->port, enable);
|
||||
caninfo("CAN%" PRIu8 " txint enable: %d\n", priv->port, enable);
|
||||
|
||||
/* Support only disabling the transmit mailbox interrupt */
|
||||
|
||||
@ -788,6 +842,44 @@ static void stm32can_txint(FAR struct can_dev_s *dev, bool enable)
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_CAN_ERRORS
|
||||
/****************************************************************************
|
||||
* Name: stm32can_errint
|
||||
*
|
||||
* Description:
|
||||
* Call to enable or disable CAN error interrupts.
|
||||
*
|
||||
* Input Parameters:
|
||||
* dev - An instance of the "upper half" can driver state structure.
|
||||
*
|
||||
* Returned Value:
|
||||
* None
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static void stm32can_errint(FAR struct can_dev_s *dev, bool enable)
|
||||
{
|
||||
FAR struct stm32_can_s *priv = dev->cd_priv;
|
||||
uint32_t regval = 0;
|
||||
|
||||
caninfo("CAN%" PRIu8 " errint enable: %d\n", priv->port, enable);
|
||||
|
||||
/* Enable/disable the transmit mailbox interrupt */
|
||||
|
||||
regval = stm32can_getreg(priv, STM32_CAN_IER_OFFSET);
|
||||
if (enable)
|
||||
{
|
||||
regval |= STM32_CAN_ERRINT;
|
||||
}
|
||||
else
|
||||
{
|
||||
regval &= ~STM32_CAN_ERRINT;
|
||||
}
|
||||
|
||||
stm32can_putreg(priv, STM32_CAN_IER_OFFSET, regval);
|
||||
}
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Name: stm32can_ioctl
|
||||
*
|
||||
@ -1649,6 +1741,172 @@ static int stm32can_txinterrupt(int irq, FAR void *context, FAR void *arg)
|
||||
return OK;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_CAN_ERRORS
|
||||
/****************************************************************************
|
||||
* Name: stm32can_sceinterrupt
|
||||
*
|
||||
* Description:
|
||||
* CAN status change interrupt handler
|
||||
*
|
||||
* Input Parameters:
|
||||
* irq - The IRQ number of the interrupt.
|
||||
* context - The register state save array at the time of the interrupt.
|
||||
*
|
||||
* Returned Value:
|
||||
* Zero on success; a negated errno on failure
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static int stm32can_sceinterrupt(int irq, FAR void *context, FAR void *arg)
|
||||
{
|
||||
FAR struct can_dev_s *dev = (FAR struct can_dev_s *)arg;
|
||||
FAR struct stm32_can_s *priv = NULL;
|
||||
struct can_hdr_s hdr;
|
||||
uint32_t regval = 0;
|
||||
uint16_t errbits = 0;
|
||||
uint8_t data[CAN_ERROR_DLC];
|
||||
int ret = OK;
|
||||
|
||||
DEBUGASSERT(dev != NULL && dev->cd_priv != NULL);
|
||||
priv = dev->cd_priv;
|
||||
|
||||
/* Check Error Interrupt flag */
|
||||
|
||||
regval = stm32can_getreg(priv, STM32_CAN_MSR_OFFSET);
|
||||
if (regval & CAN_MSR_ERRI)
|
||||
{
|
||||
/* Encode error bits */
|
||||
|
||||
errbits = 0;
|
||||
memset(data, 0, sizeof(data));
|
||||
|
||||
/* Get Error statur register */
|
||||
|
||||
regval = stm32can_getreg(priv, STM32_CAN_ESR_OFFSET);
|
||||
|
||||
if (regval & CAN_ESR_EWGF)
|
||||
{
|
||||
/* Error warning flag */
|
||||
|
||||
data[1] |= (CAN_ERROR1_RXWARNING | CAN_ERROR1_TXWARNING);
|
||||
errbits |= CAN_ERROR_CONTROLLER;
|
||||
}
|
||||
|
||||
if (regval & CAN_ESR_EPVF)
|
||||
{
|
||||
/* Error passive flag */
|
||||
|
||||
data[1] |= (CAN_ERROR1_RXPASSIVE | CAN_ERROR1_TXPASSIVE);
|
||||
errbits |= CAN_ERROR_CONTROLLER;
|
||||
}
|
||||
|
||||
if (regval & CAN_ESR_BOFF)
|
||||
{
|
||||
/* Bus-off flag */
|
||||
|
||||
errbits |= CAN_ERROR_BUSOFF;
|
||||
}
|
||||
|
||||
/* Last error code */
|
||||
|
||||
if (regval & CAN_ESR_LEC_MASK)
|
||||
{
|
||||
if (regval & CAN_ESR_STUFFERROR)
|
||||
{
|
||||
/* Stuff Error */
|
||||
|
||||
errbits |= CAN_ERROR_PROTOCOL;
|
||||
data[2] |= CAN_ERROR2_STUFF;
|
||||
}
|
||||
else if (regval & CAN_ESR_FORMERROR)
|
||||
{
|
||||
/* Format Error */
|
||||
|
||||
errbits |= CAN_ERROR_PROTOCOL;
|
||||
data[2] |= CAN_ERROR2_FORM;
|
||||
}
|
||||
else if (regval & CAN_ESR_ACKERROR)
|
||||
{
|
||||
/* Acknowledge Error */
|
||||
|
||||
errbits |= CAN_ERROR_NOACK;
|
||||
}
|
||||
else if (regval & CAN_ESR_BRECERROR)
|
||||
{
|
||||
/* Bit recessive Error */
|
||||
|
||||
errbits |= CAN_ERROR_PROTOCOL;
|
||||
data[2] |= CAN_ERROR2_BIT1;
|
||||
}
|
||||
else if (regval & CAN_ESR_BDOMERROR)
|
||||
{
|
||||
/* Bit dominant Error */
|
||||
|
||||
errbits |= CAN_ERROR_PROTOCOL;
|
||||
data[2] |= CAN_ERROR2_BIT0;
|
||||
}
|
||||
else if (regval & CAN_ESR_CRCERRPR)
|
||||
{
|
||||
/* Receive CRC Error */
|
||||
|
||||
errbits |= CAN_ERROR_PROTOCOL;
|
||||
data[3] |= CAN_ERROR3_CRCSEQ;
|
||||
}
|
||||
}
|
||||
|
||||
/* Get transmit status register */
|
||||
|
||||
regval = stm32can_getreg(priv, STM32_CAN_TSR_OFFSET);
|
||||
|
||||
if (regval & CAN_TSR_ALST0 || regval & CAN_TSR_ALST1 ||
|
||||
regval & CAN_TSR_ALST2)
|
||||
{
|
||||
/* Lost arbitration Error */
|
||||
|
||||
errbits |= CAN_ERROR_LOSTARB;
|
||||
}
|
||||
|
||||
/* Clear TSR register */
|
||||
|
||||
stm32can_putreg(priv, STM32_CAN_TSR_OFFSET, regval);
|
||||
|
||||
/* Clear ERRI flag */
|
||||
|
||||
stm32can_putreg(priv, STM32_CAN_MSR_OFFSET, CAN_MSR_ERRI);
|
||||
}
|
||||
|
||||
/* TODO: RX overflow and TX overflow */
|
||||
|
||||
/* Report a CAN error */
|
||||
|
||||
if (errbits != 0)
|
||||
{
|
||||
canerr("ERROR: errbits = %08" PRIx16 "\n", errbits);
|
||||
|
||||
/* Format the CAN header for the error report. */
|
||||
|
||||
hdr.ch_id = errbits;
|
||||
hdr.ch_dlc = CAN_ERROR_DLC;
|
||||
hdr.ch_rtr = 0;
|
||||
hdr.ch_error = 1;
|
||||
#ifdef CONFIG_CAN_EXTID
|
||||
hdr.ch_extid = 0;
|
||||
#endif
|
||||
hdr.ch_unused = 0;
|
||||
|
||||
/* And provide the error report to the upper half logic */
|
||||
|
||||
ret = can_receive(dev, &hdr, data);
|
||||
if (ret < 0)
|
||||
{
|
||||
canerr("ERROR: can_receive failed: %d\n", ret);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Name: stm32can_bittiming
|
||||
*
|
||||
|
Loading…
Reference in New Issue
Block a user