第一步配置ADC的参数
这里我的开发板上的三个传感器接的分别是STM32的PA1\PA2\PA3
一个接的是光敏电阻,一个是MQ2传感器,一个是MQ135传感器。
这里我记录一下我的学习过程以防哪天自己忘了有个可以翻阅的笔记。
ADC的配置步骤:
使能端口时钟和ADC时钟
配置端口为模拟输入模式
采样的通道数
采样的时钟数
采样的模式(是否扫描)
触发方式
开启校准
等待校准完成
在这里插入图片描述
这两位可以打开对应的时钟
这里可以配置ADC的预分配系数
ADCPRE[1:0]: ADC预分频 (ADC prescaler)
由软件置’1’或清’0’来确定ADC时钟频率
00: PCLK2 2分频后作为ADC时钟
01: PCLK2 4分频后作为ADC时钟
10: PCLK2 6分频后作为ADC时钟
11: PCLK2 8分频后作为ADC时钟
由于ADC的工作频率不能超过14M,我用的是72M所以至少要进行六分频;
把PA1\PA2\PA3都设置成模拟输入模式
这里配置成3个转换因为我有三个模拟输入通道
三个通道谁先转换需要填到对应位置
我就设置成第一个通道第一个转换,第二个通道第2个转换,第三个通道第3个转换。
三个通道都的采样时间都设置成一样239.5周期
设置成独立模式和扫描进行
连续转换,数据右对齐,外部触发模式,软件触发,开启AD转换器,校准 硬件校准等配置
第二步配置DMA的参数
开启DMA时钟
//设置外设寄存器的地址
//设置数据存储区的地址
DMA1_Channel1->CMAR = (u32)DMA_Buffer;
DMA_Buffer[]是我自己定义的数组
//设置要传输的数据量
DMA1_Channel1->CNDTR =sizeof(DMA_Buffer)/sizeof(DMA_Buffer[0]);
这个可以计算出每次传输的数据量
//设置通道的优先级:高
DMA1_Channel1->CCR |=(2<<12);//优先级为高
//设置数据传输的方向:从外设读
DMA1_Channel1->CCR &=~(1<<4);//从外设读
//非存储器到存储器模式
DMA1_Channel1->CCR &=~(1<<14);//非存储器模式
//循环模式进行DMA转换
DMA1_Channel1->CCR |=(1<<5);//循环模式
DMA1_Channel1->CCR &=~(1<<6);//外设不增量模式,读取的是一个寄存器
DMA1_Channel1->CCR|=(1<<7); //存储器增量模式,数组要进行指针移动
DMA1_Channel1->CCR |=(1<<8);//外设数据宽度:16位
DMA1_Channel1->CCR |=(1<<10);//存储器数据宽度:16位
DMA1_Channel1->CCR|=1<<0;//开启DMA传输
ADC1->CR2 |=(1<<22);//启动ADC的规则转换
---------------------------------------------------------------------------------------
手动分割线
这里基本上ADC和DMA都配置完了
分别是这几个函数
#include "adc.h"
#include "stdio.h"
/*
ADC12_IN1 PA1 MQ135
ADC12_IN2 PA2 MQ2
ADC12_IN3 PA3 光敏电阻
配置三个通道 PA1 PA2 PA3
*/
void ADC_Sensor_Init(void)
{
#if reg_program
//1.使能时钟,A端口时钟和ADC1时钟
RCC->APB2ENR |=(1<<2)|(1<<9);
//2.配置ADCCLK时钟<14M
RCC->CFGR|=(2<<14);//72M六分频
//3.PA1模拟输入模式0000
GPIOA->CRL &=~(0xf<<1*4);
//PA2模拟输入模式0000
GPIOA->CRL &=~(0xf<<2*4);
//PA3模拟输入模式0000
GPIOA->CRL &=~(0xf<<3*4);
//4.配置ADC1
//规则通道总数:
ADC1->SQR1 |=(2<<20);//有3个转换L[3:0]0010
//转换的顺序,先转换那个通道
ADC1->SQR3 |=(1<<0);//第1个转换通道放的是通道1SQ1[4:0]
ADC1->SQR3 |=(2<<5);//第2个转换通道放的是通道2SQ2[4:0]
ADC1->SQR3 |=(3<<10);//第3个转换通道放的是通道3SQ3[4:0]
//通道1的采样时间
ADC1->SMPR2 |=(0x7<<3);//SMP1[2:0],111,239.5周期
//通道2的采样时间
ADC1->SMPR2 |=(0x7<<6);//SMP2[2:0],111,239.5周期
//通道3的采样时间
ADC1->SMPR2 |=(0x7<<9);//SMP3[2:0],111,239.5周期
ADC1->CR1 &=~(0xf<<16);//DUALMOD[3:0],0000,独立模式
ADC1->CR1 |=(1<<8);//扫描模式
ADC1->CR2 |=(1<1);//连续转换模式
ADC1->CR2 |=(1<20);//开启外部触发模式
ADC1->CR2 |=(0x7<<17);//开启软件触发EXTSEL[2:0]:111
ADC1->CR2 &=~(1<<11);//数据对齐:右对齐
ADC1->CR2 |=(1<<0);//开启AD转化
//硬件复位校准
ADC1->CR2 |=(1<<3);
//等待校准寄存器被复位初始化
while(ADC1->CR2 & (1<<3));//硬件校准初始化后该位为0
//软件开始校准
ADC1->CR2|=(1<<2);
while(ADC1->CR2 & (1<<2));//软件校准初始化后该位为0
#else
ADC_InitTypeDef ADC1_InitStructure;//定义ADC1结构体变量
GPIO_InitTypeDef GPIO_InitStructure;//定义GPIO结构体变量
//开端口A时钟,开ADC1时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_ADC1,ENABLE);
RCC_ADCCLKConfig(RCC_PCLK2_Div6); //设置ADC分频因子6 72M/6=12,ADC最大时间不能超过14M
//PA3 模拟输入
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
GPIO_InitStructure.GPIO_Mode =GPIO_Mode_AIN;
GPIO_Init(GPIOA, &GPIO_InitStructure);
ADC_DeInit(ADC1);
ADC1_InitStructure.ADC_Mode = ADC_Mode_Independent;
ADC1_InitStructure.ADC_ScanConvMode = DISABLE;
ADC1_InitStructure.ADC_ContinuousConvMode =DISABLE;
ADC1_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //转换由软件而不是外部触发启动
ADC1_InitStructure.ADC_DataAlign=ADC_DataAlign_Right;
ADC1_InitStructure.ADC_NbrOfChannel=1;
ADC_Init(ADC1,&ADC1_InitStructure);
ADC_Cmd(ADC1,ENABLE);
ADC_ResetCalibration(ADC1);
while(ADC_GetResetCalibrationStatus(ADC1)); //等待复位校准结束
ADC_StartCalibration(ADC1);
while(ADC_GetCalibrationStatus(ADC1)); //等待校准结束
#endif
}
/*存在bug,三个获取ADC转换的函数不能同时调用,MDA下三个数据可以都读取出来*/
/*************************************************************************
uint16_t GZ_ADCValue(void)
{
#if reg_program
//开启规则转换
ADC1->CR2 |=(0x1<22);
//等待转换完成
while(!(ADC1->SR &(1<<1)));
printf("GZ数据获取完成\r\n");
return ADC1->DR;
#else
ADC_SoftwareStartConvCmd(ADC1, ENABLE); //使能指定的ADC1的软件转换启动功能
ADC_RegularChannelConfig(ADC1,ADC_Channel_3,1, ADC_SampleTime_239Cycles5 ); //ADC1,ADC通道,采样时间为239.5周期
ADC_SoftwareStartConvCmd(ADC1, ENABLE); //使能指定的ADC1的软件转换启动功能
while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));//等待转换结束
return ADC_GetConversionValue(ADC1); //返回最近一次ADC1规则组的转换结果
#endif
}
#include "dma.h"
#include "delay.h"
#include "stdio.h"
uint16_t DMA_Buffer[30]={0};//DMA数据接收区
/*
函数名称:My_DMA_Confing
函数功能:DMA的初始化配置
函数参数:无
函数返回值:无
*/
void My_DMA_Confing(void)
{
//开启DMA时钟
RCC->AHBENR |=(1<<0);
Delay_nms(5);
//设置外设寄存器的地址
DMA1_Channel1->CPAR = (u32)&(ADC1->DR);
//设置数据存储区的地址
DMA1_Channel1->CMAR = (u32)DMA_Buffer;
//设置要传输的数据量
DMA1_Channel1->CNDTR =sizeof(DMA_Buffer)/sizeof(DMA_Buffer[0]);
//设置通道的优先级:高
DMA1_Channel1->CCR |=(2<<12);//优先级为高
//设置数据传输的方向:从外设读
DMA1_Channel1->CCR &=~(1<<4);//从外设读
//非存储器到存储器模式
DMA1_Channel1->CCR &=~(1<<14);//非存储器模式
//循环模式进行DMA转换
DMA1_Channel1->CCR |=(1<<5);//循环模式
DMA1_Channel1->CCR &=~(1<<6);//外设不增量模式,读取的是一个寄存器
DMA1_Channel1->CCR|=(1<<7); //存储器增量模式,数组要进行指针移动
DMA1_Channel1->CCR |=(1<<8);//外设数据宽度:16位
DMA1_Channel1->CCR |=(1<<10);//存储器数据宽度:16位
DMA1_Channel1->CCR|=1<<0;//开启DMA传输
ADC1->CR2 |=(1<<22);//启动ADC的规则转换
}
/*
函数名称:Sensor_DMA_ADCValue
函数功能:DMA方式获取3个通道的ADC数据
函数参数:无
函数返回值:无
*/
void Sensor_DMA_ADCValue(void)
{
RCC->AHBENR |=(1<<0);//开启DMA时钟
ADC1->CR2 |=(1<<8);//开启DMA模式进行ADC转化
u8 i = 0;
u8 lenth = ((ADC1->SQR1 &(0xf<<20))>>20);//获取转换通道数寄存器值
for(i=0;i<=lenth;i++)
{
printf("DMA_Buffer[%d]=%d ",i,DMA_Buffer[i]);
}
printf("\r\n");
printf("MQ135的值:DMA_Buffer[0]=%d\r\n",DMA_Buffer[0]);
printf("MQ2的值:DMA_Buffer[1]=%d\r\n",DMA_Buffer[1]);
printf("GZ的值:DMA_Buffer[2]=%d\r\n",DMA_Buffer[2]);
}
主函数里的测试代码
测试效果
总结
DMA存取数据每次都是读的是同一个寄存器,开了三个通道,转换完成的标志是规则组,规则组里面的通道数有三个,但是现在仍然不明白为什么不能一个通道一个通道的采集,我天真的写了三个几乎一模一样的函数去读取不同通道的数值,编译也能通过但是在主函数里调用就会卡死,我想这可能就是DMA的优势之处,搬砖这个叫法真的有点儿那个意思。可能转换通道哪里改成一之后我就可以调用了,每次只读取一次ADC值。明天再试吧,今天就这样了