DMA的英文Direct memory access,意思就是傳輸將數據從一個地址空間復制到另一個地址空間,設置好后
自動傳輸而不需要處理器參與。STM32F030F4只有DMA1,DMA1有5個通道,要想使用必須進行一些設置。
下面以ADC多通道采集為例,大致分3步:
1. GPIO的配置。該配置主要設置ADC采集涉及的IO口,本例設置ADC連接9個IO口進行模擬量采集。
2. ADC的配置。設置多通道采集速率、連續轉換模式、使能和DMA通道的連接等。注意該芯片只有ADC1。
3. DMA的配置。設置DMA的開啟、源地址、目標地址等。該芯片只有DMA1,但有5個通道,本例用通道1。
下面是main.c代碼:
#include "stm32f0xx.h"
#include "stm32f0xx_rcc.h"
#include "stm32f0xx_gpio.h"
#include "stm32f0xx_adc.h"
#include "stm32f0xx_dma.h"
// 原版創作,引用請注明出處:https://www.cnblogs.com/beiyhs/p/12061278.html 北有寒山
// 6 腳PA0 ADC_IN0
// 7 腳PA1 ADC_IN1
// 8 腳PA2 ADC_IN2
// 9 腳PA3 ADC_IN3
// 10腳PA4 ADC_IN4
// 11腳PA5 ADC_IN5
// 12腳PA6 ADC_IN6
// 13腳PA7 ADC_IN7
// 14腳PB1 ADC_IN9 注意:STM32F030F4芯片沒有ADC_IN8通道哦!
#define N 50 //預定義每通道采50次
#define M 9 //預定義為9個通道
uint16_t ad_value[N][M]; //定義二維數組,用來存放ADC轉換結果,也是DMA的目標地址
uint16_t ad_avg[M]; //9個ADC通道采集50次后平均值結果,M從0-8
float adc_data[M]; //9個ADC通道平均值轉換電壓值結果,M從0-8
void GPIO_cfg(void); // 1. GPIO的配置函數聲明
void ADC1_cfg(void); // 2. ADC的配置函數聲明
void DMA_cfg(void); // 3. DMA的配置函數聲明
void delay_ms(void); // 延時函數
void filter(void); // 9通道,每通道50次求平均函數
//*************************************************************************************************
void GPIO_cfg(void)
{
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE); //打開A口時鍾
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3|
GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7; //PA0/1/2/3/4/5/6/7/ 作為模擬通道輸入引腳
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN; //模擬輸入引腳
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; //PB1 作為模擬通道輸入引腳
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN; //模擬輸入引腳
GPIO_Init(GPIOB, &GPIO_InitStructure);
}
//***************************************************************************************************************
void ADC1_cfg(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE); //使能AHB預分頻器到外設ADC1的開關
ADC_DeInit(ADC1); //ADC復位
RCC_ADCCLKConfig(RCC_ADCCLK_PCLK_Div4) ; //時鍾分頻48M/4=12M 最大時鍾不超過14M
ADC_InitTypeDef ADC_InitStruct; //聲明ADC結構變量,在stm32f0xx_adc.c中
ADC_InitStruct.ADC_Resolution=ADC_Resolution_12b; //采集設為12位精度即4095
ADC_InitStruct.ADC_ContinuousConvMode=ENABLE; //轉換工作在連續轉換模式
ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right; //ADC數據右對齊
ADC_InitStruct.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None; //外部觸發轉換關閉
ADC_InitStruct.ADC_ScanDirection = ADC_ScanDirection_Upward; //1-9升序,Backward降序
ADC_Init(ADC1,&ADC_InitStruct); //根據ADC_InitStruct中指定的參數初始化外設ADCx的寄存器
//設置指定ADC的規則組通道,設置它們的轉化順序和采樣時間
ADC_ChannelConfig(ADC1,ADC_Channel_0,ADC_SampleTime_55_5Cycles);
ADC_ChannelConfig(ADC1,ADC_Channel_1,ADC_SampleTime_55_5Cycles);
ADC_ChannelConfig(ADC1,ADC_Channel_2,ADC_SampleTime_55_5Cycles);
ADC_ChannelConfig(ADC1,ADC_Channel_3,ADC_SampleTime_55_5Cycles);
ADC_ChannelConfig(ADC1,ADC_Channel_4,ADC_SampleTime_55_5Cycles);
ADC_ChannelConfig(ADC1,ADC_Channel_5,ADC_SampleTime_55_5Cycles);
ADC_ChannelConfig(ADC1,ADC_Channel_6,ADC_SampleTime_55_5Cycles);
ADC_ChannelConfig(ADC1,ADC_Channel_7,ADC_SampleTime_55_5Cycles);
ADC_ChannelConfig(ADC1,ADC_Channel_9,ADC_SampleTime_55_5Cycles);
//ADC_ChannelConfig(ADC1,ADC_Channel_0, ADC_SampleTime_239Cycles5 ); //等待更長采集周期
ADC_GetCalibrationFactor(ADC1); //校准ADC
ADC_DMACmd(ADC1, ENABLE); //使能ADC1的DMA通道,還需獨立配置DMA通道等參數
ADC_Cmd(ADC1,ENABLE); //使能的ADC1
while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_ADEN)); //等待ADC准備好
ADC_StartOfConversion(ADC1); //啟動轉換
}
//****************************************************************************************************
void DMA_cfg(void)
{
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //1 使能DMA傳輸,開啟DMA時鍾
DMA_InitTypeDef DMA_InitStructu; //2 聲明DMA結構變量
DMA_DeInit(DMA1_Channel1); //3 將DMA的通道1寄存器復位
DMA_InitStructu.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR; //4 DMA外設ADC基地址 源地址
DMA_InitStructu.DMA_MemoryBaseAddr = (uint32_t)&ad_value; //5 DMA目標地址,內存AD_Value首地址
DMA_InitStructu.DMA_DIR = DMA_DIR_PeripheralSRC; //6 內存作為數據傳輸的目的地 傳輸方向
DMA_InitStructu.DMA_BufferSize = N*M; //7 DMA通道的DMA緩存的大小50*9 每次
DMA_InitStructu.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //8 外設地址寄存器不變 源地址不變
DMA_InitStructu.DMA_MemoryInc = DMA_MemoryInc_Enable; //9 內存地址寄存器遞增 目標地址加1
DMA_InitStructu.DMA_PeripheralDataSize=DMA_PeripheralDataSize_HalfWord; //10 ADC 數據寬度為16位
DMA_InitStructu.DMA_MemoryDataSize=DMA_MemoryDataSize_HalfWord; //11 內存 數據寬度為16位
DMA_InitStructu.DMA_Mode = DMA_Mode_Circular; //12 工作在循環緩存模式/Normal正常轉換
DMA_InitStructu.DMA_Priority = DMA_Priority_High; //13 DMA通道 高優先級 Medium 中優先
DMA_InitStructu.DMA_M2M = DMA_M2M_Disable; //14 DMA通道 關閉內存到內存傳輸
DMA_Init(DMA1_Channel1, &DMA_InitStructu); //15 據DMA_InitStruct指定參數初始化DMA通道1
DMA_Cmd(DMA1_Channel1, ENABLE); //16 啟動DMA通道
}
//***********************************************************************************************
void filter(void)
{
int sum = 0; //每通道累加和變量
uint8_t count_M=0; //通道數變量
uint8_t count_N=0; //次數變量
for( count_M=0;count_M<M;count_M++) //M為9列,就是通道數,從0-9
{
for(count_N=0;count_N<N;count_N++) //N為50行,就是次數,從0-50
{
sum += ad_value[count_N][count_M]; //通道50次ADC值求和
}
ad_avg[count_M]=sum/N; //通道50次ADC值求平均
sum=0; //完成通道后清空和值,進入下一輪通道
}
}
//*******************************************************************************************
void delay_ms(void)
{
unsigned char i,j;
for(i=255;i>0;i--)
{
for(j=200;j>0;j--);
}
}
//**********************************************************************************************
//**********************************************************************************************
int main(void)
{
GPIO_cfg(); //初始化9個ADC的IO口
ADC1_cfg(); //初始ADC
DMA_cfg(); //初始DMA
while(1) //主循環
{
delay_ms(); //延時等待9通道50次采集結束
filter(); //ADC0-9通道50次采集求平均
adc_data[0]=(ad_avg[0]*3.263)/4095; //PA0 ADC通道0平均后轉換電壓值
adc_data[1]=(ad_avg[1]*3.263)/4095; //PA1 ADC通道1平均后轉換電壓值
adc_data[2]=(ad_avg[2]*3.263)/4095; //PA2 ADC通道2平均后轉換電壓值
adc_data[3]=(ad_avg[3]*3.263)/4095; //PA3 ADC通道3平均后轉換電壓值
adc_data[4]=(ad_avg[4]*3.263)/4095; //PA4 ADC通道4平均后轉換電壓值
adc_data[5]=(ad_avg[5]*3.263)/4095; //PA5 ADC通道5平均后轉換電壓值
adc_data[6]=(ad_avg[6]*3.245)/4095; //PA6 ADC通道6平均后轉換電壓值
adc_data[7]=(ad_avg[7]*3.263)/4095; //PA7 ADC通道7平均后轉換電壓值
adc_data[8]=(ad_avg[8]*3.263)/4095; //PB1 ADC通道9平均后轉換電壓值
}
說明:
1. 程序在uVision開發環境仿真運行,掛載的STM32F030F4實驗板的PA0、PA1等9個通道給入模
擬電壓。在程序adc_data[8]=(ad_avg[8]*3.263)/4095; 等處設斷點,則可看到采集電壓值。
2. 程序在GPIO_cfg()、ADC1_cfg()、DMA_cfg() 函數執行完后,ADC的采集已經開始並且DMA
通道已經打開。這時ADC執行通道0-9的連續循環采集,但采集值存放的地址不變只有一個,每
次采集完由DMA傳送到內存的二維數組ad_value[N][M],每采集DMA傳輸一次,數組從首地址
開始加1,直到N*M=9*50個地址用完,DMA重新從ad_value[N][M]首地址再傳,周而復始。
3. ADC通過DMA傳輸采集值到內存過程,不需要處理器干預,可以騰出資源處理別的事物。上例
只是在主環里面需要轉換電壓值時,從數組讀取數據、求平均並轉換,需要處理器計算。