STM32 进阶 ADC案例2:独立模式多通道采集 寄存器代码

需求描述

基于寄存器操作,用一个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
			);

	}
		
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

雁过留声花欲落

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值