怎样用arduino实现脉冲计数器

怎样用arduino实现脉冲计数器,第1张

参考我这个程序

//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:


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/yw/11627030.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-05-17
下一篇 2023-05-17

发表评论

登录后才能评论

评论列表(0条)

保存