需求描述
基于寄存器操作,用一个ADC同时采集多个通道模拟电压。
PC0是10通道,采集的是可变电阻器的电压。PC2对应的是12通道,使用杜邦线连接到电源或地,测试他们的电压。
当多个通道同时采集时,一般就需要使用DMA来传输数据,否则数据如果来不及取出,则会导致数据被覆盖。

寄存器实现
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库设置



添加的其他代码
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);
}
}