文章目錄
- 需求
- 一、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.數據處理及主函數調用。