STM32的定時器還有一個模式叫做輸出比較 翻轉(zhuǎn)模式。這種模式toggle,顧名思義toggle,可以翻轉(zhuǎn)電平,但是條件是toggle:當計數(shù)值達到比較值時,才會在對應的通道引腳翻轉(zhuǎn)原先的電平。利用這個特點,我們可以在引腳上生成PWM波。
下面就講講如何利用這個“翻轉(zhuǎn)”這個特點,來輸出PWM波。還是基于我自己的規(guī)工程。
1、工程的修改
1)這里用到了定時器,所以需要將stm32f10x_tim.h添加到STM32F10x_StdPeriod_Driver工程組中。
2)打開stm32f0x_conf.h文件,將其中原先被屏蔽的語句toggle:#include "stm32f10x_tim.h"的注釋去掉。
3)新建OCToggle.c與OCToggle.h兩個文件,分別保存在BSP文件夾里下的src與inc中,然后在將OCToggle.c添加到BSP工程組。
2、OCToggle.c與OCToggle.h文件程序的編寫
首先是引腳的初始化。我使用TIM2,所以需要初始化TIM2對應的引腳PA0、PA1、PA2、PA3這四個引腳將它們配置成復用推挽輸出,代碼如下:
/*************************************************************
Function : OCToggle_GPIO_Init
Deion: 輸出比較翻轉(zhuǎn)模式下定時器對應通道引腳初始化
Input : none
return : none
*************************************************************/
static void OCToggle_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_Apb2PeriphClockCmd(RCC_Apb2Periph_GPIOA, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//定時器各通道引腳配置成復用推挽輸出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
接下去當然是要配置定時器了。它的代碼如下:
/*************************************************************
Function : OCToggle_TIM2_Init
Deion: 輸出比較翻轉(zhuǎn)模式定時器2初始化
Input : none
return : none
*************************************************************/
static void OCToggle_TIM2_Init(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);//初始化TIM2時鐘
/* -------------------------------------------------------
TIM_OCMode_Toggle模式計數(shù)值等于比較值翻轉(zhuǎn)電平
在PA0引腳輸出頻率為72M/CCR1_Val/2=732Hz,占空比為50%的PWM波
在PA1引腳輸出頻率為72M/CCR2_Val/2=1099Hz,占空比為50%的PWM波
在PA2引腳輸出頻率為72M/CCR3_Val/2=2197Hz,占空比為50%的PWM波
在PA3引腳輸出頻率為72M/CCR4_Val/2=4395Hz,占空比為50%的PWM波
---------------------------------------------------------*/
TIM_TimeBaseStructure.TIM_Period = 65535;//定時器計數(shù)周期
TIM_TimeBaseStructure.TIM_Prescaler = 1 - 1;//預分頻
TIM_TimeBaseStructure.TIM_ClockDivision = 1 - 1;//時鐘不分頻
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;//增計數(shù)
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);//初始化定時器
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Toggle;//輸出比較主動模式
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;//輸出使能
TIM_OCInitStructure.TIM_Pulse = CCR1_Val;//設置比較值(跳變值)
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;//有效電平為高電平
TIM_OC1Init(TIM2, &TIM_OCInitStructure);//初始化輸出比較寄存器
TIM_OC1PreloadConfig(TIM2, TIM_OCPreload_Disable);//關(guān)閉預轉(zhuǎn)載
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;//輸出使能
TIM_OCInitStructure.TIM_Pulse = CCR2_Val;//設置比較值(跳變值)
TIM_OC2Init(TIM2, &TIM_OCInitStructure);//初始化輸出比較寄存器
TIM_OC2PreloadConfig(TIM2, TIM_OCPreload_Disable);
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;//輸出使能
TIM_OCInitStructure.TIM_Pulse = CCR3_Val;//設置比較值(跳變值)
TIM_OC3Init(TIM2, &TIM_OCInitStructure);//初始化輸出比較寄存器
TIM_OC3PreloadConfig(TIM2, TIM_OCPreload_Disable);
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;//輸出使能
TIM_OCInitStructure.TIM_Pulse = CCR4_Val;//設置比較值(跳變值)
TIM_OC4Init(TIM2, &TIM_OCInitStructure);//初始化輸出比較寄存器
TIM_OC4PreloadConfig(TIM2, TIM_OCPreload_Disable);
TIM_ITConfig(TIM2, TIM_IT_CC1 | TIM_IT_CC2 | TIM_IT_CC3 | TIM_IT_CC4, ENABLE);//清除中斷標志
TIM_Cmd(TIM2, ENABLE);//打開定時器2
}
不對定時器做任何分頻,并且讓它滿計數(shù),即計數(shù)周期為65535。接下去再設置個通道的的工作模式為輸出比較翻轉(zhuǎn)模式,設置通道1的比較值為CCR1_Val、CCR3_Val、CCR3_Val、CCR4_Val,它們的值在OCToggle.h中定義。然后打開個通道的輸出比較事件的中斷。最后在打開定時器。這樣的話,定時器這段就配置完成了。
既然打開了中斷,則需要配置下中斷,設置TIM2的中斷優(yōu)先級為1,代碼如下:
/*************************************************************
Function : OCToggle_Int_Init
Deion: 輸出比較翻轉(zhuǎn)模式中斷初始化
Input : none
return : none
*************************************************************/
static void OCToggle_Int_Init(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;//中斷優(yōu)先級為1
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
還要編寫一個總函數(shù):OCToggle_Init()函數(shù)將上面的初始化相關(guān)代碼都在這個函數(shù)中調(diào)用,代碼如下:
/*************************************************************
Function : OCToggle_Init
Deion: 輸出比較翻轉(zhuǎn)模式初始化
Input : none
return : none
*************************************************************/
void OCToggle_Init(void)
{
OCToggle_GPIO_Init();
OCToggle_TIM2_Init();
OCToggle_Int_Init();
}
接下去OCToggle.h的代碼:
#ifndef __OCTOGGLE_H__
#define __OCTOGGLE_H__
#include "stm32f10x.h"
#define CCR1_Val 49152
#define CCR2_Val 32768
#define CCR3_Val 16384
#define CCR4_Val 8192
void OCToggle_Init(void);
#endif
這里之所以將CCR1_Val的值在這個h文件中宏定義,而不在c文件中直接定義成變量,原因是為了在其toggle他文件中調(diào)用方便,如果定義成變量的話,在其他文件還要用extern關(guān)鍵字來聲明,比較麻煩!
3、stm32f10x_it.c文件的修改
TIM2的中斷服務函數(shù)的程序如下:
/*************************************************************
Function : TIM2_IRQHandler
Deion: 定時器2中斷服務程序
Input : none
return : none
*************************************************************/
void TIM2_IRQHandler(void)
{
static u16 capture = 0;
if (TIM_GetitStatus(TIM2, TIM_IT_CC1) != RESET)//通道1檢測到比較事件
{
TIM_ClearITPendingBit(TIM2, TIM_IT_CC1);//清除標志位
capture = TIM_GetCapture1(TIM2);
TIM_SetCompare1(TIM2, capture + CCR1_Val);//重新設置比較值
}
if (TIM_GetITStatus(TIM2, TIM_IT_CC2) != RESET)//通道1檢測到比較事件
{
TIM_ClearITPendingBit(TIM2, TIM_IT_CC2);//清除標志位
capture = TIM_GetCapture2(TIM2);
TIM_SetCompare2(TIM2, capture + CCR2_Val);//重新設置比較值
}
if (TIM_GetITStatus(TIM2, TIM_IT_CC3) != RESET)//通道1檢測到比較事件
{
TIM_ClearITPendingBit(TIM2, TIM_IT_CC3);//清除標志位
capture = TIM_GetCapture3(TIM2);
TIM_SetCompare3(TIM2, capture + CCR3_Val);//重新設置比較值
}
if (TIM_GetITStatus(TIM2, TIM_IT_CC4) != RESET)//通道1檢測到比較事件
{
TIM_ClearITPendingBit(TIM2, TIM_IT_CC4);//清除標志位
capture = TIM_GetCapture4(TIM2);
TIM_SetCompare4(TIM2, capture + CCR4_Val);//重新設置比較值
}
}
在這個中斷服務程序中,每當檢測到輸出比較時事件時,通道對應的引腳就會自動翻轉(zhuǎn)電平,然后我們在中斷中,重新設置它的比較值,在原來的計數(shù)值的基礎上再增加與計數(shù)值相同的計數(shù)值作為下一次的比較值。這樣的話,就會形成占空比為50%的PWM波。以通道1為例解釋下。通道1原來設置的計數(shù)值為49152,當定時器的計數(shù)值達到這個數(shù)時,就翻轉(zhuǎn)通道1對應的輸出引腳PA0翻轉(zhuǎn)電平,然后在設置新的計數(shù)值為49152+49152,因為定時器只有16位,會溢出,所以下一個比較值為32768,這樣的話,如果49152計數(shù)值時間內(nèi)引腳輸出高電平的話,下一個49152計數(shù)值時間內(nèi)就會輸出低電平,形成頻率為72M/49152/2=732Hz,占空比為50%的PWM波。同樣的,通道2會輸出頻率為1099Hz,占空比為50%的PWM波;通道3輸出頻率為2197Hz,占空比為50%的PWM波;通道4輸出頻率為4395Hz,占空比為50%的PWM波。
4、main函數(shù)的編寫
main函數(shù)很簡單,只是調(diào)用一些初始化函數(shù):
/*************************************************************
Function : main
Deion: main入口
Input : none
return : none
*************************************************************/
int main(void)
{
BSP_Init();
OCToggle_Init();
PRINTF("\nmain() is running!\r\n");
while(1)
{
LED1_Toggle();
Delay_ms(1000);
}
}
5、測試
用 示波器的探頭分別連接引腳PA0、PA1、PA2、PA3??梢钥吹较旅娴默F(xiàn)象:
連接PA0可以檢測到頻率為732Hz,占空比為50%的PWM波。如下圖所示:
連接PA1可以檢測到頻率為1099Hz,占空比為50%的PWM波。如下圖所示:
連接PA2可以檢測到頻率為2197Hz,占空比為50%的PWM波。如下圖所示:
連接PA3可以檢測到頻率為4395Hz,占空比為50%的PWM波。如下圖所示:
原文鏈接:http://www.eeworld.com.cn/mcu/article_2016101130330.html