使用STM32F103和STM32F401CCU6对双轴摇杆(两个电压通道)进行ADC采样并通过DMA读取数值

STM32 ADC(模数转换)工作模式

单次转换模式

In Single Conversion mode, the ADC does one conversion. This mode is started either by setting the ADON bit in the ADC_CR2 register (for a regular channel only) or by an external trigger (for a regular or injected channel), while the CONT bit is 0. Once the conversion of the selected channel is complete:

If a regular channel was converted:
– The converted data is stored in the 16-bit ADC_DR register
– The EOC (End Of Conversion) flag is set
– and an interrupt is generated if the EOCIE is set.
If an injected channel was converted:
– The converted data is stored in the 16-bit ADC_DRJ1 register
– The JEOC (End Of Conversion Injected) flag is set
– and an interrupt is generated if the JEOCIE bit is set.
The ADC is then stopped.

连续转换模式

In continuous conversion mode, ADC starts another conversion as soon as it finishes one. This mode is started either by an external trigger or by setting the ADON bit in the ADC_CR2 register, while the CONT bit is 1. After each conversion:

If a regular channel was converted:
– The converted data is stored in the 16-bit ADC_DR register
– The EOC (End Of Conversion) flag is set
– An interrupt is generated if the EOCIE is set.
If an injected channel was converted:
– The converted data is stored in the 16-bit ADC_DRJ1 register
– The JEOC (End Of Conversion Injected) flag is set
– An interrupt is generated if the JEOCIE bit is set.

扫描模式

This mode is used to scan a group of analog channels. A single conversion is performed for each channel of the group. After each end of conversion, the next channel of the group is converted automatically. If the CONT bit is set, conversion does not stop at the last selected group channel but continues again from the first selected group channel.

When using scan mode, DMA bit must be set and the direct memory access controller is used to transfer the converted data of regular group channels to SRAM after each update of the ADC_DR register. The injected channel converted data is always stored in the ADC_JDRx registers.

非连续模式

This mode is enabled by setting the DISCEN bit in the ADC_CR1 register. It can be used to convert a short sequence of n conversions (n <=8) which is a part of the sequence of conversions selected in the ADC_SQRx registers. The value of n is specified by writing to the DISCNUM[2:0] bits in the ADC_CR1 register.

When an external trigger occurs, it starts the next n conversions selected in the ADC_SQRx registers until all the conversions in the sequence are done. The total sequence length is defined by the L[3:0] bits in the ADC_SQR1 register.

读取ADC结果的几种方式

The Polling Method

It’s the easiest way in code in order to perform an analog to digital conversion using the ADC on an analog input channel. However, it’s not an efficient way in all cases as it’s considered to be a blocking way of using the ADC. As in this way, we start the A/D conversion and wait for the ADC until it completes the conversion so the CPU can resume processing the main code.

中断模式

The interrupt method is an efficient way to do ADC conversion in a non-blocking manner, so the CPU can resume executing the main code routine until the ADC completes the conversion and fires an interrupt signal so the CPU can switch to the ISR context and save the conversion results for further processing.

However, when you’re dealing with multiple channels in a circular mode or so, you’ll have periodic interrupts from the ADC that are too much for the CPU to handle. This will introduce jitter injection and interrupt latency and all sorts of timing issues to the system. This can be avoided by using DMA.

DMA方式

Lastly, the DMA method is the most efficient way of converting multiple ADC channels at very high rates and still transfers the results to the memory without CPU intervention which is so cool and time-saving technique.

STM32F103C8T6的代码实现

管脚与ADC的映射关系

  • PA0:7 ADC1_IN0:7
  • PB0 ADC1_IN8
  • PB1 ADC1_IN9

实现两个通道电压采集到DMA

  • 确定要采集的信号通道数量, 每个信号通道要保留的采样数, 比如下面的例子中是2个通道, 每个通道4个采样
  • 根据上面的数量得到ARRAYSIZE, 声明用于DMA的内存变量 __IO uint16_t ADCConvertedValue[ARRAYSIZE]
  • 初始化时钟: ADC1, GPIOA, DMA1
  • 初始化GPIOA用于采集的两个pin
  • 初始化ADC1
  • 初始化DMA1

