STM32 timer triggers ADC multi-channel continuous sampling, and DMA caches the results

Posted by Snake PHP on Mon, 31 Jan 2022 10:16:58 +0100

The ADC of STM32 is very flexible. In terms of sampling trigger, it supports not only software trigger, timer or other hardware circuit automatic trigger, but also automatic trigger of the next channel / round conversion after conversion. Storage of conversion results: it supports not only software reading and transfer, but also DMA automatic storage of conversion results. STM32 books mostly introduce the "software trigger + query method to read the conversion results". This method is enough to deal with the acquisition of DC signals such as temperature and humidity. However, when the application needs to improve the sampling rate of A/D conversion, this practice gradually can not meet the requirements: 1. The software needs to determine through frequent queries or interruptions that the next round of A/D conversion will be triggered in time when it arrives at the sampling interval. Other work of the processor is frequently interrupted, which increases the difficulty of software development. 2. The timing accuracy / jitter time of timer timing interrupt / query method is about the time when the processor executes several instructions. For STM32, when the sampling rate is greater than 10KSPS, the signal-to-noise ratio of A/D will be reduced by sampling timing jitter, which will reduce the signal-to-noise ratio of A/D to equivalent to that of 10 bit A/D (see my previous blog for specific mathematical analysis:(
STM32 provides two methods to solve this problem: Method 1: let the ADC continuously convert, and the conversion results are directly transported to the memory through DMA. Since the time of ADC conversion can be accurately determined by ADC clock CLKADC frequency and sample holding time, this method effectively reduces the aperture jitter of conversion interval and improves the signal-to-noise ratio. It is especially suitable for high sampling rates above 200KSPS. Interested readers can refer to my blog: . html. Because the CLKADC frequency and sample holding time cannot be adjusted continuously, the defect of this method is that the sampling rate cannot be adjusted continuously, such as 20.05KSPS, 44.1KSPS and other common but non integer sampling rates cannot be generated. The second method introduced in this paper is the A/D conversion method directly starting from the TRGO signal of timer 3, which can effectively realize the continuous adjustment of sampling rate below 200KSPS. Due to the use of timer hardware to directly start A/D conversion without software participation, this method can also effectively avoid the aperture jitter of sampling interval. In addition, in order to avoid the problems of reduced execution efficiency and increased development difficulty caused by the frequent participation of software in the reading of conversion results, this paper provides a code for reading automatic continuous conversion results through DMA.
Users are welcome to reprint the following original content, but please indicate the source:
1, ADC module configuration
 1 RCC_ADCCLKConfig(RCC_PCLK2_Div8);   //set up ADC Frequency division factor 8 72 M/8=9,ADC The maximum time cannot exceed 14 minutes M  ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;//ADC1 Working in independent mode
 3 ADC_InitStructure.ADC_ScanConvMode = ENABLE;//A / D conversion works in scan mode (multi-channel)
 4 ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;//ADC Working in discontinuous mode
 5 ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T3_TRGO;//Timer 3 TRGO Trigger conversion
 6 ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//ADC Align data right
 7 ADC_InitStructure.ADC_NbrOfChannel = 2;//Converted ADC The number of channels is 2
 8 ADC_Init(ADC1, &ADC_InitStructure);//To initialize the following parameters ADC_InitStructure
 9 ADC_RegularChannelConfig(ADC1, ADC_Channel_6, 1, ADC_SampleTime_13Cycles5);//ADC1 Channel 6, sampling time 13.5 Cycles 
10 ADC_RegularChannelConfig(ADC1, ADC_Channel_7, 2, ADC_SampleTime_13Cycles5);//ADC1 Channel 7, sampling time 13.5 Cycles 
11 ADC_DMACmd(ADC1, ENABLE);  //Enable ADC1 of DMA transmission mode
12 ADC_Cmd(ADC1, ENABLE);//Enable ADC1
13 ADC_ResetCalibration(ADC1);//Reset ADC1 Calibration register for
14 while(ADC_GetResetCalibrationStatus(ADC1));
15 ADC_StartCalibration(ADC1); //Start calibration ADC1
16 while(ADC_GetCalibrationStatus(ADC1)); //Wait for calibration to complete
17 ADC_SoftwareStartConvCmd(ADC1, ENABLE);//Enable ADC1 Software conversion 
Notable among them are:
1. The ADC is configured to be triggered by an external signal, which is a TRGO generated by TIM3. Note that STM32 does not support TRGO of other timers as the trigger source of ADC.
2. ADC is configured to operate in discontinuous mode (ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;) The so-called "continuous working mode" is the working mode mentioned above to realize sampling timing by configuring each A/D conversion time. If this mode is enabled, it means that the ADC will carry out continuous conversion channel by channel and non-stop after being triggered once, and will not wait until the next timer trigger signal TRGO to start the A/D conversion.
3. ADC is configured as multi-channel scanning mode (ADC_InitStructure.ADC_ScanConvMode = ENABLE;), In this way, the ADC will complete a round of conversion of each channel in the regular channel group after being triggered by TIM3 each time.
2, DMA configuration
 1   DMA_DeInit(DMA1_Channel1);
 2   DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address;//Source address of transmission
 3   DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&ADCConvertedValue;//Destination address
 4   DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //Peripherals as the source
 5   DMA_InitStructure.DMA_BufferSize = 2000;//The data length is 2000
 6   DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//The peripheral address register is not incremented
 7   DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//Memory address increment
 8   DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;//Peripheral transmission is in bytes
 9   DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;//Memory in words
10   DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;//Cycle mode
11   DMA_InitStructure.DMA_Priority = DMA_Priority_High;//4 One of the priorities(High priority)
12   DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //Non memory to memory
13   DMA_Init(DMA1_Channel1, &DMA_InitStructure);//Initialize according to the above parameters DMA_InitStructure
15   DMA_ITConfig(DMA1_Channel1, DMA_IT_TC, ENABLE);//to configure DMA1 Channel 1 transmission completion interrupt 
16   DMA_Cmd(DMA1_Channel1, ENABLE);//Enable DMA1
Notable among them are:
1. DMA is configured as peripheral to memory mode, and the data width of each transmission is half a word (16 bits).
2. The buffer length is 2000 half words (DMA_InitStructure.DMA_BufferSize = 2000;), Since two channels are configured in front, the buffer can store the results of 1000 samples at a time.
2. Using circular mode (DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;), DMA1 interrupt is enabled (dma_init (DMA1_channel1, & dma_initstructure);) In this way, the DMA interrupt will be triggered after 1000 samples are completed, so that the software can read the results of 1000 conversions at one time. After the next ADC conversion, the result will overwrite the contents of this buffer from the beginning.
The configuration procedure of the supporting interrupt controller is as follows:
1 NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel1_IRQn;
2 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;//The preemption priority is set to 1
3 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;//The sub priority is set to 1
4 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//Interrupt enable
5 NVIC_Init(&NVIC_InitStructure);//Initialize the interrupt according to the specified parameters     
The preemption priority and sub priority can be determined according to the actual needs of the application.
DMA interrupt service procedure is as follows:
 1 void DMA1_Channel1_IRQHandler(void)
 2 {    
 3     if(DMA_GetITStatus(DMA1_IT_TC1))//Judge whether the transmission of channel 1 is completed
 4      {
 5         DMA_ClearITPendingBit(DMA1_IT_TC1);    //Clear the transmission completion flag bit of channel 1
 7     ////////////Code should be written here from DMA Read the data from the memory area pointed to, otherwise it may be overwritten//////////
10      }
11 }
3, TIM3 configuration
TIM3 configuration code is as follows:
 1 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //Enable TIM3 Clock
 3 TIM_TimeBaseStructure.TIM_Period = 1000-1;
 4 TIM_TimeBaseStructure.TIM_Prescaler = 72-1;
 5 TIM_TimeBaseStructure.TIM_ClockDivision = 0x0;      //Sampling frequency division TIM_CKD_DIV1
 6 TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;     //Count up
 7 TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
 9 TIM_SelectOutputTrigger(TIM3, TIM_TRGOSource_Update);//Set output TRGO signal
Notable among them are:
1. After configuring the cycle register and prescaler register, determine the overflow frequency of TIM3 as 1KHz. These values can be determined according to the sampling rate required by the application.
2. After TIM3 overflows, TRGO signal will be output (TIM_SelectOutputTrigger(TIM3, TIM_TRGOSource_Update);).










Topics: stm32 dma ADC