Nuru_Banmian
Nuru_Banmian
Published on 2025-07-21 / 68 Visits
0
0

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

需求描述

​ 基于寄存器操作,用一个ADC同时采集多个通道模拟电压。

​ PC0是10通道,采集的是可变电阻器的电压。PC2对应的是12通道,使用杜邦线连接到电源或地,测试他们的电压。

​ 当多个通道同时采集时,一般就需要使用DMA来传输数据,否则数据如果来不及取出,则会导致数据被覆盖。

ADC-独立模式多通道采集-需求描述

寄存器实现

adc.h

#ifndef __ADC_H
#define __ADC_H

#include "stm32f10x.h"

// 初始化
void ADC1_Init(void);

// DMA 相关初始化
void ADC1_DMA_Init(void);

// 开启转换(带DMA)
void ADC1_DMA_StartConvert(uint32_t destAddr, uint8_t len);

#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;

    RCC->APB2ENR |= RCC_APB2ENR_IOPCEN;

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

    // 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;
    // 设置通道12的采样时间,001 - 7.5个时钟周期
    ADC1->SMPR1 &= ~ADC_SMPR1_SMP12;
    ADC1->SMPR1 |= ADC_SMPR1_SMP12_0;

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

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

    // 将通道号 12 保存到序列中的第二位
    ADC1->SQR3 &= ~ADC_SQR3_SQ2;
    ADC1->SQR3 |= 12 << 5;

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

// DMA 相关初始化
void ADC1_DMA_Init(void)
{
    // 1. 开启DMA模块时钟
    RCC->AHBENR |= RCC_AHBENR_DMA1EN;

    // 2. 设置通道1的传输方向, DIR = 0  (从外设读模式)
    DMA1_Channel1->CCR &= ~DMA_CCR1_DIR;

    // 3. 数据宽度, SIZE = 01, 16位
    DMA1_Channel1->CCR &= ~DMA_CCR1_PSIZE_1;
    DMA1_Channel1->CCR |= DMA_CCR1_PSIZE_0;
    DMA1_Channel1->CCR &= ~DMA_CCR1_MSIZE_1;
    DMA1_Channel1->CCR |= DMA_CCR1_MSIZE_0;

    // 4. 地址自增,外设不增,内存地址要增
    DMA1_Channel1->CCR &= ~DMA_CCR1_PINC;
    DMA1_Channel1->CCR |= DMA_CCR1_MINC;

    // 5. 开启循环模式
    DMA1_Channel1->CCR |= DMA_CCR1_CIRC;

    // 6. 开启DMA模式
    ADC1->CR2 |= ADC_CR2_DMA;

}

// 开启转换(带DMA)
void ADC1_DMA_StartConvert(uint32_t destAddr, uint8_t len)
{
    // 0. DMA配置源地址和目的地址,以及数据长度
    DMA1_Channel1->CPAR = (uint32_t)&(ADC1->DR);
    DMA1_Channel1->CMAR = destAddr;
    DMA1_Channel1->CNDTR = len;

    DMA1_Channel1->CCR |= DMA_CCR1_EN;

    // 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)
    {}
}

main.c

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

// 定义数组,保存转换后的信号值
uint16_t data[2] = {0};

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

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

	// 开启AD转换
	ADC1_DMA_StartConvert((uint32_t)data, 2);

	while(1)
	{
		// 向串口发送打印转换结果
		printf("V变阻器 = %.2f, V_PC2 = %.2f\n", data[0] * 3.3 / 4095, data[1] * 3.3 / 4095);
		Delay_ms(1000);
	}
}

HAL库实现

HAL库设置

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

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

ADC-独立模式多通道采集-HAL库设置-3

添加的其他代码

int main(void)
{
    HAL_Init();
    SystemClock_Config();
    MX_GPIO_Init();
    MX_DMA_Init();
    MX_ADC1_Init();
    MX_USART1_UART_Init();
    HAL_ADC_Start_DMA(&hadc1, (uint32_t *)data, 2);
    while (1)
    {
        printf("滑动变阻器=%.2f, 电源电压=%.2f\r\n",
               data[0] * 3.3 / 4095,
               data[1] * 3.3 / 4095);

        HAL_Delay(1000);
    }
}


Comment