昨天学习了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位!!!特别注意
ADC读了一次就不读了:ADC配置循环ENABLE即可
后记:
参数的理解是第一位,不能只看别人的代码复制过来,要自己去明白理解才行。