STM32第十三课:DMA多通道采集光照烟雾


需求

通过DMA实现光照强度和烟雾浓度的多通道采集


一、DMA(直接存储器存取)

在这里插入图片描述
在这里插入图片描述
作用:
把外设的寄存器里面数据直接传输到存储器中
把存储器里面数据直接传输到外设的寄存器中
把存储器里面数据直接传输到存储器中
绕开了CPU。

二、实现流程

1、时钟使能
2、设置外设寄存器地址
3、设置存储器地址
4、设置要传输的数据量
5、设置通道优先级
6、设置传输方向:外设到存储器,还是存储器到存储器
循环模式
外设和存储器的增量模式:外设和存储器的地址是否向后偏移
外设和存储器的数据宽度:一个数据的大小(8位,16位,32位)
7、使通道和ADC转换

1.时钟使能

DMA有很多通道,本次配置的为DMA1通道
在这里插入图片描述
可见DMA1在AHB外设时钟使能寄存器的第0位。

RCC->AHBENR |= 0x1<<0;

2.设置外设寄存器地址

在这里插入图片描述
由于该ADC1的规则组通道数据寄存器在DR上,所以直接赋给就行。

DMA1_Channel1->CPAR = (uint32_t)&(ADC1->DR);//设置外设寄存器地址

3.设置存储器地址

存储器地址需要自己设置一个变量,我这里为了方便后续取中位数,定义了个结构体数组,存放烟雾和光照两种变量,每种最多能存放10个。
在这里插入图片描述

DMA1_Channel1->CMAR = (uint32_t)adcvalue;//设置存储器地址

结构体及定义

typedef struct{
	uint16_t light;
	uint16_t mq2;
}ADCARR;
ADCARR adcvalue[10]={0};

4.设置要传输的数据量

在这里插入图片描述
这个根据需求来设置,想要一次传多少数据就填多少。
我这里为了获取每组10个数据,共20个量。
所以这里我填写的是20。

DMA1_Channel1->CNDTR = 20;//3、设置要传输的数据量

5.设置通道优先级

在这里插入图片描述
在这里插入图片描述
由于此时就1个通道,所以填那个都无所谓。

DMA1_Channel1->CCR |= 0x3<<12;//4、设置通道优先级   最高

6.设置传输方向

外设到存储器,还是存储器到存储器
打开循环模式(循环读)
外设和存储器的增量模式:外设和存储器的地址是否向后偏移
外设和存储器的数据宽度:一个数据的大小(8位,16位,32位)
全在CCR寄存器上一位一位配就行了

		//5、设置传输方向:	//外设到存储器,还是存储器到存储器
	DMA1_Channel1->CCR &= ~(0x1<<14);//选择外设和存储器的之间的传输
	DMA1_Channel1->CCR &= ~(0x1<<4);//从外设读

	//外设和存储器的数据宽度:一个数据的大小(8位,16位,32位)
	DMA1_Channel1->CCR &= ~(0x3<<10);//存储器数据宽度
	DMA1_Channel1->CCR |= 0x1<<10;
	
	DMA1_Channel1->CCR &= ~(0x3<<8);//外设寄存器数据宽度
	DMA1_Channel1->CCR |= 0x1<<8;
	//外设和存储器的增量模式:外设和存储器的地址是否向后偏移
	DMA1_Channel1->CCR |= 0x1<<7;//存储器的指针增量打开
	DMA1_Channel1->CCR &= ~(0x1<<6);//外设的指针增量关闭
	DMA1_Channel1->CCR |= 0x1<<5;//循环模式打开

7.使通道和ADC转换

使能一下CRR第0位

	DMA1_Channel1->CCR |= 0x1<<0;

将ADC的扫描和循环开启,最后开启ADC1的转换

ADC_SoftwareStartConvCmd(ADC1, ENABLE);

此时ADC1就会不停转换,并将光照强度参数和烟雾浓度参数通过DMA从DR寄存器发送到我们定义的变量(存储器)中。

三、数据处理

