STM32輸入捕獲加DMA傳輸的解決方案

在要求精度測量的情況下 進入中斷存儲數據的方式顯然會浪費很多的系統資源並造成精確度的確實,所以一般使用DMA進行數據傳輸

本程序中使用的是TIM2其配置如下:

void TIM2_Cap_Init(u16 arr,u16 psc)
{	 
	GPIO_InitTypeDef GPIO_InitStructure;//初始化GPIO結構體
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;//初始化計時器結構體
 	NVIC_InitTypeDef NVIC_InitStructure;//中斷配置結構體

	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);	//TIM2時鐘使能
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);  //GPIOA時鐘使能
	
	GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_0;  //PA0清楚之前的設置 
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //PA0 輸入
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	GPIO_ResetBits(GPIOA,GPIO_Pin_0);						 
	

	TIM_TimeBaseStructure.TIM_Period = arr; 
	TIM_TimeBaseStructure.TIM_Prescaler =psc;   
	TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //時鐘不分頻
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //向上計數
	TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); 
  
	
	TIM2_ICInitStructure.TIM_Channel = TIM_Channel_1; //選擇通道1
        TIM2_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;	//上升沿觸發
        TIM2_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; //映射到TI上
        TIM2_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;	 //輸入不分頻
        TIM2_ICInitStructure.TIM_ICFilter = 0x00;//不濾波
	
        TIM_ICInit(TIM2, &TIM2_ICInitStructure);
   
	NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;  
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;  
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;  
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; 
	NVIC_Init(&NVIC_InitStructure);   
	
	TIM_ITConfig(TIM2,TIM_IT_Update|TIM_IT_CC1,ENABLE);	

	TIM_ARRPreloadConfig(TIM2,ENABLE);
	TIM_DMACmd(TIM2,TIM_DMA_Update, ENABLE);  			
        TIM_DMACmd(TIM2,TIM_DMA_CC1, ENABLE);  
	TIM_Cmd(TIM2,ENABLE);
}
DMA配置如下:

void MYDMA_Config(DMA_Channel_TypeDef* DMA_CHx,u32 cpar,u32 cmar,u16 cndtr)
{
 	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);//打開DMA1時鐘	
	
        DMA_DeInit(DMA_CHx);   
	DMA1_MEM_LEN=cndtr;
	DMA_InitStructure.DMA_PeripheralBaseAddr = cpar;//外設地址 
	DMA_InitStructure.DMA_MemoryBaseAddr = cmar;  //內存地址
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;  //傳輸方向外設到內存
	DMA_InitStructure.DMA_BufferSize = cndtr;  //傳輸數量
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//外設地址不自增  
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;  //內存地址自增
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;//外設數據大小爲半字節
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; //內存數據大小爲半字節
	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);  
	  	
} 
這裏已經可以實現對TIM2->CCR1到內存的傳輸但是TIM2->CNT不會自動清零所以需要在中斷中清零這個寄存器:

void TIM2_IRQHandler(void)
{ 
		TIM2->CNT=0;//清楚CNT寄存器
		TIM2->SR=0;
}

主函數設計:

#define Lenth 12
u32 res=0;
u16 buffer[Lenth];
 int main(void)
 {	
	 u32 sec;
	 int i=0;
	 NVIC_Configuration();	 
	 delay_init();	    		
	 uart_init(115200);
	 MYDMA_Config(DMA1_Channel5,(u32)&TIM2->CCR1,(u32)buffer,12);//傳輸方向TIM2->CCR1到buffer
	 DMA_Cmd(DMA1_Channel5,ENABLE);//打開DMA通道15
	 TIM2_Cap_Init(65535,0);
	 printf("配置完成\n");
	 while(1)
	 {
		 while(DMA_GetFlagStatus(DMA1_FLAG_TC5)==RESET);//等待DMA1通過5接收完成
		 DMA_ClearFlag(DMA1_FLAG_TC5);
		 TIM_Cmd(TIM5,DISABLE);
		 for(i=0;i<12;i++)//數據量爲12個加起來求平均
		 {
			 res+=buffer[i];
			 buffer[i]=0;
		 }
		 printf("counts:%d\r\n",72000000/((res/12)+25));//轉換爲頻率
		 res=0;
		 DMA_Cmd(DMA1_Channel5,ENABLE);
		 TIM_Cmd(TIM5,ENABLE);
  }
 }

測試後中斷中佔用25個時鐘週期 所以在計算加上去

該程序會將計算出的頻率值通過串口發送至電腦,在1K到200K範圍內達到千分之一精度




發佈了36 篇原創文章 · 獲贊 47 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章