STM32F10X PWM配置例程詳解,測試無誤
阿新 • • 發佈:2019-02-03
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
}
歡迎討論,共同進步