为了使获取的数据更加精准,我定义了一个新的函数用来求光照和烟雾获取的每10个参数中间的平均数。
代码如下:

void Get_Smoke_Light_MidValue()
{
	uint16_t Mid_Light_Value[10]={0};
	uint16_t Mid_Smoke_Value[10]={0};
	uint16_t i=0,j=0,temp=0;
	for(i=0;i<10;i++){
		Mid_Light_Value[i] = adcvalue[i].light;
	}
  for(i=0;i<10;i++){
		Mid_Smoke_Value[i] = adcvalue[i].mq2;
	}
	for(i=0;i<10-1;i++)//光照
	{
		for(j=0;j<9-i;j++)
		{
			if(Mid_Light_Value[j]<Mid_Light_Value[j+1]){
				temp = Mid_Light_Value[j];
				Mid_Light_Value[j] = Mid_Light_Value[j+1];
				Mid_Light_Value[j+1] = temp;
				}
		}
	}
	temp=0;
		for(i=0;i<10-1;i++)//烟雾
	{
		for(j=0;j<9-i;j++)
		{
			if(Mid_Smoke_Value[j]<Mid_Smoke_Value[j+1]){
				temp = Mid_Smoke_Value[j];
				Mid_Smoke_Value[j] = Mid_Smoke_Value[j+1];
				Mid_Smoke_Value[j+1] = temp;
				}
		}
	}
	
	adcData.light = (Mid_Light_Value[4]+Mid_Light_Value[5])/2;
	adcData.mq2 = (Mid_Smoke_Value[4]+Mid_Smoke_Value[5])/2;
	printf("Light = %d\r\n",adcData.light);
	printf("Smoke = %d\r\n",adcData.mq2);
	return; 
}

四、需求实现

main.c

#include "stm32f10x.h"
#include "usart.h"
#include "stdio.h"
#include "delay.h"
#include "string.h"
#include "pwm.h"
#include "adc.h"


int main()
{
		NVIC_SetPriorityGrouping(5);//两位抢占两位次级
    Usart1_Config(); 
	  SysTick_Config(72000);
	  RGBpwm_Config();
	  uint8_t cai_count=0;
	  uint16_t cont=0;
	  Adc_Config();
    while(1)
    {	
			if(ledcnt[0]>=ledcnt[1]){//过去3s
			ledcnt[0]=0;
					Get_Smoke_Light_MidValue();
		}
    }
		
		return 0;
}

adc.c

#include "ADC.h"

ADCARR adcvalue[10]={0};
ADCARR adcData={0};

