1. 程式人生 > >STM32F10X PWM配置例程詳解,測試無誤

STM32F10X PWM配置例程詳解,測試無誤

STM32F10X PWM配置例程詳解,測試無誤 

硬體平臺:STM32F10X  PWM模組 + JLink + 示波器

軟體平臺:Keil 4 

一、基礎知識

首先,根據晶片的型號,STM32小容量、中容量產品和STM32F105xx/STM32F107xx的互聯型產品,包含一個高階控制定時器(TIM1)。大容量產品的STM32F103xx包含有二個高階控制定時器(TIM1和TIM8)。

一個高階定時器可以輸出七路PWM波,而一個通用定時器則最多隻能輸出四路互補PWM波。

通用定時器和高階定時器相互獨立,互不影響,可同時操作。

如果需要的PWM 較多,比如控制六軸的話,可以自行選取不同的定時器,一個不夠的話可選兩個。

其次,每個通用的定時器一般只有4路通道,每個通道有一個比較暫存器,初始化的時候設定不同的值後,可以生成4路PWM訊號,不過這4路的PWM頻率相同,佔空比可以不一樣。 

最後,有任何關於引腳複用、及相關暫存器的具體問題,以相應的資料手冊為準。

PWM的實質還是定時器TIMER模組的使用。

二、相應模組

程式涉及的模組有:

RCC:復位及時鐘控制模組,用於初始化STM32 USART外設時鐘及IO口複用時鐘;

GPIO:通用輸入輸出口複用配置模組;

Delay:利用系統時鐘SysTick,也號稱“滴答”,寫的延時模組;

Led:系統執行提示模組;

Timer:定時器模組配置,PWM配置也在其中。

三:程式碼

RCC

  #include "Rcc.h"
  
  void RCC_Init(void)
  {	 
  	 ErrorStatus HSEStartUpStatus;
  	//定義列舉型別錯誤狀態變數	
  	 
  	 RCC_DeInit();//復位系統時鐘設定
  		
  	 RCC_HSEConfig(RCC_HSE_ON);
  	 //開啟外部高速時鐘晶振,使能HSE
  	/*RCC_HSE_ON  開
  	 _off 關  _bypass hse晶振被外部時鐘旁路*/	
  	 
  	HSEStartUpStatus = RCC_WaitForHSEStartUp();
  	/*RCC_WaitForHSEStartUp()返回一個ErrorStatus列舉值,
  	success好,error未好*/
  	
  	 if(HSEStartUpStatus == SUCCESS)//HES就緒
  	 {		 
  		 RCC_HCLKConfig(RCC_SYSCLK_Div1);
  		 //AHB時鐘(HCLK)=系統時鐘		
  
  			RCC_PCLK1Config(RCC_HCLK_Div2);
  		 //設定低速AHB時鐘(APB1)為HCLK的2分頻			 
  		 
  		 RCC_PCLK2Config(RCC_HCLK_Div1);
  		 //設定高速AHB時鐘(APB2)=HCLK時鐘			 	
  		 
  		 FLASH_SetLatency(FLASH_Latency_2);
  		 //設定FLASH延時週期數為2
  		 
  		 //使能領取指快取
  		 FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
  		 
  		 RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9);
  		 //設定PLL時鐘源及倍頻係數,為HSE的9倍頻 8MHz * 9 = 72MHz
  		 /*void RCC_PLLConfig(u32 RCC_PLLSource, u32 RCC_PLLMul)
  		 RCC_PLLSource_HSI_Div2   pll輸入時鐘=hsi/2;
  		 RCC_PLLSource_HSE_Div1   pll輸入時鐘 =hse
  		 RCC_PLLSource_HSE_Div2   pll輸入時鐘=hse/2
  		 
  		 RCC_PLLMul_2  ------_16       pll輸入時鐘*2---16
  		 pll輸出時鐘不得超過72MHZ*/	 
  		 
  		 RCC_PLLCmd(ENABLE);
  		 //ENABLE  / DISABLE
  		 
  		 while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);//等待PLL輸出穩定
  		 /*FlagStatus RCC_GetFlagStatus(u8 RCC_FLAG)  檢查指定RCC標誌位
  		 返回SET OR RESET
  		 RCC_FLAG_HSIRDY  HSI晶振就緒
  		 RCC_FLAG_HSERDY
  		 RCC_FLAG_PLLRDY
  		 RCC_FLAG_LSERDY 
  		 RCC_FLAG_LSIRDY.......*/		 
  		 
  		 RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
  		 //設定PLL為系統時鐘源
  		 /*void RCC_SYSCLKConfig(u32 RCC_SYSCLKSource)  設定系統時鐘
  		 RCC_SYSCLKSource_HSI 
  		 RCC_SYSCLKSource_HSE 
  		 RCC_SYSCLKSource_PLLCLK  選HSI  HSE PLL 作為系統時鐘*/ 		 
  		 
  		 while(RCC_GetSYSCLKSource() != 0x08);
  		 //判斷PLL是否是系統時鐘
  		 /*u8 RCC_GetSYSCLKSource(void)  返回用作系統時鐘的時鐘源
  		 0x00:HSI   0x04:HSE 0x08:PLL */
  	 }	 
  	 
  	 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | 
  													RCC_APB2Periph_AFIO |
  													RCC_APB2Periph_GPIOB , ENABLE);
  	 RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
  	 //U2  U3 時鐘在APB1
  	 //開啟GPIO時鐘,複用功能,串列埠1的時鐘	             											 
  
  	RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);//使能CAN1時鐘	
  	//好奇怪,是因為官方的庫函式更新?
  	//不是說F10X系列只有一個CAN,而F4有CAN1  CAN2 嗎?
  	//怎麼他的系統配置檔案裡面是can1?????
  	 
  	 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //時鐘使能
  	 
  	 /*void RCC_APB2PeriphClockCmd(u32 RCC_APB2Periph, FunctionalState NewState) 
  		enable 或 disable apb2 外設時鐘
  	 RCC_APB2Periph_AFIO  功能複用IO 時鐘
  	 RCC_APB2Periph_GPIOA/B/C/D/E   GPIOA/B/C/D/E 時鐘
  	 RCC_APB2Periph_ADC1/ADC2			ADC1/2 時鐘
  	 RCC_APB2Periph_TIM1 
  	 RCC_APB2Periph_SPI1
  	 RCC_APB2Periph_USART1 
  	 RCC_APB2Periph_ALL			全部APB2外設時鐘*/
  }

