STM32的HAL库开发---单通道ADC采集实验

news/2025/2/22 4:47:51

一、实验简要

1、功能描述

通过ADC1通道1PA1)采集电位器的电压,并显示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.5ADC时钟周期为例,可以得到转换时间为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()函数, 配置NVICCLOCKGPIO

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();
    }
}


http://www.niftyadmin.cn/n/5858132.html

相关文章

cmake:定位Qt的ui文件

如题。在工程中&#xff0c;将h&#xff0c;cpp&#xff0c;ui文件放置到不同文件夹下&#xff0c;会存在cmake找不到ui文件&#xff0c;导致编译报错情况。 cmake通过指定文件路径&#xff0c;确保工程找到ui文件。 标识1&#xff1a;ui文件保存路径。 标识2&#xff1a;添加…

网站搭建基本流程

需求分析&#xff1a; 实现网站搭建的过程&#xff1a;首先进行网站的需求性分析 网站可分为前台系统和后台系统&#xff0c;由不同的功能拆分为不同的模块 如下是一个电商网站可以拆分出的模块&#xff1a; 在编写代码前&#xff0c;我们要先对网站进行架构&#xff0c;通过…

Spring Boot 开发入门

文章来源&#xff1a;开发您的第一个 Spring Boot 应用程序 (Developing Your First Spring Boot Application) _ Spring Boot3.4.0中文文档(官方文档中文翻译)|Spring 教程 —— CADN开发者文档中心 本节介绍如何开发一个小型的 “Hello World&#xff01;” Web 应用程序&…

MVTEC数据集笔记

前言 网上的博客只有从论文里摘出的介绍&#xff0c;没有数据集文件详细的样子&#xff0c;下载数据集之后&#xff0c;对数据集具体的构成做一个补充的笔记。 下载链接&#xff1a;https://ai-studio-online.bj.bcebos.com/v1/7d4a3cf558254bbaaf4778ea336cb14ed8bbb96a7f2a…

Pycharm打开的jupyter notebook无法在pycharm中关闭怎么解决

首先你可以先看一下你的pycharm的jupyter界面的输出&#xff1a; 可以看到第一行有个启动命令 找到这个–port的端口号&#xff0c;现在我们可以走下面的步骤&#xff0c;假设你找到的是–port47187 &#xff1a; 步骤 1&#xff1a;定位占用端口的进程&#xff08;Linux/Mac…

【JAVA工程师从0开始学AI】,第二步:从强类型到动态语言:Java工程师的Python语法避坑指南

这是一篇介绍Python语法与JAVA语法区别文章&#xff0c;让我们以对比的方式&#xff0c;来学习一下Python的语法。 首先我们看一下下面的Python代码&#xff0c;和具体在java 当中分别代表了什么意思 numbers [1,2,3,4,5,6,7,8,9] # ① 创建数字列表&#xff08;像Java的Ar…

高子昂医编---23岁,医疗编上岸,正式开启养老生活

作为一个只想毕业后就找个稳定工作躺平一生的普通人&#xff0c;直接放弃加入考研考公大军&#xff0c;加入了竞争稍微小一点的考编大军&#xff01;毕业那年在学校辛苦奋斗四个多月&#xff0c;直接一战上岸&#xff01;成为了一名有编制的医务工作者&#xff01;现在在我们家…

WLAN无线2.4G/5G频段划分和可用信道

互联网各领域资料分享专区(不定期更新)&#xff1a; Sheet