STM32开发项目:片上ADC的使用介绍与扩展库

it2025-05-01  9

日期作者版本说明2020.10.22TaoV0.0完成了基于STM32F103与F407的片上ADC扩展函数库源代码的撰写;2020.10.23TaoV0.1完成了基于STM32F103与F407的片上ADC扩展函数库主体内容的撰写,并修复了源代码中的一处bug:在获取ADC采样数据平均值的函数中,for循环的计数控制变量应为uint16_t类型;2020.11.06TaoV0.21.增加了GPIO端口配置函数;2. 部分细节的优化调整;2020.11.21TaoV0.31.使用了GPIO扩展库实现ADC端口的配置;2.增加了在FreeRTOS中使用ADC采集数据的操作说明;

目录

片上ADC介绍扩展库特性扩展库源码基于STM32F103头文件源文件 基于STM32F407头文件源文件 使用指南单次单通道采样(不使用DMA)连续多通道采样(使用DMA)在FreeRTOS任务中

片上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在使用的时候还是有一些区别的:

STM32F103的ADC需要校准,而STM32F407的ADC无需校准。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); } }
最新回复(0)