使用STM32F103和STM32F401CCU6对双轴摇杆(两个电压通道)进行ADC采样并通过DMA读取数值
STM32 ADC(模数转换)工作模式
单次转换模式
In Single Conversion mode, the ADC does one conversion. This mode is started either by setting the ADON bit in the ADC_CR2 register (for a regular channel only) or by an external trigger (for a regular or injected channel), while the CONT bit is 0. Once the conversion of the selected channel is complete:
If a regular channel was converted:
– The converted data is stored in the 16-bit ADC_DR register
– The EOC (End Of Conversion) flag is set
– and an interrupt is generated if the EOCIE is set.
If an injected channel was converted:
– The converted data is stored in the 16-bit ADC_DRJ1 register
– The JEOC (End Of Conversion Injected) flag is set
– and an interrupt is generated if the JEOCIE bit is set.
The ADC is then stopped.
连续转换模式
In continuous conversion mode, ADC starts another conversion as soon as it finishes one. This mode is started either by an external trigger or by setting the ADON bit in the ADC_CR2 register, while the CONT bit is 1. After each conversion:
If a regular channel was converted:
– The converted data is stored in the 16-bit ADC_DR register
– The EOC (End Of Conversion) flag is set
– An interrupt is generated if the EOCIE is set.
If an injected channel was converted:
– The converted data is stored in the 16-bit ADC_DRJ1 register
– The JEOC (End Of Conversion Injected) flag is set
– An interrupt is generated if the JEOCIE bit is set.
扫描模式
This mode is used to scan a group of analog channels. A single conversion is performed for each channel of the group. After each end of conversion, the next channel of the group is converted automatically. If the CONT bit is set, conversion does not stop at the last selected group channel but continues again from the first selected group channel.
When using scan mode, DMA bit must be set and the direct memory access controller is used to transfer the converted data of regular group channels to SRAM after each update of the ADC_DR register. The injected channel converted data is always stored in the ADC_JDRx registers.
非连续模式
This mode is enabled by setting the DISCEN bit in the ADC_CR1 register. It can be used to convert a short sequence of n conversions (n <=8) which is a part of the sequence of conversions selected in the ADC_SQRx registers. The value of n is specified by writing to the DISCNUM[2:0] bits in the ADC_CR1 register.
When an external trigger occurs, it starts the next n conversions selected in the ADC_SQRx registers until all the conversions in the sequence are done. The total sequence length is defined by the L[3:0] bits in the ADC_SQR1 register.
读取ADC结果的几种方式
The Polling Method
It’s the easiest way in code in order to perform an analog to digital conversion using the ADC on an analog input channel. However, it’s not an efficient way in all cases as it’s considered to be a blocking way of using the ADC. As in this way, we start the A/D conversion and wait for the ADC until it completes the conversion so the CPU can resume processing the main code.
中断模式
The interrupt method is an efficient way to do ADC conversion in a non-blocking manner, so the CPU can resume executing the main code routine until the ADC completes the conversion and fires an interrupt signal so the CPU can switch to the ISR context and save the conversion results for further processing.
However, when you’re dealing with multiple channels in a circular mode or so, you’ll have periodic interrupts from the ADC that are too much for the CPU to handle. This will introduce jitter injection and interrupt latency and all sorts of timing issues to the system. This can be avoided by using DMA.
DMA方式
Lastly, the DMA method is the most efficient way of converting multiple ADC channels at very high rates and still transfers the results to the memory without CPU intervention which is so cool and time-saving technique.
STM32F103C8T6的代码实现
管脚与ADC的映射关系
PA0:7 ADC1_IN0:7
PB0 ADC1_IN8
PB1 ADC1_IN9
实现两个通道电压采集到DMA
确定要采集的信号通道数量, 每个信号通道要保留的采样数, 比如下面的例子中是2个通道, 每个通道4个采样
根据上面的数量得到ARRAYSIZE, 声明用于DMA的内存变量 __IO uint16_t ADCConvertedValue[ARRAYSIZE]
初始化时钟: ADC1, GPIOA, DMA1
初始化GPIOA用于采集的两个pin
初始化ADC1
初始化DMA1
代码
#include #include "timer.h" #include "usart.h" #define ARRAYSIZE 2*4 __IO uint16_t ADCConvertedValue[ARRAYSIZE]; void RCC_Configuration(void) { /* ADCCLK = PCLK2/4 */ RCC_ADCCLKConfig(RCC_PCLK2_Div4); /* Enable peripheral clocks ------------------------------------------------*/ /* Enable DMA1 clock */ RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); /* Enable ADC1 and GPIOC clock */ RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_GPIOA, ENABLE); } void GPIO_Configuration(void) { GPIO_InitTypeDef GPIO_InitStructure; /* Configure PA.00 (ADC Channel0) as analog input -------------------------*/ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; GPIO_Init(GPIOA, &GPIO_InitStructure); } int main(void) { SystemInit(); Systick_Init(); USART_Configuration(); /* System clocks configuration ---------------------------------------------*/ RCC_Configuration(); /* GPIO configuration ------------------------------------------------------*/ GPIO_Configuration(); /* ADC1 configuration ------------------------------------------------------*/ ADC_InitTypeDef ADC_InitStructure; ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //We will convert multiple channels ADC_InitStructure.ADC_ScanConvMode = ENABLE; ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //right 12-bit data alignment in ADC data register ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; // Set it to the number of channels ADC_InitStructure.ADC_NbrOfChannel = 2; ADC_Init(ADC1, &ADC_InitStructure); /* ADC1 regular channel0 configuration, rank decides the order in ADCConvertedValue, start from 1 */ ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_41Cycles5); /* ADC1 regular channel1 configuration */ ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_41Cycles5); /* Enable ADC1 DMA */ ADC_DMACmd(ADC1, ENABLE); /* Enable ADC1 */ ADC_Cmd(ADC1, ENABLE); /* Enable ADC1 reset calibration register */ ADC_ResetCalibration(ADC1); /* Check the end of ADC1 reset calibration register */ while(ADC_GetResetCalibrationStatus(ADC1)); /* Start ADC1 calibration */ ADC_StartCalibration(ADC1); /* Check the end of ADC1 calibration */ while(ADC_GetCalibrationStatus(ADC1)); /* Start ADC1 Software Conversion */ ADC_SoftwareStartConvCmd(ADC1, ENABLE); /* DMA1 channel1 configuration ----------------------------------------------*/ DMA_InitTypeDef DMA_InitStructure; DMA_DeInit(DMA1_Channel1); // ADC1_DR_Address ((uint32_t)0x4001244C) DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&(ADC1->DR); DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)ADCConvertedValue; /* Direction: DMA_DIR_PeripheralSRC:from peripheral, DMA_DIR_PeripheralDST:to peripheral */ DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; /* Specifies the buffer size, in data unit, of the specified Stream. The data unit is equal to the configuration set in DMA_PeripheralDataSize or DMA_MemoryDataSize members depending in the transfer direction. Set it to the number of channels */ DMA_InitStructure.DMA_BufferSize = ARRAYSIZE; DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; // Specifies whether the memory address register should be incremented or not DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; // Priority among DMA channels DMA_InitStructure.DMA_Priority = DMA_Priority_High; // From Memory to Memory DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; DMA_Init(DMA1_Channel1, &DMA_InitStructure); /* Enable DMA1 channel1 */ DMA_Cmd(DMA1_Channel1, ENABLE); while(1) { for (u8 i = 0; i < ARRAYSIZE; i++) { printf("%d ", *(ADCConvertedValue + i)); } printf("rn"); Systick_Delay_ms(500); } } void ADC1_IRQHandler(void) { ADC_ClearITPendingBit(ADC1, ADC_IT_EOC); ADC_SoftwareStartConvCmd(ADC1,ENABLE); } STM32F401CCU6的代码实现 只有1个16通道ADC. One 12-bit analog-to-digital converter is embedded and shares up to 16 external channels, performing conversions in the single-shot or scan mode. In scan mode, automatic conversion is performed on a selected group of analog inputs. The ADC can be served by the DMA controller. An analog watchdog feature allows very precise monitoring of the converted voltage of one, some or all selected channels. An interrupt is generated when the converted voltage is outside the programmed thresholds. To synchronize A/D conversion and timers, the ADCs could be triggered by any of TIM1, TIM2, TIM3, TIM4 or TIM5 timer. 管脚与ADC的映射关系 PA0:7 ADC1_IN0:7 PB0 ADC1_IN8 PB1 ADC1_IN9 PC0:5 ADC1_IN10:15 因为F401CCU6的PC口只有PC13,PC14,PC15, 所以可以用的ADC只有ADC1_IN0 - IN9
上一篇:STM32 ADC学习
下一篇:STM32学习笔记-PWM波形输出
推荐阅读最新更新时间:2024-10-30 22:15