//库函数
void Adc_Config(void)
{
	//开时钟ADC1和PC,PA
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOC|RCC_APB2Periph_ADC1,ENABLE);
	RCC_ADCCLKConfig(RCC_PCLK2_Div6); 
	//配置DMA
	RCC->AHBENR |= 0x1<<0;//开启DMA时钟
	DMA1_Channel1->CPAR = (uint32_t)&(ADC1->DR);//设置外设寄存器地址
	DMA1_Channel1->CMAR = (uint32_t)adcvalue;//设置存储器地址
	DMA1_Channel1->CNDTR = 20;//3、设置要传输的数据量
	DMA1_Channel1->CCR |= 0x3<<12;//4、设置通道优先级   最高
		//5、设置传输方向:	//外设到存储器,还是存储器到存储器
	DMA1_Channel1->CCR &= ~(0x1<<14);//选择外设和存储器的之间的传输
	DMA1_Channel1->CCR &= ~(0x1<<4);//从外设读

	//外设和存储器的数据宽度:一个数据的大小(8位,16位,32位)
	DMA1_Channel1->CCR &= ~(0x3<<10);//存储器数据宽度
	DMA1_Channel1->CCR |= 0x1<<10;
	
	DMA1_Channel1->CCR &= ~(0x3<<8);//外设寄存器数据宽度
	DMA1_Channel1->CCR |= 0x1<<8;
	//外设和存储器的增量模式:外设和存储器的地址是否向后偏移
	DMA1_Channel1->CCR |= 0x1<<7;//存储器的指针增量打开
	DMA1_Channel1->CCR &= ~(0x1<<6);//外设的指针增量关闭
	DMA1_Channel1->CCR |= 0x1<<5;//循环模式打开
	//6、使能所使用的通道
	DMA1_Channel1->CCR |= 0x1<<0;
	
	//配置GPIO口
	GPIO_InitTypeDef GPIO_InitStructure;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;//PA5,光敏
  GPIO_Init(GPIOA, &GPIO_InitStructure);
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;//PC1,烟雾
  GPIO_Init(GPIOC, &GPIO_InitStructure);
	//配置ADC1
	ADC_InitTypeDef ADC_InitStruct={0};  
	ADC_InitStruct.ADC_Mode = ADC_Mode_Independent;//ADC独立模式
	ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right;//数据右对齐
	ADC_InitStruct.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//选择软件SWSTART位触发
	ADC_InitStruct.ADC_ContinuousConvMode = ENABLE;//连续模式
	ADC_InitStruct.ADC_ScanConvMode = ENABLE;//开启扫描
	ADC_InitStruct.ADC_NbrOfChannel = 2;
	ADC_Init(ADC1,&ADC_InitStruct);
	//配置通道
	ADC_RegularChannelConfig(ADC1, ADC_Channel_5,1, ADC_SampleTime_239Cycles5);
	ADC_RegularChannelConfig(ADC1, ADC_Channel_11,2, ADC_SampleTime_239Cycles5);	
	ADC_Cmd(ADC1, ENABLE);
	ADC1->CR2 |= 0x1<<8;//开启ADC的DMA请求
	//校准
	ADC_ResetCalibration(ADC1);
	  while(ADC_GetResetCalibrationStatus(ADC1));
  ADC_StartCalibration(ADC1);
		while(ADC_GetCalibrationStatus(ADC1));	
	ADC_SoftwareStartConvCmd(ADC1, ENABLE);
}

void Get_Smoke_Light_MidValue()
{
	uint16_t Mid_Light_Value[10]={0};
	uint16_t Mid_Smoke_Value[10]={0};
	uint16_t i=0,j=0,temp=0;
	for(i=0;i<10;i++){
		Mid_Light_Value[i] = adcvalue[i].light;
	}
  for(i=0;i<10;i++){
		Mid_Smoke_Value[i] = adcvalue[i].mq2;
	}
	for(i=0;i<10-1;i++)//光照
	{
		for(j=0;j<9-i;j++)
		{
			if(Mid_Light_Value[j]<Mid_Light_Value[j+1]){
				temp = Mid_Light_Value[j];
				Mid_Light_Value[j] = Mid_Light_Value[j+1];
				Mid_Light_Value[j+1] = temp;
				}
		}
	}
	temp=0;
		for(i=0;i<10-1;i++)//烟雾
	{
		for(j=0;j<9-i;j++)
		{
			if(Mid_Smoke_Value[j]<Mid_Smoke_Value[j+1]){
				temp = Mid_Smoke_Value[j];
				Mid_Smoke_Value[j] = Mid_Smoke_Value[j+1];
				Mid_Smoke_Value[j+1] = temp;
				}
		}
	}
	
	adcData.light = (Mid_Light_Value[4]+Mid_Light_Value[5])/2;
	adcData.mq2 = (Mid_Smoke_Value[4]+Mid_Smoke_Value[5])/2;
	printf("Light = %d\r\n",adcData.light);
	printf("Smoke = %d\r\n",adcData.mq2);
	return; 
}

adc.h

#ifndef _ADC_H_
#define _ADC_H_
#include "stm32f10x.h"
#include "stdio.h"


typedef struct{
	uint16_t light;
	uint16_t mq2;
}ADCARR;
void Get_Smoke_Light_MidValue();
void Adc_Config(void);
#endif
		

总结

1.先开时钟,配置DMA。
2.打开ADC1的两个通道(烟雾和光照),开启扫描和循环。
3.使能DMA并开启ADC转换。
4.数据处理及主函数调用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值