当前位置:   article > 正文

STM32 -ADC+DMA使用(巨全面)_stm32 adc dma

stm32 adc dma

1.前言

在STM32中,ADC可以将引脚上连续变化的模拟电压转换为内存中存储的数字变量,建立模拟电路到数字电路的桥梁¹。STM32的ADC为12位,AD最大值是4095,对应最大电压3.3V,可对0-3.3v之间的任意电压量化¹。STM32的ADC有18个输入通道,可测量16个外部和2个内部信号源¹。

在多通道数据采集中,如果使用DMA,那么ADC1和ADC3可以(ADC2不具备DMA功能)³。多通道采集会出现数据覆盖的现象,可以使用DMA进行数据传输²。例如,假若同时有三个通道进行采集数据:第一个通道采集完数据放到DR寄存器中,通过DMA迅速把数据搬移到16位数组的a [0];第二个通道采集完数据放到DR寄存器中,覆盖了第一个通道的数据,通过DMA迅速把数据搬移到16位数组的a [1];第三个通道采集完数据放到DR寄存器中,覆盖了第二个通道存放的数据,通过DMA迅速把数据搬移到16位数组的a [3]²。

这种方式可以有效地解决在多通道数据采集中的数据覆盖问题,提高数据采集的效率和精度¹²。具体的实现方式和步骤可能会因为具体的STM32型号和应用场景而有所不同,需要根据具体情况进行调整和优化¹²。希望这个解释能帮助你理解双ADC多通道+DMA的工作原理和

2.前期引入

2.1 ADC(模/数变换)

ADC是模/数变换的缩写,它允许我们将模拟信号转换为数字信号。在STM32中,ADC模块可以独立工作,但配合DMA传输可以更高效地采集多路信号。

2.2 DMA(直接内存访问)

DMA是直接内存访问的缩写,它允许数据在不经过CPU的情况下直接传输到内存。在STM32中,我们可以配置DMA来与ADC协同工作,实现高效的数据传输。

3.开发流程

  1. 初始化ADC和DMA。
  2. 配置ADC的工作模式,包括扫描模式、双ADC模式和连续转换模式。
  3. 配置DMA的工作模式,选择正常缓存模式或循环工作模式。
  4. 设置外部触发,以便ADC在上升沿触发时进行转换。
  5. 处理DMA中断,连接用户和外设。
  6. 编写代码示例,展示ADC+DMA的应用。

4.代码实例

3.1ADC多通道+DMA

  1. RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); //开启ADC1的时钟
  2. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //开启GPIOA的时钟
  3. RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //开启DMA1的时钟
  4. /*设置ADC时钟*/
  5. RCC_ADCCLKConfig(RCC_PCLK2_Div6); //选择时钟6分频,ADCCLK = 72MHz / 6 = 12MHz
  6. /*GPIO初始化*/
  7. GPIO_InitTypeDef GPIO_InitStructure;
  8. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
  9. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;
  10. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  11. GPIO_Init(GPIOA, &GPIO_InitStructure); //将PA0、PA1、PA2和PA3引脚初始化为模拟输入
  12. /*规则组通道配置*/
  13. ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5); //规则组序列1的位置,配置为通道0
  14. ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_55Cycles5); //规则组序列2的位置,配置为通道1
  15. ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 3, ADC_SampleTime_55Cycles5); //规则组序列3的位置,配置为通道2
  16. ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 4, ADC_SampleTime_55Cycles5); //规则组序列4的位置,配置为通道3
  17. /*ADC初始化*/
  18. ADC_InitTypeDef ADC_InitStructure; //定义结构体变量
  19. ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //模式,选择独立模式,即单独使用ADC1
  20. ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //数据对齐,选择右齐
  21. ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //外部触发,使用软件触发,不需要外部触发
  22. ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; //连续转换,使能,每转换一次规则组序列后立刻开始下一次转换
  23. ADC_InitStructure.ADC_ScanConvMode = ENABLE; //扫描模式,使能,扫描规则组的序列,扫描数量由ADC_NbrOfChannel确定
  24. ADC_InitStructure.ADC_NbrOfChannel = 4; //通道数,为4,扫描规则组的前4个通道
  25. ADC_Init(ADC1, &ADC_InitStructure); //将结构体变量交给ADC_Init,配置ADC1
  26. /*DMA初始化*/
  27. DMA_InitTypeDef DMA_InitStructure; //定义结构体变量
  28. DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR; //外设基地址,给定形参AddrA
  29. DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; //外设数据宽度,选择半字,对应16为的ADC数据寄存器
  30. DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设地址不自增,选择失能,始终以ADC数据寄存器为源
  31. DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)AD_Value; //存储器基地址,给定存放AD转换结果的全局数组AD_Value
  32. DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; //存储器数据宽度,选择半字,与源数据宽度对应
  33. DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //存储器地址自增,选择使能,每次转运后,数组移到下一个位置
  34. DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //数据传输方向,选择由外设到存储器,ADC数据寄存器转到数组
  35. DMA_InitStructure.DMA_BufferSize = 4; //转运的数据大小(转运次数),与ADC通道数一致
  36. DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; //模式,选择循环模式,与ADC的连续转换一致
  37. DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //存储器到存储器,选择失能,数据由ADC外设触发转运到存储器
  38. DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; //优先级,选择中等
  39. DMA_Init(DMA1_Channel1, &DMA_InitStructure); //将结构体变量交给DMA_Init,配置DMA1的通道1
  40. /*DMA和ADC使能*/
  41. DMA_Cmd(DMA1_Channel1, ENABLE); //DMA1的通道1使能
  42. ADC_DMACmd(ADC1, ENABLE); //ADC1触发DMA1的信号使能
  43. ADC_Cmd(ADC1, ENABLE); //ADC1使能
  44. /*ADC校准*/
  45. ADC_ResetCalibration(ADC1); //固定流程,内部有电路会自动执行校准
  46. while (ADC_GetResetCalibrationStatus(ADC1) == SET);
  47. ADC_StartCalibration(ADC1);
  48. while (ADC_GetCalibrationStatus(ADC1) == SET);
  49. /*ADC触发*/
  50. ADC_SoftwareStartConvCmd(ADC1, ENABLE); //软件触发ADC开始工作,由于ADC处于连续转换模式,故触发一次后ADC就可以一直连续不断地工作

