一、ADC介绍
12位ADC是一种逐次逼近型模拟数字转换器。它有多达18个通道,可测量16个外部和2个内部信号源。各通道的A/D转换可以单次、连续、扫描或间断模式执行。ADC的结果可以左对齐或右对齐方式存储在16位数据寄存器中。
二、框图分析
按照顺序分析:
1、电压输入范围:ADC 输入范围为:VREF- ≤ VIN ≤ VREF+。由 VREF-、VREF+ 、VDDA、 VSSA、这四个外部引脚决定。一般把 VSSA 和 VREF-接地,把 VREF+和 VDDA 接 3V3,得到ADC 的输入电压范围为:0~3.3V。
2、输入通道:可分为注入通道和规则通道 前者常用。关于输入通道查询,可以参考《STM32F103xCDE_数据手册》
下面是总结好的表格:F103x 系列 对应的引脚几乎完全一样 下面是ZET6的
3、 转换顺序:
规则序列 :
通过控制ADC规则序列寄存器 ADC_SQR1 、ADC_SQR2、ADC_SQR3 来配置规则通道的转换顺序,
如果通道 16 想第一次转换,那么在 SQ1[4:0] 写 16 即可。SQR2 控制着规则序列中的第 7 到第12 个转换,对应的位为:SQ7[4:0]~SQ12[4:0],如果通道 1 想第 8 个转换,则 SQ8[4:0] 写 1 即可。SQR1 控制着规则序列中的第 13 到第 16 个转换,对应位为:SQ13[4:0]~SQ16[4:0],如果通道 6 想第 10 个转换,则 SQ10[4:0] 写 6 即可。具体使用多少个通道,由 SQR1 的位L[3:0]决定,最多 16 个通道。
注入序列:
ADC注入序列寄存器ADC_JSQR 。注入序列寄存器 JSQR 只有一个,最多支持 4 个通道,具体多少个由 JSQR 的 JL[2:0]决定。如果 JL 的 值小于 4 的话,则 JSQR 跟 SQR 决定转换顺序的设置不一样,第一次转换的不是 JSQR1[4:0],而是 JCQRx[4:0] ,x = (4-JL),跟 SQR 刚好相反。如果 JL=00(1个转换),那么转换的顺序是从 JSQR4[4:0]开始,而不是从 JSQR1[4:0]开始,这个要注意,编程的时候不要搞错。当 JL 等于 4 时,跟 SQR 一样。
4、触发源选择
通道配置完成,转换顺序也已经确定,下面就是触发源的选择。通过ADC控制寄存器 1(ADC_CR1)、ADC控制寄存器 2(ADC_CR2),选择触发源:
5、转换时间
ADC 时钟:
ADC 输入时钟 ADC_CLK 由 PCLK2 经过分频产生,最大是 14M,分频因子由 RCC 时钟配置寄存器 RCC_CFGR 的位 15:14 ADCPRE[1:0]设置,可以是 2/4/6/8 分频,一般我们设置 PCLK2=HCLK=72M。一般选择8分频。
采样时间:
采样的周期数可通过 ADC 采样时间寄存器 ADC_SMPR1 和 ADC_SMPR2 中的SMP[2:0]位设置,ADC_SMPR2 控制的是通道 0~9,ADC_SMPR1 控制的是通道 10~17。每个通道可以分别用不同的时间采样。其中采样周期最小是 1.5 个,即如果我们要达到最快的采样,那么应该设置采样周期为 1.5个周期,这里说的周期就是 1/ADC_CLK。
ADC 的转换时间跟 ADC 的输入时钟和采样时间有关公式为:Tconv = 采样时间 + 12.5 个周期。当 ADCLK = 14MHZ (最高),采样时间设置为 1.5 周期(最快),那么总
的转换时间(最短)Tconv = 1.5 周期 + 12.5 周期 = 14 周期 = 1us。一般我们设置 PCLK2=72M,经过 ADC 预分频器能分频到最大的时钟只能是 12M,采样周期设置为 1.5 个周期,算出最短的转换时间为 1.17us(常用1.17us)。
6、数据寄存器:一切准备就绪后,ADC 转换后的数据根据转换组的不同,规则组的数据放在 ADC_DR寄存器,注入组的数据放在 JDRx。
规则数据寄存器 :
注入数据寄存器:
7、中断:
转换结束中断:
数据转换结束后,可以产生中断,中断分为三种:规则通道转换结束中断,注入转换通道转换结束中断,模拟看门狗中断。可以根据相应的中断标志位,编写对应的中断服务函数。事件是否发生,可以访问ADC状态寄存器(ADC_SR)
模拟看门狗中断:
当被 ADC 转换的模拟电压低于低阈值或者高于高阈值时,就会产生中断,前提是我们开启了模拟看门狗中断,其中低阈值和高阈值由 ADC_LTR 和 ADC_HTR 设置。例如我们设置高阈值是 2.5V,那么模拟电压超过 2.5V 的时候,就会产生模拟看门狗中断,反之低阈值也一样。
三、编程相关
在编程的时候,我们只需要配置对用的结构体即可:
ADC_InitTypeDef 结构体
typedef struct
{
uint32_t ADC_Mode; // ADC 工作模式选择
FunctionalState ADC_ScanConvMode; /* ADC 扫描(多通道) 或者单次(单通道)模式选择 */
FunctionalState ADC_ContinuousConvMode; // ADC 单次转换或者连续转换选择
uint32_t ADC_ExternalTrigConv; // ADC 转换触发信号选择
uint32_t ADC_DataAlign; // ADC 数据寄存器对齐格式
uint8_t ADC_NbrOfChannel; // ADC 采集通道数
}ADC_InitTypeDef;
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
ADC_Mode:配置 ADC 的模式,当使用一个 ADC 时是独立模式,使用两个 ADC 时
是双模式,在双模式下还有很多细分模式可选。
ScanConvMode:可选参数为 ENABLE 和 DISABLE,配置是否使用扫描。如果是单通
道 AD 转 换 使 用 DISABLE , 如 果 是 多 通 道 AD 转 换 使 用 ENABLE 。
ADC_ContinuousConvMode:可选参数为 ENABLE 和 DISABLE,配置是启动自动连
续转换还是单次转换。使用 ENABLE 配置为使能自动连续转换;使用 DISABLE 配置为单
次转换,转换一次后停止需要手动控制才重新启动转换。
ADC_ExternalTrigConv:外部触发选择,框图中中列举了很多外部触发条件,可根据
项目需求配置触发来源。一般使用软件自动触发。
ADC_DataAlign:转换结果数据对齐模式,可选右对齐 ADC_DataAlign_Right 或者左
对齐 ADC_DataAlign_Left。一般选择右对齐模式。不然读取数据 还得处理。
ADC_NbrOfChannel:AD 转换通道数目,根据实际设置即可。具体的通道数和通道的
转换顺序是配置规则序列或注入序列寄存器
实例一:《中断单通道读取ADC》
编程要点
- 初始 ADC 用到的 GPIO;
- 设置 ADC 的工作参数并初始化;
- 设置 ADC 工作时钟;
- 设置 ADC 转换通道顺序及采样时间;
- 配置使能 ADC 转换完成中断,在中断内读取转换完数据;
- 使能 ADC;
- 使能软件触发 ADC 转换。
ADC 转换结果数据使用中断方式读取,这里没有使用 DMA 进行数据传输。
1.初始 ADC 用到的 GPIO
void ADCx_GPIO_Config(void)
{ //以PA1为例
GPIO_InitTypeDef GPIO_InitStruct;
//开启GPIO时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
//选择模拟模式
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AIN;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_1; GPIO_Init(ADCx_GPIO_PORT,&GPIO_InitStruct);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
2.配置ADC
void ADCx_Config(void)
{ //ADC1 Channel_1
ADC_InitTypeDef ADC_InitStruct;
//开启ADC时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);
ADC_InitStruct.ADC_Mode = ADC_Mode_Independent; // 只使用一个ADC,属于单模式
ADC_InitStruct.ADC_ScanConvMode = DISABLE; //只有一个通道,关闭扫描模式
ADC_InitStruct.ADC_ContinuousConvMode = ENABLE; // 连续转换模式
ADC_InitStruct.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;// 不用外部触发转换,软件启即可
ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right; // 转换结果右对齐
ADC_InitStruct.ADC_NbrOfChannel = 1; // 转换通道个数
//初始化
ADC_Init(ADC1,&ADC_InitStruct); //配置ADC时钟N狿CLK2的8分频,即9MHz
RCC_ADCCLKConfig(RCC_PCLK2_Div8); // 配置ADC 通道的转换顺序和采样时间
ADC_RegularChannelConfig(ADC1,ADC_1_Channel,1,ADC_SampleTime_55Cycles5); // ADC 转换结束产生中断,在中断服务程序中读取转换值
ADC_ITConfig(ADC1, ADC_IT_EOC, ENABLE);
// 开启ADC ,并开始转换
ADC_Cmd(ADC1,ENABLE); ADC_ResetCalibration(ADC1);
// 等待校准寄存器初始化完成
while(ADC_GetResetCalibrationStatus(ADC1));
// ADC开始校准
ADC_StartCalibration(ADC1);
// 等待校准完成
while(ADC_GetCalibrationStatus(ADC1)); // 由于没有采用外部触发,所以使用软件触发ADC转换
ADC_SoftwareStartConvCmd(ADC1,ENABLE);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
3.配置NVIC
void ADC_NVIC_Config(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
// 优先级分组
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); // 配置中断优先级
NVIC_InitStructure.NVIC_IRQChannel = ADC1_2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
4.编写中断服务函数
void ADC1_2_IRQHandler(void)
{ uint8_t i = 0;
if (ADC_GetITStatus(ADC_x,ADC_IT_EOC)==SET)
{ //读取ADC的转换值 ADC_Convert_Value =(float)ADC_GetConversionValue(ADC_x)/4096*3.3;
ADC_ClearITPendingBit(ADC_x,ADC_IT_EOC); //清除转换完成标志位,等待下一次
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
5.测试函数
uint16_t ADC_USART_Value = 0;
uint16_t ADC_USART_Value2 = 0;
void _ADC_IT_Init(void)
{
ADCx_GPIO_Config();
ADCx_Config();
ADC_NVIC_Config();
}
void delay(uint32_t count)
{
while(count--);
}
int main(void)
{
LED_GPIO_Config();
USARTx_Config();
ADC_IT_Init();
printf("\r\n ----这是一个ADC多通道采集实验----\r\n"); while(1)
{
ADC_USART_Value 2=(float) ADC_USART_Value ; printf("\r\n The current AD value = 0x%04X \r\n", ADC_USART_Value; printf("\r\n The current AD value = %f V \r\n",ADC_USART_Value2); printf("\r\n\r\n");
delay(0x3fffff); } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
实例二:《DMA多通道读取ADC》
编程要点
- 初始 ADC 用到的 GPIO;
- 设置 ADC 的工作参数并初始化;
- 设置 ADC 工作时钟;
- 设置 ADC 转换通道顺序及采样时间;
- 配置使能 ADC 转换完成中断,在中断内读取转换完数据;
- 使能 ADC;
- 使能软件触发 ADC 转换。
这里使用 DMA 进行数据传输,还需配置DMA。
有一点需要注意 仅 ADC1 和 ADC3 有DMA传输功能。分别对应
DMA1 的通道 1
DMA2 的通道 5
1.初始 ADC 用到的 GPIO
void ADCx_GPIO_Config(void)
{ GPIO_InitTypeDef GPIO_InitStruct; RCC_APB2PeriphClockCmd(ADCx_GPIO_CLK,ENABLE); GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AIN;
GPIO_InitStruct.GPIO_Pin = ADC_1_Pin| ADC_1_Pin| //ADC_2_Pin和SUART冲突 因此删除 ADC_3_Pin| ADC_4_Pin| ADC_5_Pin| ADC_6_Pin| ADC_7_Pin;
GPIO_Init(ADCx_GPIO_PORT,&GPIO_InitStruct);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
2.初始 DMA
void ADC_DMA_Config(void)
{
DMA_InitTypeDef DMA_InitStruct; RCC_AHBPeriphClockCmd(DMA_CLK,ENABLE); //外设基址为:ADC 数据寄存器地址
DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)(&(ADC_x->DR)); // 存储器地址
DMA_InitStruct.DMA_MemoryBaseAddr = (uint32_t)ADC_USART_Value; // 数据源来自外设
DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralSRC; //缓冲区大小,应该等于数据目的地的大小
DMA_InitStruct.DMA_BufferSize = BUFFERSIZE; // 外设寄存器只有一个,地址不用递增
DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //存储器地址递增
DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable; //外设数据大小为半字,即两个字节
DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; //内存数据大小也为半字,跟外设数据大小相同
DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; //循环传输模式
DMA_InitStruct.DMA_Mode = DMA_Mode_Circular; //DMA传输通道优先级为高,当使用一个DMA通道时,优先级设置不影响
DMA_InitStruct.DMA_Priority = DMA_Priority_High; //禁止存储器到存储器模式,因为是从外设到存储器
DMA_InitStruct.DMA_M2M = DMA_M2M_Disable; DMA_Init(DMA_CHANNEL,&DMA_InitStruct); DMA_Cmd(DMA_CHANNEL,ENABLE);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
3.初始 ADC
void ADCx_Config(void)
{
ADC_InitTypeDef ADC_InitStruct;
//开启ADC时钟
RCC_APB2PeriphClockCmd(ADCx_CLK,ENABLE);
ADC_InitStruct.ADC_Mode = ADC_Mode_Independent; // 只使用一个ADC,属于单模式
ADC_InitStruct.ADC_ScanConvMode = ENABLE; //6通道开启扫描
ADC_InitStruct.ADC_ContinuousConvMode = ENABLE;// 连续转换模式
ADC_InitStruct.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;// 不用外部触发转换,软件开启即可
ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right; // 转换结果右对齐
ADC_InitStruct.ADC_NbrOfChannel = ADC_num; // 转换通道个数
//初始化
ADC_Init(ADC_x,&ADC_InitStruct); //配置ADC时钟N狿CLK2的8分频,即9MHz
RCC_ADCCLKConfig(RCC_PCLK2_Div8); // 配置ADC 通道的转换顺序和采样时间
ADC_RegularChannelConfig(ADC_x,ADC_1_Channel,1,ADC_SampleTime_55Cycles5);
ADC_RegularChannelConfig(ADC_x,ADC_4_Channel,2,ADC_SampleTime_55Cycles5); //A2 A3用于串口
ADC_RegularChannelConfig(ADC_x,ADC_5_Channel,3,ADC_SampleTime_55Cycles5);
ADC_RegularChannelConfig(ADC_x,ADC_6_Channel,4,ADC_SampleTime_55Cycles5);
ADC_RegularChannelConfig(ADC_x,ADC_7_Channel,5,ADC_SampleTime_55Cycles5);
// 使能ADC DMA 请求
ADC_DMACmd(ADC_x, ENABLE); // 开启ADC ,并开始转换
ADC_Cmd(ADC_x,ENABLE); ADC_ResetCalibration(ADC_x);
// 等待校准寄存器初始化完成
while(ADC_GetResetCalibrationStatus(ADC_x));
// ADC开始校准
ADC_StartCalibration(ADC_x);
// 等待校准完成
while(ADC_GetCalibrationStatus(ADC_x)); // 由于没有采用外部触发,所以使用软件触发ADC转换
ADC_SoftwareStartConvCmd(ADC_x,ENABLE);
}
void ADCx_DMA_Init(void)
{
ADCx_GPIO_Config();
ADC_DMA_Config();
ADCx_Config();
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
4.测试函数
void delay(uint32_t count)
{
while(count--);
}
int main(void)
{
LED_GPIO_Config();
USARTx_Config(); ADCx_DMA_Init(); //ADC_IT_Init(); printf("\r\n ----这是一个ADC多通道采集实验----\r\n"); while(1)
{ printf("\r PA1 value = %f V \r\n",(float)ADC_USART_Value[0]/4096*3.3); printf("\r PA4 value = %f V \r\n",(float)ADC_USART_Value[1]/4096*3.3); printf("\r PA5 value = %f V \r\n",(float)ADC_USART_Value[2]/4096*3.3); printf("\r PA6 value = %f V \r\n",(float)ADC_USART_Value[3]/4096*3.3); printf("\r PA7 value = %f V \r\n\n\n\n\n",(float)ADC_USART_Value[4]/4096*3.3); delay(0x3fffff); } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
头文件
#include "bsp_dma.h"
#ifndef _BSP_DMA_H_
#define _BSP_DMA_H_
#include "stm32f10x.h"
#define BUFFERSIZE 5
#define DMA_CHANNEL DMA1_Channel1 //手册
#define DMA_CLK RCC_AHBPeriph_DMA1
void ADC_DMA_Config(void);
#endif /*_BSP_DMA_H_*/
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
#include "bsp_adc.h"
#ifndef _BSP_ADC_H_
#define _BSP_ADC_H_
#include "stm32f10x.h"
#define ADC_num 5
#define ADC_x ADC1
#define ADCx_CLK RCC_APB2Periph_ADC1
#define ADCx_GPIO_PORT GPIOA
#define ADCx_GPIO_CLK RCC_APB2Periph_GPIOA
#define ADC_1_Pin GPIO_Pin_1
#define ADC_2_Pin GPIO_Pin_2
#define ADC_3_Pin GPIO_Pin_3
#define ADC_4_Pin GPIO_Pin_4
#define ADC_5_Pin GPIO_Pin_5
#define ADC_6_Pin GPIO_Pin_6
#define ADC_7_Pin GPIO_Pin_7
#define ADC_1_Channel ADC_Channel_1
#define ADC_2_Channel ADC_Channel_2
#define ADC_3_Channel ADC_Channel_3
#define ADC_4_Channel ADC_Channel_4
#define ADC_5_Channel ADC_Channel_5
#define ADC_6_Channel ADC_Channel_6
#define ADC_7_Channel ADC_Channel_7
extern uint16_t ADC_USART_Value[ADC_num];
extern float ADC_Convert_Value[ADC_num];
void ADCx_GPIO_Config(void);
void ADCx_Config(void);
void ADC_DMA_Config(void);
void ADCx_DMA_Init(void);
#endif /*_BSP_ADC_H_*/
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
欢迎交流探讨,大家一起进步吧!冲冲冲!
文章来源: blog.csdn.net,作者:Successful 、,版权归原作者所有,如需转载,请联系作者。
原文链接:blog.csdn.net/qq_45689790/article/details/113862143