OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 3010|回复: 0

关于STM32中AD采样的三种方法分析

[复制链接]

80

主题

103

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
406
金钱
406
注册时间
2018-11-20
在线时间
25 小时
发表于 2019-8-26 15:04:05 | 显示全部楼层 |阅读模式
  在进行STM32F中AD采样的学习中,我们知道AD采样的方法有多种,按照逻辑程序处理有三种方式,一种是查询模式,一种是中断处理模式,一种是DMA模式。三种方法按照处理复杂方法DMA模式处理模式效率最高,其次是中断处理模式,最差是查询模式,相信很多学者在学习AD采样程序时,很多例程采用DMA模式,在这里我针对三种程序进行分别分析。
  1、AD采样查询模式
  在AD采样查询模式中,我们需要注意的是IO口的初始化配置,这里我采用PA2作为模拟采集的引脚(AIN2)和串口3作为打印输出。
  具体如下:建立一个USART3.C和USART3.H文件,其程序为:

  1. #include "usart3.h"

  2.   #include "stdarg.h"

  3.   u8 SendBuff[SENDBUFF_SIZE];

  4.   void USART3_Config(void)

  5.   {

  6.   //定义结构体

  7.   GPIO_InitTypeDef GPIO_InitStructure;

  8.   USART_InitTypeDef USART_InitStructure;

  9.   //开启外部时钟

  10.   RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE);

  11.   RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE );

  12.   // USART3 GPIO config

  13.   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;

  14.   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;

  15.   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

  16.   GPIO_Init(GPIOB, &GPIO_InitStructure);

  17.   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;

  18.   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOAtiNG;

  19.   GPIO_Init(GPIOB, &GPIO_InitStructure);

  20.   //USART3 mode config

  21.   USART_InitStructure.USART_BaudRate = 115200;

  22.   USART_InitStructure.USART_WordLength = USART_WordLength_8b;

  23.   USART_InitStructure.USART_StopBits = USART_StopBits_1;

  24.   USART_InitStructure.USART_Parity = USART_Parity_No;

  25.   USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;

  26.   USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;

  27.   USART_Init(USART3, &USART_InitStructure);

  28.   USART_Cmd(USART3, ENABLE);

  29.   }
复制代码

  其次建立一个ADC.C和一个ADC.H文件,其中ADC.C中程序为:

  1.  void ADC1_Init(void)

  2.   {

  3.   ADC1_GPIO_Config();

  4.   ADC1_Mode_Config();

  5.   }

  6.   static void ADC1_GPIO_Config(void)

  7.   {

  8.   GPIO_InitTypeDef GPIO_InitStructure;

  9.   //开启外部时钟

  10.   RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_GPIOA,ENABLE);

  11.   //配置PA2引脚

  12.   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;

  13.   //配置为模拟输入

  14.   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;

  15.   //调用库函数

  16.   GPIO_Init(GPIOA, &GPIO_InitStructure);

  17.   }

  18.   static void ADC1_Mode_Config(void)

  19.   {

  20.   //ADC1_ configuration

  21.   ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;

  22.   //独立ADC模式

  23.   ADC_InitStructure.ADC_ScanConvMode = DISABLE;

  24.   //禁止扫描模式

  25.   ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;

  26.   //开启连续转换模式

  27.   ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //不使用外部触发转换

  28.   ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //采集数据右对齐

  29.   ADC_InitStructure.ADC_NbrOfChannel = 1; //要转换的通道数目1

  30.   ADC_Init(ADC1,&ADC_InitStructure);

  31.   //配置ADC时钟,为PCLK2的8分频,即9Mhz

  32.   RCC_ADCCLKConfig(RCC_PCLK2_Div8);

  33.   //配置ADC1的通道2位55.5个采集周期

  34.   ADC_RegularChannelConfig(ADC1,ADC_Channel_2, 1, ADC_SampleTime_55Cycles5);

  35.   ADC_Cmd(ADC1,ENABLE);

  36.   //复位校准寄存器

  37.   ADC_ResetCalibration(ADC1);

  38.   //等待校准寄存器复位完成

  39.   while(ADC_GetResetCalibrationStatus(ADC1));

  40.   //ADC校准

  41.   ADC_StartCalibration(ADC1);

  42.   while(ADC_GetCalibrationStatus(ADC1));

  43.   //由于没有使用外部触发,所以使用软件触发ADC转换

  44.   ADC_SoftwareStartConvCmd(ADC1,ENABLE);

  45.   }
