你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【经验分享】STM32开发项目:片上ADC的使用介绍与扩展库

[复制链接]
STMCU小助手 发布时间:2022-4-13 17:00
片上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。

202010231842394.png

20201023184248351.png


扩展库特性
本扩展库针对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
头文件

  1. #ifndef __ADC_EXT_H__
  2. #define __ADC_EXT_H__        

  3. #include "stm32f10x_conf.h"
  4. #include "stm32f10x.h"

  5. #define ADC_CHANNEL_NUM                                4

  6. #define ADC_DMA_CHANNEL_DEEPTH                10
  7. #define ADC_DMA_CHANNEL_NUM                        ADC_CHANNEL_NUM
  8. #define ADC_DMA_BUFFER_SIZE                        (ADC_DMA_CHANNEL_NUM * ADC_DMA_CHANNEL_DEEPTH)

  9. extern volatile uint16_t ADC_Data[ADC_CHANNEL_NUM];
  10. extern volatile uint16_t ADC_DMA_Value[ADC_DMA_CHANNEL_DEEPTH][ADC_DMA_CHANNEL_NUM];

  11. void ADC_UserConfig1();
  12. void ADC_UserConfig2();

  13. void ADC_Config_NotUseDMA();
  14. void ADC_Config_UseDMA();
  15. void ADC_Config_SetDMA();

  16. uint16_t ADC_GetData_NotUseDMA(uint8_t channel);
  17. uint16_t ADC_GetAverageData_NotUseDMA(uint8_t channel, uint16_t count);
  18. uint16_t ADC_GetData_UseDMA(uint8_t channel);
  19. uint16_t ADC_GetAverageData_UseDMA(uint8_t channel);

  20. #endif
复制代码

