一,知識理論基礎(chǔ)
什么是呼吸燈:
顧名思義,就是一個燈。燈的亮度的變化,由亮變暗,從暗變亮,有一個漸變,規(guī)律的變化,像是人的呼吸,是燈的呼吸,所以叫呼吸燈。而要讓燈可以達(dá)到這樣的變化,我們要讓stm32的IO口上輸出一個可調(diào)的電平,這時我們就要用到PWM,那什么是PWM呢,我們繼續(xù)往下看。
什么是定時器:
講PWM我們要先認(rèn)識stm32的定時器,PWM是定時器的功能之一。STM32F103有TIME1和TIME8高級定時器,TIME2TIME5通用定時器,還有TIME6和TIME7基本定時器。我們要使用的STM32F103C8T6只具有4個定時器,TIME1TIME4.
那么定時器有什么功能呢?定時、輸出比較、輸入捕獲、互補(bǔ)輸出,其中,基本定時器就只有定時功能,通用定時器便除了互補(bǔ)輸出沒有其他都有,而高級定時器便是全都有啦,我們這里用到通用定時器TIM2。
通用定時器具體的功能有:
在這里我們要用到TIM2_CH2的PWM輸出功能。
那么什么是PWM呢?
脈沖寬度調(diào)制(PWM),是英文“Pulse Width Modulation”的縮寫,簡稱脈寬調(diào)制,是利用微處理器的數(shù)字輸出來對模擬電路進(jìn)行控制的一種非常有效的技術(shù)。簡單一點(diǎn),就是對脈沖寬度的控制。
簡單點(diǎn)說就是一個可調(diào)的脈沖,控制在一個周期內(nèi),控制高電平多長時間,低電平多長時間(占空比),從而實(shí)現(xiàn)電平的輸出。經(jīng)常用于舵機(jī)、電機(jī)控制等。。。
兩個重要的概念,頻率、占空比:
頻率是指每秒鐘信號從高電平到低電平再回到高電平的次數(shù),為一個PWM波周期的倒數(shù)。
占空比是指高電平持續(xù)時間比一個周期持續(xù)的時間。所以可以通過控制占空比(我們要編程的“數(shù)”),來控制輸出的等效電壓。
對于方波(pwm輸出的就是方形波)的話,頻率和占空比就確定了一個波。
為了不至于太難理解,我們不進(jìn)行深講,但是建議大家可以去CSDN,百度等等平臺進(jìn)行全面一點(diǎn)的認(rèn)知,對我們下學(xué)期的智能車比賽做基礎(chǔ)知識儲備。
**二,**硬件連接
具有定時器功能的引腳:
LED連接:
我們用到TIM2_CH2,自己實(shí)操時可以換一個以達(dá)到更好的學(xué)習(xí)效果。通過圖二,我們在默認(rèn)情況下(即不使用端口映射)TIM2_CH2對應(yīng)的IO口是PA1,我們將PWM輸出極性設(shè)置為高,便將LED的正極接到PA1上,負(fù)極接GND,(若將輸出極性設(shè)置成低那就反過來接,將負(fù)極接到IO口,,正極接5V)
三,軟件編程
首先我們在工程中HARDWARE文件夾下新建PWM文件夾并新建PWM.c PWM.h兩個文件,導(dǎo)入mdk5,具體操作省略,可以看前邊推文。我們將PWM的初始化函數(shù)寫到PWM.c的文件中函數(shù)命名為“TIM2_PWM_Init”(可以隨意命名)。
我們先從簡單的講起,PWM.h頭文件沒什么重點(diǎn),如下:
#ifndef __PWM_H
#define __PWM_H
#include "sys.h" //導(dǎo)入頭文件
void TIM2_PWM_Init(u16 arr,u16 psc); //函數(shù)聲明
#endif
這里要說的是因?yàn)橛玫搅藆16 的數(shù)據(jù)類型定義我們要導(dǎo)入一個頭文件“sys.h”(u8,u16,u32都是C語言數(shù)據(jù)類型,分別代表8位,16位,32位長度的數(shù)據(jù)類型,這里也可以直接調(diào)用"stm32f10x.h")
接下來是編寫PWM.c文件,編寫初始化 “void TIM2_PWM_Init(u16 arr,u16 psc);”函數(shù),函數(shù)參數(shù)為arr重裝載值決定pwm的頻率周期,psc是時鐘預(yù)分頻數(shù)(主要用于計(jì)算時間范圍為0-65534),這里有一條公式可以計(jì)算周期時間Tout= (arr+1)*(psc+1) /Tclk,其中Tclk我們用的TIM2是系統(tǒng)內(nèi)部APB1時鐘倍頻來的,(固件庫的SystemInit函數(shù)里面已經(jīng)初始化APB1的時鐘為2分頻,所以APB1的時鐘為36M,而從STM32的內(nèi)部時鐘樹圖得知:當(dāng)APB1的時鐘分頻數(shù)為1的時候,TIM27的時鐘為APB1的時鐘,而如果APB1的時鐘分頻數(shù)不為1,那么TIM27的時鐘頻率將為APB1時鐘的兩倍。因此,TIM2的時鐘為72M,即 Tclk=72M)
接下來我們先說說PWM的模式,PWM有兩個模式,PWM1和PWM2,PWM1是當(dāng)我們設(shè)定的值比arr值小時輸出高電平,PWM2是當(dāng)我們設(shè)定的值比arr值大時輸出高電平。如下圖就是PWM2模式。
我們從圖出發(fā),可以看到為什么說ARR值決定周期,定時器從0開始計(jì)數(shù)(這里是向上計(jì)數(shù)模式,向下計(jì)數(shù)則相反,也是上邊公式為什么要+1),數(shù)到ARR時產(chǎn)生溢出(更新)事件(可以從這個地方設(shè)置中斷,本次用不到中斷,不進(jìn)行講解),重新回到0 ,這便是一個周期,我們要設(shè)置的值便是圖中CCRx,這個值會跟ARR進(jìn)行比較(所以叫輸出比較),通過模式設(shè)定決定輸出高低電平。(為了不至于太難理解請一定結(jié)合上圖一起看)。我們先看看完整的代碼,然后一個一個函數(shù)講PWM.c
#include "PWM.h"
void TIM2_PWM_Init(u16 arr,u16 psc)
{
//結(jié)構(gòu)體變量定義
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
GPIO_InitTypeDef GPIO_InitStruct;
TIM_OCInitTypeDef TIM_OCInitStruct;
//時鐘使能 TIM2 、GPIOA、 AFIO ①
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE); //使能TIM2掛載在APB1上的時鐘
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); //使能GPIOA、復(fù)用功能時鐘AFIO
//TIM2定時器初始化 ②
TIM_TimeBaseInitStruct.TIM_Period=arr; //重裝載值arr
TIM_TimeBaseInitStruct.TIM_Prescaler=psc; //預(yù)分頻值psc
TIM_TimeBaseInitStruct.TIM_CounterMode=TIM_CounterMode_Up; //向上計(jì)數(shù)模式
TIM_TimeBaseInitStruct.TIM_ClockDivision=0; //時鐘分割為0 ,TDTS = Tck_tim
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStruct);
//TIM2定時器使能
TIM_Cmd(TIM2,ENABLE);
//TIM2通道2初始化 ③
TIM_OCInitStruct.TIM_OCMode=TIM_OCMode_PWM1; //PWM模式1
TIM_OCInitStruct.TIM_OCPolarity=TIM_OCPolarity_High; //高電平有效
TIM_OCInitStruct.TIM_OutputState=TIM_OutputState_Enable; //輸出比較使能
TIM_OC2Init(TIM2,&TIM_OCInitStruct);
//TIM2通道2預(yù)裝載寄存器使能
TIM_OC2PreloadConfig(TIM2,TIM_OCPreload_Enable);
//GPIO PA1初始化 ④
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP; //復(fù)用推挽輸出
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_1; //PA.1
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz; //50MHz速度
GPIO_Init(GPIOA,&GPIO_InitStruct);
}
首先我們先總結(jié)一下初始化pwm輸出的編程步驟:
步驟介紹
使能時鐘
初始化定時器
初始化定時器通道
初始化GPIO
現(xiàn)在我們一個點(diǎn)一個點(diǎn)的講解:
使能時鐘,這里 GPIO掛載在APB2總線上,之前文章說過,而我們要用到的TIM2是掛載在APB1上的,所以我們要使能的時鐘是RCC_APB1,這里要注意的是通用定時器是掛載在APB1上,高級定時器則是在APB2上?!狙a(bǔ)充:時鐘函數(shù)的申明在stm32f10x_rcc.h,這里是上一講寫少了的】
這里我們要寫的代碼是:
//時鐘使能 TIM2 、GPIOA、 AFIO ①
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE); //使能TIM2掛載在APB1上的時鐘
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); //使能GPIOA
初始化定時器,初始化定時器跟初始化GPIO的操作類似,我們先看看要用到的函數(shù)【定時器相關(guān)函數(shù)申明在文件stm32f10x_time.h中】:
這里函數(shù)的兩個參數(shù)一個是TIMx ,x可以是2,3,4,說明這個初始化函數(shù)只適用在通用定時器初始化上,第二個參數(shù)是一個結(jié)構(gòu)體變量,里邊的成員有:
typedef struct
{
uint16_t TIM_Prescaler; //預(yù)分頻值
uint16_t TIM_CounterMode; //計(jì)數(shù)模式
uint16_t TIM_Period; // 重裝載值
uint16_t TIM_ClockDivision; //時間分割
uint8_t TIM_RepetitionCounter; //重復(fù)計(jì)數(shù),就是重復(fù)溢出多少次才給你來一個溢出中斷,如果初始化為0的話,計(jì)數(shù)器溢出一次,中斷一次!
} TIM_TimeBaseInitTypeDef;
其中預(yù)分頻值跟重裝載值上邊講過了,計(jì)數(shù)模式有向上計(jì)數(shù)、向下計(jì)數(shù)、中央對齊模式(中央對齊模式有模式1、2、3),這里我們用到向上計(jì)數(shù)模式,對于向上計(jì)數(shù)模式在上邊有講過了,便是從0計(jì)數(shù)到ARR重裝載值,而向下計(jì)數(shù)的話便是從ARR計(jì)數(shù)到0。時間分割主要是用于數(shù)字濾波器相關(guān),我們在此用不到只要設(shè)置為0就好了,重復(fù)計(jì)數(shù)模式,在這里我們用不到,上邊注釋有稍微講了一下,可以自行再了解一下。所以這里我們要設(shè)置參數(shù)如下:
TIM_TimeBaseInitStruct.TIM_Period=arr; //重裝載值arr
TIM_TimeBaseInitStruct.TIM_Prescaler=psc; //預(yù)分頻值psc
TIM_TimeBaseInitStruct.TIM_CounterMode=TIM_CounterMode_Up; //向上計(jì)數(shù)模式
TIM_TimeBaseInitStruct.TIM_ClockDivision=0; //時鐘分割為0 ,TDTS = Tck_tim
ARR值與psc值我們作為參數(shù),在調(diào)用時再進(jìn)行設(shè)置。所以完整的初始化函數(shù)
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct; //定義結(jié)構(gòu)體變量
TIM_TimeBaseInitStruct.TIM_Period=arr; //重裝載值arr
TIM_TimeBaseInitStruct.TIM_Prescaler=psc; //預(yù)分頻值psc
TIM_TimeBaseInitStruct.TIM_CounterMode=TIM_CounterMode_Up; //向上計(jì)數(shù)模式
TIM_TimeBaseInitStruct.TIM_ClockDivision=0; //時鐘分割為0 ,TDTS = Tck_tim
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStruct);
之后我們要對定時器使能,使用void TIM_Cmd();(省略參數(shù))
同樣,這個函數(shù)適用于通用定時器,使用比較簡單,如下:
TIM_Cmd(TIM2,ENABLE);
這里第2步就寫完了。
初始化定時器通道,通用定時器有4個通道上邊圖3有進(jìn)行講解,這里我們要用到是通道2即TIM2_CH2;每一個定時器通道都有單獨(dú)的初始化函數(shù)。
一樣是有兩個參數(shù),一個是定時器TIMx(同樣是只適用通用定時器2 3 4),一個是結(jié)構(gòu)體變量,我們看看結(jié)構(gòu)體變量里的成員。
typedef struct
{
uint16_t TIM_OCMode; //輸出模式
uint16_t TIM_OutputState; //輸出比較使能位
uint16_t TIM_OutputNState; //高級定時器輸出比較N狀態(tài)
uint16_t TIM_Pulse; //比較值(圖9 CCRx)
uint16_t TIM_OCPolarity; //輸出比較極性
uint16_t TIM_OCNPolarity; //高級定時器輸出比較N極性
uint16_t TIM_OCIdleState; //設(shè)置高級定時器空閑狀態(tài)
uint16_t TIM_OCNIdleState; //設(shè)置高級定時器N空閑狀態(tài)
} TIM_OCInitTypeDef;
我們用到的是通用定時器所以不用看那些高級定時器才能用的參數(shù),所以這里我們只要設(shè)置4個參數(shù)就可以了。首先第一個輸出模式。
這里我們用到PWM模式1,PWM模式2上邊有講到,至于其他的模式在此不叫講述,可以自行百度。
TIM_OCMode= TIM_OCMode_PWM1;
第二個TIM_OutputState這個是使能位,我們選擇使能就好了
TIM_OutputState=TIM_OutputState_Enable;
第三個是輸出極性,也就是我們要的是高電平有效還是低電平有效,這個跟我們LED引腳連接相關(guān),這里我們選擇高電平有效,LED的連接上我們將正極接到GPIO口上;
TIM_OCInitStruct.TIM_OCPolarity=TIM_OCPolarity_High;
第四個是比較值,我們在后邊主函數(shù)會用另一個函數(shù)直接設(shè)置,這個數(shù)也就是我們圖9CCRx對應(yīng)的那個值,也可以稱之為占空比,這里我們不用設(shè)置;
所以我們通道2初始化結(jié)構(gòu)體的參數(shù)設(shè)置是:
TIM_OCInitStruct.TIM_OCMode=TIM_OCMode_PWM1;
TIM_OCInitStruct.TIM_OCPolarity=TIM_OCPolarity_High;
TIM_OCInitStruct.TIM_OutputState=TIM_OutputState_Enable;
這里我們還需要通過void TIM_OC2PreloadConfig();(省略參數(shù))這個函數(shù)來使能通道2上的預(yù)裝載寄存器
他有兩個參數(shù),一個設(shè)置是哪個通用定時器,一個是使能,比較簡單,這里直接設(shè)置:
TIM_OC2PreloadConfig(TIM2,TIM_OCPreload_Enable);
那么我們通道2初始化步驟完整的代碼如下:
TIM_OCInitTypeDef TIM_OCInitStruct; //定義結(jié)構(gòu)體變量
//TIM2通道2初始化 ③
TIM_OCInitStruct.TIM_OCMode=TIM_OCMode_PWM1; //PWM模式1
TIM_OCInitStruct.TIM_OCPolarity=TIM_OCPolarity_High; //高電平有效
TIM_OCInitStruct.TIM_OutputState=TIM_OutputState_Enable; //輸出比較使能
TIM_OC2Init(TIM2,&TIM_OCInitStruct);
//TIM2通道2預(yù)裝載寄存器使能
TIM_OC2PreloadConfig(TIM2,TIM_OCPreload_Enable);
GPIO初始化,這里上一篇已經(jīng)講過了,不過這里要注意的是我們使用的是復(fù)用推挽輸出模式,這個是有固定要求的,可以查閱《stm32中文參考手冊》
那么GPIO初始化代碼如下【補(bǔ)充:GPIO系列函數(shù)申明在文件stm32f10x_gpio.h中】:
GPIO_InitTypeDef GPIO_InitStruct;
//GPIO PA1初始化 ④
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP; //復(fù)用推挽輸出
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_1; //PA.1
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz; //50MHz速度
GPIO_Init(GPIOA,&GPIO_InitStruct);
綜上,PWM.c文件中的PWM初始化函數(shù)就寫好了,我們接著寫主函數(shù)main.c,先看完整代碼:
#include "stm32f10x.h"
#include "delay.h"
#include "PWM.h"
int main(void)
{
int ledpwm=0; //定義占空比變量
TIM2_PWM_Init(899,0); //初始化PWM ARR=899;PSC=0
delay_init(); //初始化延時函數(shù)
while(1)
{
delay_ms(5); //穩(wěn)定pwm波
for(ledpwm =0; ledpwm <=255; ledpwm ++) //從0到255一個個加
{
TIM_SetCompare2(TIM2, ledpwm); //設(shè)置TIM2_CH2占空比
delay_ms(10); //延時10ms
}
for(ledpwm =255; ledpwm >=0; ledpwm --) //從255到0,一個個減
{
TIM_SetCompare2(TIM2, ledpwm); //設(shè)置TIM2_CH2占空比
delay_ms(10); //延時10ms
}
}
}
導(dǎo)入PWM.h頭文件,然后初始化pwm,arr=899,psc=0;初始化延時函數(shù),然后通過for循環(huán)從0到255計(jì)數(shù),這個相信有點(diǎn)C語言基礎(chǔ)的都沒問題,然后是一個新函數(shù),void TIM_SetCompare2();設(shè)置通道2捕獲比較寄存器的值。
兩個參數(shù),一個是哪個通用定時器,一個是比較寄存器的值,比較簡單,如下
TIM_SetCompare2(TIM2, ledpwm);
然后這里為什么是255呢,這個值是可以計(jì)算的,LED的最大亮度對應(yīng)的電壓通過占空比計(jì)算出對應(yīng)數(shù)值就好了,再大的數(shù)值對LED的亮度也就沒用了,亮度最大了,還可能燒壞LED。
Stm32的高電平 是5v 我們設(shè)置的ARR值是899,那么最大就是899,假設(shè)我們設(shè)置的比較值是450,那沒就是50%的輸出電平也就是2.5v,以此計(jì)算。
完整文件PWM.h PWM.c main.c就這三個文件要寫,寫好了編譯燒寫就可以了,在自己動手實(shí)操一遍后建議換一個定時器和通道再操作一遍,會更加熟練。
四,燒寫驗(yàn)證
話不多說,上圖(家里沒示波器,我用軟件調(diào)試來查看輸出的波形)
可以看到波形從小到大再到?。梢酝ㄟ^圖片下邊的時間結(jié)合波形寬度看出),再看看LED的變化:
可以看到LED漸漸從亮到暗再到亮,說明我們實(shí)驗(yàn)結(jié)果完美達(dá)標(biāo)。
-
電機(jī)控制
+關(guān)注
關(guān)注
3556文章
1923瀏覽量
270337 -
定時器
+關(guān)注
關(guān)注
23文章
3270瀏覽量
116443 -
PWM波
+關(guān)注
關(guān)注
0文章
100瀏覽量
17168 -
呼吸燈
+關(guān)注
關(guān)注
10文章
111瀏覽量
42996 -
STM32F103C8T6
+關(guān)注
關(guān)注
110文章
163瀏覽量
84892
發(fā)布評論請先 登錄
相關(guān)推薦
怎么通過PWM來實(shí)現(xiàn)呼吸燈的
stm32是怎樣使用延時去實(shí)現(xiàn)一種呼吸燈的
PWM實(shí)現(xiàn)呼吸燈的應(yīng)用
STM32呼吸燈的原理是什么
如何通過STM32103實(shí)現(xiàn)呼吸燈的亮滅
請問一下STM32 PWM是如何去實(shí)現(xiàn)呼吸燈設(shè)計(jì)的
STM32實(shí)現(xiàn)PWM呼吸燈的程序合集免費(fèi)下載

使用STM32F103RB單片機(jī)實(shí)現(xiàn)PWM呼吸燈實(shí)驗(yàn)的資料免費(fèi)下載

STM32_PWM呼吸燈

STM32F103 呼吸燈的實(shí)現(xiàn)

STM32 使用PWM實(shí)現(xiàn)呼吸燈

stm32使用延時實(shí)現(xiàn)呼吸燈寄存器版

STM32G0開發(fā)筆記:用PWM來實(shí)現(xiàn)LED呼吸燈效果

評論