34.ADC基本原理与配置
ADC基本原理与应用
参考资料
正点原子STM32FX开发板
《STM32FX开发指南》-第X章 ADC实验
STM32FXxx官方资料:
《STM32FX中文参考手册》-第X章 模数转换(ADC)
笔记基于正点原子官方视频
视频连接https://www.bilibili.com/video/BV1Wx411d7wT?p=71&spm_id_from=333.1007.top_right_bar_window_history.content.click
如有侵权,联系删除
一、ADC基本原理
1.ADC简介
Analog-to-Digital Converter的缩写。指模/数转换器或者模拟/数字转换器。是指将连续变量的模拟信号转换为离散的数字信号的器件。
典型的模拟数字转换器将模拟信号转换为表示一定比例电压值的数字信号。
2.STM32 ADC特点
- 可配置12位、10位、8位或6位分辨率
- 在转换结束、注入转换结束以及发生模拟看门狗或溢出事件时产生中断
- 单次和连续转换模式
- 用于自动将通道0转换为通道“n”的扫描模式
- 数据对齐以保持内置数据一致性
- 可独立设置各通道采样时间
- 外部触发器选项,可为规则转换和注入转换配置极性
- 不连续采样模式
- 双重/三重模式(具有2个或更多ADC的器件提供)
- 双重/三重ADC模式下可配置的 DMA数据存储
- 双重/三重交替模式下可配置的转换间延迟
- ADC转换类型(参见数据手册)
- ADC电源要求:全速运行时为2.4V到3.6 V,慢速运行时为1.8 v
- ADC输入范围:VREF-≤VIN ≤VREF+
- 规则通道转换期间可产生 DMA请求
3.STM32芯片的ADC控制器数量 和通道数量
4.STM32系列ADC外部通道和引脚对应关系
总结(针对正点原子开发板使用芯片): 144脚芯片因为带PF脚,所以多8个通道,为24个外部通道。小于144脚芯片只有16个外部通道。
5.ADC框图
6.ADC引脚
7.ADC测量电压范围
VREF- ≤ VIN ≤ VREF+
通常情况下,VSSA 和 VREF-接GND, VREF+和
VDDA 接 3.3V,则ADC 的输入电压范围为: 0~3.3V。
测量更大范围电压(例如:-N<Vin<+N)
① 先加法:Vin+N之后,测量输入范围为:Vi1=0~2N
② 再乘法:输入范围缩小为0~3.3V。2N/x=3.3,乘法因子x=3.3/2N
③ 最后换算。
或者先乘法,再加法,都是可以的。
① 先乘法:N*x=3.3/2=1.65,x=1.65/N
② 再加法:加上1.65。
③ 最后换算。
1)实现思路1
2)实现思路2
8.STM32通道组
① **规则通道组:**相当正常运行的程序。最多16个通道。规则通道和它的转换顺序在ADC_SQRx寄存器中选择,规则组转换的总数应写入ADC_SQR1寄存器的L[3:0]中
② **注入通道组:**相当于中断。最多4个通道。注入组和它的转换顺序在ADC_JSQR寄存器中选择。注入组里转化的总数应写入ADC_JSQR寄存器的L[1:0]中
9.ADC触发方式(软件触发、外部触发)
1)ADC软件触发方式:ADC_CR2
位0: ADON:A/D转换器开启/关闭(A/D Converter ON /OFF)
此位由软件置1和清零。
注:0:禁止ADC转换并转至掉电模式
1:使能ADC
**位22 ** JswSTART:开始转换注入通道(Start conversion of injected channels)
转换开始后,软件将该位置1,而硬件将该位清零。
0:复位状态
1:开始转换注入通道
注:该位只能在ADON =1时置1,否则不会启动转换。
位30: swSTART:开始转换常规通道(Start conversion of regular channels)
通过软件将该位置1可开始转换,而硬件会在转换开始后将该位清零。0:复位状态
1:开始转换常规通道
注:该位只能在ADON=1时置1,否则不会启动转换。
2)ADC外部触发方式:规则通道
3)ADC外部触发方式:注入通道
4)ADC外部触发极性
10.ADC中断
11.ADC时钟配置
M3:确保ADC时钟不要超过14MHz。可以设置分频系数为4、6、8 。
M4/M7:确保ADC时钟不要超过36MHz。可以设置分频系数为6、8 。
12.ADC分辨率(ADC_CR1)
13.ADC采样时间
14.ADC数据对齐方式
15.ADC转换结果
规则通道数据寄存器ADC_DR
注入通道数据寄存器ADC_JDRx
16.ADC数据计算方法
如果分辨率为12,采集范围为0~3.3V。
那么ADC转换数据范围为:0~(2^12-1),即0~4095
如果采集的数字量为X,那么输入电压为Y=3.3/(2^12)*X
17.STM32的ADC模式
STM32的ADC的各通道可以单次,连续,扫描或者间断模式执行。
1)单次转换模式
在单次转换模式下,ADC执行一次转换。CONT位为О时,可通过以下方式启动此模式;
- 将ADC_CR2寄存器中的SWSTART位置1(仅适用于规则通道>
- 将JSwSTART位置1(适用于注入通道)
- 外部触发(适用于规则通道或注入通道)
完成所选通道的转换之后:
-
如果转换了规则通道;
- 转换数据存储在16位ADC_DR寄存器中
- EOC(转换结束)标志置1
- EOCIE位置1时将产生中断
-
如果转换了注入通道;
- 转换数据存储在16位ADC_JDR1寄存器中
- JEOC(注入转换结束)标志置1
- JEOCIE位置1时将产生中断
然后,ADC停止。
2)连续转换模式
在连续转换模式下,ADC结束一个转换后立即启动一个新的转换。CONT位为1时,可通过外部触发或将ADC_CR2寄存器中的SWSTRT位置1来启动此模式(仅适用于规则通道)。每次转换之后:
- 如果转换了规则通道组:
- 上次转换的数据存储在16位ADC_DR寄存器中
- EOC(转换结束)标志置1
- EOCIE位置1时将产生中断
无法连续转换注入通道。连续模式下唯一的例外情况是,注入通道配置为在规则通道之后自动转换(使用JAUTO位),请参见自动注入一节。
3)扫描模式
此模式用于扫描一组模拟通道。
通过将ADC_CR1寄存器中的SCAN位置1来选择扫描模式。将此位置1后,ADC会扫描在ADC_SQRx寄存器(对于规则通道〉或ADC_JSQR寄存器(对于注入通道)中选择的所有通道。为组中的每个通道都执行一次转换。每次转换结束后,会自动转换该组中的下一个通道。如果将CONT位置1,规则通道转换不会在组中最后一个所选通道处停止,而是再次从第一个所选通道继续转换。
如果将DMA位置1,则在每次规则通道转换之后,均使用直接存储器访问(DMA)控制器将转换自规则通道组的数据(存储在ADC_DR寄存器中)传输到SRAM。
在以下情况下,ADC_SR寄存器中的EOC位置1:
- 如果EOCS位清零,在每个规则组序列转换结束时
- 如果 EOCS位置1,在每个规则通道转换结束时
从注入通道转换的数据始终存储在ADC_JDRx寄存器中。
ADC_CR1控制寄存器1
二、ADC相关寄存器
1.ADC_CR1寄存器
在扫描模式下,由ADC_SQRx或者ADC_JSQRx寄存器选中的通道被转换。如果设置了EOCIE或者JEOCIE为0,在最后一个通道转换完毕后才会产生EOC或者JEOC中断。
2.ADC_CR2寄存器
3.ADC_SMPR1寄存器
4.ADC_SMPR2寄存器
5.ADC_SQR1/SQR2/SQR3规则序列寄存器
6.ADC_JSQR注入系列寄存器
7.ADC_DR规则通道数据寄存器
8.ADC_JDR注入通道数据寄存器
6.ADC_SR状态寄存器
三、ADC相关HAL库函数
1.HAL库文件
stm32f4xx_hal_adc.c/stm32f4xx_hal_adc.h
位置:工程文件 - HALLIB - stm32f4xx_hal_adc.c/stm32f4xx_hal_adc.h
stm32f4xx_hal_adc_ex.c/stm32f4xx_hal_adc_ex.h
位置:工程文件 - HALLIB - stm32f4xx_hal_adc_ex.c/stm32f4xx_hal_adc_ex.h
2.常用HAL库函数一(通用函数/常规通道)
-
HAL_StatusTypeDef HAL_ADC_Init(ADC_HandleTypeDef* hadc);
ADC初始化函数,设置ADC的基本参数(如采样时间、分辨率、数据对其方式等) -
void HAL_ADC_MspInit(ADC_HandleTypeDef* hadc);
ADC回调函数(弱函数),我们可以根据需要来改写(改写内容:时钟使能IO口的配置等)。 -
HAL_StatusTypeDef HAL_ADC_Start(ADC_HandleTypeDef* hadc);
开启ADC(软件层面) -
HAL_StatusTypeDef HAL_ADC_Stop(ADC_HandleTypeDef* hadc);
关闭ADC(软件层面) -
HAL_StatusTypeDef HAL_ADC_PollForConversion(ADC_HandleTypeDef* hadc, uint32_t Timeout);
等待转换结束(判断EOC位是否发出结束指令) -
HAL_StatusTypeDef HAL_ADC_Start_IT(ADC_HandleTypeDef* hadc);
启动ADC的同时开启中断 -
HAL_StatusTypeDef HAL_ADC_Stop_IT(ADC_HandleTypeDef* hadc);
启动ADC的同时关闭中断 -
HAL_StatusTypeDef HAL_ADC_ConfigChannel(ADC_HandleTypeDef* hadc, ADC_ChannelConfTypeDef* sConfig);
ADC通道配置(规则序列) -
uint32_t HAL_ADC_GetValue(ADC_HandleTypeDef* hadc);
3.常用HAL库函数二(中断相关)
-
HAL_StatusTypeDef HAL_ADC_Start_IT(ADC_HandleTypeDef* hadc);
-
HAL_StatusTypeDef HAL_ADC_Stop_IT(ADC_HandleTypeDef* hadc);
-
void HAL_ADC_IRQHandler(ADC_HandleTypeDef* hadc);
-
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc);
-
void HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef* hadc);
-
void HAL_ADC_LevelOutOfWindowCallback(ADC_HandleTypeDef* hadc);
-
void HAL_ADC_ErrorCallback(ADC_HandleTypeDef *hadc);
4.常用HAL库函数三(注入模式/多重模式)
-
HAL_StatusTypeDef HAL_ADCEx_InjectedStart(ADC_HandleTypeDef* hadc);
-
HAL_StatusTypeDef HAL_ADCEx_InjectedStop(ADC_HandleTypeDef* hadc);
-
HAL_StatusTypeDef HAL_ADCEx_InjectedPollForConversion(ADC_HandleTypeDef* hadc, uint32_t Timeout);
-
uint32_t HAL_ADCEx_InjectedGetValue(ADC_HandleTypeDef* hadc, uint32_t InjectedRank);
-
HAL_StatusTypeDef HAL_ADCEx_MultiModeStart_DMA(ADC_HandleTypeDef* hadc, uint32_t* pData, uint32_t Length);
-
HAL_StatusTypeDef HAL_ADCEx_MultiModeStop_DMA(ADC_HandleTypeDef* hadc);
-
uint32_t HAL_ADCEx_MultiModeGetValue(ADC_HandleTypeDef* hadc);
-
hadcHAL_StatusTypeDef HAL_ADCEx_InjectedConfigChannel(ADC_HandleTypeDef* hadc,ADC_InjectionConfTypeDef* sConfigInjected);
-
HAL_StatusTypeDef HAL_ADCEx_MultiModeConfigChannel(ADC_HandleTypeDef* hadc, ADC_MultiModeTypeDef* multimode);
4.ADC初始化函数 HAL_ADC_Init
函数体:HAL_StatusTypeDef HAL_ADC_Init(ADC_HandleTypeDef* hadc);
位置:工程文件 - HALLIB - stm32f4xx_hal_adc.c
函数体只有一个,时函数体指针,查看定义如下:
typedef struct
{
ADC_TypeDef *Instance;
ADC_InitTypeDef Init;
__IO uint32_t NbrOfCurrentConversionRank;
DMA_HandleTypeDef *DMA_Handle;
HAL_LockTypeDef Lock;
__IO HAL_ADC_StateTypeDef State;
__IO uint32_t ErrorCode;
}ADC_HandleTypeDef;
ADC初始化结构体ADC_InitTypeDef定义:
typedef struct
{
uint32_t ClockPrescaler; //预分频系数
uint32_t Resolution; //分辨率:12/10/8/6
uint32_t DataAlign; // 对齐方式
uint32_t ScanConvMode; //扫描模式
uint32_t EOCSelection; //EOC中断
uint32_t ContinuousConvMode; //连续转换模式
uint32_t DMAContinuousRequests; //关闭/打开DMA请求
uint32_t NbrOfConversion; //规则序列转换数
uint32_t DiscontinuousConvMode; //不连续采样模式
uint32_t NbrOfDiscConversion; // 不连续采用模式通道数
uint32_t ExternalTrigConv; //触发方式
uint32_t ExternalTrigConvEdge; //触发极性
}ADC_InitTypeDef;
5.ADC规则通道配置函数HAL_ADC_ConfigChannel
函数体:HAL_StatusTypeDef HAL_ADC_ConfigChannel(ADC_HandleTypeDef* hadc, ADC_ChannelConfTypeDef* sConfig)
typedef struct
{
uint32_t Channel; //通道
uint32_t Rank; //序列号
uint32_t SamplingTime; //采样时间
uint32_t Offset; // 转换结果偏移量:注入通道
}ADC_ChannelConfTypeDef;
6.开启ADC转换
HAL_ADC_Start(&ADC1_Handler);
7.等待转换完成
HAL_StatusTypeDef HAL_ADC_PollForConversion(ADC_HandleTypeDef* hadc, uint32_t Timeout)
8.获取转换结果(规则通道)
uint32_t HAL_ADC_GetValue(ADC_HandleTypeDef* hadc);
9.正点原子给的adc.c文档(供参考)
#include "adc.h"
#include "delay.h"
ADC_HandleTypeDef ADC1_Handler;//ADC句柄
//初始化ADC
//ch: ADC_channels
//通道值 0~16取值范围为:ADC_CHANNEL_0~ADC_CHANNEL_16
void MY_ADC_Init(void)
{
ADC1_Handler.Instance=ADC1;
ADC1_Handler.Init.ClockPrescaler=ADC_CLOCK_SYNC_PCLK_DIV4; //4分频,ADCCLK=PCLK2/4=90/4=22.5MHZ
ADC1_Handler.Init.Resolution=ADC_RESOLUTION_12B; //12位模式
ADC1_Handler.Init.DataAlign=ADC_DATAALIGN_RIGHT; //右对齐
ADC1_Handler.Init.ScanConvMode=DISABLE; //非扫描模式
ADC1_Handler.Init.EOCSelection=DISABLE; //关闭EOC中断
ADC1_Handler.Init.ContinuousConvMode=DISABLE; //关闭连续转换
ADC1_Handler.Init.NbrOfConversion=1; //1个转换在规则序列中 也就是只转换规则序列1
ADC1_Handler.Init.DiscontinuousConvMode=DISABLE; //禁止不连续采样模式
ADC1_Handler.Init.NbrOfDiscConversion=0; //不连续采样通道数为0
ADC1_Handler.Init.ExternalTrigConv=ADC_SOFTWARE_START; //软件触发
ADC1_Handler.Init.ExternalTrigConvEdge=ADC_EXTERNALTRIGCONVEDGE_NONE;//使用软件触发
ADC1_Handler.Init.DMAContinuousRequests=DISABLE; //关闭DMA请求
HAL_ADC_Init(&ADC1_Handler); //初始化
}
//ADC底层驱动,引脚配置,时钟使能
//此函数会被HAL_ADC_Init()调用
//hadc:ADC句柄
void HAL_ADC_MspInit(ADC_HandleTypeDef* hadc)
{
GPIO_InitTypeDef GPIO_Initure;
__HAL_RCC_ADC1_CLK_ENABLE(); //使能ADC1时钟
__HAL_RCC_GPIOA_CLK_ENABLE(); //开启GPIOA时钟
GPIO_Initure.Pin=GPIO_PIN_5; //PA5
GPIO_Initure.Mode=GPIO_MODE_ANALOG; //模拟
GPIO_Initure.Pull=GPIO_NOPULL; //不带上下拉
HAL_GPIO_Init(GPIOA,&GPIO_Initure);
}
//获得ADC值
//ch: 通道值 0~16,取值范围为:ADC_CHANNEL_0~ADC_CHANNEL_16
//返回值:转换结果
u16 Get_Adc(u32 ch)
{
ADC_ChannelConfTypeDef ADC1_ChanConf;
ADC1_ChanConf.Channel=ch; //通道
ADC1_ChanConf.Rank=1; //第1个序列,序列1
ADC1_ChanConf.SamplingTime=ADC_SAMPLETIME_480CYCLES; //采样时间
ADC1_ChanConf.Offset=0;
HAL_ADC_ConfigChannel(&ADC1_Handler,&ADC1_ChanConf); //通道配置
HAL_ADC_Start(&ADC1_Handler); //开启ADC
HAL_ADC_PollForConversion(&ADC1_Handler,10); //轮询转换
return (u16)HAL_ADC_GetValue(&ADC1_Handler); //返回最近一次ADC1规则组的转换结果
}
//获取指定通道的转换值,取times次,然后平均
//times:获取次数
//返回值:通道ch的times次转换结果平均值
u16 Get_Adc_Average(u32 ch,u8 times)
{
u32 temp_val=0;
u8 t;
for(t=0;t<times;t++)
{
temp_val+=Get_Adc(ch);
delay_ms(5);
}
return temp_val/times;
}
哒哒哒呱呱呱: 我已经解决了
m0_69344585: 同一个疑问,蹲
不枯萎的小花蕾: 以F429为例,默认调用时钟初始化函数之后: SYSCLK(系统时钟) =180MHz PLL 主时钟 =180MHz AHB 总线时钟(HCLK=SYSCLK/1) =180MHz APB1 总线时钟(PCLK1=HCLK/4) =45MHz APB2 总线时钟(PCLK2=HCLK/2) =90MHz 所以APB1的分频系数=AHB/APB1时钟=4 所以,通用定时器时钟CK_INT=2*45M=90M
la_ferrari: 请问使用a1~a12来对曲面拟合,在小侧偏角时候误差较大怎么解决
哒哒哒呱呱呱: 大佬,报这个错误如何解决呀A signal with more than 8 (or 16) bits does not lie on a (two-)byte limit.