复制代码

  然后在主函数main中其程序代码如下:

  1.  int main(void)

  2.   {

  3.   USART3_Config();

  4.   ADC1_Init();

  5.   printf("输入ADC值");

  6.   while(1)

  7.   {

  8.   ADC_ConvertedValue = ADC_GetConversionValue(ADC1);

  9.   ADC_ConvertedValueLocal =(float)ADC_ConvertedValue/4096*3.3; //读取ADC转换的值

  10.   printf("\r\n the current AD value = 0x%04X \r\n",ADC_ConvertedValue);

  11.   printf("\r\n the current AD value = %f V \r\n",ADC_ConvertedValueLocal);

  12.   Delay(0xFFFFEE);

  13.   }

  14.   }

复制代码

  这样采用查询的方法即可以采集ADC的电压值,一个值为16进制转换的值,一个是转换计算的值。说明一下:ADC_ConvertedValue = ADC_GetConversionValue(ADC1);
  一定要放在while中,只有这样,采集的ADC电压值才是实时采集的电压值。放在while外面,则采集的电压值为第一次的电压值,且读取的电压值不会变化。对于4096的值来源在于ADC采集的数值是12位ADC,即是2的12次方。
  2、中断查询ADC程序
  对于中断查询采集ADC程序主要是在ADC.C和main函数中有差别。具体ADC.C程序为:
  1. <p>  void ADC1_Init(void)</p>
  2. <p>  {</p>
  3. <p>  ADC1_GPIO_Config();</p>
  4. <p>  ADC1_Mode_Config();</p>
  5. <p>  ADC_NVIC_Config();</p>
  6. <p>  }</p>
  7. <p>  static void ADC_NVIC_Config(void)</p>
  8. <p>  {</p>
  9. <p>  NVIC_InitTypeDef NVIC_InitStructure;</p>
  10. <p>  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);</p>
  11. <p>  NVIC_InitStructure.NVIC_IRQChannel = ADC1_2_IRQn;</p>
  12. <p>  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;</p>
  13. <p>  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;</p>
  14. <p>  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;</p>
  15. <p>  NVIC_Init(&NVIC_InitStructure);</p>
  16. <p>  }</p>
  17. <p>  static void ADC1_GPIO_Config(void)</p>
  18. <p>  {</p>
  19. <p>  GPIO_InitTypeDef GPIO_InitStructure;</p>
  20. <p>  //开启外部时钟</p>
  21. <p>  RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 |
  22. RCC_APB2Periph_GPIOA,ENABLE);</p>
  23. <p>  //配置PA2引脚</p>
  24. <p>  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;</p>
  25. <p>  //配置为模拟输入</p>
  26. <p>  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;</p>
  27. <p>  //调用库函数</p>
  28. <p>  GPIO_Init(GPIOA, &GPIO_InitStructure);</p>
  29. <p>  }</p>
  30. <p>  static void ADC1_Mode_Config(void)</p>
  31. <p>  {</p>
  32. <p>  //ADC1_ configuration</p>
  33. <p>  ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;</p>
  34. <p>  //独立ADC模式</p>
  35. <p>  ADC_InitStructure.ADC_ScanConvMode = DISABLE;</p>
  36. <p>  //禁止扫描模式</p>
  37. <p>  ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;</p>
  38. <p>  //开启连续转换模式</p>
  39. <p>  ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
  40. //不使用外部触发转换</p>
  41. <p>  ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //采集数据右对齐</p>
  42. <p>  ADC_InitStructure.ADC_NbrOfChannel = 1; //要转换的通道数目1</p>
  43. <p>  ADC_Init(ADC1,&ADC_InitStructure);</p>
  44. <p>  //配置ADC时钟,为PCLK2的8分频,即9Mhz</p>
  45. <p>  RCC_ADCCLKConfig(RCC_PCLK2_Div8);</p>
  46. <p>  //配置ADC1的通道2位55.5个采集周期</p>
  47. <p>  ADC_RegularChannelConfig(ADC1,ADC_Channel_2, 1,
  48. ADC_SampleTime_55Cycles5);</p>
  49. <p>  ADC_ITConfig(ADC1, ADC_IT_EOC, ENABLE); //开启ADC采集中断</p>
  50. <p>  ADC_Cmd(ADC1,ENABLE);</p>
  51. <p>  //复位校准寄存器</p>
  52. <p>  ADC_ResetCalibration(ADC1);</p>
  53. <p>  //等待校准寄存器复位完成</p>
  54. <p>  while(ADC_GetResetCalibrationStatus(ADC1));</p>
  55. <p>  //ADC校准</p>
  56. <p>  ADC_StartCalibration(ADC1);</p>
  57. <p>  while(ADC_GetCalibrationStatus(ADC1));</p>
  58. <p>  //由于没有使用外部触发,所以使用软件触发ADC转换</p>
  59. <p>  ADC_SoftwareStartConvCmd(ADC1,ENABLE);</p>
  60. <p>  }</p>
