//code by Crenn from http://thebestcasescenario.com
//project by Charles Gantt from http://themakersworkbench.com
/*To disable interrupts:
cli() // disable global interrupts
and to enable them:
sei() // enable interrupts
*/
//Varibles used for calculations
int NbTopsFan
int Calc
//The pin location of the sensor
int hallsensor = 2
typedef struct{ //Defines the structure for multiple fans and their dividers
char fantype
unsigned int fandiv
}fanspec
//Definitions of the fans
fanspec fanspace[3]={{0,1},{1,2},{2,8}}
char fan = 1 //This is the varible used to select the fan and it's divider, set 1 for unipole hall effect sensor
//and 2 for bipole hall effect sensor
void rpm () //This is the function that the interupt calls
{
NbTopsFan++
}
//This is the setup function where the serial port is initialised,
//and the interrupt is attached
void setup()
{
pinMode(hallsensor, INPUT)
Serial.begin(9600)
attachInterrupt(0, rpm, RISING)
}
void loop ()
{
NbTopsFan = 0 //Set NbTops to 0 ready for calculations
sei() //Enables interrupts
delay (1000) //Wait 1 second
cli() //Disable interrupts
Calc = ((NbTopsFan * 60)/fanspace[fan].fandiv)//Times NbTopsFan (which is apprioxiamately the fequency the fan is spinning at) by 60 seconds before dividing by the fan's divider
Serial.print (Calc, DEC)//Prints the number calculated above
Serial.print (" rpm\r\n")//Prints " rpm" and a new line
}
ArduinoUno输出⾃定义频率的PWM(详解)PWM
简单的说 ,就是在⼀些情况下,GPIO脚位不在的切换“有电”“没电”,
每秒种循环的⼏次即为其 Frequency(频率),
每次“有电”时间站⼀个循环的百分⽐称为其占空⽐
Arduino⾥的PWM
⾸先,Arduino Uno的5,6,9,10,3,11接⼝可以通过简单语句analogWrite(pin, dutyCycle)来实现⼀个指定占空⽐的PWM。其中pin的值选择(5,6,9,10,3,11),dutyCycle的值在0~255之间,0为占空⽐0%,255为占空⽐100%。但是这种⽅式PWM信号的频率是固定的默认值,⼤约1000Hz左右(16MHz/64/256)。
其次,⼿动切换⾼电平和低电平,再在中间加⼊delay函数,可以实现⾃定义频率的PWM:
void setup()
{
pinMode(13, OUTPUT)
}
void loop()
{
digitalWrite(13, HIGH)
delayMicroseconds(100)// Approximately 10% duty cycle @ 1KHz
digitalWrite(13, LOW)
delayMicroseconds(1000 - 100)//修改这⾥的1000可以调整频率
}
这个例⼦中:⼀个循环是1000us = 1ms,所以⼀秒循环1000次,因此Frequency是1KHz,
每个循环中:有电的⽐率是100/1000 * 100% = 10%,所以duty cycle(占空⽐)为10%
这样就可以模拟出5V * 10%=0.5V的电压
好处是任何⼀个引脚都能通过这样输出PWM,
但是,这种 *** 作需要CPU全神贯注的查数,任何其他的进程的⼲扰会导致输出的信号频率不准。
综上,需要底层的⼿段来控制Arduino实现PWM的频率调节。
调节Arduino⾥的时钟频率
Arduino Uno⾥有三个Timer:Timer0,Timer1,Timer2。 三个Timer都可以⾃定义调整频率,但是各有特点。Timer0负责控制delay 等函数,动了Timer0的频率会导致计时函数不准;Timer1的计数器是16位的,和Timer0,Timer2的8位计数器不太⼀样;Timer2的频率可调的档位更多,因为它有7档预除数,下⽂会进⼀步解释。这⾥选择Timer2进⾏调节 *** 作,先上代码:
void setup() {
// put your setup code here, to run once:
pinMode(3, OUTPUT)
pinMode(11, OUTPUT)
TCCR2A = _BV(COM2A0) | _BV(COM2B1) | _BV(WGM21) | _BV(WGM20)//Set Timer2 to varying top limit fast PWM mode
TCCR2B = _BV(WGM22) | _BV(CS22) | _BV(CS21) | _BV(CS20)//another way to set prescaler CS2=fff
OCR2A = 155//Top value A
OCR2B = 30//Toggle value B, Output at pin 3
//CS2 Divisor Frequency
//001 1 31372.55
//010 8 3921.16
//011 32 980.39
//100 64 490.20 <--DEFAULT
//101 128 245.10
//110 256 122.55
//111 1024 30.64
}
void loop() {
// put your main code here, to run repeatedly:
}
由以上代码可见,需要的设置分为三个部分:pinMode,TCCR2A/B,OCR2A/B(这⾥的2是因为选择了Timer2)。
脉冲⽣成模式控制位(WGM):⽤来设置时钟的模式
时钟选择位(CS):设置时钟的预定标器
输出模式控制位(COMnA和COMnB):使能/禁⽤/反相 输出A和输出B
输出⽐较器(OCRnA和OCRnB):当计数器等于这两个值时,输出值根据不同的模式进⾏变化
pinMode:
Timer2所控制的管脚是pin11和pin3(Timer0控制5,6;Timer1控制9,10–这是chip的 datasheet上规定),所以把这两个管脚设为输出。
OCR2A/B:
这部分的解释需要提到Timer的结构和原理。
每个Timer⾥都有⼀个计数器和两个⽐较寄存器。Timer2⾥计数器从0数到255(8位)然后归0继续从头数;Timer2的两个⽐较寄存器分别为OCR2A和OCR2B。
⽐较寄存器就是你设置⼀个⼩于255的数,⽐如155。当计数器数到0时输出为⾼电平,数到155的时候改变输出为低电平。这样就实现了占空⽐的调节。在普通模式下(Fast
PWM),OCR2A控制pin11的占空⽐,OCR2B控制pin3的占空⽐。如下图所⽰
。
。
TCCR2A/B:
理解了Timer的原理,下⾯来讨论这个PWM的频率。Arduino Uno的芯⽚ATmega328,晶振频率为16MHz。Timer计数器的频率会在这个基础上除以⼀个预除数,Timer2可选择的预除数有(1,8,32,64,128,256,1024)。也就是说,如果预除数设为64(默认),计数器计数的频率是16MHz/64 。⼜因为计数器要数256下才会完成⼀个PWM周期,所以输出PWM的频率
16MHz/64/256(Frequency = 16 MHz / 64 / 256 = 976.5625Hz), 约等于1000Hz。若果要获得最低的输出频率,预除数要选1024,得到的PWM是61Hz。
TCCR2A/B就是来控制Timer2计数器的模式与预除数的⼤⼩的,由于是分位赋值,看起来怪怪的,我来解释⼀下。先说CS2位,这个就是来控制Timer2计数器预除数的: _BV(CS22) | _BV(CS21) | _BV(CS20)的三部分由逻辑按位或“|”链接;每个BV是按位赋注(bit value)的意思;_BV(CS22 )= 在CS2⾥,1
2(把1左移2位) = 00000100;得到三部分分别是
00000100,00000010,00000001;按位或最终得到0111;查代码⾥的表得到对应的预除数是1024。
模式选择:
现在的问题是,我需要的是100Hz,不是1024预除数下的61Hz,如何实现?这就需要控制计数器模式来微调频率。请看下图:
这张图中的模式可以在原有的fast PWM基础上提⾼频率,得到图中OCnB所⽰的信号。这个模式叫做Varying the timer top limit: fast PWM。⽐较寄存器OCR2A在这⾥不再控制管脚11的占空⽐,⽽是设定⼀个计数器的上限:计数器不⽤数到255⽽是达到OCR2A就可以归零。OCR2B依然控制管脚3的占空⽐。
为了让pin11有活⼲,这⾥设置TCCR2A⾥的COM2A位=01(表⽰数到极限就把pin11的电平反转,本应⽤不需要),COM2B位=10(表⽰pin3输出⾮反转PWM)。
那么是如何选择模式的?剩下的WGM2位就是确定模式的。在fast PWM模式下,WGM2位是011,Varying the timer top limit: fast PWM模式下,WGM2位是111。所以需要_BV(WGM22) | _BV(WGM21) | _BV(WGM20)。处于我所不理解的原因,这个赋值可以被分为两部分分别写在TCCR2A和TCCR2B⾥。有明⽩的⾼⼿⿇烦留个⾔解释⼀下。
频率计算
到这⾥,所有设置已经解释完。下⾯来计算⼀下100Hz输出的PWM具体参数应该设为多少。
pin3的输出频率=16MHz / 1024/ (OCR2A + 1),因此100Hz对应的OCR2A=155。(+1是因为fast PWM是从0开始数到上限值)
占空⽐ = (OCR2B+1)/ (OCR2A+1),所以:
占空⽐OCR2B值
20%30
占空⽐OCR2B值
25%38
33%51
50%77
100%155
常见的模式(“快速PWM”和“相位修正PWM”)
快速PWM
对于快速PWM来说,时钟都是从0计数到255。当计数器=0时,输出⾼电平1,当计数器等于⽐较寄存器时,输出低电平0。所以输出⽐较器越⼤,占空⽐越⾼。这就是传说中的快速PWM模式。后⾯的例⼦会解释如何⽤OCRnA和OCRnB设置两路输出的占空⽐。很明显这种情况下,这两路输出的周期是相同的,只是占空⽐不同。
快速PWM的例⼦
下⾯这个例⼦以Timer2为例,把Pin3和Pin11作为快速PWM的两个输出管脚。其中:
WGM的设置为011,表⽰选择了快速PWM模式;
COM2A和COM2B设置为10,表⽰A和B输出都是⾮反转的PWM;
CS的设置为100,表⽰时钟周期是系统时钟的1/64;
OCR2A和OCR2B分别是180和50,表⽰两路输出的占空⽐;
pinMode(3, OUTPUT)
pinMode(11, OUTPUT)
TCCR2A = _BV(COM2A1) | _BV(COM2B1) | _BV(WGM21) | _BV(WGM20)
TCCR2B = _BV(CS22)
OCR2A = 180
OCR2B = 50
这段代码看上去有点晕,其实很简单。_BV(n)的意思就是1
n,是移位命令。
COM2A1,表⽰COM2A的第1位(靠,其实是第2位,不过程序员们是从0开始数数的)。所以_BV(COM2A1)表⽰COM2A = 10;
类似的,_BV(WGM21) | _BV(WGM20) 表⽰ WGM2 = 011。
在Arduino Duemilanove开发板,上⾯这⼏⾏代码的结果为:
输出 A 频率: 16 MHz / 64 / 256 = 976.5625Hz
输出 A 占空⽐: (180+1) / 256 = 70.7%
输出 B 频率: 16 MHz / 64 / 256 = 976.5625Hz
输出 B 占空⽐: (50+1) / 256 = 19.9%
频率的计算⾥都除以了256,这是因为除以64是得到了时钟的计数周期,⽽256个计数周期是⼀个循环,所以PWM的周期指的是这个循环。
另外,占空⽐的计算都加了1,这个还是因为⽆聊的程序员们都从0开始计数。
相位修正PWM
另外⼀种PWM模式是相位修正模式,也有⼈把它叫做“双斜率PWM”。这种模式下,计数器从0数到255,然后从255再倒数到0。当计数器在上升过程中遇到⽐较器的时候,输出0;在下降过程中遇到⽐较器的时候,输出1。说实话,我觉得这种模式除了频率降低了⼀倍之外,没看出和快速PWM有什么区别。可能是在集成电路的底层级别上有区别吧。原⽂说“它具有更加对称的输出”,好吧,也许⽼外都⽐较傻吧。
相位修正PWM的例⼦
下⾯的例⼦还是以Timer2为例,设置Pin3和Pin11为输出管脚。其中WGM设置为001,表⽰相位修正模式,其他位设置和前⾯的例⼦相同:
pinMode(3, OUTPUT)
pinMode(11, OUTPUT)
TCCR2A = _BV(COM2A1) | _BV(COM2B1) | _BV(WGM20)
TCCR2B = _BV(CS22)
OCR2A = 180
OCR2B = 50
在Arduino Duemilanove开发板,上⾯这⼏⾏代码的结果为:
输出 A 频率: 16 MHz / 64 / 255 / 2 = 490.196Hz
输出 A 占空⽐: 180 / 255 = 70.6%
输出 B 频率: 16 MHz / 64 / 255 / 2 = 490.196Hz
输出 B 占空⽐: 50 / 255 = 19.6%
⼀般来说,普通⽤户是不需要设置这些时钟参数。Arduino默认有⼀些设置,所有的时钟周期都是系统周期的1/64。Timer0默认是快速PWM,⽽Timer1和Timer2默认是相位修正PWM。具体的设置可以查看Arduino源代码中writing.c的设置。
需要特别特别注意的是,Arduino的开发系统中,millis()和delay()这两个函数是基于Timer0时钟的,所以如果你修改了Timer0的时钟周期,这两个函数也会受到影响。直接的效果就是delay(1000)不再是标准的1秒,也许会变成1/64秒,这个需要特别注意
在程序中使⽤analogWrite(pin, duty_cycle)函数的时候,就启动了PWM模式;当调⽤digitalWrite()函数时则取消了PWM模式。请参考wiring_analog.c和 wiring_digital.c⽂件。
还有⼀件很有意思的现象,对于快速PWM模式,如果我们设置analogWrite(5, 0),实际上应该有1/256的占空⽐,事实上你会发现输出的是永远低电平的0。这个实际上是在Arduino系统中强制设定的,如果发现输⼊的是0,那么就关闭PWM。随之⽽来的问题是,如果我们设置analogWrite(5, 1),那么占空⽐是多少呢?答案是2/256,也就是说0和1之间是有⼀个跳跃
。
¥
5
百度文库VIP限时优惠现在开通,立享6亿+VIP内容
立即获取
ArduinoUno输出自定义频率的PWM(详解)
ArduinoUno输出⾃定义频率的PWM(详解)
PWM
简单的说 ,就是在⼀些情况下,GPIO脚位不在的切换“有电”“没电”,
每秒种循环的⼏次即为其 Frequency(频率),
每次“有电”时间站⼀个循环的百分⽐称为其占空⽐
第 1 页
Arduino⾥的PWM
⾸先,Arduino Uno的5,6,9,10,3,11接⼝可以通过简单语句analogWrite(pin, dutyCycle)来实现⼀个指定占空⽐的PWM。其中pin的值选择(5,6,9,10,3,11),dutyCycle的值在0~255之间,0为占空⽐0%,255为占空⽐100%。但是这种⽅式PWM信号的频率是固定的默认值,⼤约1000Hz左右(16MHz/64/256)。
其次,⼿动切换⾼电平和低电平,再在中间加⼊delay函数,可以实现⾃定义频率的PWM:
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)