Nuru_Banmian
Nuru_Banmian
Published on 2025-07-20 / 47 Visits
0
0

ADC-独立模式单通道采集-寄存器&HAL库实现

需求描述

​ 基于寄存器操作,菜鸡可变电阻器的电压,并通过串口把电压数据发送到电脑端。

硬件电路设计

ADC-独立模式单通道采集-硬件电路设计-1

​ PC0口为ADC的10通道,范围是0~3.3V。只需要一个ADC,使用独立模式即可。

ADC-独立模式单通道采集-硬件电路设计-2

使用到的寄存器

扫描模式

代码

/* 3. 禁用扫描模式(只有一个通道,不需要扫描) */
ADC1->CR1 &= ~ADC_CR1_SCAN;

寄存器

ADC-独立模式单通道采集-扫描模式-1

ADC-独立模式单通道采集-扫描模式-2

连续转换

代码

/* 4. 启用连续转换(转换不会停止) */
ADC1->CR2 |= ADC_CR2_CONT;

寄存器

ADC-独立模式单通道采集-连续转换-1

ADC-独立模式单通道采集-连续转换-2

转换结果数据对齐方式

代码

/* 5. 转换结果右对齐 0:右对齐 1: 左对齐*/
ADC1->CR2 &= ~ADC_CR2_ALIGN;

寄存器

ADC-独立模式单通道采集-转换结果数据对齐方式-1

ADC-独立模式单通道采集-转换结果数据对齐方式-2

规则通道外部触发转换模式

代码

/* 6. 禁用规则通道外部触发 */
ADC1->CR2 &= ~ADC_CR2_EXTTRIG;

寄存器

ADC-独立模式单通道采集-规则通道外部触发转换模式-1

ADC-独立模式单通道采集-规则通道外部触发转换模式-2

配置启动规则转换的外部事件

代码

/* 7. 使用软件启动转换  111=软件启动*/
ADC1->CR2 |= ADC_CR2_EXTSEL;

寄存器

ADC-独立模式单通道采集-配置启动规则转换的外部事件-1

ADC-独立模式单通道采集-配置启动规则转换的外部事件-2

规则组长度

代码

/* 8. 规则转换序列长度(有几个通道需要转换)000:1个 001:2个 ... */
ADC1->SQR1 &= ~ADC_SQR1_L;

寄存器

ADC-独立模式单通道采集-规则组长度-1

ADC-独立模式单通道采集-规则组长度-2

给规则组设置转换序列

SQRx寄存器一共有三个,前面我们设置了规则组的长度,假如我们设置长度为3,则要给SQ1、2、3分辨设置我们要读取的通道,随后我们启动ADC采集的时候,他会按着SQ1、2、3的顺序读取通道。

代码

/* 9. 规则转换序列中的第一个转换  通道10, 就是SQR3[4:0]=01010   */
ADC1->SQR3 &= ~ADC_SQR3_SQ1; /* [4:0]位置为0 */
ADC1->SQR3 |= (10 << 0);     /* 设置最后5位 */

寄存器

ADC-独立模式单通道采集-给规则组设置转换序列-1

ADC-独立模式单通道采集-给规则组设置转换序列-2

设置采样时间

代码

/* 10. 通道10采样时间 设置7.5个周期=001*/
ADC1->SMPR1 &= ~ADC_SMPR1_SMP10_2;
ADC1->SMPR1 &= ~ADC_SMPR1_SMP10_1;
ADC1->SMPR1 |= ADC_SMPR1_SMP10;

寄存器

ADC-独立模式单通道采集-设置采样时间

启动ADC

代码

/* 1. 启动 ADC1 ADON:0->1 是从断电模式下唤醒*/
ADC1->CR2 |= ADC_CR2_ADON;

寄存器

ADC-独立模式单通道采集-启动ADC-1

ADC-独立模式单通道采集-启动ADC-2

AD校准

代码

/* 2. 启用AD校准, 校准完成之后该位会自动清0*/
ADC1->CR2 |= ADC_CR2_CAL;

寄存器

ADC-独立模式单通道采集-AD校准-1

