脉冲宽度调制基本上是一个随时间变化而变化的方波。基本的PWM信号如下图所示。
有很多术语与PWM相关:
如图所示,Ton表示导通时间,Toff表示信号的关断时间。周期是导通和关断时间的总和,并按照以下公式计算: T t o t a l = T o n + T o f f T_{total}=T_{on}+T_{off} Ttotal=Ton+Toff
占空比用于计算为一段时间的导通时间。使用上面计算的周期,占空比计算为:
D = T o n T o n + T o f f = T o n T t o t a l D=\frac{T_{on}}{T_{on}+T_{off}}=\frac{T_{on}}{T_{total}} D=Ton+ToffTon=TtotalTon
analogWrite()函数将模拟值(PWM波)写入引脚。它可用于以不同的亮度点亮LED或以各种速度驱动电机。在调用analogWrite()函数之后,引脚将产生指定占空比的稳定方波,直到下一次调用analogWrite()或在相同引脚上调用digitalRead()或digitalWrite()。大多数引脚上的PWM信号频率约为490Hz。在Uno和类似的板上,引脚5和6的频率约为980Hz。Leonardo上的引脚3和11也以980Hz运行。
在大多数Arduino板上(ATmega168或ATmega328),此功能在引脚3,5,6,9,10和11上工作。在Arduino Mega上,它在引脚2-13和44-46上工作。旧的Arduino ATmega8板仅支持引脚9,10和11上的 analogWrite()。
Arduino Due支持引脚2至13以及引脚DAC0和DAC1上的 analogWrite()。与PWM引脚不同,DAC0和DAC1是数模转换器,用作真正的模拟输出。
在调用analogWrite()之前,不需要调用pinMode()将引脚设置为输出。
analogWrite ( pin , value ) ;
//value − the duty cycle: between 0 (always off) and 255 (always on).
value - 占空比:0(始终导通)到255(始终关断)之间。
int ledPin = 9; // LED connected to digital pin 9
int analogPin = 3; // potentiometer connected to analog pin 3
int val = 0; // variable to store the read value
void setup() {
pinMode(ledPin, OUTPUT); // sets the pin as output
}
void loop() {
val = analogRead(analogPin); // read the input pin
analogWrite(ledPin, (val / 4)); // analogRead values go from 0 to 1023,
// analogWrite values from 0 to 255
}
例如:
analogWrite(5, 168);
上面的指令将使第5引脚输出3.3V电压,因为 168 255 × 5 ≈ 3.3 \frac {168}{255}\times {5} \approx 3.3 255168×5≈3.3
Arduino控制板预设采用1kHz 和 500Hz两组不同的PWM输出频率
Arduino的PWM输出频率是由ATmega微处理器内部的三个系统定时器:
下面的程序语句将Timer1(9、10端口输出)的PWM频率调整成31250Hz。
void setup() {
TCCR1B = TCCR1B & 0b11111000 | 0x01;
}
关于参数的详细说明以及频率对照表
物品 | 数量 |
---|---|
LED | 1个 |
10kΩ可变电阻 | 1个 |
- Arduino 的模拟输入(analogRead)的范围值介于0~1023之间,而模拟输出(analogWrite)介于0 ~ 255。
byte potPin = A0;
byte ledPin = 11;
int potSpeed = 0;
byte val = 0;
void setup() {
pinMode(ledPin OUTPUT);
}
void loop() {
potSpeed = analogRead(potPin);
val = map(potSpeed, 0, 1023, 0, 255);
analogWrite(ledPin, val);
}
实验说明: 随机, 通过随机调整接在数字11端口的LED亮度,以及随机持续时间来模拟烛光效果。
random
byte rnd = random(200); //从0~199之间挑选一个数字,存入rnd
byte rnd = random(20, 50); //从20~49之间挑选一个数字,存入rnd
然而,Arduino每次挑选的数字并不是那么随意,为了提高不重复的比率,在每次执行random()函数之前,先执行randomSeed()函数,
byte ledPin = 11;
void setup() {
pinMode(ledPin, OUTPUT);
randomSeed(analogRead(A5); //空接端口的读取值很不稳定,适合于做randomSeed()参数。
}
void loop() {
analogWrite(ledPin, random(135) + 120);
delay(random(200));
}
实验说明: 接收用户输入的0~255数值来改变接在11引脚的LED亮度。
用户通过按键的输入值是字符串格式,而模拟输出指令所需要的参数是数字格式。有两种方法把字符或字符串转换成数字。
pwm = pwm * 10 + (_in - '0')
pwm = pwm * 10 + (_in - '0')
pwm = pwm * 10 + (_in - '0')
byte ledPin = 11;
void setup() {
Serial.begin(9600);
}
void loop() {
int pwm = 0;
byte _in;
if (Serial.available()) {
_in = Serial.read();
while (_in != '\n') {
if (_in >= '0' && _in <='9') {
pwm = pwm * 10 + (_in - '0');
}
_in = Serial.read();
}
if (pwm >255) pwm = 255;
analogWrite(ledPin, pwm);
}
}
先声明一个空字符串变量,每次收到的新字符,存入字符串变量(data),以及变量i,每次接收到新的字符,就将它存入字符串变量,最后通过atoi()转换。
byte ledPin = 11;
void setup(){
Serial.begin(9600);
Serial.println("LED ready:");
}
void loop() {
int pwm;
char data[4];
byte i = 0;
char chr;
if (Serial.available()) {
while ((chr = Serial.read()) != '\n') {
if ( chr >= '0' && chr <= '9' && i < 3) {
data[i] = chr;
i++;
}
}
data[i] = '\0';
pwm = atoi(data);
if (pwm > 255) pwm = 255;
Serial.print("PWM: ");
Serial.println(pwm);
analogWrite(ledPin, pwm);
}
}