代码

#include <stdio.h>
#include "timer.h"
#include "usart.h"#define ARRAYSIZE 2*4
__IO uint16_t ADCConvertedValue[ARRAYSIZE];void RCC_Configuration(void)
{/* ADCCLK = PCLK2/4 */RCC_ADCCLKConfig(RCC_PCLK2_Div4); /* Enable peripheral clocks ------------------------------------------------*//* Enable DMA1 clock */RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);/* Enable ADC1 and GPIOC clock */RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_GPIOA, ENABLE);
}void GPIO_Configuration(void)
{GPIO_InitTypeDef GPIO_InitStructure;/* Configure PA.00 (ADC Channel0) as analog input -------------------------*/GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_0 | GPIO_Pin_1;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;GPIO_Init(GPIOA, &GPIO_InitStructure);
}int main(void)
{SystemInit();Systick_Init();USART_Configuration();/* System clocks configuration ---------------------------------------------*/RCC_Configuration();/* GPIO configuration ------------------------------------------------------*/GPIO_Configuration();/* ADC1 configuration ------------------------------------------------------*/ADC_InitTypeDef ADC_InitStructure;ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;//We will convert multiple channelsADC_InitStructure.ADC_ScanConvMode = ENABLE;ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//right 12-bit data alignment in ADC data registerADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;// Set it to the number of channelsADC_InitStructure.ADC_NbrOfChannel = 2;ADC_Init(ADC1, &ADC_InitStructure);/* ADC1 regular channel0 configuration, rank decides the order in ADCConvertedValue, start from 1 */ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_41Cycles5);/* ADC1 regular channel1 configuration */ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_41Cycles5);/* Enable ADC1 DMA */ADC_DMACmd(ADC1, ENABLE);/* Enable ADC1 */ADC_Cmd(ADC1, ENABLE);/* Enable ADC1 reset calibration register */   ADC_ResetCalibration(ADC1);/* Check the end of ADC1 reset calibration register */while(ADC_GetResetCalibrationStatus(ADC1));/* Start ADC1 calibration */ADC_StartCalibration(ADC1);/* Check the end of ADC1 calibration */while(ADC_GetCalibrationStatus(ADC1));/* Start ADC1 Software Conversion */ ADC_SoftwareStartConvCmd(ADC1, ENABLE);/* DMA1 channel1 configuration ----------------------------------------------*/DMA_InitTypeDef DMA_InitStructure;DMA_DeInit(DMA1_Channel1);// ADC1_DR_Address    ((uint32_t)0x4001244C)DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&(ADC1->DR);DMA_InitStructure.DMA_MemoryBaseAddr     = (uint32_t)ADCConvertedValue;/* Direction:DMA_DIR_PeripheralSRC:from peripheral, DMA_DIR_PeripheralDST:to peripheral*/DMA_InitStructure.DMA_DIR                = DMA_DIR_PeripheralSRC;/* Specifies the buffer size, in data unit, of the specified Stream. The data unit is equal to the configuration set in DMA_PeripheralDataSize or DMA_MemoryDataSize members depending in the transfer direction. Set it to the number of channels*/DMA_InitStructure.DMA_BufferSize         = ARRAYSIZE;DMA_InitStructure.DMA_PeripheralInc      = DMA_PeripheralInc_Disable;// Specifies whether the memory address register should be incremented or notDMA_InitStructure.DMA_MemoryInc          = DMA_MemoryInc_Enable;DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;DMA_InitStructure.DMA_MemoryDataSize     = DMA_MemoryDataSize_HalfWord;DMA_InitStructure.DMA_Mode               = DMA_Mode_Circular;// Priority among DMA channelsDMA_InitStructure.DMA_Priority           = DMA_Priority_High;// From Memory to MemoryDMA_InitStructure.DMA_M2M                = DMA_M2M_Disable;DMA_Init(DMA1_Channel1, &DMA_InitStructure);/* Enable DMA1 channel1 */DMA_Cmd(DMA1_Channel1, ENABLE);while(1) {for (u8 i = 0; i < ARRAYSIZE; i++) {printf("%d ", *(ADCConvertedValue + i));}printf("\r\n");Systick_Delay_ms(500);}
}void ADC1_IRQHandler(void)
{ADC_ClearITPendingBit(ADC1, ADC_IT_EOC);ADC_SoftwareStartConvCmd(ADC1,ENABLE);
}

