月度归档:2016年01月

PWM变模拟信号(积分电路 )

就是简单的积分电路。频率不变,积分后的电平相当于把高电平的电压和对应的时间的面积,平均到一个周期里。基本上占空比是50%,转换的电压,就是最高电压的50%,占空比30%,模拟电压就是30%。如PWM是5V的电平,转换后的直流电压就是2.5V和1.5V.
积分电路的用途
积分电路常见用于黑白和彩色电视机的扫描电路中。

时间常数τ=RC=10000*0.1×10^-6=0.001秒
就是(1毫秒)。

积分电路的结构
积分电路的结构和电阻分压电路也很相似,并且与微分电路更相似,只是把微分电路中的电阻和电容交换位置而已。这也是一个用电容器和电阻器组成的另一种分压电路。但积分电路输入也不是正弦信号,而是脉冲信号,这也是积分电路与其它分压电路的不同之处。

积分电路的要求
在积分电路中,要求RC时间常数(电阻值乘以电容值)远远大于脉冲宽度,这一点是积分电路中电阻和电容必须满足的要求,这是微分电路和积分电路的又一个不同之处。

积分电路的作用
积分电路与微分电路在功能方面表现也是相反的:能够提取输入信号的平均值大小,即低频成份。这中电路功能与电容滤波电路是有点相似,从电路中提取高频成份去填补低频成份,以至达到电路频率大小变化平均。

积分电路的原理
积分电路和微分电路在结构上只是电阻和电容交换位置,从微分电路图中可以想象出积分电路图。积分电路中电路输入矩形脉冲信号U1加在电阻器上,经过电阻器后再在电容器上输出三角脉冲信号U2。这个电路也可以这样理解:在电阻器和电容器串联电路上输入矩形脉冲信号,用示波器查看电路波形,在电阻器前面会是矩形脉冲信号,在电容器前面会是三角脉冲信号。这主要利用电容器储能充电的特性将电路中矩形脉冲信号转变成三角脉冲信号输送到下一级电路中。三角脉冲波比矩形脉冲波相对来说比较平稳,矩形波是明显一起一落,三角波虽然有起伏,但起伏变化没有矩形那么快,这样看来积分电路就和滤波电容的性质差不多了。
对积分电路原理分析过程要根据输入脉冲信号在前沿阶段、平顶阶段和后沿阶段等几种情况来进行:
1、当输入信号矩形脉冲还没有出现时,输入信号电压为零,所以输出信号电压也为零。
2、当输入信号矩形脉冲出现时,输入信号通过电阻对电容进行充电。由于电容内部刚开始没有电荷,所以电容会呈短路状态,也就是刚开始电容所承受的电压为零,输出信号电压也为零。
3、当输入信号矩形脉冲出现后,随着电容充电电荷不断地增加,电容所承受电压也在不断升高,输出信号电压也会随之升高。流过电容的电流近似与输入脉冲信号电压大小成正比,所以输出信号电压大小近似与输入信号电压的积分正比,积分电路由此而得名。
4、当输入信号矩形脉冲消失时,电阻器的电压会突变为零,电容器就会对电阻器放电。输出电路的电压就是电容放电电压,随着放电的进行,电容的电压越来越低,输出电路的电压也随之越来越低。
5、当输入信号矩形脉冲消失后,由于积分电路要求时间常数远远大于脉冲宽度,所以放电速度比较慢,在电容还没有放完电时下一个脉冲信号就又到来。电容因为刚刚放了电而电压低于输入信号电压,这时电容又开始充电,随着充电的进行电容的电压越来越大,输出电压也就越来大。如此反复,一个脉冲又一个脉冲地循环。

analogWrite怎麼做PWM輸出?改PWM頻率,定时器相關

大家都知道在 Arduino UNO 有六支 pin 可以使用 analogWrite( )做 PWM 輸出,
在板子上 pin 旁邊標示有 “~” 符號, analogWrite( ) 可以用來產生模擬電壓,
很多人一定很好奇那是怎麼做到的 ?

首先來看看 PWM 模擬電壓的原理, 這在官網上就有解說了:
     http://arduino.cc/en/Tutorial/SecretsOfArduinoPWM
