一、实验简要
1、功能描述
通过ADC1通道1(PA1)采集电位器的电压,并显示ADC转换的数字量及换算后的电压值
2、确定最小刻度
VREF+ = 3.3V ---》 0V ≤ VIN ≤ 3.3V ---》最小刻度 = 3.3 / 4096 ,F1的分辨率是12位的,也就是把3.3V分为4096份。F4/F7/H7还可以自己配置分辨率,例如H7可以把分辨率配置为16位的,也就是把3.3V进行65536等分。
3,确定转换时间
采样时间239.5个ADC时钟周期为例,可以得到转换时间为21us。例如配置为最长的采样时间239.5个采样周期,那么采样时间就是239.5 + 12.5 = 252个时钟周期。配置ADC的时钟的12M,则转换时间为252 * (1 / 12000000) = 21us
4、模式组合
单次转换模式、不使用扫描模式
二、ADC寄存器介绍
2.1 ADC_CR1寄存器
这个实验只用的了SCAN位,因为是单通道,所以不需要扫描模式,将SCAN位设置为0.
同时CR1寄存器里边的双模式选择要设置为独立模式,因为只使用了一个ADC。
2.2 ADC_CR2寄存器
SWSTART位:开启转换规则通道,置为1,再=在单次转换模式下,即可触发一次转换,转换开始后,硬件会把这位置0。想再次转换,再次置1.
EXTTRIG位:使能外部触发,也就是定时器或者外部中断触发那些。这个实验里选择的软件触发,所以设置为0
EXTSE位:选择外部的触发源,这里选用软件触发。
ALIGN:设置数据对齐方式,由于ADC数据位为12位,而数据寄存器是16位的,所以将12位数据放到16位寄存器里边,涉及数据对齐问题,一般选择右对齐。
DMA位:设置是否启用DMA,本实验没有使用,设置为0。
RSTCAL位:ADC在转换之前,首先进行校准, 这个位是初始化校准寄存器的。
CAL位:软件设置为1后,ADC开始校准,校准完成后,硬件会把这位清0.
ADON位:设置开启还是关闭ADC的。这一位在F1系列,还有触发规则组转换的功能,其他系列没有。
注意:一般操作顺序是,先把ADON位置1,开启ADC,然后把RSTCAL位设置为1,初始化校准寄存器。 然会将CAL位设置为1,进行ADC校准,校准完成后,CAL位会硬件置0,可以在软件里判断这位,开ADC是否校准完成。
CONT位:设置单次转换还是连续转换。设置为0是单次转换模式,设置为1是连续转换模式。
2.3 ADC_SMPR1寄存器
SMPR1设置通道10到通道17采样时间的设置,而且采样时间是每个通道都可以单独设置的。如果想设置通道12,就往SMP12里边写即可。
2.4 ADC_SMPR2寄存器
SMPR2设置通道0到通道9采样时间的设置,而且采样时间是每个通道都可以单独设置的。本实验使用通道1,采样时间设置为最大,也就是将SMP0设置为111.
2,5 ADC_SQR1寄存器
本实验只有一个转换,只需要将L位设置为0即可。然后将第一个转化位置设置为通道1.
2.6 ADC_SQR2寄存器
这个寄存器用于配置序列里的第7个到第12个。
2.7 ADC_SQR3寄存器
这个寄存器是用来设置规则序列里的第一个到第6个。本实验为单通道,直接把SQ1设置为1即可,代表第一个转化顺序为通道1.
2.8 ADC_DR寄存器
这个寄存器是有32位的,其中低16位用于存储规则转换数据的,高16位是用于双ADC模式的。读数据的时候直接读取低16位即可。
三、单通道ADC采集实验配置步骤
1、HAL_ADC_Init()函数,用于初始化ADC,配置ADC工作参数。
2、HAL_ADCEx_Calibration_Start()函数,用于ADC校准的。
3、HAL_ADC_MspInit()函数, 配置NVIC、CLOCK、GPIO
4、HAL_ADC_ConfigChannel()函数,配置ADC相应通道相关参数
5、HAL_ADC_Start()函数,启动A/D转换
6、HAL_ADC_PollForConversion()函数,等待规则通道转换完成
7、HAL_ADC_GetValue()函数,获取规则通道A/D转换结果
四、程序
1、寄存器版本
#include "./BSP/ADC/adc.h"
void ADC_Init(void)
{
//开启ADC1时钟
RCC->APB2ENR |= (1 << 9);
//设置ADC时钟分频系数为6
RCC->CFGR |= (1 << 15);
//SCAN位 关闭扫描模式
ADC1->CR1 &= ~(1 << 8);
//EXTTRIG 开启规则通道的外部触发
//ADC1->CR2 &= ~(1 << 20)
ADC1->CR2 |= (1 << 20);
//EXTSEL 设置为SWSTART软件触发
ADC1->CR2 |= (0X07 << 17);
//ALIGN 设置数据对齐为右对齐模式
ADC1->CR2 &= ~(1 << 11);
//ADCON 开启ADC
ADC1->CR2 |= (1 << 0);
//CONT 开启连续转化模式
//ADC1->CR2 |= (1 << 1);
//SMP1 ADC1_CH1 设置采样时间为239.5个时钟周期
ADC1->SMPR2 |= (0X07 << 3);
//L 设置规则通道转换数为1
ADC1->SQR1 &= (0X0F << 20);
//SQ1 设置第一个转化为ADC通道1
ADC1->SQR3 |= (1 << 0);
//开启GPIOA时钟
RCC->APB2ENR |= (1 << 2);
//设置PA1位模拟输入模式
GPIOA->CRL &= ~(0X03 << 4);
GPIOA->CRL &= ~(0X03 << 6);
//RSTCAL 初始化校准寄存器
ADC1->CR2 |= (1 << 3);
/*********************注意***************/
//第一个不写会出问题
//这必须要等待两次 一个是等待校准寄存器初始化完成之后 ,再开启校准
//第二个是等待校准完成再开始转换
//等待校准完成
while(ADC1->CR2 & (1 << 3));
//CAL 开始校准
ADC1->CR2 |= (1 << 2);
//等待校准完成
while(ADC1->CR2 & (1 << 2));
//SWSTART 开启规则通道软件触发
ADC1->CR2 |= (1 << 22);
}
再配过程中发现,最开始就算接3.3,采集的数据总时跟0XFFF差一点,电压显示为3.26左右。后来发现必须等待校准寄存器初始化完成后,再开启校准,同时等待校准完成后,再开启转换。
2、库函数版本
adc.h头文件
#ifndef __ADC_H
#define __ADC_H
#include "stm32f1xx.h"
void ADC_Init(void);
void get_value_ADC(void);
#endif
adc.c源文件
#include "./BSP/ADC/adc.h"
#include "./SYSTEM/sys/sys.h"
ADC_HandleTypeDef hadc;
void ADC_Init(void)
{
hadc.Instance = ADC1;
//设置连续转换模式还是单次转换模式
hadc.Init.ContinuousConvMode = DISABLE;
hadc.Init.DataAlign = ADC_DATAALIGN_RIGHT;
//关闭间断模式
hadc.Init.DiscontinuousConvMode = DISABLE;
//设置ADC触发源
hadc.Init.ExternalTrigConv = ADC_SOFTWARE_START;
//设置转换数量
hadc.Init.NbrOfConversion = 1;
//设置间断模式数量
hadc.Init.NbrOfDiscConversion = 0;
hadc.Init.ScanConvMode = ADC_SCAN_DISABLE;
HAL_ADC_Init(&hadc);
ADC_ChannelConfTypeDef sConfig;
sConfig.Channel = ADC_CHANNEL_1;
sConfig.Rank = ADC_REGULAR_RANK_1;
sConfig.SamplingTime = ADC_SAMPLETIME_239CYCLES_5;
HAL_ADC_ConfigChannel(&hadc, &sConfig);
//开启ADC校准
HAL_ADCEx_Calibration_Start(&hadc);
//开启ADC
HAL_ADC_Start(&hadc);
}
void HAL_ADC_MspInit(ADC_HandleTypeDef* hadc)
{
//开启ADC1时钟
__HAL_RCC_ADC1_CLK_ENABLE();
//开启GPIOA时钟
__HAL_RCC_GPIOA_CLK_ENABLE();
//设置PA1为模拟输入模式
GPIO_InitTypeDef GPIO_Init;
GPIO_Init.Mode = GPIO_MODE_ANALOG;
GPIO_Init.Pin = GPIO_PIN_1;
GPIO_Init.Pull = GPIO_NOPULL;
GPIO_Init.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOA,&GPIO_Init);
RCC_PeriphCLKInitTypeDef PeriphClkInit;
PeriphClkInit.PeriphClockSelection =RCC_PERIPHCLK_ADC;
//设置ADC时钟分频系数为6
PeriphClkInit.AdcClockSelection = RCC_ADCPCLK2_DIV6;
HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit);
}
uint32_t value = 0;
void get_value_ADC(void)
{
//主要用于软件触发转换 用于ADC采集
HAL_ADC_Start(&hadc);
//用于ADC转换
HAL_ADC_PollForConversion(&hadc,10);
value = HAL_ADC_GetValue(&hadc);
value &= 0X0FFF;
printf("ADC:%f\r\n",((double)value * 3.3 ) / 4096);
}
main.c主函数
#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./BSP/LED/led.h"
#include "./BSP/ADC/adc.h"
uint16_t ADC_Voyage = 0;
int main(void)
{
HAL_Init(); /* 初始化HAL库 */
sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
delay_init(72); /* 延时初始化 */
led_Init(); /* LED初始化 */
usart_init(115200);
ADC_Init();
while(1)
{
LED0(1);
LED1(0);
delay_ms(500);
LED0(0);
LED1(1);
delay_ms(500);
get_value_ADC();
}
}