STM32 嵌入式學習入門(5)——PWM的實現

版權聲明:本文爲博主原創文章,未經博主允許不得轉載。 https://blog.csdn.net/FelikZhang/article/details/79331904
STM32嵌入式學習(5)——PWM的實現

上一篇博文介紹了定時器和PWM的基本的原理,本篇博文從代碼層面來介紹PWM的具體實現。同樣,還是以博主所用的開發板——正點原子開發板STM32F103ZET6爲例。

一、基於STM32的PWM輸出配置步驟(初始化操作):


1. 操作步驟(基於STM32固件庫、使用定時器3的PWM功能):
(1)使能相關時鐘(定時器3和相關IO口時鐘。):
//要使用什麼外設就要先使能相關外設所掛載的時鐘,這些內容在最開始GPIO那塊就有提到STM32的GPIO介紹
        ①使能定時器3時鐘:RCC_APB1PeriphClockCmd();
          ②使能GPIOB時鐘:RCC_APB2PeriphClockCmd();

(2)初始化IO口爲複用功能輸出。函數:GPIO_Init();
//同樣,IO口初始化的操作在STM32的GPIO介紹那篇文章裏也說到了。
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;      
//這裏我們是要把引腳用作定時器的PWM輸出引腳,因此要重映射配置。所以需要開啓AFIO時鐘。同時設置重映射。
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
        GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE); 

(3)初始化定時器:ARR,PSC等:TIM_TimeBaseInit();
ARR寄存器在上一篇文章講原理時候已經說到了。PWM原理

(4)初始化輸出比較參數:TIM_OC2Init();
(5)使能預裝載寄存器: TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable); 
(6)使能定時器。TIM_Cmd();
(7)不斷改變比較值CCRx,達到不同的佔空比效果:TIM_SetCompare2();


2.初始化源代碼:
(1)以STM32F103ZET6爲芯片的開發板的PWM初始化,這裏只是初始化一個通道用作PWM輸出
//TIM3 PWM部分初始化 
//PWM輸出初始化
//arr:自動重裝值
//psc:時鐘預分頻數
void TIM3_PWM_Init(u16 arr,u16 psc)//STM32F103ZET6
{  
	GPIO_InitTypeDef GPIO_InitStructure;
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	TIM_OCInitTypeDef  TIM_OCInitStructure;
	
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);	//使能定時器3時鐘
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_AFIO, ENABLE);  //使能GPIO外設和AFIO複用功能模塊時鐘
	
	GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE); //Timer3部分重映射  TIM3_CH2->PB5    
 
   //設置該引腳爲複用輸出功能,輸出TIM3 CH2的PWM脈衝波形	GPIOB.5
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //TIM_CH2
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //複用推輓輸出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIO
 
   //初始化TIM3
	TIM_TimeBaseStructure.TIM_Period = arr; //設置在下一個更新事件裝入活動的自動重裝載寄存器週期的值
	TIM_TimeBaseStructure.TIM_Prescaler =psc; //設置用來作爲TIMx時鐘頻率除數的預分頻值 
	TIM_TimeBaseStructure.TIM_ClockDivision = 0; //設置時鐘分割:TDTS = Tck_tim
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上計數模式
	TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根據TIM_TimeBaseInitStruct中指定的參數初始化TIMx的時間基數單位
	
   //初始化TIM3 CH2 PWM模式	 
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //選擇定時器模式:TIM脈衝寬度調製模式2
 	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比較輸出使能
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //輸出極性:TIM輸出比較極性高
	TIM_OC2Init(TIM3, &TIM_OCInitStructure);  //根據T指定的參數初始化外設TIM3 OC2

	TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);  //使能TIM3在CCR2上的預裝載寄存器
 
	TIM_Cmd(TIM3, ENABLE);  //使能TIM3
}