复制代码

  对于main函数如下:
  1. <p>  int main(void)</p>
  2. <p>  {</p>
  3. <p>  USART3_Config();</p>
  4. <p>  ADC1_Init();</p>
  5. <p>  printf("输入ADC值");</p>
  6. <p>  while(1)</p>
  7. <p>  {</p>
  8. <p>  ADC_ConvertedValueLocal =(float)ADC_ConvertedValue/4096*3.3;
  9. //读取ADC转换的值</p>
  10. <p>  printf("\r\n the current AD value = 0x%04X \r\n",ADC_ConvertedValue);</p>
  11. <p>  printf("\r\n the current AD value = %f V
  12. \r\n",ADC_ConvertedValueLocal);</p>
  13. <p>  Delay(0xFFFFEE);</p>
  14. <p>  }</p>
  15. <p>  }</p>
  16. <p>  void ADC_IRQHandler(void)</p>
  17. <p>  {</p>
  18. <p>  IF (ADC_GetITStatus(ADC1, ADC_IT_EOC) == SET)</p>
  19. <p>  {</p>
  20. <p>  ADC_ConvertedValue = ADC_GetConversionValue(ADC1);</p>
  21. <p>  }</p>
  22. <p>  ADC_ClearITPendingBit(ADC1, ADC_IT_EOC);</p>
  23. <p>  }</p>
复制代码

  在引入void ADC_IRQHandler(void)这个中断服务函数之前,一定要进行
  #define ADC_IRQHandler ADC1_2_IRQHandler
  否则中断无法执行,无法进行ADC采集。
  3、DMA模式的ADC采集程序
  采用这种方式的ADC采集程序,其在ADC.C程序为:
  1. <p>  void ADC1_Init(void)</p>
  2. <p>  {</p>
  3. <p>  ADC1_GPIO_Config();</p>
  4. <p>  ADC1_Mode_Config();</p>
  5. <p>  }</p>
  6. <p>  static void ADC1_GPIO_Config(void)</p>
  7. <p>  {</p>
  8. <p>  GPIO_InitTypeDef GPIO_InitStructure;</p>
  9. <p>  //开启外部时钟</p>
  10. <p>  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);</p>
  11. <p>  RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 |
  12. RCC_APB2Periph_GPIOA,ENABLE);</p>
  13. <p>  //配置PA2引脚</p>
  14. <p>  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;</p>
  15. <p>  //配置为模拟输入</p>
  16. <p>  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;</p>
  17. <p>  //调用库函数</p>
  18. <p>  GPIO_Init(GPIOA, &GPIO_InitStructure);</p>
  19. <p>  }</p>
  20. <p>  static void ADC1_Mode_Config(void)</p>
  21. <p>  {</p>
  22. <p>  DMA_InitTypeDef DMA_InitStructure;</p>
  23. <p>  ADC_InitTypeDef ADC_InitStructure;</p>
  24. <p>  DMA_DeInit(DMA1_Channel1);</p>
  25. <p>  DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address;</p>
  26. <p>  DMA_InitStructure.DMA_MemoryBaseAddr = (u32)&ADC_ConvertedValue;</p>
  27. <p>  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;</p>
  28. <p>  DMA_InitStructure.DMA_BufferSize = 1;</p>
  29. <p>  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;</p>
  30. <p>  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable;</p>
  31. <p>  DMA_InitStructure.DMA_PeripheralDataSize= DMA_PeripheralDataSize_HalfWord;
  32. DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;</p>
  33. <p>  DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;</p>
  34. <p>  DMA_InitStructure.DMA_Priority = DMA_Priority_High;</p>
  35. <p>  DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;</p>
  36. <p>  DMA_Init(DMA1_Channel1,&DMA_InitStructure);</p>
  37. <p>  DMA_Cmd (DMA1_Channel1,ENABLE);</p>
  38. <p>  //ADC1_ configuration</p>
  39. <p>  ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;</p>
  40. <p>  //独立ADC模式</p>
  41. <p>  ADC_InitStructure.ADC_ScanConvMode = DISABLE;</p>
  42. <p>  //禁止扫描模式</p>
  43. <p>  ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;</p>
  44. <p>  //开启连续转换模式</p>
  45. <p>  ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
  46. //不使用外部触发转换</p>
  47. <p>  ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //采集数据右对齐</p>
  48. <p>  ADC_InitStructure.ADC_NbrOfChannel = 1; //要转换的通道数目1</p>
  49. <p>  ADC_Init(ADC1,&ADC_InitStructure);</p>
  50. <p>  //配置ADC时钟,为PCLK2的8分频,即9Mhz</p>
  51. <p>  RCC_ADCCLKConfig(RCC_PCLK2_Div8);</p>
  52. <p>  //配置ADC1的通道2位55.5个采集周期</p>
  53. <p>  ADC_RegularChannelConfig(ADC1,ADC_Channel_2, 1,
  54. ADC_SampleTime_55Cycles5);</p>
  55. <p>  ADC_DMACmd(ADC1,ENABLE);</p>
  56. <p>  ADC_Cmd(ADC1,ENABLE);</p>
  57. <p>  //复位校准寄存器</p>
  58. <p>  ADC_ResetCalibration(ADC1);</p>
  59. <p>  //等待校准寄存器复位完成</p>
  60. <p>  while(ADC_GetResetCalibrationStatus(ADC1));</p>
  61. <p>  //ADC校准</p>
  62. <p>  ADC_StartCalibration(ADC1);</p>
  63. <p>  while(ADC_GetCalibrationStatus(ADC1));</p>
  64. <p>  //由于没有使用外部触发,所以使用软件触发ADC转换</p>
  65. <p>  ADC_SoftwareStartConvCmd(ADC1,ENABLE);</p>
  66. <p>  }</p>