源文件
  1. #include "adc_ext.h"

  2. volatile uint16_t ADC_Data[ADC_CHANNEL_NUM];
  3. volatile uint16_t ADC_DMA_Value[ADC_DMA_CHANNEL_DEEPTH][ADC_DMA_CHANNEL_NUM];

  4. /**
  5. * @brief 预置的ADC配置1,不使用DMA传输
  6. */
  7. void ADC_UserConfig1()
  8. {
  9.         GPIO_ConfigPort('A', 0, GPIO_Mode_AIN, 0);

  10.         ADC_Config_NotUseDMA();
  11. }


  12. /**
  13. * @brief 预置的ADC配置2,使用DMA传输
  14. */
  15. void ADC_UserConfig2()
  16. {
  17.         GPIO_ConfigPort('A', 0, GPIO_Mode_AIN, 0);
  18.         GPIO_ConfigPort('A', 1, GPIO_Mode_AIN, 0);
  19.         GPIO_ConfigPort('A', 2, GPIO_Mode_AIN, 0);
  20.         GPIO_ConfigPort('A', 3, GPIO_Mode_AIN, 0);
  21.         
  22.         ADC_Config_SetDMA();
  23.         ADC_Config_UseDMA();
  24. }


  25. /**
  26. * @brief 不使用DMA进行数据传输时的ADC配置
  27. *                 由于ADC 规则组数据寄存器 ADC_DR 只有一个,是一个 32 位的寄存器,
  28. *                 只有低 16 位有效并且只是用于独立模式存放转换完成数据。
  29. *                 如果使用多通道转换,那转换的数据就全部都挤在了 DR 里面,前一个时间点转换的通道数据,
  30. *                 就会被下一个时间点的另外一个通道转换的数据覆盖掉,所以当通道转换完成后就应该把数据取走。
  31. *
  32. *                 因此不使用DMA模式的时候,每次只能读取一个通道的数据
  33. */
  34. void ADC_Config_NotUseDMA()
  35. {
  36.         /**
  37.          * Configuration of ADC1
  38.          */
  39.         ADC_InitTypeDef ADC_InitStructure;

  40.         RCC_ADCCLKConfig(RCC_PCLK2_Div6);                                                                        //设置ADC分频因子6 72M/6=12,ADC最大时间不能超过14M
  41.         ADC_DeInit(ADC1);                                                                                                        //复位ADC1

  42.         ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;                                        //ADC工作模式:ADC1和ADC2工作在独立模式
  43.         ADC_InitStructure.ADC_ScanConvMode = DISABLE;                                                //模数转换工作在单通道模式
  44.         ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;                                        //模数转换工作在单次转换模式
  45.         ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;        //转换由软件而不是外部触发启动
  46.         ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;                                //ADC数据右对齐
  47.         ADC_InitStructure.ADC_NbrOfChannel = ADC_CHANNEL_NUM;                                //顺序进行规则转换的ADC通道的数目
  48.         ADC_Init(ADC1, &ADC_InitStructure);                                                                        //根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器

  49.         //        ADC_RegularChannelConfig(ADC1, channel, 1, ADC_SampleTime_71Cycles5);        //ADC1,ADC通道,采样时间为239.5周期

  50.         ADC_Cmd(ADC1, ENABLE);                                                                                                //使能指定的ADC1

  51.         ADC_ResetCalibration(ADC1);        //使能复位校准

  52.         while (ADC_GetResetCalibrationStatus(ADC1))
  53.                 ;        //等待复位校准结束

  54.         ADC_StartCalibration(ADC1);         //开启AD校准

  55.         while (ADC_GetCalibrationStatus(ADC1))
  56.                 ;         //等待校准结束

  57.         //        ADC_SoftwareStartConvCmd(ADC1, ENABLE);                //使能指定的ADC1的软件转换启动功能
  58. }


  59. /**
  60. * @brief 使用DMA进行数据传输时的ADC配置
  61. *                 注意规则通道的数量需要根据实际项目进行调整
  62. */
  63. void ADC_Config_UseDMA()
  64. {
  65.         /**
  66.          * Configuration of ADC
  67.          */
  68.         ADC_InitTypeDef ADC_InitStructure;

  69.         RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);                                                        //使能ADC1通道时钟
  70.         RCC_ADCCLKConfig(RCC_PCLK2_Div6);                                                                                                //设置ADC分频因子6 72M/6=12,ADC最大时间不能超过14M
  71.         ADC_DeInit(ADC1);                                                                                                                                //复位ADC1

  72.         ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
  73.         ADC_InitStructure.ADC_ScanConvMode = ENABLE;                                                                        //通道扫描
  74.         ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;                                                                //连续转换
  75.         ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
  76.         ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
  77.         ADC_InitStructure.ADC_NbrOfChannel = ADC_CHANNEL_NUM;
  78.         ADC_Init(ADC1, &ADC_InitStructure);

  79.         ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_239Cycles5);        //通道1转换结果保存到ADCConvertedValue[0~10][0]
  80.         ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_239Cycles5);         //通道2转换结果保存到ADCConvertedValue[0~10][1]
  81.         ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 3, ADC_SampleTime_239Cycles5);         //通道3转换结果保存到ADCConvertedValue[0~10][2]
  82.         ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 4, ADC_SampleTime_239Cycles5);         //通道4转换结果保存到ADCConvertedValue[0~10][3]
  83.         //        ADC_RegularChannelConfig(ADC1, ADC_Channel_4, 5, ADC_SampleTime_239Cycles5);         //通道5转换结果保存到ADCConvertedValue[0~10][4]
  84.         //        ADC_RegularChannelConfig(ADC1, ADC_Channel_5, 6, ADC_SampleTime_239Cycles5);         //通道6转换结果保存到ADCConvertedValue[0~10][5]
  85.         //        ADC_RegularChannelConfig(ADC1, ADC_Channel_6, 7, ADC_SampleTime_239Cycles5);         //通道7转换结果保存到ADCConvertedValue[0~10][6]
  86.         //        ADC_RegularChannelConfig(ADC1, ADC_Channel_7, 8, ADC_SampleTime_239Cycles5);         //通道8转换结果保存到ADCConvertedValue[0~10][7]
  87.         //        ADC_RegularChannelConfig(ADC1, ADC_Channel_8, 9, ADC_SampleTime_239Cycles5);         //通道9转换结果保存到ADCConvertedValue[0~10][8]
  88.         //        ADC_RegularChannelConfig(ADC1, ADC_Channel_9, 10, ADC_SampleTime_239Cycles5);         //通道10转换结果保存到ADCConvertedValue[0~10][9]
  89.         //        ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 11, ADC_SampleTime_239Cycles5);         //通道11转换结果保存到ADCConvertedValue[0~10][10]
  90.         //        ADC_RegularChannelConfig(ADC1, ADC_Channel_11, 12, ADC_SampleTime_239Cycles5);         //通道12转换结果保存到ADCConvertedValue[0~10][11]
  91.         //        ADC_RegularChannelConfig(ADC1, ADC_Channel_12, 13, ADC_SampleTime_239Cycles5);         //通道13转换结果保存到ADCConvertedValue[0~10][12]
  92.         //        ADC_RegularChannelConfig(ADC1, ADC_Channel_13, 14, ADC_SampleTime_239Cycles5);         //通道14转换结果保存到ADCConvertedValue[0~10][13]
  93.         //        ADC_RegularChannelConfig(ADC1, ADC_Channel_14, 15, ADC_SampleTime_239Cycles5);         //通道15转换结果保存到ADCConvertedValue[0~10][14]
  94.         //        ADC_RegularChannelConfig(ADC1, ADC_Channel_15, 16, ADC_SampleTime_239Cycles5);         //通道16转换结果保存到ADCConvertedValue[0~10][15]

  95.         ADC_DMACmd(ADC1, ENABLE); //开启ADC的DMA支持
  96.         ADC_Cmd(ADC1, ENABLE);
  97.         ADC_ResetCalibration(ADC1);
  98.         while (ADC_GetResetCalibrationStatus(ADC1))
  99.                 ;
  100.         ADC_StartCalibration(ADC1);
  101.         while (ADC_GetCalibrationStatus(ADC1))
  102.                 ;
  103.         ADC_SoftwareStartConvCmd(ADC1, ENABLE);                //使能指定的ADC1的软件转换启动功能
  104. }


  105. /**
  106. * @brief 使用DMA进行数据传输时的DMA配置
  107. */
  108. void ADC_Config_SetDMA()
  109. {
  110.         /**
  111.          * Configuration of DMA
  112.          */
  113.         DMA_InitTypeDef DMA_InitStructure;
  114.         RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);                                                                //使能时钟
  115.         DMA_DeInit(DMA1_Channel1);                                                                                                                //将通道一寄存器设为默认值

  116.         DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t) &(ADC1->DR);                                //该参数用以定义DMA外设基地址
  117.         DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t) &ADC_DMA_Value;                                //该参数用以定义DMA内存基地址(转换结果保存的地址)
  118.         DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;                                                                //该参数规定了外设是作为数据传输的目的地还是来源,此处是作为来源
  119.         DMA_InitStructure.DMA_BufferSize = ADC_DMA_BUFFER_SIZE;                                                        //定义指定DMA通道的DMA缓存的大小,单位为数据单位。这里也就是ADC_DMA_Value的大小
  120.         DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;                                //设定外设地址寄存器递增与否,此处设为不变 Disable
  121.         DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;                                                        //用来设定内存地址寄存器递增与否,此处设为递增,Enable
  122.         DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;                //数据宽度为16位
  123.         DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;                                //数据宽度为16位
  124.         DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;                                                                 //工作在循环缓存模式
  125.         DMA_InitStructure.DMA_Priority = DMA_Priority_High;                                                         //DMA通道拥有高优先级 分别4个等级 低、中、高、非常高
  126.         DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;                                                                         //使能DMA通道的内存到内存传输

  127.         DMA_Init(DMA1_Channel1, &DMA_InitStructure);                                                                         //根据DMA_InitStruct中指定的参数初始化DMA的通道
  128.         //        DMA_ITConfig(DMA1_Channel1,DMA_IT_TC, ENABLE);
  129.         DMA_Cmd(DMA1_Channel1, ENABLE);                                                                                                        //启动DMA通道一
  130. }


  131. /**
  132. * @brief 不使用DMA传输时,获取ADC的采样数据
  133. * @param channel: ADC的采样通道
  134. * @return ADC的单次采样结果
  135. */
  136. uint16_t ADC_GetData_NotUseDMA(uint8_t channel)
  137. {
  138.         //设置指定ADC的规则组通道,一个序列,采样时间
  139.         ADC_RegularChannelConfig(ADC1, channel, 1, ADC_SampleTime_71Cycles5);        //ADC1,ADC通道,采样时间为239.5周期

  140.         ADC_SoftwareStartConvCmd(ADC1, ENABLE);                //使能指定的ADC1的软件转换启动功能

  141.         /**
  142.          * 通过查询的方式获取了ADC的转化结果,
  143.          * 也可以通过中断的方式获取ADC的转换结果
  144.          */
  145.         while (!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC))
  146.                 ;                //等待转换结束
  147.         return ADC_GetConversionValue(ADC1);        //返回最近一次ADC1规则组的转换结果
  148. }


  149. /**
  150. * @brief 不使用DMA传输时,获取ADC采样数据的平均值
  151. * @param channel: ADC的采样通道
  152. * @param count: 计算平均值的采样数量
  153. * @return ADC的单次采样结果的平均值
  154. */
  155. uint16_t ADC_GetAverageData_NotUseDMA(uint8_t channel, uint16_t count)
  156. {
  157.         uint32_t temp_val = 0;

  158.         for (uint8_t t = 0; t < count; t++)
  159.         {
  160.                 temp_val += ADC_GetData_NotUseDMA(channel);
  161.         }

  162.         ADC_Data[channel] = temp_val / count;

  163.         return ADC_Data[channel];
  164. }


  165. /**
  166. * @brief 获取通过DMA传输的采样数据
  167. * @param channel: ADC的采样通道
  168. * @return ADC的采样数据
  169. */
  170. uint16_t ADC_GetData_UseDMA(uint8_t channel)
  171. {
  172.         return ADC_DMA_Value[0][channel];
  173. }


  174. /**
  175. * @brief 获取通过DMA传输的采样数据(求平均值,过采样)
  176. * @param channel: ADC的采样通道
  177. * @return ADC的采样数据
  178. */
  179. uint16_t ADC_GetAverageData_UseDMA(uint8_t channel)
  180. {
  181.         uint32_t sum = 0;

  182.         for (uint8_t j = 0; j < ADC_DMA_CHANNEL_DEEPTH; j++)
  183.         {
  184.                 sum += ADC_DMA_Value[j][channel];
  185.         }

  186.         return sum / ADC_DMA_CHANNEL_DEEPTH;                                                //求平均值并转换成电压值
  187. }