STM32F401CCU6的代码实现

只有1个16通道ADC. One 12-bit analog-to-digital converter is embedded and shares up to 16 external channels, performing conversions in the single-shot or scan mode. In scan mode, automatic conversion is performed on a selected group of analog inputs. The ADC can be served by the DMA controller. An analog watchdog feature allows very precise monitoring of the converted voltage of one, some or all selected channels. An interrupt is generated when the converted voltage is outside the programmed thresholds.
To synchronize A/D conversion and timers, the ADCs could be triggered by any of TIM1, TIM2, TIM3, TIM4 or TIM5 timer.

管脚与ADC的映射关系

  • PA0:7 ADC1_IN0:7
  • PB0 ADC1_IN8
  • PB1 ADC1_IN9
  • PC0:5 ADC1_IN10:15

因为F401CCU6的PC口只有PC13,PC14,PC15, 所以可以用的ADC只有ADC1_IN0 - IN9

STM32F4的ADC1与DMA的映射

根据STM32F2/F4/F7的DMA参考手册, 这个系列的芯片中DMA1与DMA2各有8个Stream(Stream0 - Stream7), 分别对应着不同的外设, 其中ADC1对应的是DMA2的Stream0和Stream4, 在代码中必须使用这两个, 否则DMA不起作用

实现两个通道电压采集到DMA的代码

#include <stdio.h>
#include "config.h"#include "led.h"
#include "timer.h"
#include "uart.h"#define ARRAYSIZE 2*4
__IO uint16_t ADCConvertedValue[ARRAYSIZE];void RCC_Configuration(void)
{/* Enable ADCx, DMA and GPIO clocks ****************************************/ RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);  RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);RCC_APB2PeriphResetCmd(RCC_APB2Periph_ADC1, ENABLE);RCC_APB2PeriphResetCmd(RCC_APB2Periph_ADC1, DISABLE);
}void GPIO_Configuration(void)
{/* Configure ADC1 Channel0,1 pin as analog input ******************************/GPIO_InitTypeDef      GPIO_InitStructure;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;GPIO_Init(GPIOA, &GPIO_InitStructure);
}int main(void)
{Systick_Init();USART1_Init();LED_Init();/* System clocks configuration ---------------------------------------------*/RCC_Configuration();/* GPIO configuration ------------------------------------------------------*/GPIO_Configuration();/* DMA2 Stream0 channel0 configuration **************************************/DMA_InitTypeDef DMA_InitStructure;DMA_InitStructure.DMA_Channel = DMA_Channel_0;  DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&(ADC1->DR);DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)ADCConvertedValue;DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;DMA_InitStructure.DMA_BufferSize = ARRAYSIZE;DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;DMA_InitStructure.DMA_Priority = DMA_Priority_High;DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;         DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;DMA_Init(DMA2_Stream0, &DMA_InitStructure);DMA_Cmd(DMA2_Stream0, ENABLE);/* ADC Common Init **********************************************************/ADC_CommonInitTypeDef ADC_CommonInitStructure;ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent;// 预分频4分频, ADCCLK=PCLK2/4=84/4=21Mhz,ADC时钟最好不要超过36Mhz ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div4;// DMA使能 (DMA传输下要设置使能)ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;//两个采样阶段之间的延迟x个时钟ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles;ADC_CommonInit(&ADC_CommonInitStructure);/* ADC1 Init ****************************************************************/ADC_InitTypeDef ADC_InitStructure;ADC_InitStructure.ADC_Resolution           = ADC_Resolution_12b;ADC_InitStructure.ADC_ScanConvMode         = ENABLE;ADC_InitStructure.ADC_ContinuousConvMode   = ENABLE;ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;//ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC1;ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;ADC_InitStructure.ADC_NbrOfConversion = 2;ADC_Init(ADC1, &ADC_InitStructure);/* ADC1 regular channel0,1 configuration **************************************/ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_56Cycles);ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_56Cycles);/* Enable DMA request after last transfer (Single-ADC mode) */ADC_DMARequestAfterLastTransferCmd(ADC1, ENABLE);/* Enable ADC1 DMA */ADC_DMACmd(ADC1, ENABLE);/* Enable ADC1 */ADC_Cmd(ADC1, ENABLE);ADC_SoftwareStartConv(ADC1);while(1) {LED_On();for (u8 i = 0; i < ARRAYSIZE; i++) {float a = (*(ADCConvertedValue + i) - 2048) * 512 /2048;printf("% 5d ", (int)a);}printf("\r\n");LED_Off();Systick_Delay_ms(200);}
}