GPIO

  #include "GPIO.h"
  
  void MYGPIO_Init(void)
  {
  	 GPIO_InitTypeDef GPIO_InitStructure;
  	//GPIO_InitStructure初始化結構體為GPIO_InitTypeDef結構
  	 GPIO_DeInit(GPIOA);
  	 GPIO_StructInit(&GPIO_InitStructure);
  	//函式:指向結構GPIO_InitTypeDef的指標,待初始化
  	
  	//CAN TX  : A12
  	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
  	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;	//複用推輓
  	GPIO_Init(GPIOA, &GPIO_InitStructure);			//初始化IO
  	//CAN TX  : A111
  	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
  	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;	//上拉輸入
  	GPIO_Init(GPIOA, &GPIO_InitStructure);			//初始化IO
  	
  	// USART TX :A9
  	 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;	
  	//2、GPIO_SPEED:GPIO_SPEED_10MHz/_2MHz/_50MHz   最高輸出速率
  	 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  	 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  	/*Mode,工作狀態:GPIO_MODE_AIN  ----- 模擬輸入
  														_IN_FLOATING  ----- 浮空輸入
  														_IPD  ----- 上拉輸出
  														_IPU  ----- 上拉輸入
  														_OUT_OD  ----- 開漏輸出
  														_OUT_PP  ----- 推輓輸出
  														_AF_OD  ----- 複用開漏輸出
  														_AF_PP  ----- 複用推輓輸出*/	
  	 GPIO_Init(GPIOA , &GPIO_InitStructure);
  	
  	 // USART RX :A10
  	 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;	 
  	 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
  	 //IO浮空輸入
  	 GPIO_Init(GPIOA, &GPIO_InitStructure);
  	 //初始化
  	 
  	/************pwm2    pa1**********************/
  	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; //TIM_CH2
  	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //複用推輓輸出
  	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  	GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIO
  	 
  }

Delay

  #include "delay.h"
  
  static u8  fac_us=0;							//us延時倍乘數			   
  static u16 fac_ms=0;							//ms延時倍乘數,在ucos下,代表每個節拍的ms數
  	
  	   
  //初始化延遲函式
  //SYSTICK的時鐘固定為HCLK時鐘的1/8
  //SYSCLK:系統時鐘
  void delay_init()
  {
  	SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);	//選擇外部時鐘  HCLK/8
  	fac_us=SystemCoreClock/8000000;				//為系統時鐘的1/8 
  
  	fac_ms=(u16)fac_us*1000;					//非OS下,代表每個ms需要的systick時鐘數 
  }								    
  
  //延時nus
  //nus為要延時的us數.		    								   
  void delay_us(u32 nus)
  {		
  	u32 temp;	    	 
  	SysTick->LOAD=nus*fac_us; 					//時間載入	  		 
  	SysTick->VAL=0x00;        					//清空計數器
  	SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ;	//開始倒數	  
  	do
  	{
  		temp=SysTick->CTRL;
  	}while((temp&0x01)&&!(temp&(1<<16)));		//等待時間到達   
  	SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;	//關閉計數器
  	SysTick->VAL =0X00;      					 //清空計數器	 
  }
  //延時nms
  //注意nms的範圍
  //SysTick->LOAD為24位暫存器,所以,最大延時為:
  //nms<=0xffffff*8*1000/SYSCLK
  //SYSCLK單位為Hz,nms單位為ms
  //對72M條件下,nms<=1864 
  void delay_ms(u16 nms)
  {	 		  	  
  	u32 temp;		   
  	SysTick->LOAD=(u32)nms*fac_ms;				//時間載入(SysTick->LOAD為24bit)
  	SysTick->VAL =0x00;							//清空計數器
  	SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ;	//開始倒數  
  	do
  	{
  		temp=SysTick->CTRL;
  	}while((temp&0x01)&&!(temp&(1<<16)));		//等待時間到達   
  	SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;	//關閉計數器
  	SysTick->VAL =0X00;       					//清空計數器	  	    
  } 