复制代码

基于STM32F407
头文件

  1. #ifndef __ADC_EXT_H__
  2. #define __ADC_EXT_H__        

  3. #include "stm32f4xx_conf.h"
  4. #include "stm32f4xx.h"

  5. #define ADC_CHANNEL_NUM                                        4

  6. #define ADC_DMA_CHANNEL_DEEPTH                        10
  7. #define ADC_DMA_CHANNEL_NUM                                ADC_CHANNEL_NUM
  8. #define ADC_DMA_BUFFER_SIZE                                (ADC_DMA_CHANNEL_NUM * ADC_DMA_CHANNEL_DEEPTH)

  9. extern volatile uint16_t ADC_Data[ADC_CHANNEL_NUM];
  10. extern volatile uint16_t ADC_DMA_Value[ADC_DMA_CHANNEL_DEEPTH][ADC_DMA_CHANNEL_NUM];

  11. void ADC_UserConfig1();
  12. void ADC_UserConfig2();

  13. void ADC_Config_NotUseDMA();
  14. void ADC_Config_UseDMA();
  15. void ADC_Config_SetDMA();

  16. uint16_t ADC_GetData_NotUseDMA(uint8_t channel);
  17. uint16_t ADC_GetAverageData_NotUseDMA(uint8_t channel, uint16_t count);
  18. uint16_t ADC_GetData_UseDMA(uint8_t channel);
  19. uint16_t ADC_GetAverageData_UseDMA(uint8_t channel);

  20. #endif
