STM32F4 ADC with GPT

ChibiOS public support forum for topics related to the STMicroelectronics STM32 family of micro-controllers.

Moderators: RoccoMarco, barthess

FlE96
Posts: 4
Joined: Tue Aug 29, 2017 6:35 am

STM32F4 ADC with GPT

Postby FlE96 » Tue Aug 29, 2017 8:06 am

Hi,

CONTEXT:
I'm trying to read 4 ADC channels at a rate of 48kHz each, using the GPT3 TRIGO as the trigger for the ADC1. Everytime the samples are converted, I store each channel in a fifo, to be able to read them later for DSP processing.
My clock is 180MHz which means that GPT3 is running at 90MHz.
Unfortunately, 4 channels at 48kHz each means that I have to sample at 192kHz, which is not a multiple of 90MHz.
The solution that I found is to read 5 channels which allows me to set GPT3's frequency to 90MHz, the timer interval at 375 and thus have a sampling frequency of 48kHz for each channlel. I just discard the value of the 5'th "virtual" channel.

PROBLEM:
Everytime I run the My problem is that I don't really understand the ADC callback in circular buffer mode. I have tried debugging my code and have noticed that my n i.e. the length of the converted buffer is always 16. Moreover, I saw that the address of this buffer is samplesBuffer for the first conversion, and then becomes samplesBuffer+160 for all the other conversions. I must have done something wrong, but I don't know what...

CODE:
initialisation of the ADC:

Code: Select all

#define ADC_BUF_DEPTH 32 // depth of buffer
#define ADC_CH_NUM 5    // number of used ADC channels

adcsample_t samplesMics[ADC_CH_NUM * ADC_BUF_DEPTH];

static ADCConversionGroup adc1Cfg = {
  TRUE,                                                                         // Circular buffer mode
  (uint16_t)(ADC_CH_NUM),                                           // number of channels
  ovf_adc_mics_callback,                                               // EoT callback
  NULL,                                                                        // Error callback
  0,                                                                              // CR1
  ADC_CR2_EXTSEL_SRC(8) | ADC_CR2_EXTEN_RISING,  // CR2
  ADC_SMPR1_SMP_AN11(ADC_SAMPLE_3)
  | ADC_SMPR1_SMP_AN14(ADC_SAMPLE_3)
  | ADC_SMPR1_SMP_AN15(ADC_SAMPLE_3),                 // SMPR1
  0,                                                                             // SMPR2 
  ADC_SQR1_NUM_CH(ADC_CH_NUM),                          // SQR1
  0,                                                                            // SQR2
  ADC_SQR3_SQ1_N(ADC_CHANNEL_IN7)
  | ADC_SQR3_SQ2_N(ADC_CHANNEL_IN11)
  | ADC_SQR3_SQ3_N(ADC_CHANNEL_IN14)
  | ADC_SQR3_SQ4_N(ADC_CHANNEL_IN15)                  // SQR3
};

void ovf_adc1_init(void)
{
   palSetGroupMode(GPIOC, PAL_PORT_BIT(1), 0, PAL_MODE_INPUT_ANALOG); //Pin PC1
   palSetGroupMode(GPIOC, PAL_PORT_BIT(4), 0, PAL_MODE_INPUT_ANALOG); //Pin PC4
   palSetGroupMode(GPIOC, PAL_PORT_BIT(5), 0, PAL_MODE_INPUT_ANALOG); //Pin PC5
   palSetGroupMode(GPIOA, PAL_PORT_BIT(7), 0, PAL_MODE_INPUT_ANALOG); //Pin PC5
}   
   
...
 adcStart(&ADCD1, NULL);
 adcStartConversion(&ADCD1, &adc1Cfg, samplesMics, ADC_BUF_DEPTH);
 


initialisation of the GPT:

Code: Select all

static const GPTConfig gpt3_ADC = {
  .frequency =  90000000U,
  .callback  =  NULL,
  .cr2       =  TIM_CR2_MMS_1,  /* MMS = 010 = TRGO on Update Event.        */
  .dier      =  0U
};
void ovf_tim3_init(void)
{
  gptStart(&GPTD3, &gpt3_ADC); 
  gptStartContinuous
(&GPTD3, 375); // 192 kHz
}

ADC Callback:

Code: Select all


void ovf_adc_callback
(ADCDriver *adcp, adcsample_t *buffer, size_t n)
{
     adc1_CB(buffer, n); 
}
void adc1_CB(adcsample_t *samples, uint16_t length)
{
  for(int i=0; i<length; i++)
    {
      fifo1.write(samples[i++]);
      fifo2.write(samples[i++]);
      fifo3.write(samples[i++]);
      fifo4.write(samples[i++]);
    }
}


