cancel
Showing results for 
Search instead for 
Did you mean: 

STM32F411CEU6 ADC data offset does not work

HBida
Associate III

HI,

i am using STM32F411 for a digital signal processing intensive application where the ADC is used for continuous acquisition at 1.6MSPS (via DMA), the firmware was developped on STM32L476 for convenience but the final version uses STM32F411CEU6 because the STM32L4 serie is too expensive.

Now i am facing a big problem, there is apparently no way to set the offset on ADC data as it is done on the STM32L4. The ADC is used to sample an IF at ±700KHz, of course in order to sample this IF i intruduced a DC bias so that the IF signal doesnt no swing under 0.0V, and obviously i have to set a corresponding offset on the ADC data so that when the DC biased wavefor is at zero crossing the ADC value is 0. IN my case the offset value is 1950. ON ths STM32L4 setting this offset was as simple as setting the two following parameters in teh ADC channel setup:

  sConfig.OffsetNumber = ADC_OFFSET_1;
  sConfig.Offset = 1950;

But sadly on STM32F411, even if the field sConfig.Offset is present, it has no effect and is clearly no implemented.

By deesperation i tried to copy and run some of the functions found in L4 HAL library but of course none worked.

/* Internal register offset for ADC group injected offset configuration */
/* (offset placed into a spare area of literal definition) */
#define __ADC_MASK_SHIFT(__BITS__, __MASK__)                                   \
  (((__BITS__) & (__MASK__)) >> POSITION_VAL((__MASK__)))
#define __ADC_PTR_REG_OFFSET(__REG__, __REG_OFFFSET__)                         \
 ((uint32_t *)((uint32_t) ((uint32_t)(&(__REG__)) + ((__REG_OFFFSET__) << 2U))))
 
#define ADC_JOFR1_REGOFFSET                0x00000000U
#define ADC_JOFR2_REGOFFSET                0x00001000U
#define ADC_JOFR3_REGOFFSET                0x00002000U
#define ADC_JOFR4_REGOFFSET                0x00003000U
 
#define ADC_INJ_JDRX_REGOFFSET_MASK        (ADC_JDR1_REGOFFSET | ADC_JDR2_REGOFFSET | ADC_JDR3_REGOFFSET | ADC_JDR4_REGOFFSET)
#define ADC_INJ_JOFRX_REGOFFSET_MASK       (ADC_JOFR1_REGOFFSET | ADC_JOFR2_REGOFFSET | ADC_JOFR3_REGOFFSET | ADC_JOFR4_REGOFFSET)
#define ADC_INJ_RANK_ID_JSQR_MASK          (ADC_CHANNEL_ID_NUMBER_MASK_POSBIT0)
 
__STATIC_INLINE void LL_ADC_INJ_SetOffset(ADC_TypeDef *ADCx, uint32_t Rank, uint32_t OffsetLevel)
{
  register uint32_t *preg = __ADC_PTR_REG_OFFSET(ADCx->JOFR1, __ADC_MASK_SHIFT(Rank, ADC_INJ_JOFRX_REGOFFSET_MASK));
 
  MODIFY_REG(*preg,
             ADC_JOFR1_JOFFSET1,
             OffsetLevel);
}
void ADC_SetInjectedOffset(ADC_TypeDef* ADCx, uint8_t ADC_InjectedChannel, uint16_t Offset)
{
    __IO uint32_t tmp = 0;
  /* Check the parameters */
  assert_param(IS_ADC_ALL_PERIPH(ADCx));
  assert_param(IS_ADC_INJECTED_CHANNEL(ADC_InjectedChannel));
  assert_param(IS_ADC_OFFSET(Offset));
 
  tmp = (uint32_t)ADCx;
  tmp += ADC_InjectedChannel;
 
  /* Set the selected injected channel data offset */
 *(__IO uint32_t *) tmp = (uint32_t)Offset;
}
#define 	ADC_InjectedChannel_3   ((uint8_t)0x1C)

I really hope that there is a way to set this offset and that it can work.

One could argue that I can still apply the offset on the whole buffer after acquisition, unfortunately i use extensively DSP library for heavy signal processing along with DMA double buffering and the ressource usage is 98% (that is, 98% of the MCU time is used during acquisition via DMA) so there is absolutely no way to apply recursively an offset on each and every 8K samples (per half DMA buffer) before they are processed by the DSP, the whole process is realtime, the acquisition is continuous and DSP processing takes 98% of the MCU time before the next buffer has to be processed so there is no margin.

In the HAL and STM32CubeMX i can see there is an offset for injected channels, but i am not sure how i can use my ADC as injected channel, provided that it must be in continuous DMA acquisition. I use one single channel.

Any information will be very welcome.

3 REPLIES 3

The 'F4 ADC is very different from the 'L4 ADC (ADC is the module which changes probably the most between various STM32 families). As you've noted, there's no offset for the regular channels on 'F4 (and it's irrelevant what's in Cube, you should always work out of the RM as primary information source); OTOH injected channels cannot be run in continuous mode and they don't trigger DMA.

You simply may be out of luck.

JW

HBida
Associate III

Yes, the injected channels cannot be used with DMA at all. I first checked in RM and also in the HAL sources, of course there is no mention of data offset for regular ADC channel. That said when i tried the functions above i was able to get some reaction with the last one (ADC_SetInjectedOffset(ADC_TypeDef* ADCx, uint8_t ADC_InjectedChannel, uint16_t Offset) ) sadly not with an usable result, the output data was a fixed 30 value when no IF, but in reality there is some variation du to noise floor, and the value varied with a range of ±50 when IF was present, which is not right, since it should swing from -1900 to +1900 (more or less).

Fact is, in the RM a large portion of ADC registers are marked "reserved", also in HAL library source the field sConfig.Offset has the comment : "reserved for future use", which lead me to think there is probably a way to set an offset on the F4 serie.

Also when i check old sources for the STM32F3 i can see the function is present :

https://gitlab.iri.upc.edu/humanoides/tools/stm32_hal/blob/b5099619067071f1d0071b17e1094a95d06a6399/f3/include/stm32f3xx_ll_adc.h#L2970

If ST support could have a check, i cannot use the STM32L4 for this use case because the price is simply too high.

Robmar
Senior III

I've just stared look at this as it's a useful feature, amazing no one answered this question in over 2 years!

You're setting the register's lower 8 bit but its an 11 bit value:

Bits 31:12 Reserved, must be kept at reset value. Bits 11:0 JOFFSETx[11:0]: Data offset for injected channel x