片上ADC介绍
STM32F103系列与STM32F407系列ADC的功能类似。它们总共有 3 个 ADC,精度为 12 位,每个ADC 有 16 个外部通道。另外还有两个内部 ADC 源和 VBAT通道挂在 ADC1 上。ADC 具有独立模式、双重模式和三重模式,对于不同 AD 转换要求几乎都有合适的模式可选。
电压输入范围
ADC 输入范围为:VREF- ≤ VIN ≤ VREF+。由 VREF-、VREF+ 、VDDA 、VSSA、这四个外部引脚决定。在设计原理图的时候一般把 VSSA和 VREF-接地,把 VREF+和 VDDA 接 3V3,得到ADC 的输入电压范围为:0~3.3V。
输入通道
STM32 的 ADC 多达 19 个通道,其中外部的 16 个通道就是框图中的 ADCx_IN0、ADCx_IN1…ADCx_IN5。这 16 个通道对应着不同的 IO 口。
对STM32F103系列单片机,ADC1的通道16连接到芯片内部的温度传感器,通道17连接到了内部参考电压 Vrefint。ADC2 的模拟通道 16 和 17 连接到了内部的 VSS。ADC3 的模拟通道 9、14、15、16 和 17 连接到了内部的 VSS。
对STM32F407系列单片机,ADC1的通道16连接到芯片内部的温度传感器,通道17连接到了内部参考电压 Vrefint,通道18连接到了备用电源 Vbat。ADC2 和 ADC3 的通道 16、17、18 全部连接到了内部的 VSS。
扩展库特性
本扩展库针对ADC最常见的操作方式,编写了两种ADC配置模板,其一是不使用DMA传输的单次单通道采样(void ADC_UserConfig1()),其二是使用DMA传输的连续多通道采样(void ADC_UserConfig2())。可根据需要在程序初始化的时候调用对应的配置函数实现ADC的快速配置。同时提供了在两种配置模式下的获取ADC数据与平均值数据的函数,也可在适合的位置直接调用。
使用了本扩展库,ADC的配置与读取数据将会变得十分简洁高效。
扩展库源码
STM32F103与STM32F407的ADC在使用的时候还是有一些区别的:
1.STM32F103的ADC需要校准,而STM32F407的ADC无需校准。
2.STM32F103与F407的ADC时钟配置、外设配置、DMA配置等不同。
基于STM32F103
头文件
- #ifndef __ADC_EXT_H__
- #define __ADC_EXT_H__
- #include "stm32f10x_conf.h"
- #include "stm32f10x.h"
- #define ADC_CHANNEL_NUM 4
- #define ADC_DMA_CHANNEL_DEEPTH 10
- #define ADC_DMA_CHANNEL_NUM ADC_CHANNEL_NUM
- #define ADC_DMA_BUFFER_SIZE (ADC_DMA_CHANNEL_NUM * ADC_DMA_CHANNEL_DEEPTH)
- extern volatile uint16_t ADC_Data[ADC_CHANNEL_NUM];
- extern volatile uint16_t ADC_DMA_Value[ADC_DMA_CHANNEL_DEEPTH][ADC_DMA_CHANNEL_NUM];
- void ADC_UserConfig1();
- void ADC_UserConfig2();
- void ADC_Config_NotUseDMA();
- void ADC_Config_UseDMA();
- void ADC_Config_SetDMA();
- uint16_t ADC_GetData_NotUseDMA(uint8_t channel);
- uint16_t ADC_GetAverageData_NotUseDMA(uint8_t channel, uint16_t count);
- uint16_t ADC_GetData_UseDMA(uint8_t channel);
- uint16_t ADC_GetAverageData_UseDMA(uint8_t channel);
- #endif
复制代码
源文件
- #include "adc_ext.h"
- volatile uint16_t ADC_Data[ADC_CHANNEL_NUM];
- volatile uint16_t ADC_DMA_Value[ADC_DMA_CHANNEL_DEEPTH][ADC_DMA_CHANNEL_NUM];
- /**
- * @brief 预置的ADC配置1,不使用DMA传输
- */
- void ADC_UserConfig1()
- {
- GPIO_ConfigPort('A', 0, GPIO_Mode_AIN, 0);
- ADC_Config_NotUseDMA();
- }
- /**
- * @brief 预置的ADC配置2,使用DMA传输
- */
- void ADC_UserConfig2()
- {
- GPIO_ConfigPort('A', 0, GPIO_Mode_AIN, 0);
- GPIO_ConfigPort('A', 1, GPIO_Mode_AIN, 0);
- GPIO_ConfigPort('A', 2, GPIO_Mode_AIN, 0);
- GPIO_ConfigPort('A', 3, GPIO_Mode_AIN, 0);
-
- ADC_Config_SetDMA();
- ADC_Config_UseDMA();
- }
- /**
- * @brief 不使用DMA进行数据传输时的ADC配置
- * 由于ADC 规则组数据寄存器 ADC_DR 只有一个,是一个 32 位的寄存器,
- * 只有低 16 位有效并且只是用于独立模式存放转换完成数据。
- * 如果使用多通道转换,那转换的数据就全部都挤在了 DR 里面,前一个时间点转换的通道数据,
- * 就会被下一个时间点的另外一个通道转换的数据覆盖掉,所以当通道转换完成后就应该把数据取走。
- *
- * 因此不使用DMA模式的时候,每次只能读取一个通道的数据
- */
- void ADC_Config_NotUseDMA()
- {
- /**
- * Configuration of ADC1
- */
- ADC_InitTypeDef ADC_InitStructure;
- RCC_ADCCLKConfig(RCC_PCLK2_Div6); //设置ADC分频因子6 72M/6=12,ADC最大时间不能超过14M
- ADC_DeInit(ADC1); //复位ADC1
- ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //ADC工作模式:ADC1和ADC2工作在独立模式
- ADC_InitStructure.ADC_ScanConvMode = DISABLE; //模数转换工作在单通道模式
- ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; //模数转换工作在单次转换模式
- ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //转换由软件而不是外部触发启动
- ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //ADC数据右对齐
- ADC_InitStructure.ADC_NbrOfChannel = ADC_CHANNEL_NUM; //顺序进行规则转换的ADC通道的数目
- ADC_Init(ADC1, &ADC_InitStructure); //根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器
- // ADC_RegularChannelConfig(ADC1, channel, 1, ADC_SampleTime_71Cycles5); //ADC1,ADC通道,采样时间为239.5周期
- ADC_Cmd(ADC1, ENABLE); //使能指定的ADC1
- ADC_ResetCalibration(ADC1); //使能复位校准
- while (ADC_GetResetCalibrationStatus(ADC1))
- ; //等待复位校准结束
- ADC_StartCalibration(ADC1); //开启AD校准
- while (ADC_GetCalibrationStatus(ADC1))
- ; //等待校准结束
- // ADC_SoftwareStartConvCmd(ADC1, ENABLE); //使能指定的ADC1的软件转换启动功能
- }
- /**
- * @brief 使用DMA进行数据传输时的ADC配置
- * 注意规则通道的数量需要根据实际项目进行调整
- */
- void ADC_Config_UseDMA()
- {
- /**
- * Configuration of ADC
- */
- ADC_InitTypeDef ADC_InitStructure;
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); //使能ADC1通道时钟
- RCC_ADCCLKConfig(RCC_PCLK2_Div6); //设置ADC分频因子6 72M/6=12,ADC最大时间不能超过14M
- ADC_DeInit(ADC1); //复位ADC1
- ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
- ADC_InitStructure.ADC_ScanConvMode = ENABLE; //通道扫描
- ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; //连续转换
- ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
- ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
- ADC_InitStructure.ADC_NbrOfChannel = ADC_CHANNEL_NUM;
- ADC_Init(ADC1, &ADC_InitStructure);
- ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_239Cycles5); //通道1转换结果保存到ADCConvertedValue[0~10][0]
- ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_239Cycles5); //通道2转换结果保存到ADCConvertedValue[0~10][1]
- ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 3, ADC_SampleTime_239Cycles5); //通道3转换结果保存到ADCConvertedValue[0~10][2]
- ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 4, ADC_SampleTime_239Cycles5); //通道4转换结果保存到ADCConvertedValue[0~10][3]
- // ADC_RegularChannelConfig(ADC1, ADC_Channel_4, 5, ADC_SampleTime_239Cycles5); //通道5转换结果保存到ADCConvertedValue[0~10][4]
- // ADC_RegularChannelConfig(ADC1, ADC_Channel_5, 6, ADC_SampleTime_239Cycles5); //通道6转换结果保存到ADCConvertedValue[0~10][5]
- // ADC_RegularChannelConfig(ADC1, ADC_Channel_6, 7, ADC_SampleTime_239Cycles5); //通道7转换结果保存到ADCConvertedValue[0~10][6]
- // ADC_RegularChannelConfig(ADC1, ADC_Channel_7, 8, ADC_SampleTime_239Cycles5); //通道8转换结果保存到ADCConvertedValue[0~10][7]
- // ADC_RegularChannelConfig(ADC1, ADC_Channel_8, 9, ADC_SampleTime_239Cycles5); //通道9转换结果保存到ADCConvertedValue[0~10][8]
- // ADC_RegularChannelConfig(ADC1, ADC_Channel_9, 10, ADC_SampleTime_239Cycles5); //通道10转换结果保存到ADCConvertedValue[0~10][9]
- // ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 11, ADC_SampleTime_239Cycles5); //通道11转换结果保存到ADCConvertedValue[0~10][10]
- // ADC_RegularChannelConfig(ADC1, ADC_Channel_11, 12, ADC_SampleTime_239Cycles5); //通道12转换结果保存到ADCConvertedValue[0~10][11]
- // ADC_RegularChannelConfig(ADC1, ADC_Channel_12, 13, ADC_SampleTime_239Cycles5); //通道13转换结果保存到ADCConvertedValue[0~10][12]
- // ADC_RegularChannelConfig(ADC1, ADC_Channel_13, 14, ADC_SampleTime_239Cycles5); //通道14转换结果保存到ADCConvertedValue[0~10][13]
- // ADC_RegularChannelConfig(ADC1, ADC_Channel_14, 15, ADC_SampleTime_239Cycles5); //通道15转换结果保存到ADCConvertedValue[0~10][14]
- // ADC_RegularChannelConfig(ADC1, ADC_Channel_15, 16, ADC_SampleTime_239Cycles5); //通道16转换结果保存到ADCConvertedValue[0~10][15]
- ADC_DMACmd(ADC1, ENABLE); //开启ADC的DMA支持
- ADC_Cmd(ADC1, ENABLE);
- ADC_ResetCalibration(ADC1);
- while (ADC_GetResetCalibrationStatus(ADC1))
- ;
- ADC_StartCalibration(ADC1);
- while (ADC_GetCalibrationStatus(ADC1))
- ;
- ADC_SoftwareStartConvCmd(ADC1, ENABLE); //使能指定的ADC1的软件转换启动功能
- }
- /**
- * @brief 使用DMA进行数据传输时的DMA配置
- */
- void ADC_Config_SetDMA()
- {
- /**
- * Configuration of DMA
- */
- DMA_InitTypeDef DMA_InitStructure;
- RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //使能时钟
- DMA_DeInit(DMA1_Channel1); //将通道一寄存器设为默认值
- DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t) &(ADC1->DR); //该参数用以定义DMA外设基地址
- DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t) &ADC_DMA_Value; //该参数用以定义DMA内存基地址(转换结果保存的地址)
- DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //该参数规定了外设是作为数据传输的目的地还是来源,此处是作为来源
- DMA_InitStructure.DMA_BufferSize = ADC_DMA_BUFFER_SIZE; //定义指定DMA通道的DMA缓存的大小,单位为数据单位。这里也就是ADC_DMA_Value的大小
- DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //设定外设地址寄存器递增与否,此处设为不变 Disable
- DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //用来设定内存地址寄存器递增与否,此处设为递增,Enable
- DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; //数据宽度为16位
- DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; //数据宽度为16位
- DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; //工作在循环缓存模式
- DMA_InitStructure.DMA_Priority = DMA_Priority_High; //DMA通道拥有高优先级 分别4个等级 低、中、高、非常高
- DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //使能DMA通道的内存到内存传输
- DMA_Init(DMA1_Channel1, &DMA_InitStructure); //根据DMA_InitStruct中指定的参数初始化DMA的通道
- // DMA_ITConfig(DMA1_Channel1,DMA_IT_TC, ENABLE);
- DMA_Cmd(DMA1_Channel1, ENABLE); //启动DMA通道一
- }
- /**
- * @brief 不使用DMA传输时,获取ADC的采样数据
- * @param channel: ADC的采样通道
- * @return ADC的单次采样结果
- */
- uint16_t ADC_GetData_NotUseDMA(uint8_t channel)
- {
- //设置指定ADC的规则组通道,一个序列,采样时间
- ADC_RegularChannelConfig(ADC1, channel, 1, ADC_SampleTime_71Cycles5); //ADC1,ADC通道,采样时间为239.5周期
- ADC_SoftwareStartConvCmd(ADC1, ENABLE); //使能指定的ADC1的软件转换启动功能
- /**
- * 通过查询的方式获取了ADC的转化结果,
- * 也可以通过中断的方式获取ADC的转换结果
- */
- while (!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC))
- ; //等待转换结束
- return ADC_GetConversionValue(ADC1); //返回最近一次ADC1规则组的转换结果
- }
- /**
- * @brief 不使用DMA传输时,获取ADC采样数据的平均值
- * @param channel: ADC的采样通道
- * @param count: 计算平均值的采样数量
- * @return ADC的单次采样结果的平均值
- */
- uint16_t ADC_GetAverageData_NotUseDMA(uint8_t channel, uint16_t count)
- {
- uint32_t temp_val = 0;
- for (uint8_t t = 0; t < count; t++)
- {
- temp_val += ADC_GetData_NotUseDMA(channel);
- }
- ADC_Data[channel] = temp_val / count;
- return ADC_Data[channel];
- }
- /**
- * @brief 获取通过DMA传输的采样数据
- * @param channel: ADC的采样通道
- * @return ADC的采样数据
- */
- uint16_t ADC_GetData_UseDMA(uint8_t channel)
- {
- return ADC_DMA_Value[0][channel];
- }
- /**
- * @brief 获取通过DMA传输的采样数据(求平均值,过采样)
- * @param channel: ADC的采样通道
- * @return ADC的采样数据
- */
- uint16_t ADC_GetAverageData_UseDMA(uint8_t channel)
- {
- uint32_t sum = 0;
- for (uint8_t j = 0; j < ADC_DMA_CHANNEL_DEEPTH; j++)
- {
- sum += ADC_DMA_Value[j][channel];
- }
- return sum / ADC_DMA_CHANNEL_DEEPTH; //求平均值并转换成电压值
- }
复制代码
基于STM32F407
头文件
- #ifndef __ADC_EXT_H__
- #define __ADC_EXT_H__
- #include "stm32f4xx_conf.h"
- #include "stm32f4xx.h"
- #define ADC_CHANNEL_NUM 4
- #define ADC_DMA_CHANNEL_DEEPTH 10
- #define ADC_DMA_CHANNEL_NUM ADC_CHANNEL_NUM
- #define ADC_DMA_BUFFER_SIZE (ADC_DMA_CHANNEL_NUM * ADC_DMA_CHANNEL_DEEPTH)
- extern volatile uint16_t ADC_Data[ADC_CHANNEL_NUM];
- extern volatile uint16_t ADC_DMA_Value[ADC_DMA_CHANNEL_DEEPTH][ADC_DMA_CHANNEL_NUM];
- void ADC_UserConfig1();
- void ADC_UserConfig2();
- void ADC_Config_NotUseDMA();
- void ADC_Config_UseDMA();
- void ADC_Config_SetDMA();
- uint16_t ADC_GetData_NotUseDMA(uint8_t channel);
- uint16_t ADC_GetAverageData_NotUseDMA(uint8_t channel, uint16_t count);
- uint16_t ADC_GetData_UseDMA(uint8_t channel);
- uint16_t ADC_GetAverageData_UseDMA(uint8_t channel);
- #endif
复制代码
源文件
- #include "adc_ext.h"
- /**
- * ADC时钟
- *
- * ADC输入时钟 ADC_CLK 由 PCLK2 经过分频产生,最大值是 36MHz,典型值为30MHz
- * 分频因子由 ADC 通用控制寄存器 ADC_CCR 的 ADCPRE[1:0]设置,可设置的分频系数有 2、4、6 和 8
- * 对于STM32F407的PCLK2=HCLK/2=84MHz,所以程序一般使用 4 分频或者 6 分频
- */
- /**
- * 采样时间
- *
- * ADC 需要若干个 ADC_CLK 周期完成对输入的电压进行采样,
- * 采样的周期数可通过ADC 采样时间寄存器 ADC_SMPR1 和 ADC_SMPR2 中的 SMP[2:0]位设置。
- * 每个通道可以分别用不同的时间采样。
- * 其中采样周期最小是 3 个,即如果我们要达到最快的采样,那么应该设置采样周期为 3 个周期。
- * 这里说的周期就是 1/ADC_CLK。
- */
- /**
- * 注意本库函数只实现了ADC1的配置与采样,ADC2与ADC3的配置与使用方法与此类似
- */
- volatile uint16_t ADC_Data[ADC_CHANNEL_NUM];
- volatile uint16_t ADC_DMA_Value[ADC_DMA_CHANNEL_DEEPTH][ADC_DMA_CHANNEL_NUM];
- /**
- * @brief 预置的ADC配置1,不使用DMA传输
- */
- void ADC_UserConfig1()
- {
- GPIO_ConfigPort('A', 0, GPIO_Mode_AN, GPIO_OType_OD, GPIO_PuPd_NOPULL, 0);
-
- ADC_Config_NotUseDMA();
- }
- /**
- * @brief 预置的ADC配置2,使用DMA传输
- */
- void ADC_UserConfig2()
- {
- GPIO_ConfigPort('A', 0, GPIO_Mode_AN, GPIO_OType_OD, GPIO_PuPd_NOPULL, 0);
- GPIO_ConfigPort('A', 1, GPIO_Mode_AN, GPIO_OType_OD, GPIO_PuPd_NOPULL, 0);
- GPIO_ConfigPort('A', 2, GPIO_Mode_AN, GPIO_OType_OD, GPIO_PuPd_NOPULL, 0);
- GPIO_ConfigPort('A', 3, GPIO_Mode_AN, GPIO_OType_OD, GPIO_PuPd_NOPULL, 0);
-
- ADC_Config_SetDMA();
- ADC_Config_UseDMA();
- }
- /**
- * @brief 不使用DMA进行数据传输时的ADC配置
- * 由于ADC 规则组数据寄存器 ADC_DR 只有一个,是一个 32 位的寄存器,
- * 只有低 16 位有效并且只是用于独立模式存放转换完成数据。
- * 如果使用多通道转换,那转换的数据就全部都挤在了 DR 里面,前一个时间点转换的通道数据,
- * 就会被下一个时间点的另外一个通道转换的数据覆盖掉,所以当通道转换完成后就应该把数据取走。
- *
- * 因此不使用DMA模式的时候,每次只能读取一个通道的数据
- */
- void ADC_Config_NotUseDMA()
- {
- /**
- * Configuration of ADC
- */
- ADC_CommonInitTypeDef ADC_CommonInitStructure;
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); //使能ADC1时钟
- ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent; //独立ADC模式
- ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div4; //ADC时钟为fpclock/分频
- ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled; //禁止DMA直接访问模式
- ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_20Cycles; //采样时间间隔
- ADC_CommonInit(&ADC_CommonInitStructure);
- ADC_InitTypeDef ADC_InitStructure;
- ADC_StructInit(&ADC_InitStructure);
- ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b; //设置ADC的分辨率12位
- ADC_InitStructure.ADC_ScanConvMode = ENABLE; //通道扫描模式
- ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; //连续转换模式
- ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConvEdge_None; //禁止外部边沿触发
- ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC1; //外部触发通道
- ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //设置数据右对齐
- ADC_InitStructure.ADC_NbrOfConversion = 1; //设置转换通道,固定为1
- ADC_Init(ADC1, &ADC_InitStructure);
- ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_56Cycles); //配置 ADC 通道转换顺序和采样周期
- // ADC_ITConfig(RHEOSTAT_ADC, ADC_IT_EOC, ENABLE); //ADC 转换结束产生中断,可以在中断服务程序中读取转换值
- ADC_Cmd(ADC1, ENABLE); //使能ADC
- }
- /**
- * @brief 使用DMA进行数据传输时的ADC配置
- * 注意规则通道的数量需要根据实际项目进行调整
- */
- void ADC_Config_UseDMA()
- {
- /**
- * Configuration of ADC
- */
- ADC_CommonInitTypeDef ADC_CommonInitStructure;
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); //使能ADC1时钟
- ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent; //独立ADC模式
- ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div4; //ADC时钟为Fpclock/分频
- ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled; //禁止DMA直接访问模式
- ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_20Cycles; //采样时间间隔
- ADC_CommonInit(&ADC_CommonInitStructure);
- ADC_InitTypeDef ADC_InitStructure;
- ADC_StructInit(&ADC_InitStructure);
- ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b; //设置ADC的分辨率12位
- ADC_InitStructure.ADC_ScanConvMode = ENABLE; //通道扫描模式
- ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; //连续转换模式
- ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConvEdge_None; //禁止外部边沿触发
- ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC1; //外部触发通道
- ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //设置数据右对齐
- ADC_InitStructure.ADC_NbrOfConversion = ADC_CHANNEL_NUM; //设置转换通道
- ADC_Init(ADC1, &ADC_InitStructure);
- ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_3Cycles); //通道1转换结果保存到ADCConvertedValue[0~10][0]
- ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_3Cycles); //通道2转换结果保存到ADCConvertedValue[0~10][1]
- ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 3, ADC_SampleTime_3Cycles); //通道3转换结果保存到ADCConvertedValue[0~10][2]
- ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 4, ADC_SampleTime_3Cycles); //通道4转换结果保存到ADCConvertedValue[0~10][3]
- // ADC_RegularChannelConfig(ADC1, ADC_Channel_4, 5, ADC_SampleTime_3Cycles); //通道5转换结果保存到ADCConvertedValue[0~10][4]
- // ADC_RegularChannelConfig(ADC1, ADC_Channel_5, 6, ADC_SampleTime_3Cycles); //通道6转换结果保存到ADCConvertedValue[0~10][5]
- // ADC_RegularChannelConfig(ADC1, ADC_Channel_6, 7, ADC_SampleTime_3Cycles); //通道7转换结果保存到ADCConvertedValue[0~10][6]
- // ADC_RegularChannelConfig(ADC1, ADC_Channel_7, 8, ADC_SampleTime_3Cycles); //通道8转换结果保存到ADCConvertedValue[0~10][7]
- // ADC_RegularChannelConfig(ADC1, ADC_Channel_8, 9, ADC_SampleTime_3Cycles); //通道9转换结果保存到ADCConvertedValue[0~10][8]
- // ADC_RegularChannelConfig(ADC1, ADC_Channel_9, 10, ADC_SampleTime_3Cycles); //通道10转换结果保存到ADCConvertedValue[0~10][9]
- // ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 11, ADC_SampleTime_3Cycles); //通道11转换结果保存到ADCConvertedValue[0~10][10]
- // ADC_RegularChannelConfig(ADC1, ADC_Channel_11, 12, ADC_SampleTime_3Cycles); //通道12转换结果保存到ADCConvertedValue[0~10][11]
- // ADC_RegularChannelConfig(ADC1, ADC_Channel_12, 13, ADC_SampleTime_3Cycles); //通道13转换结果保存到ADCConvertedValue[0~10][12]
- // ADC_RegularChannelConfig(ADC1, ADC_Channel_13, 14, ADC_SampleTime_3Cycles); //通道14转换结果保存到ADCConvertedValue[0~10][13]
- // ADC_RegularChannelConfig(ADC1, ADC_Channel_14, 15, ADC_SampleTime_3Cycles); //通道15转换结果保存到ADCConvertedValue[0~10][14]
- // ADC_RegularChannelConfig(ADC1, ADC_Channel_15, 16, ADC_SampleTime_3Cycles); //通道16转换结果保存到ADCConvertedValue[0~10][15]
- ADC_DMARequestAfterLastTransferCmd(ADC1, ENABLE); // 使能 DMA 请求 after last transfer (Single-ADC mode)
- ADC_DMACmd(ADC1, ENABLE); //开启ADC的DMA支持
- ADC_Cmd(ADC1, ENABLE); //使能ADC
- ADC_SoftwareStartConv(ADC1); //开始ADC转换,软件触发
- }
- /**
- * @brief 使用DMA进行数据传输时的DMA配置
- */
- void ADC_Config_SetDMA()
- {
- /**
- * Configuration of DMA
- */
- DMA_InitTypeDef DMA_InitStructure;
- RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE); //使能时钟
- DMA_DeInit(DMA2_Stream0); //将通道一寄存器设为默认值
- DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t) &(ADC1->DR); //该参数用以定义DMA外设基地址
- DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t) &ADC_DMA_Value; //该参数用以定义DMA内存基地址(转换结果保存的地址)
- DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory; //该参数规定了外设是作为数据传输的目的地还是来源,此处是作为来源
- DMA_InitStructure.DMA_BufferSize = ADC_DMA_BUFFER_SIZE; //定义指定DMA通道的DMA缓存的大小,单位为数据单位。这里也就是ADC_DMA_Value的大小
- DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //设定外设地址寄存器递增与否,此处设为不变 Disable
- DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //用来设定内存地址寄存器递增与否,此处设为递增,Enable
- DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; //数据宽度为16位
- DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; //数据宽度为16位
- DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; //工作在循环缓存模式
- DMA_InitStructure.DMA_Priority = DMA_Priority_High; //DMA通道拥有高优先级 分别4个等级 低、中、高、非常高
- DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable; //禁用DMA的FIFO模式,使用直连模式
- DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;
- DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
- DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
- DMA_InitStructure.DMA_Channel = DMA_Channel_0; //选择DMA通道
- DMA_Init(DMA2_Stream0, &DMA_InitStructure); //初始化DMA流
- DMA_Cmd(DMA2_Stream0, ENABLE); //使能DMA流
- }
- /**
- * @brief 不使用DMA传输时,获取ADC的采样数据
- * @param channel: ADC的采样通道
- * @return ADC的单次采样结果
- */
- uint16_t ADC_GetData_NotUseDMA(uint8_t channel)
- {
- ADC_RegularChannelConfig(ADC1, channel, 1, ADC_SampleTime_56Cycles); //配置 ADC 通道转换顺序和采样周期
- ADC_SoftwareStartConv(ADC1); //使能指定的ADC1的软件转换启动功能
- /**
- * 通过查询的方式获取了ADC的转化结果,
- * 也可以通过中断的方式获取ADC的转换结果
- */
- while (!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC)); //等待转换结束
- return ADC_GetConversionValue(ADC1); //返回最近一次ADC1规则组的转换结果
- }
- /**
- * @brief 不使用DMA传输时,获取ADC采样数据的平均值
- * @param channel: ADC的采样通道
- * @param count: 计算平均值的采样数量
- * @return ADC的单次采样结果的平均值
- */
- uint16_t ADC_GetAverageData_NotUseDMA(uint8_t channel, uint16_t count)
- {
- uint32_t temp_val = 0;
- for (uint16_t t = 0; t < count; t++)
- {
- temp_val += ADC_GetData_NotUseDMA(channel);
- }
- ADC_Data[channel] = temp_val / count;
- return ADC_Data[channel];
- }
- /**
- * @brief 获取通过DMA传输的采样数据
- * @param channel: ADC的采样通道
- * @return ADC的采样数据
- */
- uint16_t ADC_GetData_UseDMA(uint8_t channel)
- {
- return ADC_DMA_Value[0][channel];
- }
- /**
- * @brief 获取通过DMA传输的采样数据(求平均值,过采样)
- * @param channel: ADC的采样通道
- * @return ADC的采样数据
- */
- uint16_t ADC_GetAverageData_UseDMA(uint8_t channel)
- {
- uint32_t sum = 0;
- for (uint16_t j = 0; j < ADC_DMA_CHANNEL_DEEPTH; j++)
- {
- sum += ADC_DMA_Value[j][channel];
- }
- return sum / ADC_DMA_CHANNEL_DEEPTH; //求平均值并转换成电压值
- }
复制代码
使用指南
由于使用了相同的功能逻辑与函数名(即头文件相同),STM32F103与STM32F407的ADC扩展函数库在使用方法上是相同的。由于在ADC扩展库中提供的模板配置函数中已经完成了GPIO外设与ADC外设的配置,因此可以直接调用一步完成ADC使用前的初始化工作。
单次单通道采样(不使用DMA)
配置ADC
- void Periphl_ConfigAll()
- {
- /*省略无关代码*/
- ADC_UserConfig1();
- /*省略无关代码*/
- }
复制代码
启动采样并获取数据
采集的数据保存在全局数组ADC_Data[ADC_CHANNEL_NUM]中,同时也可以调用相应的函数返回对应的结果。
调用uint16_t ADC_GetData_NotUseDMA(uint8_t channel)完成一次数据采集。
调用uint16_t ADC_GetAverageData_NotUseDMA(uint8_t channel, uint16_t count)连续采集count个数据后,返回数据采集的平均值。
连续多通道采样(使用DMA)
配置ADC
- void Periphl_ConfigAll()
- {
- /*省略无关代码*/
- ADC_UserConfig2();
- /*省略无关代码*/
- }
复制代码
获取数据
在配置完ADC的DMA传输后就已经自动打开了ADC采样,采样的数据保存在全局二维数组ADC_DMA_Value[ADC_DMA_CHANNEL_DEEPTH][ADC_DMA_CHANNEL_NUM]中,同时也可以调用相应的函数返回对应的结果。
调用uint16_t ADC_GetData_UseDMA(uint8_t channel)返回一次数据采集结果。
调用uint16_t ADC_GetAverageData_UseDMA(uint8_t channel)返回数组中保存的最近的ADC_DMA_CHANNEL_DEEPTH个数据的平均值。
一般情况下,由于DMA的采样速度很快,调用uint16_t ADC_GetAverageData_UseDMA(uint8_t channel)返回一个平均值作为采样的结果是比较推荐的。这也是利用了过采样技术提高了数据的稳定性与分辨率。
在FreeRTOS任务中
可以创建一个任务完成ADC初始化与数据轮询的工作,但需要注意的是片上ADC的快速配置函数(ADC_UserConfig2())只能调用一次,否则可能会出错。也就是说要么在程序启动的地方配置ADC,要么在任务开始的地方配置ADC,但不能两处同时配置ADC。
- void McuAdcDaq_task(void *pvParameters)
- {
- ADC_UserConfig2();
- TickType_t ticks = xTaskGetTickCount();
- while (1)
- {
- User_RefreshSensor();
- User_MB_RefreshInputRegister();
- //100ms 一个处理周期
- vTaskDelayUntil( &ticks, 100);
- }
- }
复制代码
|