stm32l151 ADC通过DMA通道定时采样电池电量

  最近在使用stm32l151开发一个项目,我的项目需求是ADC采集电池电量,通过DMA通道传送出来。然而我并不是得到了电池电量数据后就立马连续输出,而是通过tim4定时器每1s访问一次采样得到的电池数据,并显示出来。本来网上关于stm32通过adc通道采集电池电量的代码很多,但要找到和我的需求一样的,还真没有。于是在借鉴其他人代码的基础上,根据我的特殊需求,写了一份这样功能的代码。

  因为我的需求涉及到了tim4定时器,adc和dma,所以在最终配置的时候也分为几个部分:
  1.首先是定时器tim4。
   a)初始化

void vTim4_Init(void)
{
 TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
 
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);

	/* Time base configuration */
	
	TIM_TimeBaseStructure.TIM_Period = TIMER4_PERIOD_TIMING;
	
	TIM_TimeBaseStructure.TIM_Prescaler = u32Tim4PrescalerValue;

	TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;

	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;	
	
	TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure);	
		
	/* TIM IT enable */
	TIM_ITConfig(TIM4, TIM_IT_Update |TIM_IT_Trigger, ENABLE); 

 	/* TIM14 disable */
	TIM_Cmd(TIM4, DISABLE);		
}

   b)NVIC中断设置

/* Enable the TIMER4 Interrupt */
	NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 10;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
	NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;			
	NVIC_Init(&NVIC_InitStructure);
}

   c)Tim4中断函数书写,我这里是通过给任务一个信号量longtimerTaskSemaphoreHandler,唤醒获取电量的任务来获取电量数据并显示。

void vTimer4_Interrupt(void)
{
	BaseType_t xHigherPriorityTaskWoken;
	if(TIM_GetITStatus(TIM4, TIM_IT_Update) != RESET)
	{
		xSemaphoreGiveFromISR(longtimerTaskSemaphoreHandler,&xHigherPriorityTaskWoken);
		if(xHigherPriorityTaskWoken == pdTRUE)
			portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
		TIM_ClearITPendingBit(TIM4, TIM_IT_Update);
	}
}

  2.其次是adc。
   a)adc的GPIO口初始化

void ADC_GPIO_INIT()
{
	GPIO_InitTypeDef GPIO_InitStructure;

	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);  //打开对应GPIO的时钟
	GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_1;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
}

   b)adc通道的初始化设置

void ADC_INIT()
{
	ADC_InitTypeDef ADC_InitStructure;

	RCC_HSICmd(ENABLE);                    //开启HSI时钟,非常重要,stm32l151的时钟由HSI提供
	while(RCC_GetFlagStatus(RCC_FLAG_HSIRDY) == RESET); 			
		RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);		     //使能ADC1时钟
	ADC_InitStructure.ADC_ScanConvMode             = DISABLE;        //关闭扫描模式,因为多ADC通道同时使用时才打开
	ADC_InitStructure.ADC_ContinuousConvMode       = ENABLE;        //连续转换模式,因为我需要连续取十次数据
	ADC_InitStructure.ADC_ExternalTrigConvEdge     = ADC_ExternalTrigConvEdge_None;        //不适用外部触发转换
	ADC_InitStructure.ADC_DataAlign                = ADC_DataAlign_Right;                 //采集数据右对齐
	ADC_InitStructure.ADC_Resolution               = ADC_Resolution_12b;       //分辨率是12位
	ADC_InitStructure.ADC_NbrOfConversion          = 1;                  //要转换的通道数目1
	ADC_Init(ADC1, &ADC_InitStructure);

	ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1, ADC_SampleTime_48Cycles);     //设置adc1的通道0的转换周期为48个采样周期
	ADC_DMACmd(ADC1, ENABLE);                                                 //启动DMA搬运ADC数值
	ADC_Cmd(ADC1, ENABLE);                                                    //打开ADC
	
	while(ADC_GetFlagStatus(ADC1, ADC_FLAG_ADONS) == RESET);    //等待ADC1转换完成,必须返回值是SET  
		ADC_SoftwareStartConv(ADC1);                            //打开软件触发
	
}

  3.dma的设置。
   a)dma初始化设置

