在Arduino中,可使用AnalogWrite來使用硬件產生490Hz/980Hz的pwm波,並可根據參數來設定佔空比。不瞭解這個的同窗能夠去AnalogWrite學習下,SecretsOfArduinoPWM也是講了Arduino在avr的定時/計數器上作的封裝,咱們這裏並不講Arduino相關,而是講AVR的定時/計數器,如何產生更多PWM波和定時/計數器的中斷使用。html
以ATmega358p爲例,其內部擁有一個16位計時器,兩個8位計時器,下圖則爲16位計時器的大體圖解:
對於沒有接觸過avr內部的Arduino同窗來講,這張圖看不出來任何意思,別急,這些都是AVR-GCC裏定義的縮寫,咱們先來解釋下圖中的縮寫對照:web
縮寫 | 全稱 |
---|---|
TCNT | Timer/Counter Register |
TCCR | Timer/Counter Control Register |
OCR | Output Compare Register |
OC | Output Compare Match Output |
ICR | Input Capture Register |
Int.Req | Interrupt Request |
TOV | Timer Overflow |
ICF | Input Capture Flag |
而圖中TCNT爲主要工做部件,其工做模式的是依據TCCR的設定值。以該16位計時器爲例,該計時器在ATmega358p中的序號爲1,則其全部縮寫都會與1有關,即TCNT1的工做模式由TCCR1A和TCCR1B來決定。不急着看別的,咱們先來瞅瞅TCCR1A與TCCR1B是怎麼來配置TCNT1的工做模式的:異步
TCCR1A:
學習
圖中七、六、五、4位分別應該是COM1A一、COM1A0、COM1B一、COM1B0,話說這datasheet有時候也真是會省事兒ui
TCCR1B:
.net
這兩個寄存器都是八位寄存器,再來一張表來對照一下圖中的縮寫:線程
縮寫 | 全稱 |
---|---|
COMnA | Compare Output Mode for Channel A |
COMnB | Compare Output Mode for Channel B |
WGM | Wave Generation Mode |
ICNC | Input Capture Noise Canceler |
ICES | Input Capture Edge Select |
CS | Clock Select |
咱們經過配置這兩個寄存器來控制TCNT1的工做模式,經過配置WGM來選擇波型生成模式,主要有Fast PWM/PWM Phase Correct/CTC
模式,而且也擁有不一樣的計數TOP值,有0xFF/ICR1/OCR1A
等,經過COM結合WGM對針腳的輸出作配置,用CS來選擇生成波型的prescaler,分別有1/8/64/256/1024
,或者外部時鐘。TOP值爲ICR1或OCR1A時,要對使用的寄存器進行賦值。工做模式中,Fast PWM與PWM Phase Correct這兩種計數模式不一樣的是,PWM Phase Correct能夠到達top值後又遞減至0,Fast PWM到達top值後則會觸發上升或降低沿。3d
以下配置:code
TCCR1A = _BV(COM1A1) | _BV(COM1B1) | _BV(WGM10); TCCR1B = _BV(CS12) | _BV(CS10); //WGM配置了PWM Phase Correct,使用的TOP值爲0x00FF //COM配置了在往上計數時清零,往下計數時置位 //使用了1024的prescaler OCR1A = 100; OCR1B = 50;
則咱們能夠獲得的時鐘頻率爲Fclk/(prescaler*TOP)=16000000/(1024*255)/2=30.63Hz
,且OC1A佔空比爲OCR1A/TOP=100/255=39%
,OC1B佔空比爲OCR1B/TOP=50/255=19.6%
。htm
更多配置組合請看SecretsOfArduinoPWM/PWM的祕密或直接datasheet,其中要注意的是,使用OCR1A爲TOP值時,能夠在必定範圍內配置任意頻率,但OC1A的佔空比爲始終爲50%,OC1B的佔空比爲(OCR1B+1)/(OCR1A+1)。
中斷是其它與CPU異步進行的硬件與CPU交互的一種方法。這樣咱們就不用在CPU中去等待其它某些任務的完成和觸發狀態,由其它硬件去來觸發進入CPU主進程的時機。回頭看第一張大圖,在圖中能夠找到四處(Int.Req),分別是TOVn(Timer/Counter Overflow), OCnA(Output Compare A Match), OCnB(Output Compare B Match), ICF(Input Capture Flag),根據字義咱們即可瞭解到該中斷的做用,在使用中斷時,咱們要先於TIMSK1(Timer/Counter 1 Interrupt Mask Register)中開啓相應的中斷位。
下圖爲TIMSK1:
圖中的字母應爲ICIE1, OCIE1B, OCIE1A, TOIE1,這數據手冊,也是太懶了。下表爲對照:
縮寫 | 全稱 |
---|---|
ICIE | Input Capture Interrupt Enable |
OCIE | Output Compare Interrupt Enable |
TOIE | Timer/Counter Overflow Interrupt Enable |
開啓該遮罩位後,即可打開中斷,前提也是在全局中斷打開的狀況下(sei()爲打開全局中斷,cli()爲關閉全局中斷
)。在當下的AVR-GCC環境中,咱們使用宏命令ISR來定義中斷程序,使用方法以下:
ISR(xxxx_vect) { // user code here }
而這裏的xxxx_vect則是要監聽的中斷向量地址,能夠於Atmel官網<avr/interrupt.h>: Interrupts上找獲得全部中斷對應的向量名稱。
如上面的四種向量,於mega328p中,則分別對應:
ICIE1 | TIMER1_CAPT_vect |
OCIE1A | TIMER1_COMPA_vect |
OCIE1B | TIMER1_COMPB_vect |
TOIE1 | TIMER1_OVF_vect |
而中斷又分爲兩種,一種爲事件觸發型
,這種中斷會在上一個中斷沒有運行結束前隊列等待,直至前面優先的任務完成後才能執行;另外一種爲中斷條件觸發
,若是上一個中斷尚未返回回來,那麼則不會觸發這個中斷(若是定時器爲高頻時則會出現該狀況)。因此在使用中斷的時候,進入中斷、跳轉、返回都須要耗費時鐘,不要對高頻使用中斷,甚至會使主線程一直處於堆棧狀態,且中斷中不要運行太多程序。
There are basically two types of interrupts: The first type is triggered by an event that sets the Interrupt Flag. The second type of interrupts will trigger as long as the interrupt condition is present.
The traps when using interrupts中也是羅列了一些使用中斷的坑和技巧,能夠看看。附上Atmega328p的datasheet,真是有問題就看datasheet,上面的圖與摘錄都是從datasheet裏出來的。
Have fun.