STM32F030 ADC1的DMA采样问题

搞了1天一直ADC没有出来,发现采处理的值一直是固定值..

去21IC求助贴.[STM32F0] STM32F030 ADC1采样问题请教

等了好久都没有人回复,但有人提醒我说需要等待DMA的数据完成.

后面对比了别人的代码,

/* ADC DMA request in circular mode */
ADC_DMARequestModeConfig(ADC1, ADC_DMAMode_Circular);

别人有这句代码,于是我加上这个话,则能够正常采样到数据了,

所以,必须得添加,否则无法获取到采样值.


接着又发现DMA传输到指定的数组数据顺序错乱了.....

类似别人的求助贴:[STM32F0] STM32F030 多通道ADC DMA读取问题

后面解决方式,按照最后帖子的方式解决的

        " F0的ADC在使用之前需要校准。这个7位的校准值也是放在ADC_DR中的,它也会触发DMA请求。可以参照F0的ADC-DMA例程,先做ADC校准、然后再设置DMA,再使能ADC的DMA。"


直接插入代码,正确的初始化代码:

typedef enum
{
	ADC_PORTA0 = ADC_Channel_0,    
	ADC_PORTA1 = ADC_Channel_1,
	ADC_PORTA2 = ADC_Channel_2,
	ADC_PORTA3 = ADC_Channel_3,
	ADC_PORTA4 = ADC_Channel_4,
	ADC_PORTA5 = ADC_Channel_5,
	ADC_PORTA6 = ADC_Channel_6,
	ADC_PORTA7 = ADC_Channel_7,
	ADC_PORTA8 = ADC_Channel_8,
	ADC_PORTA9 = ADC_Channel_9,
	ADC_PORTA10 = ADC_Channel_10,
	ADC_PORTA11 = ADC_Channel_11,
	ADC_PORTA12 = ADC_Channel_12,
	ADC_PORTA13 = ADC_Channel_13,
	ADC_PORTA14 = ADC_Channel_14,
	ADC_PORTA15 = ADC_Channel_15,
}AD_PORT;

typedef enum 
{
	KEY_LINE_1,
	KEY_LINE_2,
	ADC_KEY_LINE_MAX = KEY_LINE_2,
	BATTERY_AD,
	ADC_NUM_CNT,	//ADC的总数
}ADC_NUM;


volatile u16 g_uADC_ConVal[ADC_NUM_CNT] = {0};		// ADC转换值


u32 const  g_uADNum[]=
{ 	
	//KEYPORTA1, 
	ADC_PORTA9,	 
	ADC_PORTA8,	 
	ADC_PORTA2,
	//KEYPORTA0,
};


void Adc_Init(void)
{
	ADC_DeInit(ADC1);   
	
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);

	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE);

	//打开DMA1的时钟
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
	//打开ADC1的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);

	RCC_ADCCLKConfig(RCC_ADCCLK_PCLK_Div4);

	//初始化IO口
	GPIO_InitTypeDef	GPIO_InitStruct;
	GPIO_StructInit(&GPIO_InitStruct);
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AN;
	GPIO_InitStruct.GPIO_Pin =	AD_KEY1_PIN;
	GPIO_Init(AD_KEY1_PORT,&GPIO_InitStruct);				// KEY1
	GPIO_InitStruct.GPIO_Pin =	AD_KEY2_PIN;
	GPIO_Init(AD_KEY2_PORT,&GPIO_InitStruct);				// KEY2
	GPIO_InitStruct.GPIO_Pin =	BATTERY_AD_PIN;
	GPIO_Init(BATTERY_AD_PORT,&GPIO_InitStruct);;			// 电池电源采样

	//配置ADC1的DMA模式
	ADC_InitTypeDef ADC_InitStructure;
	DMA_InitTypeDef DMA_InitStructure;
	DMA_DeInit(DMA1_Channel1);
	DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&(ADC1->DR);				//定义DMA外设基地址,即为存放转换结果的寄存器
	DMA_InitStructure.DMA_MemoryBaseAddr = (u32)g_uADC_ConVal;		//定义内存基地址
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;			//定义AD外设作为数据传输的来源
	DMA_InitStructure.DMA_BufferSize = ADC_NUM_CNT;		//指定DMA通道的DMA缓存的大小,即需要开辟几个内存空间,本实验有两个转换通道,所以开辟两个
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;	//设定寄存器地址固定
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;			//设定内存地址递加,即每次DMA都是将该外设寄存器中的值传到三个内存空间中
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;		//设定外设数据宽度
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;			//设定内存的的宽度
	DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;						//设定DMA工作再循环缓存模式
	DMA_InitStructure.DMA_Priority = DMA_Priority_High;					//设定DMA选定的通道软件优先级
	DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
	DMA_Init(DMA1_Channel1,&DMA_InitStructure);

	/* ADC DMA request in circular mode */
	ADC_DMARequestModeConfig(ADC1, ADC_DMAMode_Circular);					//必须得添加,否则无法获取到采样值

	ADC_StructInit(&ADC_InitStructure);
	ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;
	ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;					//设定AD转化在连续模式
	ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConvEdge_None;			//不使用外部促发转换
	ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Left;					//采集的数据在寄存器中以左对齐的方式存放
	ADC_InitStructure.ADC_ScanDirection = ADC_ScanDirection_Backward;
  	ADC_Init(ADC1, &ADC_InitStructure); 

	for(u8 uCnt = 0;uCnt < ADC_NUM_CNT;uCnt++)
	{
		/* Convert the ADC1  with 55.5 Cycles as sampling time */ 
		ADC_ChannelConfig(ADC1, g_uADNum[uCnt] , ADC_SampleTime_55_5Cycles); 	//设置指定ADC的规则组通道,设置它们的转化顺序和采样时间
	}
	
	//ADC_ChannelConfig(ADC1, ADC_Channel_2 , ADC_SampleTime_55_5Cycles); 	//设置指定ADC的规则组通道,设置它们的转化顺序和采样时间
	//ADC_ChannelConfig(ADC1, ADC_Channel_8 , ADC_SampleTime_55_5Cycles); 	//设置指定ADC的规则组通道,设置它们的转化顺序和采样时间
	//ADC_ChannelConfig(ADC1, ADC_Channel_9 , ADC_SampleTime_55_5Cycles); 	//设置指定ADC的规则组通道,设置它们的转化顺序和采样时间
	//ADC_DMARequestModeConfig(ADC1, ADC_DMAMode_Circular); 

	
	/* ADC Calibration */
	ADC_GetCalibrationFactor(ADC1);						//校准ADC

	
	DMA_Cmd(DMA1_Channel1,ENABLE);
	
	/* Enable ADC_DMA */
	ADC_DMACmd(ADC1, ENABLE);  

	ADC_Cmd(ADC1,ENABLE);																	//使能指定的ADC1
	while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_ADEN));			<span style="white-space:pre">	</span>//等待ADC准备好


	ADC_StartOfConversion(ADC1);						//启动转换
}