void DMA_CONFIG()
{
	DMA_InitTypeDef DMA_InitStructure;

	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);                       //开启DMA时钟
	
	DMA_DeInit(DMA1_Channel1);                                             //DMA复位
	DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&(ADC1->DR));             //ADC地址
	DMA_InitStructure.DMA_MemoryBaseAddr     = (uint32_t)&ADC_ConvertedValue;     //存储地址
	DMA_InitStructure.DMA_DIR                        = DMA_DIR_PeripheralSRC;     //DMA传输方向为设备到内存
	DMA_InitStructure.DMA_BufferSize               = 10;                   //存储大小,所以前面的ADC我设置的是连续转换
	DMA_InitStructure.DMA_PeripheralInc          = DMA_PeripheralInc_Disable;                //外存地址固定                 
	DMA_InitStructure.DMA_MemoryInc            = DMA_MemoryInc_Enable;                 //内存地址递增,这样从ADC转换过来的数据才能依次传到目标地址上,否则的话就是固定写在一个地址上,并且前面的数据把后面的覆盖了                                                                             
	DMA_InitStructure.DMA_PeripheralDataSize  = DMA_PeripheralDataSize_HalfWord;     //每次发送是半字,即16位
	DMA_InitStructure.DMA_MemoryDataSize    = DMA_MemoryDataSize_HalfWord;           //每次存储数据单位为半字,即16位
	DMA_InitStructure.DMA_Mode             	= DMA_Mode_Circular;                     //循环AD
	DMA_InitStructure.DMA_Priority                  = DMA_Priority_High;      //DMA优先级高
	DMA_InitStructure.DMA_M2M                      = DMA_M2M_Disable;         //DMA禁止内存到内存
	DMA_Init(DMA1_Channel1, &DMA_InitStructure);                              //DMA初始化
	
	DMA_ITConfig(DMA1_Channel1, DMA_IT_TC, ENABLE);                           //使能DMA发送完中断
	DMA_ClearITPendingBit(DMA1_IT_TC1);                                       //清除中断位
	DMA_Cmd(DMA1_Channel1, ENABLE);                                           //启动DMA通道1	
}

   b)dma的中断设置

void DMA_CONFIG()
{
	NVIC_InitTypeDef NVIC_InitStructure;
	
	NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel1_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 13;
  	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStructure);
}

   c)dma中断函数书写。

void DMA1_Channel1_IRQHandler(void)
{  
	 u32 i;
	 u32 After_Buff=0;
	 
   	if (DMA_GetITStatus(DMA_IT_TC) != RESET)
   	{
      	DMA_ClearITPendingBit(DMA1_IT_TC1);        //清除DMA中断标志位            
		ADC_DMACmd(ADC1, DISABLE);                //停止DMA搬运ADC数值
		ADC_Cmd(ADC1,DISABLE);                    //停止ADC转换,我是通过Tim4定时器产生的中断来开关DMA和ADC。
		for(i = 0;i < buff_size;i++)
		{
			After_Buff = After_Buff+ADC_ConvertedValue[i];
		} 
		
		After_Filter=After_Buff/10;   //强制转换后电量显示正常
		After_Buff=0;
    }
   	
}

   最后回到mian()函数部分了,通过Tim4定时触发读取电池电量的任务,读取并显示。

void AdcTransfer(void)
{
	u32 powervalue;
	
	ADC_DMACmd(ADC1, ENABLE);                 //开启DMA搬运数据
	ADC_Cmd(ADC1, ENABLE);                    //开启ADC转换 
	
	while(ADC_GetFlagStatus(ADC1, ADC_FLAG_ADONS) == RESET) {};     //如果ADC允许使用
 	ADC_SoftwareStartConv(ADC1);                                 //开启ADC
 	while(!ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC));                 //如果ADC转换结束  
	powervalue = (uint16_t)((After_Filter*3300*3)>>12);
}

至此,读取ADC电量步骤圆满完成!

你可能感兴趣的:(stm32l151)