在这个项目中,我们将利用Arduino开发板和8x8点阵屏,将温度传感器LM35采集的温度数据以生动、直观的方式呈现出来。想象一下,随着环境温度的变化,点阵屏上滚动显示的温度值也随之更新,仿佛一个实时报告环境状况的微型显示屏。这不仅仅是一个技术实践的过程,更是一次将科技融入生活,用代码赋予物品新生命的有趣尝试。现在,就让我们共同开启这场数字与现实的交互之旅吧!
元件清单
硬件连接
图 1 温度计连线图
按照上述连线图进行硬件连接,注意温度传感器和点阵屏的连接方向。
示例代码
- //项目-温度计
-
- #define TEMP_SENSOR_PIN A4 // LM35温度传感器连接到A4引脚
-
-
-
- const int rowPins[8] = {6, 11, 5, 9, A0, 4, A1, 2};
-
- // 点阵屏行引脚(负极)
-
- const int colPins[8] = {10, A2, A3, 7, 3, 8, 12, 13};
-
- // 点阵屏列引脚(正极)
-
-
-
- // 定义数字0-9的8x8点阵图案
-
- byte numbers[10][8] = {
-
- // 0
-
- {0b00111100,
-
- 0b01100110,
-
- 0b01100110,
-
- 0b01100110,
-
- 0b01100110,
-
- 0b01100110,
-
- 0b01100110,
-
- 0b00111100},
-
- // 1
-
- {0b00011000,
-
- 0b00111000,
-
- 0b00011000,
-
- 0b00011000,
-
- 0b00011000,
-
- 0b00011000,
-
- 0b00011000,
-
- 0b11111110},
-
-
-
- // 2
-
- {0b00111100,
-
- 0b01100110,
-
- 0b00000110,
-
- 0b00001100,
-
- 0b00110000,
-
- 0b01100000,
-
- 0b01100110,
-
- 0b01111110},
-
-
-
- // 3
-
- {0b00111100,
-
- 0b01100110,
-
- 0b00000110,
-
- 0b00011100,
-
- 0b00000110,
-
- 0b00000110,
-
- 0b01100110,
-
- 0b00111100},
-
-
-
- // 4
-
- {0b00001100,
-
- 0b00011100,
-
- 0b00101100,
-
- 0b01001100,
-
- 0b01111110,
-
- 0b00001100,
-
- 0b00001100,
-
- 0b00001100},
-
-
-
- // 5
-
- {0b01111110,
-
- 0b01100000,
-
- 0b01100000,
-
- 0b01111100,
-
- 0b00000110,
-
- 0b00000110,
-
- 0b01100110,
-
- 0b00111100},
-
-
-
- // 6
-
- {0b00111100,
-
- 0b01100110,
-
- 0b01100000,
-
- 0b01111100,
-
- 0b01100110,
-
- 0b01100110,
-
- 0b01100110,
-
- 0b00111100},
-
-
-
- // 7
-
- {0b01111110,
-
- 0b01100110,
-
- 0b00001100,
-
- 0b00011000,
-
- 0b00110000,
-
- 0b00110000,
-
- 0b00110000,
-
- 0b00110000},
-
-
-
- // 8
-
- {0b00111100,
-
- 0b01100110,
-
- 0b01100110,
-
- 0b00111100,
-
- 0b01100110,
-
- 0b01100110,
-
- 0b01100110,
-
- 0b00111100},
-
-
-
- // 9
-
- {0b00111100,
-
- 0b01100110,
-
- 0b01100110,
-
- 0b00111110,
-
- 0b00000110,
-
- 0b00000110,
-
- 0b01100110,
-
- 0b00111100}
-
- };
-
- // 读取温度传感器的值并转换为摄氏度
-
- float readTemperature() {
-
- int reading = analogRead(TEMP_SENSOR_PIN);
-
- float voltage = reading * 5.0 / 1023.0;
-
- float temperature = voltage * 100.0; // 将电压转换为温度
-
- return temperature;
-
- }
-
-
-
- void combineNumbers(byte first[], byte second[], byte combined[], int width) {
-
- for (int i = 0; i < width; i++) {
-
- combined[i] = first[i];
-
- }
-
- for (int i = 0; i < width; i++) {
-
- combined[i + width] = second[i];
-
- }
-
- }
-
-
-
- void displayScrollingNumber(byte number[], int combinedWidth) {
-
- int width = 8; // 单个数字的宽度
-
-
-
- /* 滚动的总步数是组合后数字宽度加上点阵屏宽度,这样两个数字可以从右边完全不可见到左边完全不可见*/
-
- for (int offset = width; offset >= -combinedWidth; offset--) {
-
- for (int row = 0; row < width; row++) {
-
- digitalWrite(rowPins[row], LOW); // 激活当前行
-
- for (int col = 0; col < width; col++) {
-
- int colIndex = col - offset; // 注意这里是减去offset
-
- if (colIndex >= 0 && colIndex < combinedWidth) {
-
- // 设置数字的列值,注意这里改为从左侧开始读取位
-
- digitalWrite(colPins[col], bitRead(number[row + ((colIndex / width) * width)], width - 1 - (colIndex % width)) ? HIGH : LOW);
-
- } else {
-
- // 如果列索引超出范围,则关闭对应的列
-
- digitalWrite(colPins[col], LOW);
-
- }
-
- }
-
- // 控制滚动速度
-
- delay(8);
-
- digitalWrite(rowPins[row], HIGH);
-
- // 关闭当前行,准备激活下一行
-
- }
-
- }
-
- }
-
-
-
- void setup() {
-
- Serial.begin(9600);
-
- for (int i = 0; i < 8; i++) {
-
- pinMode(colPins[i], OUTPUT);
-
- pinMode(rowPins[i], OUTPUT);
-
- digitalWrite(rowPins[i], HIGH); // 初始化行引脚为高电平(关闭)
-
- }
-
-
-
- }
-
-
-
- void loop() {
-
- byte combinedNumber[16]; // 假设我们只组合两个数字,所以大小是16
-
- int temp = readTemperature();
-
- int tempDigits[2]; // 假设温度不会超过99度
-
- Serial.println(temp);
-
-
-
- tempDigits[0] = (temp / 10) % 10;
-
- tempDigits[1] = temp % 10;
-
- combineNumbers(numbers[tempDigits[0]], numbers[tempDigits[1]], combinedNumber, 8);
-
- displayScrollingNumber(combinedNumber, 16);
-
- // 传入组合后的数组和宽度
-
- }
复制代码
代码回顾开始的三行代码对温度传感器和点阵屏的行和列所连接的引脚进行了定义。
- #define TEMP_SENSOR_PIN A4
-
- const int rowPins[8] = {6, 11, 5, 9, A0, 4, A1, 2};
-
- const int colPins[8] = {10,A3, A2, 7, 3, 8, 12, 13};
复制代码
在二维数组 numbers[10][8] 中,numbers 是一个包含10个元素的数组,其中每个元素又是一个包含8个元素的一维数组(也叫这个数字的字模)。字模定义了每个数字在点阵屏上的显示方式,包含8个元素,对应8x8点阵屏的每一行。每个元素是一个8位的二进制数组(“0b”是一个前缀,用于明确指示随后的数字是二进制形式,不计入位长度),对应该行中的每一列。
复制代码
如果你想让点阵屏显示数字0,你可以访问numbers[0来获取这个数字的字模(如下)。 {0b00111100, 0b01100110, 0b01100110, 0b01100110, 0b01100110, 0b01100110, 0b01100110, 0b00111100} 接着通过以下步骤在点阵屏上显示出来: 1. 遍历点阵屏的每一行。 2. 对于每一行,激活该行的所有列。 3. 遍历每一列,根据数字0的一维数组中每个元素的位索引上的值,设置该列的LED是点亮还是熄灭。(比如在数组0号元素中,第2-5位索引的值为1,其他为0,效果为该行的第3-6列LED灯点亮) 4. 关闭当前行,准备显示下一行。 5. 重复以上步骤,直到所有行都被显示过一遍。
不断重复遍历操作,利用计算机的运行速度和人类的视觉暂留,就能在点阵屏上显示出数字0了。 现在大家对点阵屏如何显示数字有了一定的了解,但本项目中要进行实时的温度显示,通常我们生活的环境温度(摄氏度)不止为一位数字,更多的是两位数字组成的温度值,那么如何在这么小的点阵屏上表示两位数字呢?
通常会使用滚动的方式来进行多位数字的显示。接下来我们就一起来看看怎么让多位数字组合并滚动起来吧!
函数readTemperature()用来读取当前的环境温度,相关计算过程在项目【温度报警器】中已经做了说明,这里就不赘述了。 在setup()函数中,使用一个for循环将行和列引脚设为输出模式,并将所有行引脚设为高电平,即所有LED灯关闭状态。(点阵屏的相关知识请参考项目【点亮点阵】)
- void setup() {
-
- Serial.begin(9600);
-
- for (int i = 0; i < 8; i++) {
-
- pinMode(colPins[i], OUTPUT);
-
- pinMode(rowPins[i], OUTPUT);
-
- digitalWrite(rowPins[i], HIGH); // 初始化行引脚为高电平(关闭)
-
- }
-
-
-
- }
-
-
复制代码
在loop函数中,在串口监视器中显示温度值: - int temp = readTemperature();
-
- Serial.println(temp);
复制代码
由于字模数组中只存储了0-9的字模,所以在显示温度时需要将其先拆分成为十位和个位,便于使用字模在点阵屏上显示。所以定义一个数组来存储个位和十位的数字: - int tempDigits[2]; // 假设温度不会超过99度,用来存储个位和十位
-
- tempDigits[0] = (temp / 10) % 10;
-
- tempDigits[1] = temp % 10;
复制代码
接着,我们使用combineNumbers()函数将两个数字的字模合并成一个长字模,然后在点阵屏上滚动显示这个长字模。
- byte combinedNumber[16]; // 假设我们只组合两个数字,所以大小是16
-
- combineNumbers(numbers[tempDigits[0]], numbers[tempDigits[1]], combinedNumber, 8);
- displayScrollingNumber(combinedNumber, 16);
复制代码
以上就是loop函数的全部内容了,下面将重点对combineNumbers()和displayScrollingNumber()两个函数进行详细的解释:
- void combineNumbers(byte first[], byte second[], byte combined[], int width) {
-
- for (int i = 0; i < width; i++) {
-
- combined[i] = first[i];
-
- }
-
- for (int i = 0; i < width; i++) {
-
- combined[i + width] = second[i];
-
- }
-
- }
-
-
复制代码
这段代码定义了一个名为combineNumbers()的函数,其目的是将两个数字的字模(以字节数组形式表示)水平合并成一个新的字节数组。这个函数对于在点阵显示屏上并排显示两个数字非常有用。具体实现方式也非常简单,使用两个并列的for循环,将两个数字数组中的元素依次复制到combined[]数组中,比如将数字0和1合并后的combined[]数组如下: {0b00111100, 0b01100110, 0b01100110, 0b01100110, 0b01100110, 0b01100110, 0b01100110, 0b00111100, 0b00011000, 0b00111000, 0b00011000, 0b00011000, 0b00011000, 0b00011000, 0b00011000, 0b01111110}
最后,将合并后的数字滚动显示出来,滚动显示的逻辑如下: 外层循环(滚动步数): - for (int offset = width; offset >= -combinedWidth; offset--)
复制代码
这个循环控制数字的滚动。offset变量从数字的宽度开始(即数字完全在屏幕右侧外),递减到-combinedWidth(即数字完全在屏幕左侧外)。这样,数字从右向左滚动。
内层循环(行扫描): - for (int row = 0; row < width; row++)
复制代码
这个循环遍历点阵屏的每一行。对于每一行,代码首先激活该行(将其设为低电平),然后设置该行的每一列。
内层循环(列设置): - int colIndex = col - offset;
复制代码
计算当前列相对于数字起始位置的索引。由于数字在滚动,所以使用offset来调整列索引。
- if (colIndex >= 0 && colIndex < combinedWidth)
复制代码
检查列索引是否在数字的有效范围内。
如果在有效范围内,使用: - bitRead(number[row + ((colIndex / width) * width)], width - 1 - (colIndex % width))
复制代码
来读取该位置的位值(0或1),并据此设置列的电平(高或低),从而点亮或熄灭led。 否则,超出索引范围,使用digitalWrite(colPins[col], LOW)关闭对应的列。
最后,使用delay(8)和digitalWrite(rowPins[row], HIGH)用来控制滚动速度和关闭当前航,为激活下一行做准备。
以上就是这个项目的代码回顾了,如果想使用点阵屏来显示其他的字符,可以尝试完成课后练习的部分哦。
课后练习除了显示滚动的数字,点阵屏还可以显示其他种类的字符,比如汉字,但由于点阵屏led数量有限,不能合理的显示较复杂的汉字。英文似乎是个不错的选择,你可以尝试让点阵屏滚动显示单词吗?快来尝试一下吧!
附件下载:
|