所謂的 PWM 全稱是 Pulse-Width Modulation (PWM), 其實這也沒啥學問,
就是對 GPIO 腳位不斷的切換 “有電” “沒電”,
每秒鐘循環幾次即為其 Frequency(頻率),
每次”有電”時間佔一個循環的百分比稱為其佔空比(Duty cycle);
以下是官網上的模擬範例:
int pin = 13;
void setup() {
  pinMode(pin, OUTPUT);
}
void loop(){
  digitalWrite(pin, HIGH);
  delayMicroseconds(100); // Approximately 10% duty cycle @ 1KHz
  digitalWrite(pin, LOW);
  delayMicroseconds(1000 – 100);
}
這個範例中, 一個循環是 1000 us = 1ms, 所以一秒循環 1000次, 因此 Frequency 是 1 KHz,
每個循環中, 有電的比率是 100/1000 * 100% = 10%, 所以 duty cycle (佔空比)為 10%;
這樣就可以模擬出 5Volt x 10% = 0.5 Volt 的電壓!
如果真的這樣做, 有好處也有壞處, 官網上已經說了:
好處是任一支 pin 都可這樣用, 包括 Pint 0 到 Pin 13, 以及 Pin A0 到 A5 共 20支 pin 都可以!
壞處卻更多, 首先就是頻率(Frequency)和佔空比(duty cycle)可能受中斷(Interrup)的影響變成不是很準確 !!
最大的壞處是, 在某支 pin 做 PWM 輸出期間都沒辦法做別的事情 !!

既然說這只是示範可以這樣做, 在  Arduino 當然不可能是這麼做,
那 Arduino 是怎麼做的呢?

就是透過 Timer 計時器直接控制 pin 做 PWM 輸出, Arduino UNO 的 MCU 有三個 timer,
其中 timer0 控制 pin 5, pin 6; timer1 控制 pin 9, pin 10; timer 2 控制 pin 11, pin 3;
所以, 我們可以對這些 pin analogWrite(pin, val); 輸出 0 到 255 的 val 值到 pin ;
如果輸出 val 是 0, 它會偷偷直接改用 digitalWrite(pin, 0); 輸出,
如果 val 是 255, 也是會偷偷直接改用 digitalWrite(pin, 1); 輸出!
如果 val 是 1 到 254, 則會下命令請 pin 腳對應的 timer 計時器(定時器)幫忙!!
How ?
首先要知道 timer 的基本知識:
(1)每個 timer 一定有個 counter, 例如 timer0 的TCNT0, timer1 的TCNT1, timer2 的TCNT2;
該 counter 一定是每個 tick 會加 1, 每個 tick 通常是把 CPU 的 clock 拿來經過一個除頻電路,
然後給 timer 使用; Arduino UNO 採用 AVR ATmega328 MCU, 且 clock Rate 是 16MHz,
每個 timer 的除頻 Prescaler 是獨立設定的, 通常可以設 1, 2, 4, 8, 64, 256, or 1024 等,
這必須看 MCU 的 datasheet.

(2)每個 timer 通常提供許多 mode 運作模式, 例如 counter 溢出(Overflow)或Rollover歸零時產生中斷,
或 TCNT? 達到某個值時產生中斷等, Arduino ATmega328 的 timer 有 16種 mode, 許多 Mode 是與 PWM 有關;
要設定 timer 的 Mode 可以透過修改 timer 的控制暫存器, 例如 TCCR?A, TCCR?B,    注意以 ATmega328 為例, TCCR?A 和 TCCR?B 要合起來用, 此處的 A, B 與 channel A, channel B 無關!!

(3)每個 timer 通常有比較暫存器(Compare Register), 當 TCNT? 值與該些比較暫存器相同時可以做某事,
   不一定是對 CPU 產生中斷! Arduino 每個 timer 有兩個比較暫存器, 分別命名 OCR?A 和 OCR?B,
  其中 ? 是 0, 1, 2 分別對應到 timer0, timer1, 和 timer2 這三個計時器.

你可以先偷看 analogWrite( ) 的程序碼:
    在你 Arduino IDE 下的 hardware\arduino\cores\arduino\wiring_analog.c
很簡單, 真正請 timer 幫忙只做三件事: a.找出對應的 port, b.設定控制暫存器, c.填入 analog的值到比較暫存器!
不過你會發現看不太懂, 因為還不知道硬體 timer 控制 PWM 運作方式與原理!
不想看 datasheet 可以參考這:
http://letsmakerobots.com/conten … mers-and-interrupts

以Arduino UNO 的 timer1為例, 在 mode 5 (Fast PWM, 8 bit), 此時, TCNT1 從 0 數到 255, 然後又加 1 就變0, …
通常從 255 (此 mode 的最大值)又加 1 變為 0 之時會產生 OVF 中斷(TIMSK1的TOIE1要 set), 不過這與 PWM 無關!
PWM 不是用 Interrupt  中斷請求做的, 不必麻煩 CPU, CPU 只要下命令給 timer, timer 就會照命令執行PWM工作 !!
    PWM 是利用每個 timer 上的兩個”匹配符合輸出”暫存器(Compare Match Output) COM?A 和 COM?B;