参考

  • 这篇写得很详细 https://deepbluembedded.com/stm32-adc-tutorial-complete-guide-with-examples/
  • 可能有用的代码 https://github.com/Itachihi/stm32-adc/blob/master/readvb.c
  • 可能有用的代码 ContinuousConvMode https://github.com/mnectnky/STM32F103-One-ADC-MultiChannel-Analog-Read/blob/main/main.c
  • 可能有用的代码 ContinuousConvMode https://github.com/klesogor/ADC/blob/master/ADC.c
  • 有用的代码 DMA, adc部分在main.c https://github.com/daedaleanai/stm32f103_adc2serial/blob/main/main.c
  • 有用的代码 DMA, adc部分在main.c https://github.com/MrLuuuu/STM32F103ZET6_UartPrint/blob/master/USER/main.c
  • 用stm32f103做的电容电感测试仪, 注入型, adc部分在main.c https://github.com/MilanGb/STM32F103-LC-R-meter/blob/main/main.c
  • https://community.st.com/s/question/0D50X00009XkZ4F/adc-with-multiple-channels-settings
  • This one is helpful https://embedds.com/multichannel-adc-using-dma-on-stm32/
  • 这篇 https://blog.csdn.net/weixin_45456099/article/details/110669752
  • 直接中断输出到串口 https://www.it610.com/article/1296571998083817472.htm
  • STM32F2/F4/F7 DMA参考 https://www.st.com/resource/en/application_note/dm00046011-using-the-stm32f2-stm32f4-and-stm32f7-series-dma-controller-stmicroelectronics.pdf