复制代码

  在这里需要对ADC1_DR_Address地址值进行定义,具体定义可以在ADC.H文件中,表现为:#define ADC1_DR_Address ((u32)0x40012400+0x4c)
  在main中函数为:
  1. <p>  int main(void)</p>
  2. <p>  {</p>
  3. <p>  USART3_Config();</p>
  4. <p>  ADC1_Init();</p>
  5. <p>  printf("输入ADC值");</p>
  6. <p>  while(1)</p>
  7. <p>  {</p>
  8. <p>  ADC_ConvertedValueLocal =(float)ADC_ConvertedValue/4096*3.3;
  9. //读取ADC转换的值</p>
  10. <p>  printf("\r\n the current AD value = 0x%04X \r\n",ADC_ConvertedValue);</p>
  11. <p>  printf("\r\n the current AD value = %f V
  12. \r\n",ADC_ConvertedValueLocal);</p>
  13. <p>  Delay(0xFFFFEE);</p>
  14. <p>  }</p>
  15. <p>  }</p>
复制代码

  通过实际测试,三种程序处理方式得到的结果都是一样,这表明三种方式是可行的。不过后续在具体功能程序设计时,建议采用中断查询或者DMA模式。

AD的资料暂时没有我就给搞一些pcb以及DMA和中断的资料供大家在学习过程中参考吧
老司机倾囊相授-PCB大牛修炼秘籍
http://www.makeru.com.cn/live/3472_1296.html?s=45051



正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2025-2-26 01:51

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表