复制代码

源文件
  1. #include "adc_ext.h"

  2. /**
  3. * ADC时钟
  4. *
  5. * ADC输入时钟 ADC_CLK 由 PCLK2 经过分频产生,最大值是 36MHz,典型值为30MHz
  6. * 分频因子由 ADC 通用控制寄存器 ADC_CCR 的 ADCPRE[1:0]设置,可设置的分频系数有 2、4、6 和 8
  7. * 对于STM32F407的PCLK2=HCLK/2=84MHz,所以程序一般使用 4 分频或者 6 分频
  8. */

  9. /**
  10. * 采样时间
  11. *
  12. * ADC 需要若干个 ADC_CLK 周期完成对输入的电压进行采样,
  13. * 采样的周期数可通过ADC 采样时间寄存器 ADC_SMPR1 和 ADC_SMPR2 中的 SMP[2:0]位设置。
  14. * 每个通道可以分别用不同的时间采样。
  15. * 其中采样周期最小是 3 个,即如果我们要达到最快的采样,那么应该设置采样周期为 3 个周期。
  16. * 这里说的周期就是 1/ADC_CLK。
  17. */

  18. /**
  19. * 注意本库函数只实现了ADC1的配置与采样,ADC2与ADC3的配置与使用方法与此类似
  20. */

  21. volatile uint16_t ADC_Data[ADC_CHANNEL_NUM];
  22. volatile uint16_t ADC_DMA_Value[ADC_DMA_CHANNEL_DEEPTH][ADC_DMA_CHANNEL_NUM];

  23. /**
  24. * @brief 预置的ADC配置1,不使用DMA传输
  25. */
  26. void ADC_UserConfig1()
  27. {
  28.         GPIO_ConfigPort('A', 0, GPIO_Mode_AN, GPIO_OType_OD, GPIO_PuPd_NOPULL, 0);
  29.         
  30.         ADC_Config_NotUseDMA();
  31. }


  32. /**
  33. * @brief 预置的ADC配置2,使用DMA传输
  34. */
  35. void ADC_UserConfig2()
  36. {
  37.         GPIO_ConfigPort('A', 0, GPIO_Mode_AN, GPIO_OType_OD, GPIO_PuPd_NOPULL, 0);
  38.         GPIO_ConfigPort('A', 1, GPIO_Mode_AN, GPIO_OType_OD, GPIO_PuPd_NOPULL, 0);
  39.         GPIO_ConfigPort('A', 2, GPIO_Mode_AN, GPIO_OType_OD, GPIO_PuPd_NOPULL, 0);
  40.         GPIO_ConfigPort('A', 3, GPIO_Mode_AN, GPIO_OType_OD, GPIO_PuPd_NOPULL, 0);
  41.         
  42.         ADC_Config_SetDMA();
  43.         ADC_Config_UseDMA();
  44. }


  45. /**
  46. * @brief 不使用DMA进行数据传输时的ADC配置
  47. *                 由于ADC 规则组数据寄存器 ADC_DR 只有一个,是一个 32 位的寄存器,
  48. *                 只有低 16 位有效并且只是用于独立模式存放转换完成数据。
  49. *                 如果使用多通道转换,那转换的数据就全部都挤在了 DR 里面,前一个时间点转换的通道数据,
  50. *                 就会被下一个时间点的另外一个通道转换的数据覆盖掉,所以当通道转换完成后就应该把数据取走。
  51. *
  52. *                 因此不使用DMA模式的时候,每次只能读取一个通道的数据
  53. */
  54. void ADC_Config_NotUseDMA()
  55. {
  56.         /**
  57.          * Configuration of ADC
  58.          */

  59.         ADC_CommonInitTypeDef ADC_CommonInitStructure;

  60.         RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);                                                                                                //使能ADC1时钟
  61.         ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent;                                                                                //独立ADC模式
  62.         ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div4;                                                                                //ADC时钟为fpclock/分频
  63.         ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;                //禁止DMA直接访问模式
  64.         ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_20Cycles;        //采样时间间隔

  65.         ADC_CommonInit(&ADC_CommonInitStructure);

  66.         ADC_InitTypeDef ADC_InitStructure;

  67.         ADC_StructInit(&ADC_InitStructure);
  68.         ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;                                                                //设置ADC的分辨率12位
  69.         ADC_InitStructure.ADC_ScanConvMode = ENABLE;                                                                                        //通道扫描模式
  70.         ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;                                                                //连续转换模式

  71.         ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConvEdge_None;        //禁止外部边沿触发
  72.         ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC1;                //外部触发通道
  73.         ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;                                                                //设置数据右对齐
  74.         ADC_InitStructure.ADC_NbrOfConversion = 1;                                                                                                        //设置转换通道,固定为1
  75.         ADC_Init(ADC1, &ADC_InitStructure);

  76.         ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_56Cycles);                        //配置 ADC 通道转换顺序和采样周期

  77.         //        ADC_ITConfig(RHEOSTAT_ADC, ADC_IT_EOC, ENABLE);                                                                                                //ADC 转换结束产生中断,可以在中断服务程序中读取转换值

  78.         ADC_Cmd(ADC1, ENABLE);                                                                                                                                                                                                //使能ADC
  79. }


  80. /**
  81. * @brief 使用DMA进行数据传输时的ADC配置
  82. *          注意规则通道的数量需要根据实际项目进行调整
  83. */
  84. void ADC_Config_UseDMA()
  85. {
  86.         /**
  87.          * Configuration of ADC
  88.          */
  89.         ADC_CommonInitTypeDef ADC_CommonInitStructure;

  90.         RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);                                                        //使能ADC1时钟
  91.         ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent;                                        //独立ADC模式
  92.         ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div4;                                        //ADC时钟为Fpclock/分频
  93.         ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;                //禁止DMA直接访问模式
  94.         ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_20Cycles;        //采样时间间隔

  95.         ADC_CommonInit(&ADC_CommonInitStructure);

  96.         ADC_InitTypeDef ADC_InitStructure;

  97.         ADC_StructInit(&ADC_InitStructure);
  98.         ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;                                                                //设置ADC的分辨率12位
  99.         ADC_InitStructure.ADC_ScanConvMode = ENABLE;                                                                                        //通道扫描模式
  100.         ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;                                                                //连续转换模式

  101.         ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConvEdge_None;        //禁止外部边沿触发
  102.         ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC1;                //外部触发通道
  103.         ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;                                                                //设置数据右对齐
  104.         ADC_InitStructure.ADC_NbrOfConversion = ADC_CHANNEL_NUM;                                        //设置转换通道
  105.         ADC_Init(ADC1, &ADC_InitStructure);

  106.         ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_3Cycles);                //通道1转换结果保存到ADCConvertedValue[0~10][0]
  107.         ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_3Cycles);                 //通道2转换结果保存到ADCConvertedValue[0~10][1]
  108.         ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 3, ADC_SampleTime_3Cycles);                 //通道3转换结果保存到ADCConvertedValue[0~10][2]
  109.         ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 4, ADC_SampleTime_3Cycles);                 //通道4转换结果保存到ADCConvertedValue[0~10][3]
  110.         //        ADC_RegularChannelConfig(ADC1, ADC_Channel_4, 5, ADC_SampleTime_3Cycles);                 //通道5转换结果保存到ADCConvertedValue[0~10][4]
  111.         //        ADC_RegularChannelConfig(ADC1, ADC_Channel_5, 6, ADC_SampleTime_3Cycles);                 //通道6转换结果保存到ADCConvertedValue[0~10][5]
  112.         //        ADC_RegularChannelConfig(ADC1, ADC_Channel_6, 7, ADC_SampleTime_3Cycles);                 //通道7转换结果保存到ADCConvertedValue[0~10][6]
  113.         //        ADC_RegularChannelConfig(ADC1, ADC_Channel_7, 8, ADC_SampleTime_3Cycles);                 //通道8转换结果保存到ADCConvertedValue[0~10][7]
  114.         //        ADC_RegularChannelConfig(ADC1, ADC_Channel_8, 9, ADC_SampleTime_3Cycles);                 //通道9转换结果保存到ADCConvertedValue[0~10][8]
  115.         //        ADC_RegularChannelConfig(ADC1, ADC_Channel_9, 10, ADC_SampleTime_3Cycles);         //通道10转换结果保存到ADCConvertedValue[0~10][9]
  116.         //        ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 11, ADC_SampleTime_3Cycles);         //通道11转换结果保存到ADCConvertedValue[0~10][10]
  117.         //        ADC_RegularChannelConfig(ADC1, ADC_Channel_11, 12, ADC_SampleTime_3Cycles);         //通道12转换结果保存到ADCConvertedValue[0~10][11]
  118.         //        ADC_RegularChannelConfig(ADC1, ADC_Channel_12, 13, ADC_SampleTime_3Cycles);         //通道13转换结果保存到ADCConvertedValue[0~10][12]
  119.         //        ADC_RegularChannelConfig(ADC1, ADC_Channel_13, 14, ADC_SampleTime_3Cycles);         //通道14转换结果保存到ADCConvertedValue[0~10][13]
  120.         //        ADC_RegularChannelConfig(ADC1, ADC_Channel_14, 15, ADC_SampleTime_3Cycles);         //通道15转换结果保存到ADCConvertedValue[0~10][14]
  121.         //        ADC_RegularChannelConfig(ADC1, ADC_Channel_15, 16, ADC_SampleTime_3Cycles);         //通道16转换结果保存到ADCConvertedValue[0~10][15]

  122.         ADC_DMARequestAfterLastTransferCmd(ADC1, ENABLE);                                                                                                        // 使能 DMA 请求 after last transfer (Single-ADC mode)
  123.         ADC_DMACmd(ADC1, ENABLE);                                                                                                                                                                                 //开启ADC的DMA支持
  124.         ADC_Cmd(ADC1, ENABLE);                                                                                                                                                                                                //使能ADC
  125.         ADC_SoftwareStartConv(ADC1);                                                                                                                                                                                //开始ADC转换,软件触发
  126. }


  127. /**
  128. * @brief 使用DMA进行数据传输时的DMA配置
  129. */
  130. void ADC_Config_SetDMA()
  131. {
  132.         /**
  133.          * Configuration of DMA
  134.          */
  135.         DMA_InitTypeDef DMA_InitStructure;
  136.         RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);                                                                //使能时钟
  137.         DMA_DeInit(DMA2_Stream0);                                                                                                                                                                //将通道一寄存器设为默认值

  138.         DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t) &(ADC1->DR);                                //该参数用以定义DMA外设基地址
  139.         DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t) &ADC_DMA_Value;                        //该参数用以定义DMA内存基地址(转换结果保存的地址)
  140.         DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;                                                        //该参数规定了外设是作为数据传输的目的地还是来源,此处是作为来源
  141.         DMA_InitStructure.DMA_BufferSize = ADC_DMA_BUFFER_SIZE;                                                                //定义指定DMA通道的DMA缓存的大小,单位为数据单位。这里也就是ADC_DMA_Value的大小
  142.         DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;                                        //设定外设地址寄存器递增与否,此处设为不变 Disable
  143.         DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;                                                        //用来设定内存地址寄存器递增与否,此处设为递增,Enable
  144.         DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;                //数据宽度为16位
  145.         DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;                                //数据宽度为16位
  146.         DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;                                                                                 //工作在循环缓存模式
  147.         DMA_InitStructure.DMA_Priority = DMA_Priority_High;                                                                                 //DMA通道拥有高优先级 分别4个等级 低、中、高、非常高
  148.         DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;                                                        //禁用DMA的FIFO模式,使用直连模式
  149.         DMA_InitStructure.DMA_FIFOThreshold  = DMA_FIFOThreshold_HalfFull;
  150.         DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
  151.         DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
  152.         DMA_InitStructure.DMA_Channel = DMA_Channel_0;                                                                                        //选择DMA通道

  153.         DMA_Init(DMA2_Stream0, &DMA_InitStructure);                                                                                                         //初始化DMA流
  154.         DMA_Cmd(DMA2_Stream0, ENABLE);                                                                                                                                        //使能DMA流
  155. }


  156. /**
  157. * @brief 不使用DMA传输时,获取ADC的采样数据
  158. * @param channel: ADC的采样通道
  159. * @return ADC的单次采样结果
  160. */
  161. uint16_t ADC_GetData_NotUseDMA(uint8_t channel)
  162. {
  163.         ADC_RegularChannelConfig(ADC1, channel, 1, ADC_SampleTime_56Cycles);                        //配置 ADC 通道转换顺序和采样周期
  164.         ADC_SoftwareStartConv(ADC1);                                                                                                                                                        //使能指定的ADC1的软件转换启动功能

  165.         /**
  166.          * 通过查询的方式获取了ADC的转化结果,
  167.          * 也可以通过中断的方式获取ADC的转换结果
  168.          */
  169.         while (!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC));                                                                                        //等待转换结束

  170.         return ADC_GetConversionValue(ADC1);                                                                                                                        //返回最近一次ADC1规则组的转换结果
  171. }


  172. /**
  173. * @brief 不使用DMA传输时,获取ADC采样数据的平均值
  174. * @param channel: ADC的采样通道
  175. * @param count: 计算平均值的采样数量
  176. * @return ADC的单次采样结果的平均值
  177. */
  178. uint16_t ADC_GetAverageData_NotUseDMA(uint8_t channel, uint16_t count)
  179. {
  180.         uint32_t temp_val = 0;

  181.         for (uint16_t t = 0; t < count; t++)
  182.         {
  183.                 temp_val += ADC_GetData_NotUseDMA(channel);
  184.         }

  185.         ADC_Data[channel] = temp_val / count;

  186.         return ADC_Data[channel];
  187. }

  188. /**
  189. * @brief 获取通过DMA传输的采样数据
  190. * @param channel: ADC的采样通道
  191. * @return ADC的采样数据
  192. */
  193. uint16_t ADC_GetData_UseDMA(uint8_t channel)
  194. {
  195.         return ADC_DMA_Value[0][channel];
  196. }


  197. /**
  198. * @brief 获取通过DMA传输的采样数据(求平均值,过采样)
  199. * @param channel: ADC的采样通道
  200. * @return ADC的采样数据
  201. */
  202. uint16_t ADC_GetAverageData_UseDMA(uint8_t channel)
  203. {
  204.         uint32_t sum = 0;

  205.         for (uint16_t j = 0; j < ADC_DMA_CHANNEL_DEEPTH; j++)
  206.         {
  207.                 sum += ADC_DMA_Value[j][channel];
  208.         }

  209.         return sum / ADC_DMA_CHANNEL_DEEPTH;                                                //求平均值并转换成电压值
  210. }
