定時器
定時器是嵌入式系統中最基本的功能之一,它除了可以實現定時器功能外,還能夠實現延時、PWM輸出、波形發生器、舵機控制、節拍器、周期喚醒、自動數據采集等功能。在MicroPython中,很多函數的功能也依賴定時器。
1.獲取Timer類里面的方法:
>>> help(pyb.Timer) object <class 'Timer'> is of type type init -- <function> deinit -- <function> channel -- <function> counter -- <function> source_freq -- <function> freq -- <function> prescaler -- <function> period -- <function> callback -- <function> UP -- 0 DOWN -- 16 CENTER -- 32 PWM -- 0 PWM_INVERTED -- 1 OC_TIMING -- 2 OC_ACTIVE -- 3 OC_INACTIVE -- 4 OC_TOGGLE -- 5 OC_FORCED_ACTIVE -- 6 OC_FORCED_INACTIVE -- 7 IC -- 8 ENC_A -- 9 ENC_B -- 10 ENC_AB -- 11 HIGH -- 0 LOW -- 2 RISING -- 0 FALLING -- 2 BOTH -- 10 BRK_OFF -- 0 BRK_LOW -- 1 BRK_HIGH -- 2
定時器的使用方法是先導入Timer模塊,然后定義定時器,設置定時器ID、頻率、回調函數等參數。如:
>>> from pyb import Timer >>> tim = Timer(1,freq=100) #使用定時器1,頻率100hz >>> tim = Timer(4,freq=200,callback=print('yes')) #使用定時器4,頻率200hz,並設置回調函數 yes >>>
插入知識點:板載LED(3)和LED(4)分別是橙色的燈和藍色的燈,它們兩個都可以進行亮度調節,其它兩個(LED(1)、LED(2))沒有調節亮度功能,區別就在於LED(3)和LED(4)使用了定時器實現PWM調節亮度。LED(3)使用的是定時器2,LED(4)使用的是定時器3,所以在使用這兩個燈的亮度調節功能時不可以再使用這兩個定時器了,不然程序就會和預想的格格不入。
LED_YELLOW -- Pin(Pin.cpu.A15, mode=Pin.ALT, af=Pin.AF1_TIM2)
LED_BLUE -- Pin(Pin.cpu.B4, mode=Pin.ALT, af=Pin.AF2_TIM3)
Timer(n)
定義Timer,n=1~14。
class Timer - 控制內部定時器
定時器可用於各種任務。目前,只實現了最簡單的情況:定期調用函數。
每個計時器都包含一個以特定速率計數的計數器。計數的速率是外設時鍾頻率(Hz)除以定時器預分頻器。當計數器到達計時器周期時,它會觸發一個事件,計數器將重置為零。通過使用回調方法,timer事件可以調用Python函數。
定時器的變化率 = 外設時鍾頻率(Hz) / 定時器預分頻器
1.實例:使用固定頻率(也就是定時時間)來翻轉LED的狀態
>>> time = pyb.Timer(4) #創建使用定時器4 >>> time.init(freq=2) #定時頻率2Hz,也就是0.5秒計滿 >>> time.callback(lambda t:pyb.LED(1).toggle())
freq參數就是用戶自己想設定的時間,但是是以赫茲來表示:
>>> time = pyb.Timer(4) >>> time.init(freq=2) #定時2hz,也就是0.5s >>> time.callback(lambda t:pyb.LED(1).toggle()) >>> time.init(freq=10) #定時10hz,也就是0.1s >>> time.init(freq=0.1) #定時0.1hz,也就是10s
2.實例:定時器計滿時,執行回調函數,回調函數的形參就是我們要實現功能的函數或者匿名函數。
>>> def tick(info): #該函數是用於定時器的回調函數的形參,所有用於回調函數的用戶函數(def tick(info):)都必須要填一個定位參數 ... print('yes') ... ... ... >>> tim Timer(4, freq=0, prescaler=31249, period=26879, mode=UP, div=1) >>> tim.callback(tick) #回調函數的形參是用戶函數或者匿名函數都必須有一個位置參數才可以正常工作(位置參數可能就是一個實例),而且調用時不用些括號 >>> yes yes yes yes
驗證上面我們測試出來的又必須要的位置參數到底是什么個東西:
>>> def tick(t): ... print(t) ... ... ... >>> tim = pyb.Timer(4,freq=0.1) >>> tim.callback(tick) >>> Timer(4, freq=0, prescaler=31249, period=26879, mode=UP, div=1) Timer(4, freq=0, prescaler=31249, period=26879, mode=UP, div=1) Timer(4, freq=0, prescaler=31249, period=26879, mode=UP, div=1) Timer(4, freq=0, prescaler=31249, period=26879, mode=UP, div=1)
上面的代碼中,我們建立了一個tick函數,該函數是用於定時器中的回調函數的功能函數,我們上面測試時發現回調函數里面的形參必須是帶一個形參的函數而且賦給回調函數時還必須是直接是函數名不能帶括號。而我們定義的tick函數里面的功能是打印tick(t)里面的t值,很明顯t必須是被賦值才有打印值,實驗得出是這個值為:Timer(4, freq=0, prescaler=31249, period=26879, mode=UP, div=1)。這個值正是Timer類構造的一個tim實例對象,所以用於回調函數的形參是一個功能函數或者匿名的功能函數,而這個功能函數里面的形參是用來接受實例的形參。
結論:回調函數的功能類似於中斷函數,就是某個條件滿足時才調用,比如定時器、計數器、中斷......。回調函數的形參就是來存放和調用當條件滿足時要執行的功能函數(包括匿名函數),而我們定義的功能函數還必須包含一個形參,這個參數是用來導入觸發該事件的對象,比如定時器4這個對象,觸發條件是定時器4計時到設定的時間時。
其它例子:
tim = pyb.Timer(4, freq=100) # freq in Hz tim = pyb.Timer(4, prescaler=0, period=99) tim.counter() # 獲取計數器(也可以設置) tim.prescaler(2) # 設置預分頻器(也可以獲取) tim.period(199) # 設置周期(也可以獲取) tim.callback(lambda t: ...) # 為更新中斷設置回調(t=tim實例) tim.callback(None) # clear callback
注意: 定時器(2)和定時器(3)用於PWM分別設置LED(3)和LED(4)的強度。但是,如果相關LED的強度設置為1到254之間的值,則這些定時器僅配置為PWM。如果不使用LED的強度特征,則這些定時器可以免費用於通用目的。類似地,Timer(5)控制伺服驅動器,Timer(6)用於定時ADC / DAC讀/寫。建議在程序中使用其他定時器。
構造函數:pyb.Timer(id,...)
構造給定id 的新計時器對象。如果給出了附加參數,則計時器由初始化init(...)
。 id
可以是1到14。
Timer類里面的方法
Timer.
init
(*,freq,prescaler,period )
初始化定時器。初始化必須是頻率(以Hz為單位)或預分頻和周期:
>>> tim = pyb.Timer(1) #構造Timer類的一個對象 >>> tim Timer(1) >>> tim.init(freq=100) #設置定時器1為100赫茲觸發(1/100=0.01s) >>> tim Timer(1, freq=100, prescaler=124, period=13439, mode=UP, div=1, deadtime=0, brk=BRK_OFF) >>> tim.init(prescaler=83,period=999) #設置預分頻和周期 >>> tim Timer(1, freq=2000, prescaler=83, period=999, mode=UP, div=1, deadtime=0, brk=BRK_OFF)
freq
- 指定計時器的周期性頻率。您也可以將此視為計時器經歷一個完整周期的頻率。prescaler
[0-0xffff] - 指定要加載到 定時器的預分頻器寄存器(PSC)中的值。該定時器時鍾源是由(分割),以在所述定時器時鍾到達。定時器2-7和12-14的時鍾源為84 MHz(pyb.freq()[2] * 2),定時器1和8-11的時鍾源為168 MHz(pyb.freq()[ 3] * 2)。prescaler + 1
period
定時器 s 1,3,4 和6-15的[0-0xffff] 。定時器 s 2和5的[0-0x3fffffff] 。指定要加載到定時器的AutoReload寄存器(ARR)中的值。這確定了計時器的周期(即計數器周期時)。定時器計數器將在 定時器時鍾周期后翻轉。period + 1
mode
可以是以下之一:div
可以是1,2或4中的一個。將定時器時鍾分頻以確定數字濾波器使用的采樣時鍾。
Timer.UP
- 將計時器配置為從0到ARR(默認)Timer.DOWN
- 將計時器配置為從ARR計數到0。Timer.CENTER
- 將計時器配置為從0到ARR計數,然后再降低到0。callback
- 根據Timer .callback()deadtime
- 指定此時的互補信道上的轉換之間的“死”或非活動時間量(兩個信道都將處於非活動狀態)。deadtime
可以是0到1008之間的整數,具有以下限制:0-128步長為1. 128-256步長為2,256-512步長為8,512-1008步長為16.deadtime
措施刻度source_freq
除以div
時鍾滴答。deadtime
僅適用於計時器 1和8。
您必須指定freq或者指定period和prescaler。
Timer.
deinit
()
取消初始化計時器。
禁用回調(以及關聯的irq)。
禁用任何通道回調(以及相關的irq)。停止計時器,並禁用計時器外圍設備。
Timer.
callback
(fun)
設置定時器觸發時要調用的函數。 fun
傳遞1個參數,定時器對象。如果fun
是,None
那么將禁用回調。
Timer.
channel
(channel, mode, ...)
如果僅傳遞通道號,則返回先前初始化的通道對象(或者None
如果沒有先前通道)。
否則,初始化並返回Timer Channel對象。
每個通道都可以配置為執行pwm,輸出比較或輸入捕獲。所有通道共享相同的基礎計時器,這意味着它們共享相同的計時器時鍾。
關鍵字參數:
mode
可以是以下之一:callback
- 根據Timer Channel.callback()
Timer.PWM
- 在PWM模式下配置定時器(高電平有效)。Timer.PWM_INVERTED
- 在PWM模式下配置定時器(低電平有效)。Timer.OC_TIMING
- 表示沒有引腳被驅動。Timer.OC_ACTIVE
- 發生比較匹配時,引腳將被激活(激活由極性決定)Timer.OC_INACTIVE
- 發生比較匹配時,引腳將變為無效。Timer.OC_TOGGLE
- 發生比較匹配時,引腳將被切換。Timer.OC_FORCED_ACTIVE
- 引腳被強制激活(忽略比較匹配)。Timer.OC_FORCED_INACTIVE
- 引腳被強制激活(忽略比較匹配)。Timer.IC
- 在輸入捕捉模式下配置定時器。Timer.ENC_A
- 在編碼器模式下配置定時器。計數器僅在CH1更改時更改。Timer.ENC_B
- 在編碼器模式下配置定時器。計數器僅在CH2更改時更改。Timer.ENC_AB
- 在編碼器模式下配置定時器。當CH1或CH2發生變化時,計數器會發生變化。pin
無(默認)或Pin對象。如果指定(而非None),這將導致為該定時器通道配置指示引腳的備用功能。如果引腳不支持此定時器通道的任何備用功能,則會引發錯誤。
詳細資料官方地址:http://docs.micropython.org/en/latest/library/pyb.Timer.html?highlight=timer
定時器的通道:
>>> timer = pyb.Timer(1,freq=1000) >>> timer = pyb.Timer(2,freq=1000) >>> ch1 = timer.channel(1,pyb.Timer.PWM,pin=pyb.Pin.board.X1) #通道1 >>> ch1 = timer.channel(1,pyb.Timer.PWM,pin=pyb.Pin.board.X2) >>> ch1 = timer.channel(2,pyb.Timer.PWM,pin=pyb.Pin.board.X1) >>> ch1 = timer.channel(2,pyb.Timer.PWM,pin=pyb.Pin.board.X2) >>> ch1 = timer.channel(2,pyb.Timer.PWM,pin=pyb.Pin.board.X5)#通道2時輸出引腳不對就報錯 Traceback (most recent call last): File "<stdin>", line 1, in <module> ValueError: Pin(A4) doesn't have an af for Timer(2) >>> ch1 = timer.channel(4,pyb.Timer.PWM,pin=pyb.Pin.board.X1) #通道4 >>> ch1 = timer.channel(4,pyb.Timer.PWM,pin=pyb.Pin.board.X5) #通道4對應輸出引腳不對就報錯 Traceback (most recent call last): File "<stdin>", line 1, in <module> ValueError: Pin(A4) doesn't have an af for Timer(2) >>> ch1 = timer.channel(5,pyb.Timer.PWM,pin=pyb.Pin.board.X1) #定時器2的通道只有四個 Traceback (most recent call last): File "<stdin>", line 1, in <module> ValueError: invalid channel (5)
通道channel結論:每個定時器都有相應數量的通道,比如定時器2就有四個通道(1~4),而每個通道都對應着具體的引腳。
PWM例子:
timer = pyb.Timer(2, freq=1000) ch2 = timer.channel(2, pyb.Timer.PWM, pin=pyb.Pin.board.X2, pulse_width=8000) ch3 = timer.channel(3, pyb.Timer.PWM, pin=pyb.Pin.board.X3, pulse_width=16000)
每個定時器的通道個數圖片:
Timer.
counter
([value])
-
獲取或設置計時器計數器。
-
Timer.
freq
([value]) -
獲取或設置定時器的頻率(如果設置,則更改預分頻器和周期)。
-
Timer.
period
([value]) -
獲取或設置計時器的周期。
-
Timer.
prescaler
([value]) -
獲取或設置計時器的預分頻器。
-
Timer.
source_freq
() -
獲取計時器源的頻率。
定時器的通道號分別都對應着芯片上固定的引腳,具體引腳需查詢手冊:在手冊第66頁附近,比如PA0引腳對應的有TIM2_CH1也就是定時器1的通道2,TIM5_ch1,同時也對應着定時器5的通道1,以此類推。
一個定時器可以對應多個芯片引腳,也就是通道,一個定時器比如有四個通道那么這四個通道就共用一個定時器,我們可以用其來輸出PWM,我們每個通道只要改變空占比就可以實現4路PWM了。(這4個通道共用一個定時器,比如四個通道輸出四部PWM,其頻率是一樣的,但是占比可以單獨修改.)
class Timer Channel - 設置定時器的通道
定時器通道用於使用定時器生成/捕獲信號。
Timer Channel對象是使用Timer.channel()方法創建的。
Methods
-
channel.
capture
([value])
-
獲取或設置與頻道關聯的捕獲值。capture,compare和pulse_width是同一函數的所有別名。capture是通道處於輸入捕獲模式時使用的邏輯名稱。
-
channel.
compare
([value])
-
獲取或設置與頻道關聯的比較值。capture,compare和pulse_width是同一函數的所有別名。compare是通道處於輸出比較模式時使用的邏輯名稱。
自定義PWM驅舵機:
>>> from pyb import Pin,Timer >>> tim = Timer(5,freq=50) >>> ch1 = tim.channel(1,Timer.PWM,pin=Pin('X1'),pulse_width=8000) #pulse_width可以省略,注意定時器不要用重了 >>> ch1.pulse_width_percent(3) >>> ch1.pulse_width_percent(12)
板載舵機注意事項:
1 創建pyb.Servo(id)時。id取值1~4。 2 由於每個舵機生產批次和廠家不同,因此會存在差別。在未針對特定的舵機校准前,使用angle()方法指定旋轉角度都會存在偏差。而且指定極限角度如-90°或90°時,舵機會發出咔噠聲,說明指定的角度實際對應的參數值已經超出舵機的旋轉的范圍。時間長了會損壞舵機,請立即指定角度0,或上一次正常的角度范圍。 3 為校准電機,達到精確指定旋轉角度,可使用sv.pulse_width(width)方法,通過指定width的值,找到,舵機旋轉最小的width值和最大的width值。並再調用sv.calibration(pulse_min, pulse_max, pulse_centre)方法,用前面測出的width值校准此通道以適應這個舵機。這樣調用angle()方法時才能准確指定舵機的旋轉角度。 4 舵機的旋轉,無論你在使用angle()方法時指沒指定時間值,都需要時間才能達到指定的角度。 5 uPyBoard板雖然有4個舵機接口,但在單獨使用USB供電的情況下,也需要注意舵機的數量不要接太多。太多或舵機帶動大負載時所需電流很大,有可能會燒毀USB接口。
舵機控制資料鏈接:
https://blog.csdn.net/spritez/article/details/84843431
https://blog.csdn.net/weixin_41805734/article/details/89790559
控制舵機的PWM波是一種方波,頻率為50Hz,周期即其倒數:20ms,在每個周期里面,高電平占0.5ms到2.5ms之間。0.5ms代表的是0度,2.5ms代表的是180度,其他的度數可以按比例換算。
50Hz也就是20ms內的0.5-2.5換成百分比形式就是2.5%-12.5%。7.5即為中間。只有此值內才可以控制舵機轉動。
from pyb import Pin, Timer i = 7.5 # 20ms內的0.5-2.5換成百分比形式就是2.5%-12.5%。7.5即為中間。 sw = pyb.Switch() # 用板子上的按鍵控制 tim = Timer(5, freq=50) # 選擇定時器5,頻率50hz ch1 = tim.channel(1, Timer.PWM, pin=Pin.cpu.A0) # 選擇通道1和定時器5對應引腳A0 while True: sw_state = sw() if sw_state: i += 0.01 pyb.udelay(500) # 延時500us,減點速 if i > 12.0: i = 12.0 else: i -= 0.01 pyb.udelay(500) #延時是讓其能有時間緩沖一下,程序周期是短的,而舵機轉動時間是長的 if i < 3.0: i = 3.0 ch1.pulse_width_percent(i) # 控制占空比,20ms內0.5-2.5ms的百分比(不給滿)
載用戶按鍵按下舵機正轉,松手就復位至-80到-90°之間。
用定時器來對GPIO口的PWM調節:我們使用的是板載藍燈或者橙色燈,因為在板載上的LED燈里面就它兩個有定時器的通道口,所有說只要這個引腳有定時器通道功能就可以設置其PWM,我覺得如果沒有定時器通道時我們還可以自己模擬PWM,只是這樣子就麻煩些。
使用定時器通道來實現PWM功能:
>>> tim_B4 = Timer(3,freq=100) >>> ch1_B4 = tim_B4.channel(1,Timer.PWM,pin=Pin('B4')) #對應藍燈的定時器是3,通道號是1 >>> ch1_B4.pulse_width_percent() 0.0 >>> ch1_B4.pulse_width_percent(1) #亮度調節 >>> ch1_B4.pulse_width_percent(2) >>> ch1_B4.pulse_width_percent(100) #最亮 >>> ch1_B4.pulse_width_percent(50) >>> ch1_B4.pulse_width_percent(20) >>> ch1_B4.pulse_width_percent(10) >>> ch1_B4.pulse_width_percent(1)
模擬脈沖;
time = 0 tim = Timer(5,freq=10) def i_pwm(t): global time time += 1 if time >= 10: time = 0 tim.callback(i_pwm) def drive(pwm): if time < pwm:pyb.LED(1).on() else:pyb.LED(1).off() while True: drive(3) pyb.delay(1)
呼吸燈的另一種實現方式:
PWM
在大部分微控制器上,PWM其實是定時器的一種工作模式。定時器可以控制多個通道,分布控制不同的 GPIO 輸出可變頻率和占空比的方波。同一個定時器下的不同PWM通道,頻率都是相同的,但是可以分別設置不同的占空比。
PWM功能需要使用Timer和Pin兩個模塊,首先定義Timer並設置定時器的基本工作參數,然后指定Timer的通道,並設定PWM模式及關聯的Pin,最后設置輸出脈沖寬度或者脈沖寬度百分比(占空比)。
下面例子演示了使用PWM控制PYB V10上LED3和LED4,通過改變占空比和頻率,就可以改變LED亮度或者閃爍頻率。