Led

  #include "led.h"
  
  //初始化PB12和13為輸出口.並使能這兩個口的時鐘		    
  
  void LED_Init(void)
  { 
   GPIO_InitTypeDef  GPIO_InitStructure;
   	
   RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);	 //使能PB,PE埠時鐘
  	
   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12|GPIO_Pin_13;				 
   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		 //推輓輸出
   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;		 //IO口速度為50MHz
   GPIO_Init(GPIOB, &GPIO_InitStructure);					 //根據設定引數初始化GPIOB
   GPIO_SetBits(GPIOB,GPIO_Pin_12|GPIO_Pin_13);
  }

Timer

#include "timer.h"
#include "led.h"

//定時器3中斷服務程式
void TIM2_IRQHandler(void)   //TIM2中斷
{
	if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET) 
		//檢查指定的TIM中斷髮生與否:TIM 中斷源 
		//不等於RESET 即為 SET,就是發生了
		//(TIM_GetITStatus(TIM2, TIM_IT_Update) == SET) 
		{
			TIM_ClearITPendingBit(TIM2, TIM_IT_Update  );  
			//清除TIMx的中斷待處理位:TIM 中斷源 
			LED0=!LED0;
		}
}
//通用定時器3中斷初始化
//這裡時鐘選擇為APB1的2倍,而APB1為36M
//arr:自動重灌值。
//psc:時鐘預分頻數

//TIMX   X:1----4
//TIM2 PWM部分初始化 
//PWM輸出初始化
//arr:自動重灌值
//psc:時鐘預分頻數
void TIM2_PWM_Init(u16 arr,u16 psc)
{ 
	GPIO_InitTypeDef GPIO_InitStructure;
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	/*typedef struct 
		{ 
			u16 TIM_Period; 
			自動重灌暫存器週期的值,0x00000-----0xFFFF
			u16 TIM_Prescaler; 
			TIMX 時鐘頻率除數的預分頻值  0x0000----0xFFFF
			u8 TIM_ClockDivision; 
			時鐘分割 	TIM_CKD_DIV1  T DTS = Tck_tim  
								TIM_CKD_DIV2  T DTS = 2Tck_tim 
								TIM_CKD_DIV4  T DTS = 4Tck_tim 
			u16 TIM_CounterMode; 
			計數器模式	TIM_CounterMode_Up  TIM 向上計數模式
									TIM_CounterMode_Down   向下計數模式
									TIM_CounterMode_CenterAligned1 -----3    中央對齊模式1--3計數模式
		} TIM_TimeBaseInitTypeDef;*/
	TIM_OCInitTypeDef  TIM_OCInitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	

	 //初始化TIM2	
	TIM_TimeBaseStructure.TIM_Period = arr; 
	//設定在下一個更新事件裝入活動的自動重灌載暫存器 週期的值 就是週期 計數到5000為500ms
	TIM_TimeBaseStructure.TIM_Prescaler =psc; 
	//設定用來作為TIMx時鐘頻率除數的預分頻值  10Khz的計數頻率  
	TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; 
	//設定時鐘分割:T DTS = Tck_tim  
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  
	//TIM向上計數模式
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); 
	//根據TIM_TimeBaseInitStruct中指定的引數初始化TIMx的時間基數單位
	
	TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE ); //使能指定的TIM2中斷,允許更新中斷

	NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;  //TIM2中斷
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;  //先佔優先順序0級
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;  //從優先順序3級
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
	NVIC_Init(&NVIC_InitStructure);  //根據NVIC_InitStruct中指定的引數初始化外設NVIC暫存器

	TIM_Cmd(TIM2, ENABLE);  //使能TIMx外設
	
	//GPIO_PinRemapConfig(GPIO_PartialRemap2_TIM2, ENABLE);   怎麼用????
	//改變指定管腳的對映  Timer3部分重對映  TIM2_CH2->PB5	
	
	
	//初始化TIM2 Channel2 PWM模式	 
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; 
	//選擇定時器模式:TIM脈衝寬度調製模式1
 	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; 
	//比較輸出使能
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; 
	//輸出極性:TIM輸出比較極性高
	//TIM_OCInitStructure.TIM_Pulse = CCR2_Val;	
	//TIM_Pulse  待裝入比較暫存器的脈衝值  0x0000----0xFFFF
	TIM_OC2Init(TIM2, &TIM_OCInitStructure);  
	//根據T指定的引數初始化外設TIM2 OC2

	TIM_OC2PreloadConfig(TIM2, TIM_OCPreload_Enable);  
	//使能TIM2在CCR2上的預裝載暫存器
	
	//TIM_ARRPreloadConfig(TIM2, ENABLE);
	//使能TIM2在ARR上的預裝載暫存器
 
	TIM_Cmd(TIM2, ENABLE);  //使能TIM2
}

歡迎討論,共同進步