You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by da...@apache.org on 2021/06/16 11:58:13 UTC
[incubator-nuttx] 01/03: stm32h7 fix adc port to handle overrun and
the DR's fifo, adapt adc driver
This is an automated email from the ASF dual-hosted git repository.
davids5 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-nuttx.git
commit b9fba3edae59bc9bd0b38b3411947b8cf480f3da
Author: GAEHWILER Reto <re...@hexagon.com>
AuthorDate: Tue Jun 15 13:08:48 2021 +0200
stm32h7 fix adc port to handle overrun and the DR's fifo, adapt adc driver
* port didn't know about data-register fifo
* port didn't handle overrun condition
* driver could get stuck if interrupts were skipped due to saturation
---
arch/arm/src/stm32h7/stm32_adc.c | 95 ++++++++++++++++++++++++++++------------
drivers/analog/adc.c | 34 +++++++++++++-
include/nuttx/analog/adc.h | 14 ++++++
3 files changed, 115 insertions(+), 28 deletions(-)
diff --git a/arch/arm/src/stm32h7/stm32_adc.c b/arch/arm/src/stm32h7/stm32_adc.c
index 4609111..e216984 100644
--- a/arch/arm/src/stm32h7/stm32_adc.c
+++ b/arch/arm/src/stm32h7/stm32_adc.c
@@ -1389,6 +1389,10 @@ static int adc_setup(FAR struct adc_dev_s *dev)
clrbits |= ADC_CFGR_EXTEN_MASK;
setbits |= ADC_CFGR_EXTEN_NONE;
+ /* Set overrun mode to preserve the data register */
+
+ clrbits |= ADC_CFGR_OVRMOD;
+
/* Set CFGR configuration */
adc_modifyreg(priv, STM32_ADC_CFGR_OFFSET, clrbits, setbits);
@@ -1551,9 +1555,9 @@ static void adc_rxint(FAR struct adc_dev_s *dev, bool enable)
regval = adc_getreg(priv, STM32_ADC_IER_OFFSET);
if (enable)
{
- /* Enable end of conversion interrupt */
+ /* Enable end of conversion and overrun interrupts */
- regval |= ADC_INT_EOC;
+ regval |= ADC_INT_EOC | ADC_INT_OVR;
}
else
{
@@ -1833,7 +1837,7 @@ static int adc_interrupt(FAR struct adc_dev_s *dev, uint32_t adcisr)
FAR struct stm32_dev_s *priv = (FAR struct stm32_dev_s *)dev->ad_priv;
int32_t value;
- /* Identifies the interruption AWD or OVR */
+ /* Identifies the interruption AWD */
if ((adcisr & ADC_INT_AWD1) != 0)
{
@@ -1848,50 +1852,87 @@ static int adc_interrupt(FAR struct adc_dev_s *dev, uint32_t adcisr)
adc_startconv(priv, false);
}
+ /* OVR: Overrun */
+
if ((adcisr & ADC_INT_OVR) != 0)
{
+ /* In case of a missed ISR - due to interrupt saturation -
+ * the upper half needs to be informed to terminate properly.
+ */
+
awarn("WARNING: Overrun has occurred!\n");
+
+ /* To make use of already sampled data the conversion needs to be
+ * stopped first before reading out the data register.
+ */
+
+ adc_startconv(priv, false);
+
+ while ((adc_getreg(priv, STM32_ADC_CR_OFFSET) & ADC_CR_ADSTART) != 0);
+
+ /* Verify that the upper-half driver has bound its callback functions */
+
+ if ((priv->cb != NULL) && (priv->cb->au_reset != NULL))
+ {
+ /* Notify upper-half driver about the overrun */
+
+ priv->cb->au_reset(dev);
+ }
+
+ adc_putreg(priv, STM32_ADC_ISR_OFFSET, ADC_INT_OVR);
}
/* EOC: End of conversion */
if ((adcisr & ADC_INT_EOC) != 0)
{
- /* Read the converted value and clear EOC bit
- * (It is cleared by reading the ADC_DR)
+ /* Read from the ADC_DR register until 8 stage FIFO is empty.
+ * The FIFO is first mentioned in STM32H7 Reference Manual
+ * rev. 7, though, not yet indicated in the block diagram!
*/
- value = adc_getreg(priv, STM32_ADC_DR_OFFSET);
- value &= ADC_DR_MASK;
+ do
+ {
+ /* Read the converted value and clear EOC bit
+ * (It is cleared by reading the ADC_DR)
+ */
- /* Verify that the upper-half driver has bound its callback functions */
+ value = adc_getreg(priv, STM32_ADC_DR_OFFSET);
+ value &= ADC_DR_MASK;
- if (priv->cb != NULL)
- {
- /* Give the ADC data to the ADC driver. The ADC receive() method
- * accepts 3 parameters:
- *
- * 1) The first is the ADC device instance for this ADC block.
- * 2) The second is the channel number for the data, and
- * 3) The third is the converted data for the channel.
+ /* Verify that the upper-half driver has bound its
+ * callback functions
*/
- DEBUGASSERT(priv->cb->au_receive != NULL);
- priv->cb->au_receive(dev, priv->chanlist[priv->current], value);
- }
+ if (priv->cb != NULL)
+ {
+ /* Hand the ADC data to the ADC driver. The ADC receive()
+ * method accepts 3 parameters:
+ *
+ * 1) The first is the ADC device instance for this ADC block.
+ * 2) The second is the channel number for the data, and
+ * 3) The third is the converted data for the channel.
+ */
+
+ DEBUGASSERT(priv->cb->au_receive != NULL);
+ priv->cb->au_receive(dev, priv->chanlist[priv->current],
+ value);
+ }
- /* Set the channel number of the next channel that will complete
- * conversion
- */
+ /* Set the channel number of the next channel that will
+ * complete conversion
+ */
- priv->current++;
+ priv->current++;
- if (priv->current >= priv->nchannels)
- {
- /* Restart the conversion sequence from the beginning */
+ if (priv->current >= priv->nchannels)
+ {
+ /* Restart the conversion sequence from the beginning */
- priv->current = 0;
+ priv->current = 0;
+ }
}
+ while ((adc_getreg(priv, STM32_ADC_ISR_OFFSET) & ADC_INT_EOC) != 0);
}
return OK;
diff --git a/drivers/analog/adc.c b/drivers/analog/adc.c
index 7bc90cb..c956f9e 100644
--- a/drivers/analog/adc.c
+++ b/drivers/analog/adc.c
@@ -72,6 +72,7 @@ static int adc_close(FAR struct file *filep);
static ssize_t adc_read(FAR struct file *fielp, FAR char *buffer,
size_t buflen);
static int adc_ioctl(FAR struct file *filep, int cmd, unsigned long arg);
+static int adc_reset(FAR struct adc_dev_s *dev);
static int adc_receive(FAR struct adc_dev_s *dev, uint8_t ch,
int32_t data);
static void adc_notify(FAR struct adc_dev_s *dev);
@@ -98,7 +99,8 @@ static const struct file_operations g_adc_fops =
static const struct adc_callback_s g_adc_callback =
{
- adc_receive /* au_receive */
+ adc_receive, /* au_receive */
+ adc_reset /* au_reset */
};
/****************************************************************************
@@ -158,6 +160,10 @@ static int adc_open(FAR struct file *filep)
dev->ad_recv.af_head = 0;
dev->ad_recv.af_tail = 0;
+ /* Clear overrun indicator */
+
+ dev->ad_isovr = false;
+
/* Finally, Enable the ADC RX interrupt */
dev->ad_ops->ao_rxint(dev, true);
@@ -280,6 +286,15 @@ static ssize_t adc_read(FAR struct file *filep, FAR char *buffer,
flags = enter_critical_section();
while (dev->ad_recv.af_head == dev->ad_recv.af_tail)
{
+ /* Check if there was an overrun, if set we need to return EIO */
+
+ if (dev->ad_isovr)
+ {
+ dev->ad_isovr = false;
+ ret = -EIO;
+ goto return_with_irqdisabled;
+ }
+
/* The receive FIFO is empty -- was non-blocking mode selected? */
if (filep->f_oflags & O_NONBLOCK)
@@ -421,6 +436,23 @@ static int adc_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
}
/****************************************************************************
+ * Name: adc_reset
+ ****************************************************************************/
+
+static int adc_reset(FAR struct adc_dev_s *dev)
+{
+ /* Set overrun flag to give read a chance to recover */
+
+ dev->ad_isovr = true;
+
+ /* No need to notify here. The adc_receive callback will be called next.
+ * If an adc overrun occurs then there must be at least one conversion.
+ */
+
+ return OK;
+}
+
+/****************************************************************************
* Name: adc_receive
****************************************************************************/
diff --git a/include/nuttx/analog/adc.h b/include/nuttx/analog/adc.h
index 0fb99c1..aa09485 100644
--- a/include/nuttx/analog/adc.h
+++ b/include/nuttx/analog/adc.h
@@ -110,6 +110,19 @@ struct adc_callback_s
CODE int (*au_receive)(FAR struct adc_dev_s *dev, uint8_t ch,
int32_t data);
+
+ /* This method is called from the lower half, platform-specific ADC logic
+ * when an overrun appeared to free / reset upper half.
+ *
+ * Input Parameters:
+ * dev - The ADC device structure that was previously registered by
+ * adc_register()
+ *
+ * Returned Value:
+ * Zero on success; a negated errno value on failure.
+ */
+
+ CODE int (*au_reset)(FAR struct adc_dev_s *dev);
};
/* This describes on ADC message */
@@ -195,6 +208,7 @@ struct adc_dev_s
sem_t ad_closesem; /* Locks out new opens while close is in progress */
sem_t ad_recvsem; /* Used to wakeup user waiting for space in ad_recv.buffer */
struct adc_fifo_s ad_recv; /* Describes receive FIFO */
+ bool ad_isovr; /* Flag to indicate an ADC overrun */
/* The following is a list of poll structures of threads waiting for
* driver events. The 'struct pollfd' reference for each open is also