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
This commit is contained in:
parent
82887c10a7
commit
b9fba3edae
@ -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;
|
||||
|
||||
/* Verify that the upper-half driver has bound its callback functions */
|
||||
|
||||
if (priv->cb != NULL)
|
||||
do
|
||||
{
|
||||
/* 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.
|
||||
/* Read the converted value and clear EOC bit
|
||||
* (It is cleared by reading the ADC_DR)
|
||||
*/
|
||||
|
||||
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
|
||||
*/
|
||||
|
||||
priv->current++;
|
||||
|
||||
if (priv->current >= priv->nchannels)
|
||||
{
|
||||
/* Restart the conversion sequence from the beginning */
|
||||
|
||||
priv->current = 0;
|
||||
value = adc_getreg(priv, STM32_ADC_DR_OFFSET);
|
||||
value &= ADC_DR_MASK;
|
||||
|
||||
/* Verify that the upper-half driver has bound its
|
||||
* callback functions
|
||||
*/
|
||||
|
||||
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
|
||||
*/
|
||||
|
||||
priv->current++;
|
||||
|
||||
if (priv->current >= priv->nchannels)
|
||||
{
|
||||
/* Restart the conversion sequence from the beginning */
|
||||
|
||||
priv->current = 0;
|
||||
}
|
||||
}
|
||||
while ((adc_getreg(priv, STM32_ADC_ISR_OFFSET) & ADC_INT_EOC) != 0);
|
||||
}
|
||||
|
||||
return OK;
|
||||
|
@ -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)
|
||||
@ -420,6 +435,23 @@ static int adc_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* 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
|
||||
****************************************************************************/
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user