STM32F103和STM32F401的ADC多通道采集DMA输出相关推荐

  1. STM32F030 使用CUBEMX配置ADC多通道采集 DMA传输

    STM32CubeMX的配置 以下为测试代码 //usart重定向,注意使用前需要包含头文件<stdio.h> int fputc(int ch, FILE *f) {HAL_UART_T ...

  2. dma和通道的区别_STM32 定时器触发 ADC 多通道采集,DMA搬运至内存

    引言 ADC 的功能是将模拟信号采样得到数字信号,而有些时候,我们需要使用到定时采样,比如在计算一个采集的波形的频率的时候,我们需要精确的知道采样频率,也就是 1 s 内采集的点数,这个时候,就需要使 ...

  3. stm32cubeide 多通道adc_STM32L0开发——ADC多通道采集,IDE和IAR开发注意事项

    keil开发L0系列是免费的,官方提供许可的.因此建议Keil开发, L011F3由于flash只有8K,因此不建议HAL库,建议使用cubemx+LL(或snippets库). 0.起初,可以参考官 ...

  4. STM32 ADC多通道采集

    ADC多通道采集(阻塞模式.ADC_DMA模式) ADC多通道采集(阻塞模式) ADC多通道采集(DMA模式) ADC多通道采集(阻塞模式) 1.时钟源配置 2.配置RCC时钟(选择第三个选项 Cry ...

  5. 记录基于MXCube,使用STM32F103RCT6 ADC多通道采集 的学习经验

    记录基于MXCube,使用STM32F103RCT6 ADC多通道采集 推荐保存一下参考博客(https://www.cnblogs.com/xingboy/p/10212308.html) ADC转 ...

  6. STM32f103的数电采集电路的ADC多通道采集程序

    STM32拥有1~3个ADC(STM32F101/102 系列只有1个ADC),这些ADC可以独立使用,也可以使用双重模式(提高采样率).STM32 的ADC是12位逐次逼近型的模拟数字转换器.它有1 ...

  7. STM32L0开发——ADC多通道采集,IDE和IAR开发注意事项

    keil开发L0系列是免费的,官方提供许可的.因此建议Keil开发, L011F3由于flash只有8K,因此不建议HAL库,建议使用cubemx+LL(或snippets库). 0.起初,可以参考官 ...

  8. STM32----ADC多通道采集

    adc多通道采集数据 之前对于单通道的采集,已经做出了配置的相关解释. 在单通道采集中存储采集的值为单个变量,对于多通道而言需要使用数组来存储每个通道的转换数据. 具体的adc配置文件如下: void ...

  9. STM32自学笔记ADC多通道扫描

    STM cubemx STM32F1 ADC多通道采集(非DMA) ADC模式介绍: 扫描模式: 多通道采集必须开启,这一项CUBE已经默认设置好了.这个模式就是自动扫描你开启的所有通道进行转换,直至 ...

  10. 第 30 章 ADC—电压采集

    30.1 ADC 简介 30.2 ADC 功能框图剖析 2.1 电压输入范围 2.2 输入通道 1.规则通道 2.注入通道 2.3 转换顺序 1.规则序列 2.注入序列 2.4 触发源 2.5 转换时 ...

最新文章

  1. 【带你重拾Redis】Redis数据结构及使用场景
  2. android doGet和doPost
  3. python3 下的文件输入输出特性以及如何覆盖文件内容和接下去输入
  4. 使用SeaJS实现模块化JavaScript开发【转】
  5. 2020年高等数学方法与提高(上海理工大学)学习笔记:向量代数与空间解析几何
  6. .NET Core 在 K8S 上的开发实践--学习笔记
  7. vue 无法进入response拦截器_vue拦截器的一次实践
  8. 关于布隆过滤器的所有信息:利用Hash实现的索引方案
  9. href=“javascript:void(0);”和href=void(change_code(this));
  10. python udp 直播_Python使用UDP协议实现局域网内屏幕广播
  11. 刘意-Java基础视频(基础部分)笔记(一)
  12. 怎么下载安装Firebug和使用Firebug
  13. pcm5102a解码芯片音质评测_鱼和熊掌兼得——一台可以换芯片的PCM1794解码评测(上)...
  14. 区块链赋能金融科技!2018 PPP全球数字资产投资峰会在京召开
  15. android逆向学习路线
  16. js中关于字符串操作的命令
  17. 红花玉兰种植要求与养护要点
  18. springBoot配置多个过滤器filter的执行顺序
  19. 码农小汪-Volatile和Transient
  20. 新西兰计算机预科学费,2018年新西兰ACG预科课程介绍(含学费、开学时间)

热门文章

  1. python抓取网站数据并图形化显示(二)
  2. iOS方形图片裁剪成圆形
  3. isilon 时间设置
  4. Isilon旧机器重新初始化
  5. 形容等待时间长的句子_形容等待的句子
  6. 12x12怎么速算_12x12怎样巧算
  7. 电阻式湿度传感器原理
  8. 查看php是否支持sg11,云虚拟主机支持SG11扩展
  9. Python3-word文档操作(七):提取word文档中的图片方式一-利用word文档的压缩文件属性
  10. 服务器虚拟化三种架构,X86虚拟化之三种服务器虚拟化战略架构