Thanks in advance for any tips you can give me!

User avatar
alex31
Posts: 379
Joined: Fri May 25, 2012 10:23 am
Location: toulouse, france
Has thanked: 38 times
Been thanked: 62 times
Contact:

Re: STM32F4 ADC with GPT

Postby alex31 » Tue Aug 29, 2017 8:45 am

CONTEXT:
I'm trying to read 4 ADC channels at a rate of 48kHz each, using the GPT3 TRIGO as the trigger for the ADC1. Everytime the samples are converted, I store each channel in a fifo, to be able to read them later for DSP processing.
My clock is 180MHz which means that GPT3 is running at 90MHz.
Unfortunately, 4 channels at 48kHz each means that I have to sample at 192kHz, which is not a multiple of 90MHz.
The solution that I found is to read 5 channels which allows me to set GPT3's frequency to 90MHz, the timer interval at 375 and thus have a sampling frequency of 48kHz for each channlel. I just discard the value of the 5'th "virtual" channel.


Hello, i can comment the issues that i have seen (but there is perhaps more ...)

1/ The timer will trigger the conversion of all (4 in your case channels), not just one, so you need to configure your timer at 48Khz

2/ you specify ADC_BUF_DEPTH to 32 : do you really need double buffering ?
i have never used gpt triggered double buffer adc, but what will probably occurs with your setting is that gpt will trigger adc conversion,
your callback will be called twice with 16 sample for each channels.

3/ you did not specify ADC_SMPR2_SMP_AN7


What i'd do :

a/ fix ADC_SMPR2_SMP_AN7, and use more precise sampling for all channels, 48khz is low frequency, you can use 56 insted of 3 sampling cycles
b/ set ADC_BUF_DEPTH to 1
c/ set ADC_CH_NUM to 4
d/ configure GPT for 48Khz trigger

then your will just have to manage the 4 samples in each callback.

Alexandre

FlE96
Posts: 4
Joined: Tue Aug 29, 2017 6:35 am

Re: STM32F4 ADC with GPT

Postby FlE96 » Tue Aug 29, 2017 9:04 am

Hi Alexandre,

Thank you very much! I had no idea that the clock is for a group of channels and not for each channel individually... This website states
Note all ADC channels are internally multiplexed in the MCU. These are read sequentially. Therefore if you configure it to read 16 channels, the sampling rate will be slowed down by 16.

which confused me into thinking that I had to multiply the overall sampling frequency by the number of channels.

I will apply what you said and post the result here, but in the meantime I still have a few questions :

2/ you specify ADC_BUF_DEPTH to 32 : do you really need double buffering ?
i have never used gpt triggered double buffer adc, but what will probably occurs with your setting is that gpt will trigger adc conversion,
your callback will be called twice with 16 sample for each channels.

I am not so sure that I need, it, the reason why I usually use it is to prevent new samples to be written while I'm still processing the old ones. (the final goal is an audio application, which is why skipping samples would be very bad)
I still don't quite understand why the half and end of conversion callbacks are called for each channels, I thought it was only called at half/end of the full conversion, ie the number of channels times the depth of the buffer...

3/ you did not specify ADC_SMPR2_SMP_AN7

Indeed, a very dumb mistake. Correcting that!

Also do you have an idea on why the buffer address in the ADC callback is so weird? Is it because of the whole half-buffer mode thing?

Thanks a lot again for helping me!

User avatar
alex31
Posts: 379
Joined: Fri May 25, 2012 10:23 am
Location: toulouse, france
Has thanked: 38 times
Been thanked: 62 times
Contact:

Re: STM32F4 ADC with GPT

Postby alex31 » Tue Aug 29, 2017 10:20 am

I am not so sure that I need, it, the reason why I usually use it is to prevent new samples to be written while I'm still processing the old ones. (the final goal is an audio application, which is why skipping samples would be very bad)
I still don't quite understand why the half and end of conversion callbacks are called for each channels, I thought it was only called at half/end of the full conversion, ie the number of channels times the depth of the buffer...


If you trigger ADC with GPT timer, you will miss sample. Timer triggered ADC is when you need sparse sampling at precise time, for example in motor control application.

If you want to sample audio, you just need double buffered software triggered ADC, you will find example of that in the testhal and demo repertories of chibios. In this case, you effectively process signal on half buffer during sampling of the other buffer.

You need to configure clock tree to that your sampling is done at 48 Khz, the STM32CubeMX application can help you to configure predivider.

Alexandre