3.2双ADC多通道+DMA

双ADC多通道+DMA主要是指在STM32微控制器中,使用两个模拟数字转换器(ADC)并行工作,同时通过直接内存访问(DMA)快速读取数据。这种方式可以提高数据采集的效率和精度¹²。

  1. GPIO_InitTypeDef GPIO_InitStructure;
  2. // ADC1 GPIO 初始化
  3. ADCx_1_GPIO_APBxClock_FUN ( , ENABLE );
  4. GPIO_InitStructure.GPIO_Pin = ;
  5. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
  6. GPIO_Init(, &GPIO_InitStructure);
  7. // ADCx_2 GPIO 初始化
  8. ADCx_2_GPIO_APBxClock_FUN ( , ENABLE );
  9. GPIO_InitStructure.GPIO_Pin = ;
  10. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
  11. GPIO_Init(T, &GPIO_InitStructure);
  12. }
  13. /**
  14. * @brief 配置ADC工作模式
  15. * @param 无
  16. * @retval 无
  17. */
  18. static void ADCx_Mode_Config(void)
  19. {
  20. DMA_InitTypeDef DMA_InitStructure;
  21. ADC_InitTypeDef ADC_InitStructure;
  22. // 打开DMA时钟
  23. RCC_AHBPeriphClockCmd(, ENABLE);
  24. // 打开ADC时钟
  25. ADCx_1_APBxClock_FUN (, ENABLE );
  26. ADCx_2_APBxClock_FUN (K, ENABLE );
  27. /* ------------------DMA模式配置---------------- */
  28. // 复位DMA控制器
  29. DMA_DeInit(ADC_DMA_CHANNEL);
  30. // 配置 DMA 初始化结构体
  31. // 外设基址为:ADC 数据寄存器地址
  32. DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&( ADCx_1->DR ));
  33. // 存储器地址
  34. DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)ADC_ConvertedValue;
  35. // 数据源来自外设
  36. DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
  37. // 缓冲区大小,应该等于数据目的地的大小
  38. DMA_InitStructure.DMA_BufferSize = NOFCHANEL;
  39. // 外设寄存器只有一个,地址不用递增
  40. DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
  41. // 存储器地址递增
  42. DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
  43. // 外设数据大小
  44. DMA_InitStructure.DMA_PeripheralDataSize =
  45. DMA_PeripheralDataSize_Word;
  46. // 内存数据大小,跟外设数据大小相同
  47. DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;
  48. // 循环传输模式
  49. DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
  50. // DMA 传输通道优先级为高,当使用一个DMA通道时,优先级设置不影响
  51. DMA_InitStructure.DMA_Priority = DMA_Priority_High;
  52. // 禁止存储器到存储器模式,因为是从外设到存储器
  53. DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
  54. // 初始化DMA
  55. DMA_Init(ADC_DMA_CHANNEL, &DMA_InitStructure);
  56. // 使能 DMA 通道
  57. DMA_Cmd(ADC_DMA_CHANNEL , ENABLE);
  58. /* ----------------ADCx_1 模式配置--------------------- */
  59. // 双ADC的规则同步
  60. ADC_InitStructure.ADC_Mode = ADC_Mode_RegSimult;
  61. // 扫描模式
  62. ADC_InitStructure.ADC_ScanConvMode = ENABLE ;
  63. // 连续转换模式
  64. ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
  65. // 不用外部触发转换,软件开启即可
  66. ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
  67. // 转换结果右对齐
  68. ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
  69. // 转换通道个数
  70. ADC_InitStructure.ADC_NbrOfChannel = NOFCHANEL;
  71. // 初始化ADC
  72. ADC_Init(ADCx_1, &ADC_InitStructure);
  73. // 配置ADC时钟N狿CLK28分频,即9MHz
  74. RCC_ADCCLKConfig(RCC_PCLK2_Div8);
  75. // 配置ADC 通道的转换顺序和采样时间
  76. ADC_RegularChannelConfig(ADCx_1, ADCx_1_CHANNEL, 1,
  77. ADC_SampleTime_239Cycles5);
  78. // 使能ADC DMA 请求
  79. ADC_DMACmd(ADCx_1, ENABLE);
  80. /* ----------------ADCx_2 模式配置--------------------- */
  81. // 双ADC的规则同步
  82. ADC_InitStructure.ADC_Mode = ADC_Mode_RegSimult;
  83. // 扫描模式
  84. ADC_InitStructure.ADC_ScanConvMode = ENABLE ;
  85. // 连续转换模式
  86. ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
  87. // 不用外部触发转换,软件开启即可
  88. ADC_InitStructure.ADC_ExternalTrigConv =
  89. ADC_ExternalTrigConv_None;
  90. // 转换结果右对齐
  91. ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
  92. // 转换通道个数
  93. ADC_InitStructure.ADC_NbrOfChannel = NOFCHANEL;
  94. // 初始化ADC
  95. ADC_Init(ADCx_2, &ADC_InitStructure);
  96. // 配置ADC时钟为PCLK28分频,即9MHz
  97. RCC_ADCCLKConfig(RCC_PCLK2_Div8);
  98. // 配置ADC 通道的转换顺序和采样时间
  99. ADC_RegularChannelConfig(ADCx_2, ADCx_2_CHANNEL, 1,
  100. ADC_SampleTime_239Cycles5);
  101. /* 使能ADCx_2的外部触发转换 */
  102. ADC_ExternalTrigConvCmd(ADC2, ENABLE);
  103. /* ----------------ADCx_1 校准--------------------- */
  104. // 开启ADC ,并开始转换
  105. ADC_Cmd(ADC1, ENABLE);
  106. // 初始化ADC 校准寄存器
  107. ADC_ResetCalibration(ADC1);
  108. // 等待校准寄存器初始化完成
  109. while(ADC_GetResetCalibrationStatus(ADC1));
  110. // ADC开始校准
  111. ADC_StartCalibration(ADC1);
  112. // 等待校准完成
  113. while(ADC_GetCalibrationStatus(ADC1));
  114. /* ----------------ADC2 校准--------------------- */
  115. // 开启ADC ,并开始转换
  116. ADC_Cmd(ADC2, ENABLE);
  117. // 初始化ADC 校准寄存器
  118. ADC_ResetCalibration(ADC2);
  119. // 等待校准寄存器初始化完成
  120. while(ADC_GetResetCalibrationStatus(ADC2));
  121. // ADC开始校准
  122. ADC_StartCalibration(ADC2);
  123. // 等待校准完成
  124. while(ADC_GetCalibrationStatus(ADC2));
  125. // 由于没有采用外部触发,所以使用软件触发ADC转换
  126. ADC_SoftwareStartConvCmd(ADC1, ENABLE);
  127. }

5.结语

总的来说,ADC多通道+DMA在STM32微控制器中提供了一种高效且精确的数据采集方法。通过使用DMA,我们可以避免在多通道数据采集中的数据覆盖问题,从而提高数据采集的效率和精度。这种技术在许多实时、高精度的应用中都有广泛的应用,如音频处理、图像处理等。。。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家小花儿/article/detail/566571
推荐阅读
相关标签
  

闽ICP备14008679号