文章目录
- 需求
- 一、DMA(直接存储器存取)
- 二、实现流程
- 1.时钟使能
- 2.设置外设寄存器地址
- 3.设置存储器地址
- 4.设置要传输的数据量
- 5.设置通道优先级
- 6.设置传输方向
- 7.使通道和ADC转换
- 三、数据处理
- 四、需求实现
- 总结
需求
通过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]){//过去3sledcnt[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,PARCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOC|RCC_APB2Periph_ADC1,ENABLE);RCC_ADCCLKConfig(RCC_PCLK2_Div6); //配置DMARCC->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);//配置ADC1ADC_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.数据处理及主函数调用。