(注意雖是 Compare Match Output, 但暫存器名稱是 COMxy 不是 CMOxy 喔 !)
在timer1 的 mode 5, 又稱 Fast PWM mode, (不過請注意 Arduino 的 init( ) 設定只有 timer0 用這, 另外 timer1 和 timer2 不是用這 mode),
這時可以把 1 到 254 之間的值放入 OCR1A 或 OCR1B 以便控制 pin 9 或 pin 10
的 PWM duty cycle, 1 到 254 分別對應到 (1+1)/256, .., (254+1)/256 的 duty cycle.

會 +1 是硬體電路設計上的關係, data sheet 上說:
   Note that fast PWM holds the output high one cycle longer than the compare register value.
在 TCNT1 等於 0  之時, COM1A and/or COM1B 會輸出(當然要 TCCR1A 內的 COM1A1 and/or COM1B1 有set),
然後在 TCNT1 等於 OCR1A 則關閉 COM1A, 當 TCNT1 等於 OCR1B 則關閉 COM1B,
注意沒有立即關閉, 是延遲一個 tick 才關閉 ! 所以才會多加1, 因為一個循環是 256, 不是 255,如果不延遲加 1, 則輸出 val 是 254 時變成 254/256, 還差一點點, 所以犧牲 1/256, 就是沒有 1/256佔空比 !!

   由於 Arduino 的 init( )把 timer1 的 Prescaler 設定為 64,
(參考在你 Arduino IDE 內的   hardware\arduino\cores\arduino\wiring.c )
且把 timer1 設定為 8-bit phase correct pwm mode, 所以其頻率是 490.196Hz, 不是 976.5625Hz;
所謂的 8-bit phase correct pwm mode, 意思是 TCNT? 從 0 數到 255, 接著又從 255 倒著數回 0,
那何時把 COM1A and/or COM1B 的輸出打開或關閉呢?

根據 datasheet, 在從 0 往上數, 碰到 OCR1A 時把 COM1A 關閉,
後從 255 往回數, 數到 OCR1A 時把 COM1A 打開(有電); 對於 OCR1B 和 COM1B 也是這樣!
這使得 duty cycle (佔空比) 更準確, 也就是 val 1 ~ 254 分別對應到 1/255 到 254/255 的 duty cycle.
但是 Frequency 則不是除以 256, 是要除以 255 再除以 2, 於是: (注意是 255, 不是 256喔!)
Frequency = 16 MHz / 64 / 255 / 2 = 490.196Hz;

timer 2 也是在 init( )被設為 Prescaler 64 的 phase correct pwm (8-bit);
但是, timer0 雖然 Prescaler 也設 64, 但 PWM 是用 Fast PWM mode,
不使用 phase correct mode 是為了避免影響維護 millis( ) 的中斷 timer0 Overflow Interrupt,
即 ISR(TIMER0_OVF_vect) 這中斷處理程序, 否則 millis( ) 和 micros( ) 以及 delay() 都會受到影響 !!
因此 , timer0 控制的 PWM Frequency 976.5625Hz,
    16 MHz / 64 / 256 = 976.5625Hz
注意用 timer0 控制的pin 5, pin 6 之 PWM 的 duty cycle 無法是 1/256, 它是 0 再來就 2/256了!
結論:

timer0 控制 pin 5, pin 6, PWM 頻率 976.5625Hz, duty cycle可以 2/256 ~ 255/256 (對應 1 到254);
timer1 控制 pin 9, pin 10, PWM 頻率 490.196Hz, duty cycle 可以 1/255 ~ 254/255(對應 1 到254);
timer2 控制 pin 11, pin 3, PWM 頻率 與 duty cycle 跟 timer1 控制的相同 !!