User avatar
Giovanni
Site Admin
Posts: 14455
Joined: Wed May 27, 2009 8:48 am
Location: Salerno, Italy
Has thanked: 1076 times
Been thanked: 922 times
Contact:

Re: STM32F4 ADC with GPT

Postby Giovanni » Tue Aug 29, 2017 1:36 pm

I think that triggering the ADC with GPT is the best solution for audio sampling, why you say it would miss samples?

A deep buffer is required in order to have enough time to process the semi-buffers.

Giovanni

User avatar
alex31
Posts: 379
Joined: Fri May 25, 2012 10:23 am
Location: toulouse, france
Has thanked: 38 times
Been thanked: 62 times
Contact:

Re: STM32F4 ADC with GPT

Postby alex31 » Wed Aug 30, 2017 10:07 am

I think that triggering the ADC with GPT is the best solution for audio sampling, why you say it would miss samples?

A deep buffer is required in order to have enough time to process the semi-buffers.

Giovanni


Hello,

When one do software triggered continuous conversion, with double buffer, one could be sure to not miss sample if signal processing of half buffer is done sufficiently fast so there is no overbuffer condition.

When the ADC is Timer triggered, there is two clock source, clock of the timer, and clock of the ADC, and i don't see how to manage that to not miss any sample.

Or perhaps you mean using a timer as ADC clock source for software triggered continuous conversion ?

If it's possible, that would permit to precisely set sampling frequency !

Alexandre

FlE96
Posts: 4
Joined: Tue Aug 29, 2017 6:35 am

Re: STM32F4 ADC with GPT

Postby FlE96 » Thu Aug 31, 2017 7:17 am

Giovanni wrote:I think that triggering the ADC with GPT is the best solution for audio sampling, why you say it would miss samples?

A deep buffer is required in order to have enough time to process the semi-buffers.

Giovanni


Could you explain a bit more about the interrupts with the ADC drivers? I'm not really sure wheter the interrupt is triggered at half the whole transmission, the transmission of half the depth or other. WHen ever my interrupt triggers, it gives me a length parameter of 16. I have measured the rate at which this interrupt fires and it's 3kHz, which corresponds to 16 samples at 48kHz. Why 16, is it 4 samples of each of my 4 channels? If so, does my interrupt get called 8 times per full buffer (32/4=8)?

Is my idea of calling a routine to fill a buffer at every half interrupt correct? I'm not so sure on how to do the double buffering with ChibiOS's ADC drivers.
With CubeHAL I had 2 different interrupts : half_transfer_complete and full_transfer_complete, with 2 different DMA buffers. Then it was just a matter of operating on buffer "ping" in half_transfer_complete and "pong" in full_transfer_complete.

Thanks in advance!

EDIT : I just read in the adc_lld.h code that the n parameter is actually the number of rows. Then I believe that means that 16*4=64 samples are available, and that I'm indeed reading at a rate of 48kHz per sequence and not per sample (which is what I want). Could you confirm?

User avatar
Giovanni
Site Admin
Posts: 14455
Joined: Wed May 27, 2009 8:48 am
Location: Salerno, Italy
Has thanked: 1076 times
Been thanked: 922 times
Contact:

Re: STM32F4 ADC with GPT

Postby Giovanni » Thu Aug 31, 2017 7:55 am

Yes, triggered comversion is always for sequence (a row of the matrix), making it per sample would not make sense.

Circular conversions triggers two callbacks, one at mid buffer and another at the end, see the ADC demos, there is an example in the callback.

Giovanni

FlE96
Posts: 4
Joined: Tue Aug 29, 2017 6:35 am

Re: STM32F4 ADC with GPT

Postby FlE96 » Thu Aug 31, 2017 8:00 am

Thanks for your answer!

But the *samples parameter anways gives the address of the beginning of the buffer right? Or does it alternates between the address of the middle and the address of the beginning? (couldn't really understant the GPT ADC example : all that is done in the interrupt is the incrementation of 2 variables, nx and ny, which purpose I don't really understand...)

sorry for the newbie questions

User avatar
Giovanni
Site Admin
Posts: 14455
Joined: Wed May 27, 2009 8:48 am
Location: Salerno, Italy
Has thanked: 1076 times
Been thanked: 922 times
Contact:

Re: STM32F4 ADC with GPT

Postby Giovanni » Thu Aug 31, 2017 8:14 am

The passed pointer always points to the semi buffer you should process, samples is the number of samples to process.

The demo just increases those variables to show that the callback is called 2 times for cycle (x and y must be equal, +-1 row, after breaking the program).

Giovanni


Return to “STM32 Support”

Who is online

Users browsing this forum: No registered users and 13 guests