analogWrite()
可以认为 analogWrite() 是把 0-255 映射到 0-5V. 最简单直观的实验就是用它来调节 LED 的亮度。
RGB LED
/* RGB fading Modified from the following example. http://arduino.cc/en/Tutorial/Fading */ int rPin = 11; // red int gPin = 9; // green int bPin = 10; // blue void setup() { //Serial.begin(9600); } void loop() { fading(rPin); // red fading(gPin); // green fading(bPin); // blue } void fading(int ledPin) { // fade in from min to max in increments of 5 points: for (int fadeValue = 0; fadeValue <= 255; fadeValue += 5) { analogWrite(ledPin, fadeValue); // sets the value (range from 0 to 255): delay(10); // wait for 10 milliseconds to see the dimming effect } // fade out from max to min in increments of 5 points: for (int fadeValue = 255; fadeValue >= 0; fadeValue -= 5) { analogWrite(ledPin, fadeValue); // sets the value (range from 0 to 255): delay(10); // wait for 10 milliseconds to see the dimming effect } }
上面的代码简单地对 lab kit 中的 RGB LED 进行测试。
红绿蓝每种颜色的变化范围有 256 种可能性(0 到 255),256x256x256 = 16777216, 因此你可以用 RGB LED 混合出 1600 万种颜色。
Tip
|
结合 Lab04 中的按键方法,你可以通过按键调节 analogWrite(ledPin, val); 中的 val 值,进行手动控制。 |
呼吸灯
// 呼吸灯 // 在 Uno 上 pin 3, 5, 6, 9, 10, 11 支持 analogWrite() // 也就是带 ~ 的 pin #define LED_PIN 3 // 存放 LED 亮度值的查找表(lookup table) uint8_t sine_table[] = { 1, 1, 2, 3, 5, 8, 11, 15, 20, 25, 30, 36, 43, 49, 56, 64, 72, 80, 88, 97, 105, 114, 123, 132, 141, 150, 158, 167, 175, 183, 191, 199, 206, 212, 219, 225, 230, 235, 240, 244, 247, 250, 252, 253, 254, 255, 254, 253, 252, 250, 247, 244, 240, 235, 230, 225, 219, 212, 206, 199, 191, 183, 175, 167, 158, 150, 141, 132, 123, 114, 105, 97, 88, 80, 72, 64, 56, 49, 43, 36, 30, 25, 20, 15, 11, 8, 5, 3, 2, 1, 0}; void setup() { //pinMode(LED_PIN, OUTPUT); // 用 analogWrite() 时无需把 pin 设置成输出 } void loop() { for (int i = 0; i < 91; i++) { // <1> analogWrite(LED_PIN, sine_table[i]); delay(50); } }
在程序中标示 <1> 处,用 for 循环去遍历查找表中的 91 个数据,这并不是一个好的做法。因为一旦表格中的数据个数发生了变化,或变多变少之后,不好数清楚。像 91 这样的数字在编程中被称为魔术数字(Magic number), 以后你自己或别人看你代码时,都不知道这儿为什么是这个数字而不是另一个,天知道它是从哪儿来的。
我们可以用 C 语言的 sizeof 运算符来帮助我们偷懒,避免出现魔术数字。见下面的版本。
// 呼吸灯 // 在 Uno 上 pin 3, 5, 6, 9, 10, 11 支持 analogWrite() // 也就是带 ~ 的 pin #define LED_PIN 3 // 存放 LED 亮度值的查找表(lookup table) uint8_t sine_table[] = { 1, 1, 2, 3, 5, 8, 11, 15, 20, 25, 30, 36, 43, 49, 56, 64, 72, 80, 88, 97, 105, 114, 123, 132, 141, 150, 158, 167, 175, 183, 191, 199, 206, 212, 219, 225, 230, 235, 240, 244, 247, 250, 252, 253, 254, 255, 254, 253, 252, 250, 247, 244, 240, 235, 230, 225, 219, 212, 206, 199, 191, 183, 175, 167, 158, 150, 141, 132, 123, 114, 105, 97, 88, 80, 72, 64, 56, 49, 43, 36, 30, 25, 20, 15, 11, 8, 5, 3, 2, 1, 0}; // 计算查找表中的元素数量 int numData = (sizeof sine_table) / (sizeof sine_table[0]); // 在这里 numData = 91 <1> void setup() { Serial.begin(9600); Serial.println(numData); // 打印出数字帮助我们确认 } void loop() { for (int i = 0; i < numData; i++) { analogWrite(LED_PIN, sine_table[i]); delay(50); } }
-
sizeof sine_table 计算出数组 sine_table[] 一共占用了多少个 bytes, sizeof sine_table[0] 计算出数组中每个元素占用了多少个 bytes. 两者相除,就得到了数组中的元素数量。Magic number 消失了!
我们再来看下面的代码片断:
int sine_table[] = {1, 2, 3, 4}; int numData = (sizeof sine_table) / (sizeof sine_table[0]); // numData = 4
因为 C 语言并没有规定一个 int 类型占用几个字节。例如一台计算机上 int 用 4 bytes, sizeof sine_table 就得到 16, sizeof sine_table[0] 得到 4, 16/4 = 4, 通过采用除法(/),这段代码在任何计算机上得到的结果 numData 都为 4, 提高了程序的可移植性。
Note
|
sizeof 是 C 语言的一个运算符(+, -, *, /… 都是运算符), 可以用它"测量"一个变量或一种数据类型占用的字节数。 int a, b; b = sizeof(a); // 在 Arduino 上,结果是 b = 2, 因为一个 int 占用两个 byte //b = sizeof a; // 上面的语句也可以写成这样,当 sizeof 作用于变量名时,() 可以省略 int b; // 计算一个长整型数据类型要占用几个 byte // 计算数据类型时,sizeof 后面必须加 () b = sizeof(unsigned long); // 在 Arduino 上,结果是 b = 4 |
问题: 为什么用 analogWrite() 写完一个亮度值之后要加延时?改变延时的时间长短,甚至去掉延时,会有什么效果?
analogRead()
读电位器
pot.
读 LM35 温度传感器
因为 LM35 被设计成输出电压和环境温度成线性关系,因此你可以直接用万用表测量中间引脚的电压值得到温度值。例如,测得的电压是 234mV,则温度是 234/10 = 23.4 摄氏度。
/* 用 LM35 进行温度测量 inspired by ladyada https://learn.adafruit.com/tmp36-temperature-sensor pscmpf http://pscmpf.blogspot.com/2008/12/arduino-lm35-sensor.html */ int reading; // 存放 ADC 原始数据 float voltage; // 存放电压值 float temperature; // 存放温度值 int sensorPin = A0; // LM35 第 2 pin 接到 A0 上 void setup() { Serial.begin(9600); } void loop() { // 读 sensorPin 上的电压 // 会得到一个 0 to 1023 之间的值 reading = analogRead(sensorPin); // 转换成电压值 voltage = reading * 5.0; // 5V Arduino voltage = voltage / 1024.0; //voltage = (reading * 5) / 1024; // 这行代码似乎可以代替上面两行,试一试把上两行换成这一行 // 观察有什么效果 // 打印电压 // 随时可以用 print 查看数据的正确性 Serial.print(voltage); Serial.println(" V"); // 得到温度值,单位 °C temperature = voltage * 100; // 把温度值发送到电脑串口 Serial.print(temperature); Serial.println(" °C"); delay(1000); // 等一会儿再进行下次测量 }
Arduino 的 analogRead() 是把模拟输入引脚上的电压和 5V 作比较,把比值映射到 0 到 1023 之间。
Vin
reading = ----- * 1024
5V
只要对公式进行简单变形就得到:
reading
--------- * 5V = Vin
1024
Vin: LM35 输出的电压
这个公式反应在上面的代码中。
我们得到电压的单位是 V, 电压和温度的对应关系是 10mV/°C = 0.01V/°C,因此:
voltage
---------- = temperature (单位: °C)
0.01V/°C
再对上面的代码作一些很小的修改。
/* LM35 输出 CSV 文件进行数据记录 */ int reading; // 存放 ADC 原始数据 float voltage; // 存放电压值 float temperature; // 存放温度值 int sensorPin = A0; // LM35 第 2 pin 接到 A0 上 unsigned long time; // 时间戳 void setup() { Serial.begin(9600); } void loop() { time = millis(); // 读当前时间 // 读 sensorPin 上的电压 // 会得到一个 0 to 1023 之间的值 reading = analogRead(sensorPin); // 转换成电压值 voltage = reading * 5.0; // 5V Arduino voltage = voltage / 1024.0; // 得到温度值,单位 °C temperature = voltage * 100; //prints time since program started Serial.print(time/1000); // in seconds Serial.print(", "); // 打印 CSV (Comma-Separated Values) 格式 Serial.print(temperature); // 把温度值发送到电脑串口 delay(1000); // 等一会儿再进行下次测量 }
打开 Arduino IDE 的串口监视器,PC 就可以开始接收 CSV 格式的数据,下面是约 1 分钟的数据。
0, 24.41
1, 24.41
2, 24.41
3, 24.90
4, 24.41
5, 24.41
6, 24.41
7, 24.41
8, 24.41
9, 25.39
10, 26.86
11, 27.83
12, 28.32
13, 28.81
14, 29.30
15, 29.79
16, 29.79
17, 30.27
18, 30.27
19, 30.27
20, 30.76
21, 30.76
22, 30.76
23, 30.76
24, 30.76
25, 30.76
26, 31.25
27, 31.25
28, 31.74
29, 31.74
30, 31.25
31, 31.25
32, 31.25
33, 30.76
34, 30.76
35, 30.76
36, 30.27
37, 29.79
38, 29.79
39, 29.30
40, 29.30
41, 28.81
42, 28.81
43, 28.32
44, 28.32
45, 28.32
46, 28.32
47, 28.32
48, 27.83
49, 27.83
50, 27.83
51, 27.83
52, 27.34
53, 27.34
54, 26.86
55, 26.86
56, 26.86
57, 26.86
再用电子表格或别的软件,你可以把数据可视化。
Wow, cool, 现在你有一个简易的温度记录仪(temperature logger)了! 只是它需要用电脑来存数据,如果给 Arduino 加一个 SD 卡,就能脱机存储了。
Your turn!
你可以发挥自己的想象力,用 lab kit 中的模拟量传感器设计自己的实验!