需求描述
基于寄存器操作,用一个ADC同时采集多个通道模拟电压。
PC0是10通道,采集的是可变电阻器的电压。PC1对应的是11通道,使用杜邦线连接到电源或地,测试他们的电压。
当多个通道同时采集时,一般就需要使用DMA来传输数据,否则数据如果来不及取出,则会导致数据被覆盖。
adc.h
#ifndef __ADC_H__
#define __ADC_H__
#include "stm32f10x.h"
// 单通道测量的版本
void ADC_SingleInit(void);
// 启动函数
void ADC_SingleStart(void);
// 读出电压值
double ADC_SingleReadV(void);
// ADC带着DMA初始化
void ADC_DoubleDMAInit(void);
// 多通道版本的开始测量
void ADC_DoubleStart(uint32_t buffer_addr,uint16_t len);
#endif /* __ADC_H__ */
adc.c
#include "adc.h"
void ADC_SingleInit(void){
// 0. GPIO PC0模拟输入
// 0.1 先放时钟
RCC->APB2ENR |= RCC_APB2ENR_IOPCEN;
// 0.2 配00 00 必须配 因为GPIO CRL的复位值是0x44444444
GPIOC->CRL &= ~ (GPIO_CRL_CNF0|GPIO_CRL_MODE0);
// 1.ADC配置
// 1.1 要对ADC做一个分频
RCC->CFGR |= RCC_CFGR_ADCPRE_1;
RCC->CFGR &=~ RCC_CFGR_ADCPRE_0;
// 1.0 放ADC的时钟
RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;
// 1.2 关闭扫描模式 因为当前常规通道只有一首歌
ADC1->CR1 &= ~ ADC_CR1_SCAN;
// 1.3 关闭外部触发
ADC1->CR2 &= ~ ADC_CR2_EXTTRIG;
// 1.4 开启右对齐 将12位测量结果放在16位寄存器的低位
ADC1->CR2 &= ~ ADC_CR2_ALIGN;
// 1.5 开启连续转换模式(因为要单曲循环 疯狂地对这一位采样)
ADC1->CR2 |= ADC_CR2_CONT;
// 1.6 采样时间 原则是 你可以一个一个试 等到试到不变的时候 选最短的那个
ADC1->SMPR1 |= ADC_SMPR1_SMP10_0;
ADC1->SMPR1 &=~ ADC_SMPR1_SMP10_1;
ADC1->SMPR1 &=~ ADC_SMPR1_SMP10_2;
// 1.7 歌单只放一首歌 ADC_IN10
ADC1->SQR3 |= 10;
// 1.8 歌单的长度
ADC1->SQR1 &= ~ ADC_SQR1_L;
//ADC1->CR2 |= ADC_CR2_EXTSEL;
}
// 启动函数
void ADC_SingleStart(void){
// 0. 先上电唤醒
ADC1->CR2 |= ADC_CR2_ADON;
// 1. 把ADC校准一下
ADC1->CR2 |= ADC_CR2_CAL;
// 2. 等待校准结束
while (ADC1->CR2 & ADC_CR2_CAL)
{
}
// 3. 再次给ADON写1 启动转换
ADC1->CR2 |= ADC_CR2_ADON;
//ADC1->CR2 |= ADC_CR2_SWSTART;
}
// 读出电压值
double ADC_SingleReadV(void){
return ((uint16_t)ADC1->DR)*3.3/4095;
}
// ADC带着DMA初始化
void ADC_DoubleDMAInit(void){
// 0. GPIO PC0模拟输入 PC1模拟输入
// 0.1 先放时钟
RCC->APB2ENR |= RCC_APB2ENR_IOPCEN;
// 0.2 配00 00 必须配 因为GPIO CRL的复位值是0x44444444
GPIOC->CRL &= ~ (GPIO_CRL_CNF0|GPIO_CRL_MODE0);
// new * 添加PC1引脚的模拟输入模式
GPIOC->CRL &= ~ (GPIO_CRL_CNF1|GPIO_CRL_MODE1);
// 1.ADC配置
// 1.1 要对ADC做一个分频
RCC->CFGR |= RCC_CFGR_ADCPRE_1;
RCC->CFGR &=~ RCC_CFGR_ADCPRE_0;
// 1.0 放ADC的时钟
RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;
// new * 1.2 这一次要跟着歌单去扫描多个通道 所以需要开扫描模式
ADC1->CR1 |= ADC_CR1_SCAN;
// 1.3 关闭外部触发
ADC1->CR2 &= ~ ADC_CR2_EXTTRIG;
// 1.4 开启右对齐 将12位测量结果放在16位寄存器的低位
ADC1->CR2 &= ~ ADC_CR2_ALIGN;
// 1.5 开启连续转换模式(因为要单曲循环 疯狂地对这一位采样)
ADC1->CR2 |= ADC_CR2_CONT;
// 1.6 采样时间 原则是 你可以一个一个试 等到试到不变的时候 选最短的那个
ADC1->SMPR1 |= ADC_SMPR1_SMP10_0;
ADC1->SMPR1 |= ADC_SMPR1_SMP10_1;
ADC1->SMPR1 &=~ ADC_SMPR1_SMP10_2;
// new * 通道11的采样时间配置
ADC1->SMPR1 |= ADC_SMPR1_SMP11_0;
ADC1->SMPR1 &=~ ADC_SMPR1_SMP11_1;
ADC1->SMPR1 &=~ ADC_SMPR1_SMP11_2;
// new * 1.7 歌单只放2首歌 ADC_IN10 ADC_IN11
ADC1->SQR3 |= 10;
ADC1->SQR3 |= 11<<5;
// new* 1.8 歌单的长度 长度要改成2
ADC1->SQR1 &= ~ ADC_SQR1_L;
ADC1->SQR1 |= ADC_SQR1_L_0;
// ------------DMA------------------
// 2.0 根据手册得知 ADC1 在DMA1的通道1上
// 2.1 先放DMA1的时钟
RCC->AHBENR |= RCC_AHBENR_DMA1EN;
// 2.2 配片上外设的地址
DMA1_Channel1->CPAR = (uint32_t)&(ADC1->DR);
// 2.3 通道的优先级(可以随便配,此处是最高)
DMA1_Channel1->CCR |= DMA_CCR1_PL;
// 2.4 ----------传输方向
// 2.4.1 存储器到存储器模式关闭
DMA1_Channel1->CCR &= ~ DMA_CCR1_MEM2MEM;
// 2.4.1 方向 从存储器读还是从外设读
DMA1_Channel1->CCR &= ~ DMA_CCR1_DIR;
// 2.4.2 如果你希望把ADC循环测量数据全拿来
// 应该开DMA的循环模式
DMA1_Channel1->CCR |= DMA_CCR1_CIRC;
// 2.4.3 存储器和外设的地址要不要增量
// 存储器的地址增量
DMA1_Channel1->CCR |= DMA_CCR1_MINC;
// 外设的地址不增量
DMA1_Channel1->CCR &= ~ DMA_CCR1_PINC;
// 2.4.4 外设的数据宽度 16位
DMA1_Channel1->CCR &= ~ DMA_CCR1_PSIZE_1;
DMA1_Channel1->CCR |= DMA_CCR1_PSIZE_0;
// 2.4.5 内存的地址也是16位
DMA1_Channel1->CCR &= ~ DMA_CCR1_MSIZE_1;
DMA1_Channel1->CCR |= DMA_CCR1_MSIZE_0;
// 管道的使能
// 数组的地址
// 数据的长度
}
// 多通道版本的开始测量
void ADC_DoubleStart(uint32_t buffer_addr,uint16_t len){
// -----先铺管道
// 0. 先设定数组的地址
DMA1_Channel1->CMAR = buffer_addr;
// 1 再设定长度
DMA1_Channel1->CNDTR = len;
// 2 把管道打开
DMA1_Channel1->CCR |= DMA_CCR1_EN;
// ----管道铺好了
// -1 先让ADC1自己可以发出DMA请求
ADC1->CR2 |= ADC_CR2_DMA;
// --------------------------------以上是新增
// 0. 先上电唤醒
ADC1->CR2 |= ADC_CR2_ADON;
// 1. 把ADC校准一下
ADC1->CR2 |= ADC_CR2_CAL;
// 2. 等待校准结束
while (ADC1->CR2 & ADC_CR2_CAL)
{
}
// 3. 再次给ADON写1 启动转换
ADC1->CR2 |= ADC_CR2_ADON;
}
main.c
#include "usart1.h"
#include "string.h"
#include <stdio.h>
#include "m24c02.h"
#include "dma.h"
#include "adc.h"
int main(void){
Usart1_Init();
ADC_DoubleDMAInit();
uint16_t buffer[2] = {0};
ADC_DoubleStart((uint32_t)buffer,2);
while (1)
{
Delay_s(1);
printf(
"PC0 = %.2f v, PC1 = %.2f v",
buffer[0] * 3.3 / 4095,
buffer[1] * 3.3 / 4095
);
}
}