在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.