Q: 那 PWM 的 Frequency 可不可以更改?
A: 可以, 偷改 timer 的 Prescaler 就可以達到更改 Frequency 的目的 !
但是, 千萬不要更改 timer0 的 Prescaler, 否則 millis( ) 和 micros( ) 以及 delay() 都會受到影響 !!!
以下是以 timer1 控制的 pin 9, pin 10 為例(注意兩個 pin 的頻率相同!)
在你的 setup( ) { 內, 寫如下兩句即可:
      int fff = 3;  // 可以是 1, 2, 3, 4, 5
      TCCR1B = TCCR1B & 0xF8 | fff;
其中 fff 與對應頻率如下:
  fff   Prescaler   Frequency
   1           1        31372.549 Hz
   2           8          3921.569
   3         64            490.196   <–DEFAULT
   4       256           122.549
   5    1024               30.637 Hz

至於 timer2 控制的 pin 11 和 pin 3,
則在 setup( ) { 內寫:
     TCCR2B = TCCR2B & 0xF8 | ?;
此處的 ? 可以有七種:
   ?  Prescaler   Frequency
   1         1       31372.549 Hz
   2         8         3921.569
   3            32          980.392
   4        64         490.196   <–DEFAULT
   5      128         245.098
   6      256         122.549
   7    1024           30.637 Hz

如果你堅持要改 timer0 的 Prescaler, 以更改 pin 5, pin 6 的 PWM 頻率:
(注意 millis( ) 和 micros( ) 以及 delay() 都會受到影響 !! )
則在 setup( ) { 內寫:
     TCCR0B = TCCR0B & 0xF8 | ?;
此處的 ? 可以有五種:
   ?  Prescaler   Frequency
   1          1       362500 Hz
   2          8           7812.5
   3        64             976.5625   <–DEFAULT
   4      256             244.140625
   5    1024               61.03515625 Hz

參考:
http://playground.arduino.cc/Main/TimerPWMCheatsheet
http://www.atmel.com/Images/doc8161.pdf

arduino 更改pwm频率的方法

我们的PWM口只能通过输出占空比来输出模拟量,但是如果要输出1-60KHz或更高的脉冲频率的话,就不好做到啦。我一直在需找解决方案,无意中找到啦,现提供给大家分享,希望能有所帮助。

源代码如下:Here are some usage examples of the function:

// Set pin 9’s PWM frequency to 3906 Hz (31250/8 = 3906)
// Note that the base frequency for pins 3, 9, 10, and 11 is 31250 Hz
setPwmFrequency(9, 8);

// Set pin 6’s PWM frequency to 62500 Hz (62500/1 = 62500)
// Note that the base frequency for pins 5 and 6 is 62500 Hz
setPwmFrequency(6, 1);

// Set pin 10’s PWM frequency to 31 Hz (31250/1024 = 31)
setPwmFrequency(10, 1024);

Please keep in mind that changing the PWM frequency changes the Atmega’s timers and disrupts the normal operation of many functions that rely on time (delay(), millis(), Servo library).

/**
* Divides a given PWM pin frequency by a divisor.
*
* The resulting frequency is equal to the base frequency divided by
* the given divisor:
*   – Base frequencies:
*      o The base frequency for pins 3, 9, 10, and 11 is 31250 Hz.
*      o The base frequency for pins 5 and 6 is 62500 Hz.
*   – Divisors:
*      o The divisors available on pins 5, 6, 9 and 10 are: 1, 8, 64,
*        256, and 1024.
*      o The divisors available on pins 3 and 11 are: 1, 8, 32, 64,
*        128, 256, and 1024.
*
* PWM frequencies are tied together in pairs of pins. If one in a
* pair is changed, the other is also changed to match:
*   – Pins 5 and 6 are paired on timer0
*   – Pins 9 and 10 are paired on timer1
*   – Pins 3 and 11 are paired on timer2
*
* Note that this function will have side effects on anything else
* that uses timers:
*   – Changes on pins 3, 5, 6, or 11 may cause the delay() and
*     millis() functions to stop working. Other timing-related
*     functions may also be affected.
*   – Changes on pins 9 or 10 will cause the Servo library to function
*     incorrectly.
*
* Thanks to macegr of the Arduino forums for his documentation of the
* PWM frequency divisors. His post can be viewed at:
*   http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1235060559/0#4
*/
void setPwmFrequency(int pin, int divisor) {
  byte mode;
  if(pin == 5 || pin == 6 || pin == 9 || pin == 10) {
    switch(divisor) {
      case 1: mode = 0x01; break;
      case 8: mode = 0x02; break;
      case 64: mode = 0x03; break;
      case 256: mode = 0x04; break;
      case 1024: mode = 0x05; break;
      default: return;
    }
    if(pin == 5 || pin == 6) {
      TCCR0B = TCCR0B & 0b11111000 | mode;
    } else {
      TCCR1B = TCCR1B & 0b11111000 | mode;
    }
  } else if(pin == 3 || pin == 11) {
    switch(divisor) {
      case 1: mode = 0x01; break;
      case 8: mode = 0x02; break;
      case 32: mode = 0x03; break;
      case 64: mode = 0x04; break;
      case 128: mode = 0x05; break;
      case 256: mode = 0x06; break;
      case 1024: mode = 0x7; break;
      default: return;
    }
    TCCR2B = TCCR2B & 0b11111000 | mode;
  }
}

提供参考网址:
http://arduino.cc/playground/Code/PwmFrequency
http://arduino.cc/playground/Main/TimerPWMCheatsheet

电脑风扇 PWM频率的资料

2.4  PWM Control Input Signal
The following requirements are measured at the PWM (control) pin of the fan cable connector:
PWM Frequency: Target frequency 25 kHz, acceptable operational range 21 kHz to 28 kHz
Maximum voltage for logic low:  VIL = 0.8 V
Absolute maximum current sourced:  Imax = 5 mA (short circuit current)
Absolute maximum voltage level:  VMax = 5.25 V (open circuit voltage)

摘自Intel的PWM specification