另外补充,ADC_ScanDirection_Upward及ADC_ScanDirection_Backward的简要说明。假设ADC1有18个通道,1,2....18

ADC_ScanDirection_Upward表示从1~18开始扫描

ADC_ScanDirection_Backward表示从18~1方向扫描

这样就决定了,用户指定的内存数组里面存值的顺序.

像上面的代码,则对应关系为g_uADC_ConVal[0]--->AIN9  g_uADC_ConVal[1]--->AIN8 g_uADC_ConVal[2]-->AIN2


来自:http://blog.csdn.net/lan120576664

  • 6
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
STM32F030可以通过ADC模块进行音频采样,但是采样率需要根据具体情况进行调整,因为F030ADC转换速度受到很多因素的影响,如时钟、采样时间、ADC分辨率等等,需要根据实际情况进行调试。 下面是一个简单的示例代码,以实现8K采样率的音频采样: ```c #include "stm32f0xx.h" #define SAMPLE_RATE 8000 #define NUM_SAMPLES (SAMPLE_RATE/1000) // 每毫秒采样点数 #define BUFFER_SIZE 16 // 缓冲区大小 uint16_t ADC_Data[BUFFER_SIZE][NUM_SAMPLES]; // 存储采样数据的缓冲区 volatile uint8_t Buffer_Ready = 0; // 缓冲区就绪标志 void ADC1_IRQHandler(void) { static uint8_t Buffer_Index = 0; // 当前缓冲区索引 static uint16_t Sample_Index = 0; // 当前采样点索引 if (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC)) { ADC_Data[Buffer_Index][Sample_Index++] = ADC_GetConversionValue(ADC1); if (Sample_Index >= NUM_SAMPLES) { Sample_Index = 0; if (++Buffer_Index >= BUFFER_SIZE) { Buffer_Index = 0; Buffer_Ready = 1; } } ADC_ClearFlag(ADC1, ADC_FLAG_EOC); } } int main(void) { // 初始化ADC GPIO GPIO_InitTypeDef GPIO_InitStruct; RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE); GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AN; GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0; GPIO_Init(GPIOA, &GPIO_InitStruct); // 初始化ADC时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); // 初始化ADC ADC_InitTypeDef ADC_InitStruct; ADC_DeInit(ADC1); ADC_InitStruct.ADC_Resolution = ADC_Resolution_12b; ADC_InitStruct.ADC_ContinuousConvMode = ENABLE; ADC_InitStruct.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None; ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right; ADC_InitStruct.ADC_ScanDirection = ADC_ScanDirection_Upward; ADC_Init(ADC1, &ADC_InitStruct); // 配置ADC通道 ADC_ChannelConfig(ADC1, ADC_Channel_0, ADC_SampleTime_28_5Cycles); // 开启ADC DMA ADC_DMARequestModeConfig(ADC1, ADC_DMAMode_Circular); ADC_DMACmd(ADC1, ENABLE); // 开启ADC中断 ADC_ITConfig(ADC1, ADC_IT_EOC, ENABLE); NVIC_EnableIRQ(ADC1_IRQn); // 启动ADC转换 ADC_Cmd(ADC1, ENABLE); ADC_StartOfConversion(ADC1); while (1) { if (Buffer_Ready) { // 处理采样数据 // ... Buffer_Ready = 0; } } } ``` 在上述代码中,我们开启了ADC中断,每当ADC转换完成时,就会触发中断处理函数ADC1_IRQHandler(),在该函数中,我们将采样数据存储到一个缓冲区中。当缓冲区满时,就将其标记为就绪状态,并等待主程序读取数据。 需要注意的是,上述代码中使用了DMA来加速ADC数据传输,这可以大大提高采样效率。同时,还需要根据具体情况进行采样时间和分辨率的配置,例如本例中使用了28.5个时钟周期的采样时间和12位分辨率。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值