使用DMA功能操作外设时,可以极大的简化代码,提高程序的执行效率。特别是在需要频繁操作的外设上。比如现在要采集单片机16个ADC通道的电压值,就可以使用DMA功能,直接将ADC通道转换好的值,传输到数组中。需要操纵ADC的值时,直接去数组中拿数据就行。不需要再去判断ADC数据转换是否结束。下面直接通过代码来实现。
首先初始化ADC,这里将ADC的16个采样通道全部开启。
- void ADC1_Init ( void )
- {
- GPIO_InitTypeDef GPIO_InitStructure;
- ADC_InitTypeDef ADC_InitStructure;
-
- RCC_APB2PeriphClockCmd ( RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOC | RCC_APB2Periph_ADC1 | RCC_APB2Periph_AFIO, ENABLE );
- RCC_ADCCLKConfig ( RCC_PCLK2_Div6 ); // 72M / 6 = 12M ADC最大不能超过14M
- // ADC1 --- ADC8
- 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;
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
- GPIO_Init ( GPIOA, &GPIO_InitStructure );
- // ADC9 ADC10
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
- GPIO_Init ( GPIOB, &GPIO_InitStructure );
- // ADC11 --- ADC 16
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5;
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
- GPIO_Init ( GPIOC, &GPIO_InitStructure );
- ADC_DeInit ( ADC1 );
- ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //独立模式
- ADC_InitStructure.ADC_ScanConvMode = ENABLE; //扫描模式
- ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; //连续转换
- ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //不使用外部触发
- ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //数据右对齐
- ADC_InitStructure.ADC_NbrOfChannel = 16; //ADC通道数
- ADC_Init ( ADC1, &ADC_InitStructure );
- //规则通道配置
- ADC_RegularChannelConfig ( ADC1, ADC_Channel_0, 1, ADC_SampleTime_239Cycles5 );
- ADC_RegularChannelConfig ( ADC1, ADC_Channel_1, 2, ADC_SampleTime_239Cycles5 );
- ADC_RegularChannelConfig ( ADC1, ADC_Channel_2, 3, ADC_SampleTime_239Cycles5 );
- ADC_RegularChannelConfig ( ADC1, ADC_Channel_3, 4, ADC_SampleTime_239Cycles5 );
- ADC_RegularChannelConfig ( ADC1, ADC_Channel_4, 5, ADC_SampleTime_239Cycles5 );
- ADC_RegularChannelConfig ( ADC1, ADC_Channel_5, 6, ADC_SampleTime_239Cycles5 );
- ADC_RegularChannelConfig ( ADC1, ADC_Channel_6, 7, ADC_SampleTime_239Cycles5 );
- ADC_RegularChannelConfig ( ADC1, ADC_Channel_7, 8, ADC_SampleTime_239Cycles5 );
- ADC_RegularChannelConfig ( ADC1, ADC_Channel_8, 9, ADC_SampleTime_239Cycles5 );
- ADC_RegularChannelConfig ( ADC1, ADC_Channel_9, 10, ADC_SampleTime_239Cycles5 );
- ADC_RegularChannelConfig ( ADC1, ADC_Channel_10, 11, ADC_SampleTime_239Cycles5 );
- ADC_RegularChannelConfig ( ADC1, ADC_Channel_11, 12, ADC_SampleTime_239Cycles5 );
- ADC_RegularChannelConfig ( ADC1, ADC_Channel_12, 13, ADC_SampleTime_239Cycles5 );
- ADC_RegularChannelConfig ( ADC1, ADC_Channel_13, 14, ADC_SampleTime_239Cycles5 );
- ADC_RegularChannelConfig ( ADC1, ADC_Channel_14, 15, ADC_SampleTime_239Cycles5 );
- ADC_RegularChannelConfig ( ADC1, ADC_Channel_15, 16, ADC_SampleTime_239Cycles5 );
- ADC_DMACmd ( ADC1, ENABLE ); //使能ADC DMA传输
- ADC_Cmd ( ADC1, ENABLE ); //使能ADC1
-
- ADC_ResetCalibration ( ADC1 ); //使能复位校准
- while ( ADC_GetResetCalibrationStatus ( ADC1 ) ); //等待复位校准结束
-
- ADC_StartCalibration ( ADC1 ); //开启AD校准
- while ( ADC_GetCalibrationStatus ( ADC1 ) ); //等待校准结束
- // ADC_SoftwareStartConvCmd ( ADC1, ENABLE ); //启动转换
- }
复制代码
ADC设置为扫描模式,连续转换,16个通道依次扫描采样,一轮采样结束后,接着开启下一轮采样。
下面初始化DMA功能。
- void MYDMA_Config ( DMA_Channel_TypeDef *DMA_CHx, u32 cpar, u32 cmar, u16 cndtr )
- {
- DMA_InitTypeDef DMA_InitStructure;
- RCC_AHBPeriphClockCmd ( RCC_AHBPeriph_DMA1, ENABLE );
-
- DMA_DeInit ( DMA_CHx );
-
- DMA_InitStructure.DMA_PeripheralBaseAddr = cpar; //外设基地址
- DMA_InitStructure.DMA_MemoryBaseAddr = cmar; //内存基地址
- DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //数据传输从外设到内存
- DMA_InitStructure.DMA_BufferSize = cndtr; //DMA缓存
- DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设地址不变
- DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //内存地址递增
- DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; //外设数据16位
- DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; //内存地址1位
- 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的数据方向要设置为从外设到内存,也就是将外设ADC采样的数据结果保存到内部存储空间中。
初始化完成之后,在主函数中直接启动DMA转换功能。
- #define ADC1_DR_Address ((uint32_t)0x4001244C)
- u16 AD_Value[6];
- float volta[16];
- u8 i, j;
- uint16_t ADC1ConvertedValue[10][16];
- uint32_t ADC1ConvertedVoltage[16];
- int main ( void )
- {
- delay_init(); //延时函数初始化
- NVIC_PriorityGroupConfig ( NVIC_PriorityGroup_2 );
- uart_init ( 115200 );
- LED_Init(); //初始化与LED连接的硬件接口
- MYDMA_Config ( DMA1_Channel1, ( u32 ) &ADC1->DR, ( u32 ) &ADC1ConvertedValue, 160 );
- ADC1_Init();
- DMA_Cmd ( DMA1_Channel1, ENABLE ); //启动DMA通道
- ADC_SoftwareStartConvCmd ( ADC1, ENABLE ); //启动ADC1软件转换
- while ( 1 )
- {
- ADC1ConvertedVoltage[0] = 0;
- for ( i = 0; i < 16; i++ )
- {
- ADC1ConvertedVoltage= 0;
- for ( j = 0; j < 10; j++ ) //采样10次取平均值
- {
- ADC1ConvertedVoltage += ADC1ConvertedValue[j];
- }
- ADC1ConvertedVoltage = ADC1ConvertedVoltage / 10;
- }
- for(i=0;i<16;i++)
- {
- volta = ADC1ConvertedVoltage * 3.3 / 4096;
- printf("AD%2d: value: %4d vol: %f\r\n",i,ADC1ConvertedVoltage,volta);
- }
- printf ( "\r\n\r\n" );
- LED0 = 0;
- LED1 = 1;
- delay_ms ( 500 ); //延时300ms
- LED0 = 1;
- LED1 = 0;
- delay_ms ( 500 ); //延时300ms
- }
- }
复制代码
通过一个二维数据存储16个通道的转换值,每个通道存储10个数据然后取平均值。最后将采样的16个通道值依次打印出来。
|