复制代码

使用指南
由于使用了相同的功能逻辑与函数名(即头文件相同),STM32F103与STM32F407的ADC扩展函数库在使用方法上是相同的。由于在ADC扩展库中提供的模板配置函数中已经完成了GPIO外设与ADC外设的配置,因此可以直接调用一步完成ADC使用前的初始化工作。

单次单通道采样(不使用DMA)
配置ADC
  1. void Periphl_ConfigAll()
  2. {
  3.         /*省略无关代码*/
  4.         ADC_UserConfig1();
  5.         /*省略无关代码*/
  6. }
复制代码

启动采样并获取数据
采集的数据保存在全局数组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
  1. void Periphl_ConfigAll()
  2. {
  3.         /*省略无关代码*/
  4.         ADC_UserConfig2();
  5.         /*省略无关代码*/
  6. }
复制代码

获取数据
在配置完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。

  1. void McuAdcDaq_task(void *pvParameters)
  2. {
  3.         ADC_UserConfig2();

  4.         TickType_t ticks = xTaskGetTickCount();
  5.         while (1)
  6.         {
  7.                 User_RefreshSensor();
  8.                 User_MB_RefreshInputRegister();

  9.                 //100ms 一个处理周期
  10.                 vTaskDelayUntil( &ticks, 100);
  11.         }
  12. }
复制代码




收藏 评论0 发布时间:2022-4-13 17:00

举报

0个回答

所属标签

相似分享

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版