(2)下面的代碼是博主做嵌入式循跡小車的項目中的PWM初始化,用了TIM1的兩個通道(通道1和通道4)去分別控制兩個驅動輪的轉速,從而實現讓小車轉向的功能。和上面的初始化代碼的不同主要在於小車項目中用了兩個通道,同時項目使用的開發板的芯片是STM32F103RCT6。在這裏貼出源代碼,做一比較。
//PWM輸出初始化
//arr:自動重裝值
//psc:時鐘預分頻數
void TIM1_PWM_Init(u16 arr,u16 psc)//STM32F103RCT6
{
	GPIO_InitTypeDef GPIO_InitStructure;
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	TIM_OCInitTypeDef  TIM_OCInitStructure;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1,ENABLE);
	
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);  //使能GPIOA外設時鐘使能
	
	//設置該引腳爲複用輸出功能,輸出TIM1 CH1和CH4的PWM脈衝波形
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8|GPIO_Pin_11;//TIM_CH1
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //複用推輓輸出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	
	TIM_TimeBaseStructure.TIM_Period = arr; //設置在下一個更新事件裝入活動的自動重裝載寄存器週期的值	 80K
	TIM_TimeBaseStructure.TIM_Prescaler =psc; //設置用來作爲TIMx時鐘頻率除數的預分頻值  不分頻
	TIM_TimeBaseStructure.TIM_ClockDivision = 0; //設置時鐘分割:TDTS = Tck_tim
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上計數模式
	
	TIM_TimeBaseInit(TIM1,&TIM_TimeBaseStructure); //根據TIM_TimeBaseInitStruct中指定的參數初始化TIMx的時間基數單位
 
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //選擇定時器模式:TIM脈衝寬度調製模式2
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比較輸出使能
	TIM_OCInitStructure.TIM_Pulse = 0; //設置待裝入捕獲比較寄存器的脈衝值
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //輸出極性:TIM輸出比較極性高
	
	TIM_OC1Init(TIM1, &TIM_OCInitStructure);  //根據TIM_OC1InitStruct中指定的參數初始化外設TIM1的通道1
	TIM_OC4Init(TIM1, &TIM_OCInitStructure);  //根據TIM_OC2InitStruct中指定的參數初始化外設TIM1的通道4

	TIM_CtrlPWMOutputs(TIM1,ENABLE);	//MOE 主輸出使能	
	
	TIM_OC1PreloadConfig(TIM1,TIM_OCPreload_Enable);  //CH1預裝載使能 通道1
	TIM_OC4PreloadConfig(TIM1,TIM_OCPreload_Enable);  //CH4預裝載使能 通道4

	TIM_ARRPreloadConfig(TIM1,ENABLE); //使能TIMx在ARR上的預裝載寄存器(x==1)
	
	TIM_Cmd(TIM1,ENABLE);  //使能TIM1

}

初始化函數在主函數的開頭處調用,調用函數的兩個參數根據自己需要去設定。


二、PWM的操作:

上面的初始化結束後就可以利用相關引腳輸出PWM波了。這個輸出過程簡單說就是改變比較值CCRx的操作,就是往CCR寄存器裏寫值。這一點上一篇文章裏有分析到。下面先介紹一下往CCRx寄存器裏寫值操作的函數(來自官方庫函數):TIM_SetCompare1();
/**
  * @brief  Sets the TIMx Capture Compare1 Register value
  * @param  TIMx: where x can be 1 to 17 except 6 and 7 to select the TIM peripheral.
  * @param  Compare1: specifies the Capture Compare1 register new value.
  * @retval None
  */
void TIM_SetCompare1(TIM_TypeDef* TIMx, uint16_t Compare1)
{
  /* Check the parameters */
  assert_param(IS_TIM_LIST8_PERIPH(TIMx));//有效性判斷
  /* Set the Capture Compare1 Register value */
  TIMx->CCR1 = Compare1;
}
說明:和這個函數類似的一共有四個:TIM_SetCompare1();   TIM_SetCompare2();   TIM_SetCompare3();   TIM_SetCompare4();其實現框架都是一樣的,四個函數分別對應四個通道,使用哪個通道調用哪個函數,要對應起來。


下面先介紹還是以自己嵌入式循跡小車的項目爲例,比如小車直行時就是在執行下面這個函數。
u8 Case_F_SIGN(void)//直行
{
	while(1){
		delay_ms(10);
		pwmvalR=413;
	    	pwmvalL=413;
		TIM_SetCompare1(TIM1,pwmvalR);
		TIM_SetCompare4(TIM1,pwmvalL);
		t=SensorScan();
		if(t!=F_SIGN)
			return t;
	}
}

pwmvalR和pwmvalL是兩個全局變量,它們的值是根據項目需要和小車硬件情況實際測出來的,然後調用兩個函數,寫入相關寄存器,,下面是調用SensorScan();看小車是不是還沿着軌跡在行駛,如果不是,那麼跳出while(1)循環,否則,返回主調函數當前小車的狀態。這段代碼現在看寫得不是特別好了,但是說明這個函數的用法還是沒問題的。







發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章