前提:本人使用STM32F103标准库学习,其他芯片可以参考其原理
各部分配置我会进行详细讲解,一些很基础简单的如开启时钟我就会略过
一、ADC配置
(1).GPIO配置
1.选择ADC引脚
查找芯片手册:这里我选择ADC1_IN10 即PC0 和 ADC1_IN11 即PC1
2.GPIO初始化配置
标准库代码:
GPIO_InitTypeDef GPIO_InitStructure; //GPIO_InitStructure用于存放GPIO的参数
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC , ENABLE); //使能GPIOC的时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; //设置引脚
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //设置输入类型
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //设置GPIO的输出速度
GPIO_Init(GPIOC, &GPIO_InitStructure); //根据参数初始化GPIO
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; //设置引脚
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //设置输入类型
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //设置GPIO的输出速度
GPIO_Init(GPIOC, &GPIO_InitStructure); //根据参数初始化GPIO
详细讲解:
(2).ADC初始化配置
1. ADC初始化配置流程:
1.1 配置ADC时钟(选择分频系数);
1.2 开启时钟
1.3 选择ADC的模式
1.4 是否开启扫描模式
1.5 是否开启连续转换模式
1.6 ADC外部触发的选择
1.7 选择ADC数据的对齐方式
1.8 选择ADC的通道数
1.9 初始化ADC
1.10 配置规则组通道的参数
1.11 使能ADC,允许外部触发ADC
1.12 进行ADC的复位校准过程
标准库代码:
ADC_InitTypeDef ADC_InitStructure; //ADC_InitStructure用于存放ADC的参数
RCC_ADCCLKConfig(RCC_PCLK2_Div6); //设置ADC时钟分频,ADCCLK=PCLK2/6=12MHz
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 , ENABLE); //使能ADC1的时钟
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;//设置独立转换模式
ADC_InitStructure.ADC_ScanConvMode = ENABLE; //使能扫描模式
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; //禁止连续转换模式
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T3_TRGO; //配置T3触发
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfChannel = 2;
ADC_Init(ADC1, &ADC_InitStructure);
ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 1, ADC_SampleTime_239Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_Channel_11, 2, ADC_SampleTime_239Cycles5);
ADC_Cmd(ADC1, ENABLE); //使能ADC1
ADC_ExternalTrigConvCmd(ADC1, ENABLE); //使用外部事件启动ADC转换
//ADC校验
ADC_ResetCalibration(ADC1); //启动ADC复位校准,即将RSTCAL赋值为1
while(ADC_GetResetCalibrationStatus(ADC1)); //读取并判断RSTCAL,RSTCAL为0跳出while语句
ADC_StartCalibration(ADC1); //启动ADC校准,即将CAL赋值为1
while(ADC_GetCalibrationStatus(ADC1)); //读取并判断CAL,CAL为0跳出while语句
2.详细讲解:
2.1设置ADC的时钟频率(选择分频系数)
根据参考手册可知,ADC输入频率不得超过14MHz:
据参考手册的时钟树:这里我配置的APB2位72Mhz,所以这里最少选择6分频,ADC时钟为12Mhz
2.2 选择ADC模式
参考手册:
标准库对应原码:
#define ADC_Mode_Independent ((uint32_t)0x00000000)
#define ADC_Mode_RegInjecSimult ((uint32_t)0x00010000)
#define ADC_Mode_RegSimult_AlterTrig ((uint32_t)0x00020000)
#define ADC_Mode_InjecSimult_FastInterl ((uint32_t)0x00030000)
#define ADC_Mode_InjecSimult_SlowInterl ((uint32_t)0x00040000)
#define ADC_Mode_InjecSimult ((uint32_t)0x00050000)
#define ADC_Mode_RegSimult ((uint32_t)0x00060000)
#define ADC_Mode_FastInterl ((uint32_t)0x00070000)
#define ADC_Mode_SlowInterl ((uint32_t)0x00080000)
#define ADC_Mode_AlterTrig ((uint32_t)0x00090000)
2.3 扫描模式和连续转换模式
参考手册介绍:
补充:CONT位 即寄存器的连续转换设置位:0:单次转换模式,1:连续转换模式
理解:
开启多ADC通道:开启扫描模式,自动依此扫描规则通道的ADC。
开启连续扫描:结束一轮后,将从头开始,开始新一轮扫描,不会停止。
开启DMA转态下: 在每个通道结束后,DMA将转运数据到指定地址。
2.4ADC外部触发选择
参考手册介绍:
标准库对应原码:
#define ADC_ExternalTrigConv_T1_CC1 ((uint32_t)0x00000000) /*!< For ADC1 and ADC2 */
#define ADC_ExternalTrigConv_T1_CC2 ((uint32_t)0x00020000) /*!< For ADC1 and ADC2 */
#define ADC_ExternalTrigConv_T2_CC2 ((uint32_t)0x00060000) /*!< For ADC1 and ADC2 */
#define ADC_ExternalTrigConv_T3_TRGO ((uint32_t)0x00080000) /*!< For ADC1 and ADC2 */
#define ADC_ExternalTrigConv_T4_CC4 ((uint32_t)0x000A0000) /*!< For ADC1 and ADC2 */
#define ADC_ExternalTrigConv_Ext_IT11_TIM8_TRGO ((uint32_t)0x000C0000) /*!< For ADC1 and ADC2 */
#define ADC_ExternalTrigConv_T1_CC3 ((uint32_t)0x00040000) /*!< For ADC1, ADC2 and ADC3 */
#define ADC_ExternalTrigConv_None ((uint32_t)0x000E0000) /*!< For ADC1, ADC2 and ADC3 */
#define ADC_ExternalTrigConv_T3_CC1 ((uint32_t)0x00000000) /*!< For ADC3 only */
#define ADC_ExternalTrigConv_T2_CC3 ((uint32_t)0x00020000) /*!< For ADC3 only */
#define ADC_ExternalTrigConv_T8_CC1 ((uint32_t)0x00060000) /*!< For ADC3 only */
#define ADC_ExternalTrigConv_T8_TRGO ((uint32_t)0x00080000) /*!< For ADC3 only */
#define ADC_ExternalTrigConv_T5_CC1 ((uint32_t)0x000A0000) /*!< For ADC3 only */
#define ADC_ExternalTrigConv_T5_CC3 ((uint32_t)0x000C0000) /*!< For ADC3 only */
这里我会这篇主要涉及定时器触发的使用,稍微提一下软件触发模式
要是使用软件触发选择ADC_ExternalTrigConv_None,
然后用标准库的 ADC_SoftwareStartConvCmd(ADC1, ENABLE); 开启和关闭转换
2.5 ADC数据对齐方式
这里转换数据将存在16位的寄存器中,可以选择左对齐和右对齐
#define ADC_DataAlign_Right ((uint32_t)0x00000000)
#define ADC_DataAlign_Left ((uint32_t)0x00000800)
2.6 ADC的通道选择和配置
注意:这里有规则组和注入组概念
规则组通道大小1-16逐个扫描,注入组优先级高于规则组,这里我主要用规则组。
首选选择要开启规则通道的数目
ADC_InitStructure.ADC_NbrOfChannel = 2;
然后配置通道的参数 :
括号内依此是: (选择ADC1/2 ,选择ADC的哪个通道,第几个(转换顺序),ADC采样时间)
ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 1, ADC_SampleTime_239Cycles5);
这里我们开始已经通过查芯片手册选择了:ADC1的通道10和通道11。
关于ADC的采样时间:
采样时间越长,数据精准度越好
最后就是校准复位操作,按照上述代码就行。
二、定时器触发配置
(1).定时器触发初始化配置
标准库代码:
static void ConfigTimer3(u16 arr, u16 psc)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; //TIM_TimeBaseStructure用于存放TIM3的参数
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //使能TIM3的时钟
TIM_DeInit(TIM3);
TIM_TimeBaseStructure.TIM_Period = arr; //设置自动重装载值
TIM_TimeBaseStructure.TIM_Prescaler = psc; //设置预分频器值
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:tDTS = tCK_INT
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //设置向上计数模式
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据参数初始化定时器
TIM_SelectOutputTrigger(TIM3,TIM_TRGOSource_Update); //选择更新事件为触发输入
TIM_Cmd(TIM3, ENABLE); //使能定时器
}
这里配置的原理和上一篇
DAC理解(DAC定时器触发DMA转运学习总结)-CSDN博客
这里就不赘述;
三、DMA配置
(1).DMA通道选择,查找对应的要搬运的数据地址
1.DMA通道选择,通过查找参考手册,可看见DMA的每个通道的作用,这里选择DMA1_CH1
即选择ADC1。
(2).DMA初始化配置
标准库代码:
static void ConfigDMA1Ch1(u16* s_arrADC1Data)
{
DMA_InitTypeDef DMA_InitStructure; //DMA_InitStructure用于存放DMA的参数
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //使能DMA1的时钟
DMA_DeInit(DMA1_Channel1); //将DMA1_CH1寄存器设置为默认值
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR; //设置外设地址
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)s_arrADC1Data; //设置存储器地址
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //设置为外设到存储器模式
DMA_InitStructure.DMA_BufferSize = 2; //设置要传输的数据项数目
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_Medium; //设置为中等优先级
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //禁止存储器到存储器访问
DMA_Init(DMA1_Channel1, &DMA_InitStructure); //根据参数初始化DMA1_Channel1
DMA_Cmd(DMA1_Channel1, ENABLE); //使能DMA1_Channel1
}
四、联合使用
接下来就是配合起来一起使用:
(1)计算ADC的转换时间:
ADC转换时间:T = [(转换周期+12.5) * 每个周期的时间] * 通道个数
例:
T = [(239.5+12.5) * 1/12Mhz] * 2 = 42us;
(2)ADC整体初始化配置代码:
/*
ADC配置
定时器触发,1ms一次
ADC转换时间:(239.5+12.5)/12MHZ = 21us, 两次42us,时间足够
*/
void InitADC(void)
{
ConfigDMA1Ch1(ADC_Data); //DMA初始化
ConfigADC1(); //ADC初始化
ConfigTimer3(100, 720); //定时器触发配置,1ms触发一次,1ms >> 42us 可以转换完成
}
贴一下完整工程:
ADC.c
#include "ADC.h"
u16 ADC_Data[2]={};
/*
ADC配置
定时器触发,1ms一次
ADC转换时间:(239.5+12.5)/12MHZ = 21us, 两次42us,时间足够
*/
void InitADC(void)
{
ConfigDMA1Ch1(ADC_Data);
ConfigADC1();
ConfigTimer3(100, 720);
}
/*
配置ADC函数
模式: 独立转换模式,扫描模式,禁止连续转换,使用定时器触发转换
通道配置: PC0--ADC1_IN10 , PC1--ADC2_IN11
*/
static void ConfigADC1(void)
{
GPIO_InitTypeDef GPIO_InitStructure; //GPIO_InitStructure用于存放GPIO的参数
ADC_InitTypeDef ADC_InitStructure; //ADC_InitStructure用于存放ADC的参数
RCC_ADCCLKConfig(RCC_PCLK2_Div6); //设置ADC时钟分频,ADCCLK=PCLK2/6=12MHz
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 , ENABLE); //使能ADC1的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC , ENABLE); //使能GPIOC的时钟
// PC1配置 ADC1_IN10
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; //设置引脚
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //设置输入类型
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //设置GPIO的输出速度
GPIO_Init(GPIOC, &GPIO_InitStructure); //根据参数初始化GPIO
// PC1配置 ADC2_IN11
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; //设置引脚
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //设置输入类型
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //设置GPIO的输出速度
GPIO_Init(GPIOC, &GPIO_InitStructure); //根据参数初始化GPIO
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;//设置独立转换模式
ADC_InitStructure.ADC_ScanConvMode = ENABLE; //使能扫描模式
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; //禁止连续转换模式
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T3_TRGO; //配置T3触发
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfChannel = 2;
ADC_Init(ADC1, &ADC_InitStructure);
ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 1, ADC_SampleTime_239Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_Channel_11, 2, ADC_SampleTime_239Cycles5);
//使能ADC1的DMA
ADC_Cmd(ADC1, ENABLE); //使能ADC1
ADC_ExternalTrigConvCmd(ADC1, ENABLE); //使用外部事件启动ADC转换
ADC_DMACmd(ADC1, ENABLE);
//ADC校验
ADC_ResetCalibration(ADC1); //启动ADC复位校准,即将RSTCAL赋值为1
while(ADC_GetResetCalibrationStatus(ADC1)); //读取并判断RSTCAL,RSTCAL为0跳出while语句
ADC_StartCalibration(ADC1); //启动ADC校准,即将CAL赋值为1
while(ADC_GetCalibrationStatus(ADC1)); //读取并判断CAL,CAL为0跳出while语句
}
/*
配置DMA函数
通道配置:
通道数: 2
通道缓冲区:ADC_Data
*/
static void ConfigDMA1Ch1(u16* s_arrADC1Data)
{
DMA_InitTypeDef DMA_InitStructure; //DMA_InitStructure用于存放DMA的参数
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //使能DMA1的时钟
DMA_DeInit(DMA1_Channel1); //将DMA1_CH1寄存器设置为默认值
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR; //设置外设地址
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)s_arrADC1Data; //设置存储器地址
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //设置为外设到存储器模式
DMA_InitStructure.DMA_BufferSize = 2; //设置要传输的数据项数目
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_Medium; //设置为中等优先级
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //禁止存储器到存储器访问
DMA_Init(DMA1_Channel1, &DMA_InitStructure); //根据参数初始化DMA1_Channel1
DMA_Cmd(DMA1_Channel1, ENABLE); //使能DMA1_Channel1
}
/*
TIM3配置
配置更新事件触发,不中断
*/
static void ConfigTimer3(u16 arr, u16 psc)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; //TIM_TimeBaseStructure用于存放TIM3的参数
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //使能TIM3的时钟
TIM_TimeBaseStructure.TIM_Period = arr; //设置自动重装载值
TIM_TimeBaseStructure.TIM_Prescaler = psc; //设置预分频器值
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:tDTS = tCK_INT
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //设置向上计数模式
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据参数初始化定时器
TIM_SelectOutputTrigger(TIM3,TIM_TRGOSource_Update); //选择更新事件为触发输入
TIM_Cmd(TIM3, ENABLE); //使能定时器
}
ADC.h
#ifndef _ADC_H_
#define _ADC_H_
#include "stm32f10x_conf.h"
extern u16 ADC_Data[2];
void InitADC(void); //初始化ADC模块
static void ConfigADC1(void);
static void ConfigDMA1Ch1(u16* s_arrADC1Data);
static void ConfigTimer3(u16 arr, u16 psc);
#endif