昨天學習了DMA寫了一個用DMA讀取ADC資料的程式,記錄下整個過程和一點心得
DMA配置詳細說明
(MDK的漢字2複制過來就是亂碼,我重新打了一遍注釋,暈~)
//DMA1各通道配置
//外設->存儲器/16位資料寬度
//DMA_CHx:DMA通道CHx
//cpar:外設位址
//cmar:存儲器位址
//cndtr:資料傳輸量(因為我是一個16位的資料,是以是一)
void MYDMA_Config(DMA_Channel_TypeDef* DMA_CHx,u32 cpar,u32 cmar,u16 cndtr)
{
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //使能DMA時鐘
DMA_DeInit(DMA_CHx); //重設DMA為預設值
DMA_InitStructure.DMA_PeripheralBaseAddr = cpar; //外設位址
DMA_InitStructure.DMA_MemoryBaseAddr = cmar; //存儲器位址
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //外設到存儲器的傳輸模式
DMA_InitStructure.DMA_BufferSize = 1; //資料量為1(應該為cndtr參數)
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable; //
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord ; //16位!!!特别注意
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord ; //16位!!!特别注意
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; //循環模式
DMA_InitStructure.DMA_Priority = DMA_Priority_High; //優先級高
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //(記憶體到記憶體禁止)
DMA_Init(DMA_CHx,&DMA_InitStructure); //初始化
DMA_SetCurrDataCounter(DMA1_Channel1,1);//設定資料量(應該為cndtr參數,若有其它需要改變即
//可,我這裡是為了直覺)
}
ADC相關配置
//ADC初始化
void Adc_Init(void)
{
ADC_InitTypeDef ADC_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |RCC_APB2Periph_ADC1 , ENABLE ); //相關時鐘使能
RCC_ADCCLKConfig(RCC_PCLK2_Div6); //設定分頻因子,ADC時間最大不過14M
//PA1寄存器初始化
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模拟輸入模式
GPIO_Init(GPIOA, &GPIO_InitStructure);
ADC_DeInit(ADC1);
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //獨立模式,不與其它ADC協作
ADC_InitStructure.ADC_ScanConvMode = DISABLE; //單通道模式
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; //!!!單次轉換
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //!!軟體觸發
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //資料右對齊
ADC_InitStructure.ADC_NbrOfChannel = 1; //
ADC_Init(ADC1, &ADC_InitStructure); //
ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1, ADC_SampleTime_239Cycles5 );
//配置采樣時間
ADC_Cmd(ADC1, ENABLE); //使能指定ADC
ADC_DMACmd(ADC1, ENABLE);//使能ADC - DMA
ADC_ResetCalibration(ADC1); //複位校準
while(ADC_GetResetCalibrationStatus(ADC1)); //等待複位校準
ADC_StartCalibration(ADC1); //開啟AD校準
while(ADC_GetCalibrationStatus(ADC1)); //等待結束
ADC_SoftwareStartConvCmd(ADC1, ENABLE); //軟體啟動
}
主程式
u16 SendBuff = 0; //資料緩存區
u16 MYGet_Adc()
{
u32 temp_val=0;
u8 t;
for(t=0;t<5;t++)
{
temp_val+=SendBuff;
delay_ms(5);
}
return temp_val/5;
}
int main(void)
{
u16 adcx = 0;//用于顯示的資料
float temp;
delay_init();
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
uart_init(115200); //波特率
LED_Init();
LCD_Init();
Adc_Init();
POINT_COLOR=BLUE;//字型顔色
LCD_ShowString(60,0,300,24,24,"MODE: ADC-DMA");
LCD_ShowString(60,300,300,24,24,"ADC_CH0_VAL:");
LCD_ShowString(60,350,300,24,24,"ADC_CH0_VOL:0.000V");
POINT_COLOR=RED;
MYDMA_Config(DMA1_Channel1,(u32)&ADC1->DR, (u32)&SendBuff,1);//配置說明見上
DMA_Cmd(DMA1_Channel1, ENABLE); //開啟DMA通道
while(1)
{
adcx = MYGet_Adc();//»ñÈ¡DMAÊý¾Ý
LCD_ShowxNum(204,300,adcx,4,24,0);//直接讀取的值
temp=(float)adcx*(3.3/4096);
adcx=temp;
LCD_ShowxNum(204,350,adcx,1,24,0);//轉化為電壓
temp-=adcx;
temp*=1000;
LCD_ShowxNum(228,350,temp,3,24,0X80);
LED1 = ~LED1;//提示程式正常運作
delay_ms(250);
}
}
可能出現的問題:
資料錯位:可能是DMA啟動在ADC之前,而你設定存儲器主動加一,那資料自然錯位
資料殘缺:配置DMA時設定錯誤,一定要仔細
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord ; //16位!!!特别注意
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord ; //16位!!!特别注意