ADC-独立模式单通道采集-AD校准-2

启动转换

代码

/* 5. 开始转换 规则通道 */
ADC1->CR2 |= ADC_CR2_SWSTART;

寄存器

ADC-独立模式单通道采集-启动转换-1

ADC-独立模式单通道采集-启动转换-2

寄存器实现

adc.h

#ifndef __ADC_H
#define __ADC_H

#include "stm32f10x.h"

// 初始化
void ADC1_Init(void);

// 开启转换
void ADC1_StartConvert(void);

// 返回转换后的模拟电压值
double ADC1_ReadV(void);

#endif

adc.c

#include "adc.h"

// 初始化
void ADC1_Init(void)
{
    // 1. 时钟配置
    RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;
    // CFGR:ADCPRE - 10,6分频,得到 12MHz
    RCC->CFGR |= RCC_CFGR_ADCPRE_1;
    RCC->CFGR &= ~RCC_CFGR_ADCPRE_0;

    // 2. GPIO工作模式,PC0,模拟输入,00 00
    GPIOC->CRL &= ~(GPIO_CRL_MODE0 | GPIO_CRL_CNF0);

    // 3. ADC配置
    // 3.1 工作模式:禁用扫描模式
    ADC1->CR1 &= ~ADC_CR1_SCAN;

    // 3.2 启用连续转换模式(单曲循环)、
    ADC1->CR2 |= ADC_CR2_CONT;

    // 3.3 数据右对齐(默认)
    ADC1->CR2 &= ~ADC_CR2_ALIGN;

    // 3.4 设置通道10的采样时间,001 - 7.5个时钟周期
    ADC1->SMPR1 &= ~ADC_SMPR1_SMP10;
    ADC1->SMPR1 |= ADC_SMPR1_SMP10_0;

    // 3.5 规则组通道序列配置
    // 3.5.1 规则组中的通道个数 L
    ADC1->SQR1 &= ~ADC_SQR1_L;

    // 3.5.2 将通道号 10 保存到序列中的第一位
    ADC1->SQR3 &= ~ADC_SQR3_SQ1;
    ADC1->SQR3 |= 10 << 0;

    // 3.5 选择软件触发AD转换
    // ADC1->CR2 |= ADC_CR2_EXTTRIG;
    // ADC1->CR2 |= ADC_CR2_EXTSEL;    // 选择的就是SWSTART控制AD转换
}

// 开启转换
void ADC1_StartConvert(void)
{
    // 1. 上电唤醒
    ADC1->CR2 |= ADC_CR2_ADON;

    // 2. 执行校准
    ADC1->CR2 |= ADC_CR2_CAL;
    // 等待校准完成
    while (ADC1->CR2 & ADC_CR2_CAL)
    {}

    // 3. 启动转换
    // ADC1->CR2 |= ADC_CR2_SWSTART;
    ADC1->CR2 |= ADC_CR2_ADON;

    // 4. 等待转换完成
    while ((ADC1->SR & ADC_SR_EOC) == 0)
    {}
}

// 返回转换后的模拟电压值
double ADC1_ReadV(void)
{
    return ADC1->DR * 3.3 / 4095;
}

main.c

#include "usart.h"
#include "adc.h"
#include "delay.h"

int main(void)
{
	// 初始化
	USART_Init();
	ADC1_Init();

	printf("Hello, world!\n");

	// 开启AD转换
	ADC1_StartConvert();

	while(1)
	{
		// 向串口发送打印转换结果
		printf("V = %.2f\n", ADC1_ReadV());
		Delay_ms(1000);
	}
}

HAL库实现

HAL库设置

ADC-独立模式单通道采集-HAL库设置-1

ADC-独立模式单通道采集-HAL库设置-2

添加的其他代码

main函数

int main(void)
{
  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  MX_ADC1_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
    // 启动ADC转换
    HAL_ADC_Start(&hadc1);
    while (1)
    {
        float v = HAL_ADC_GetValue(&hadc1) / 4095.0 * 3.3;
        printf("电压=%.2fV\r\n", v);
        HAL_Delay(1000);
    }
}


Comment