【学习笔记】stm32单片机ADC功能的使用及配置方法

今天继续学习笔记。
前面几天学完了 ADC 模块，于是在写完定时器后就接着把 ADC 模块也写了。

（一）ADC 是什么

在网上稍微查一下我们就知道 ADC 的定义：模数转换，即 Analog-to-Digital Converter，常称 ADC，是指将连续变量的模拟信号转换为离散的数字信号的器件，例如，通过 ADC 把温度传感器产生的电信号转化为数字信号 0101 之类的。
与 ADC 模块对应的就是 DAC 模块，DAC 模块可以把数字信号转化为模拟信号，stm32 有 ADC 模块但没有 DAC 模块。

（二）stm32 上的 ADC 功能

我们用的芯片 STM32F103C8T6 的 ADC 资源有 ADC1、ADC2，10 个外部输入通道，其中注入组有 4 个通道，其余为规则组。
注入组的四个通道互不干扰，可以同时使用和转换。
规则组则须按列表依次转换，并且只有一个储存位置，后转换的数据将会顶顶掉前一个数据的位置，需要 DMA 的配合才能完成多通道的 ADC 转换。
此外，规则组的配置还有多种模式选择，主要是是否开启连续转换和扫描模式。
连续转换就是在完成一次转换后立刻衔接下一次转换，扫描模式则是接着扫描规则组配置的列表的其他通道，若不开启则只转换一个通道。

（三）AD 单通道配置

首先我们学习如何配置单通道转换。
先看结构图

根据结构图我们知道了如何配置 AD 单通道。
第一步，我们需要开启 ADC 和 GPIO 的时钟：
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

第二步，我们需要设置 ADC 时钟，调整其预分频，因为 ADC 的最大频率是 14Hz，所以我们只能配置 6 分频往上：
RCC_ADCCLKConfig(RCC_PCLK2_Div6);

第三步，初始化 GPIO 口，配置为 ADC 模式：
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);

第四步，配置规则组通道（注入组没用过）：
ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);

第五步，ADC 初始化，其中配置了 ADC 独立模式、不连续非扫描模式，无外部触发源（软件触发），只有一个通道，数据右对齐：
ADC_InitTypeDef ADC_InitStructure;
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
ADC_InitStructure.ADC_ScanConvMode = DISABLE;
ADC_InitStructure.ADC_NbrOfChannel = 1;
ADC_Init(ADC1, &ADC_InitStructure);

第六步，启动 ADC：
ADC_Cmd(ADC1, ENABLE);

第七步，ADC 校验，这个是固定流程：
ADC_ResetCalibration(ADC1);
while (ADC_GetResetCalibrationStatus(ADC1) == SET);
ADC_StartCalibration(ADC1);
while (ADC_GetCalibrationStatus(ADC1) == SET);

自此，ADC 配置完成，然后我们可以开启转换：
ADC_SoftwareStartConvCmd(ADC1, ENABLE);

转换过程需要时间，我们可以检查标志位判断转换是否完成：
while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);

最后，我们通过获取数据：
ADC_GetConversionValue(ADC1);

（四）AD 多通道

AD 多通道就是同时开启多个通道转换，需要用到 DMA。
DMA 结构图如下：

那么如何配置 AD 多通道呢？
前三步与 AD 单通道基本一样，不过需要多开启一个 DMA 时钟并且多初始化几个引脚：
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);

RCC_ADCCLKConfig(RCC_PCLK2_Div6);

GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);

接着我们配置配置规则组通道，也要多配置几个，并且规定好顺序：
ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_55Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 3, ADC_SampleTime_55Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 4, ADC_SampleTime_55Cycles5);

接下来初始化 ADC，这次我们要开启连续扫描模式，通道数设置为 4：
ADC_InitTypeDef ADC_InitStructure;
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
ADC_InitStructure.ADC_ScanConvMode = ENABLE;
ADC_InitStructure.ADC_NbrOfChannel = 4;
ADC_Init(ADC1, &ADC_InitStructure);

然后我们初始化 DMA，这里。外设和储存器都设置为半个字大小（即 16 位），外设地址为指向寄存器 DR 的指针（前面说的储存数据的位置就是它），外设地址不自增（始终读取 DR 地址），储存器指向储存数据的数组，开启循环：
DMA_InitTypeDef DMA_InitStructure;
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)AD_Value;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_BufferSize = 4;
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;
DMA_Init(DMA1_Channel1, &DMA_InitStructure);

接着我们开启 DMA 并且使 ADC1 触发 DMA1 的信号使能，最后开启 ADC：
DMA_Cmd(DMA1_Channel1, ENABLE);
ADC_DMACmd(ADC1, ENABLE);
ADC_Cmd(ADC1, ENABLE);

还有 ADC 校验也是必不可少：
ADC_ResetCalibration(ADC1);
while (ADC_GetResetCalibrationStatus(ADC1) == SET);
ADC_StartCalibration(ADC1);
while (ADC_GetCalibrationStatus(ADC1) == SET);

最后开启转换，我们就能在储存数据的数组里得到想要的数据：
ADC_SoftwareStartConvCmd(ADC1, ENABLE);

那么 ADC 的功能就这么多，886。