智能硬件 Arduino 实验记录
实验一&实验二 在Proteus中进行Arduino仿真
Arduino 环境搭建
-
按照自己的平台,在 Arduino 官网下载对应版本的 Arduino IDE。
-
安装过程中可以勾选驱动安装选项,便于后面连接电脑和开发板的连接。
-
启动 Arduino 后,可以考虑调整代码字号,
文件 - 首选项 - 编辑器字体(参考字号20)
。 -
修改 Arduino 默认的项目所在路径,方便以后统一管理项目。在
文件 - 首选项 - 项目文件夹位置
这里填入目标项目所在路径即可。 -
修改 Arduino 默认的编译后文件存放路径, 从
文件 - 首选项
的设置页面下方的preferences.txt
这里的可点击文字进入设置文件, 添加build.path=[your-path]
([]及其内容以你需要设置的目标路径替代)。
Proteus 环境搭建
- 由于Proteus收费,可以考虑下载破解版用于学习。可以关注公众号【软件安装管家目录】输入
proteus
即可获取下载链接(非广告)。此处提供proteus 8
的百度云下载【链接:https://pan.baidu.com/s/1YZ5-xsalW2afOpdH58i6zQ?pwd=9u5w
提取码:9u5w】。 - 添加
Arduino
库文件,以在Proteus
中进行模拟。【链接:https://pan.baidu.com/s/1yKpKJmJv1c5IV9RZcfSJvQ?pwd=4ymd
提取码:4ymd】
编译 hex 文件
进入 Arduino IDE
,选择 文件 - 实例 - Basics - Blink
,保存源码,再选择 项目 - 验证/编译
编译成 hex 文件。
在 Proteus 中进行仿真
- 进入
Proteus 8
,新建工程(创建 default 模板原理图 - 不创建 PCB 布板设计 - 没有布件项目 - 完成)。 - 在左侧
device
方格处右键(从库中挑选),按关键字arduino
选择 开发板ARDUINO UNO V2.0
,按LED
关键字 选择LED-RED
, 按resis 220
关键字选择电阻3WATT220R
。 - 在左边界面的按钮中选择终端,在方格中选择
GROUND
。 - 将以上元器件放置在模拟图上,并用线连接起来。
- 双击
arduino
模拟开发版,在Update HEX File
处选择编译得到的 hex 文件。 - 点击左下角的
运行仿真按钮
,可观察到 LED 灯元器件亮灭交替,亮灭状态各持续 1 秒。
实验三 七段数码管闪烁
要求:利用 Arduino 实现七段数码管从数字 0
到 数字 9
的循环闪烁。
编写共阴数码管对照表
首先,要区分共阴数码管和共阳数码管。共阴数码管就是把七段数码管上的 7 个LED灯的一段统一接地,某一引脚被设置为高电平,则对应的 LED 灯就亮起。共阳数码管同理,只是统一接入高电平,被设置为低电平的引脚所对应的 LED 亮起。
本次实验选择共阴数码管,按照共阴数码管的引脚编号,让 Arduino 上的引脚 1~7 分别自上到下对应 L1~L7 。那么我们可以枚举出每个数字对应要求的引脚状态。
int seg_7_cathold[10][7] = {
HIGH, HIGH, HIGH, HIGH, HIGH, HIGH, LOW, /*0*/
LOW, HIGH, HIGH, LOW, LOW, LOW, LOW, /*1*/
HIGH, HIGH, LOW, HIGH, HIGH, LOW, HIGH, /*2*/
HIGH, HIGH, HIGH, HIGH, LOW, LOW, HIGH, /*3*/
LOW, HIGH, HIGH, LOW, LOW, HIGH, HIGH, /*4*/
HIGH, LOW, HIGH, HIGH, LOW, HIGH, HIGH, /*5*/
HIGH, LOW, HIGH, HIGH, HIGH, HIGH, HIGH, /*6*/
HIGH, HIGH, HIGH, LOW, LOW, LOW, LOW, /*7*/
HIGH, HIGH, HIGH, HIGH, HIGH, HIGH, HIGH, /*8*/
HIGH, HIGH, HIGH, HIGH, LOW, HIGH, HIGH /*9*/
};
循环计数
用 C 程序编写一个一位的十进制计数器,即限制其允许计数值为 [0, 9] 之间的整数。
int cnt = 0; // 十进制计数器
void setup() {
// 将引脚 1~7 均设置为输出模式
int i;
for (i = 1; i <= 7; ++i) {
pinMode(i, OUTPUT);
}
}
// 按照传入参数,让七段数码管显示对应的数字
void light(int cnt) {
int num = cnt % 10;
int i;
for (i = 0; i < 7; ++i) {
digitalWrite(i + 1, seg_7_cathold[num][i]);
}
}
void loop() {
light(cnt);
cnt = (cnt + 1) % 10;
delay(1000);
}
实验结果
可观察到七段数码管循环闪烁数字 0 到数字 9 ,截取部分数码管闪烁结果如下。
实验四 LCD显示器实验
实验目的:在 Proteus 仿真环境中,模拟 Arduino 开发板控制 LCD 显示器1602。
选择器件
在 Proteus 8 仿真环境中,在左侧菜单 元件模式
的 菜单面板
中右键选择 从库添加
(或者按 P
按钮),添加以下组件并放置在模拟图上。
- Arduino UNO V2.0
- LCD LM016L
仿真环境线路连接
按图中所示完成线路连接。
驱动LCD显示屏
驱动 LCD 的 关键代码 如下:
//指明 LCD 显示屏上的各个引脚分别连接到 Arduino 的哪一个引脚
const int rs = 12, en = 11, d4 = 5, d5 = 4, d6 = 3, d7 = 2;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7); //初始化液晶显示器
lcd.begin(16, 2); // 设置lcd的参数: 列数, 行数
lcd.print("hello, world!"); // 在液晶显示屏上光标所在位置显示出字符串
lcd.setCursor(0, 1); // 设置光标位置: 列, 行
完整代码如下:
#include <LiquidCrystal.h>
const int rs = 12, en = 11, d4 = 5, d5 = 4, d6 = 3, d7 = 2;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);
void setup() {
lcd.begin(16, 2);
lcd.print("hello, world!");
}
void loop() {
lcd.setCursor(0, 1);
lcd.print(millis() / 1000); // millis函数: 从程序运行开始到此时间点的时长(ms)
}
将完整代码编译后,生成 hex 文件,在 Proteus 中双击 Arduino 开发板,选择 Update hex file
为其更新 hex 文件路径。点击运行,即可观察到 LCD 显示屏第一行打印出 字符串 hello world!
,第二行随时更新 程序已运行时间 (单位为秒),如图所示。
实验五 蜂鸣器实验
选择器件
在 Proteus 8 仿真环境中,在左侧菜单 元件模式
的 菜单面板
中右键选择 从库添加
(或者按 P
按钮),添加以下组件并放置在模拟图上。
- Arduino UNO V2.0
- BUZZER (选择 DC Operated 有源蜂鸣器,直流电可直接驱动)
这里的 Buzzer 也可以替换为 Sounder 或 Speaker 。使用 Buzzer 时发现放置后初始状态无法工作,将 工作电压设置为 5V 、 负载电阻设置为至少 81Ω 后,Buzzer 可以正常发声。
仿真线路连接
图中只是为了切换方便所以放置了 3 个设备,实际只需要选择 Buzzer 、 Speaker 、 Sounder 其中一个放置即可,接线参考图中的 Buzzer。
驱动蜂鸣器
要让蜂鸣器发声需要输入脉冲信号,根据脉冲的频率,蜂鸣器会以不同的频率发声,也就产生不同音高。Arduino 库文件 pitch.h
中为大部分音高预先定义了宏,我们只需要根据宏名就可以获得其频率,宏名含义参照如下。
#define NOTE_C3 131 // 表示音名为 C3 的频率
#define NOTE_CS3 139 // 该宏表示比 C3 高半音的音名 #C3 的频率(S 即 Sharp,表示符号 #)
Arduino 提供了驱动蜂鸣器的函数,如下。
// pin是连接蜂鸣器的引脚, note是发声的频率,即音高,noteDuration是该音发声的时长(ms)
tone(pin, note, noteDuration);
noTone(pin); // 蜂鸣器停止发声
所以,要完整地演奏一段旋律,也就是一个音名的 序列 ,以及每个音对应的时长,这些信息可以从歌曲的简谱中得到。
部分乐理知识
以下是我自己了解到的一些简单乐理,因为是个人近期自学所得的理解,所以可能不太准确,仅作参考。
图中矩形标识部分为中央C所在的 八度 (即 C4 ~ B4),我们将这个八度分为12个 音程 (区间内的 所有白键加上所有黑键 ),相邻的白键与黑键之间相差一个 半音 ,相邻的白键之间若无黑键,则也是相差一个半音。这个八度中的白键分别对应 唱名 do、re、mi、fa、so、la、xi , 音名 则为 C4、D4、E4、F4、G4、A4、B4 。
黑键则是以比白键 高半音 或 低半音 的形式存在,如:比 D4 高半音的音可以表示为 #D4
,比 D4 低半音的音可以表示为 bD4
,这样就能表示出黑键的音名了。
图中标识的这个区间则是 1 = C 的普通音的范围,低音则是 C3 ~ B3 的范围,高音则是在 C5 ~ B5 的范围,唱名也是一一对应的。
如果曲谱使用的是其他调性,如 1 = F ,则表示将 F4 作为 do ,自 F4 起的 12 个键(包括黑键和白键)就是 1 = F 的普通音所在的八度范围。因为自然音中 mi 和 fa 、前一个八度的 xi 和后一个八度的 do ,都只差一个半音,所以 1 = F 中的唱名 do ~ xi 都不会像 1 = C 一样都落在白键上,而是会有几个唱名位于黑键,这里就需要自行对应调整了。
我们可以从简谱中确定我们的旋律,以天空之城为例,由于低音调的部分在蜂鸣器上演奏不那么悦耳,所以我把整体上移一个八度。
将简谱的音调升一个八度后,确定了 C5 作为唱名 do,以 四四拍 为节奏(即一小节四拍)。如果一小节中只有一个音符,那这个音符就是 全音符 (只写第一拍,其他拍用短横线占位)。如果只有两个音符则为 二分音符 ,只有 4 个音符则为 四分音符 。再细分,则会有 八分音符 、 十六分音符 、 三十二分音符 ,分别以下划一横、下划两横、下划三横的方式表示。0 为 休止符 ,表示停止演奏对应的时长。
那么我们只需要根据我们的需要确定最小的 三十二分音符 的时间长度即可表示全曲时长。
编写歌曲旋律和音符时长序列
根据简谱的唱名,可以对应出音名,以 库文件pitch.h
中定义的音名来确定蜂鸣器需要的旋律。我们只需要预先定义好单个三十二分音符所占时长,再结合简谱中的音符类型(手动升一个八度),就可以确定音符的时长,进而完成完整的乐曲。
这里提供一个 在线模拟钢琴 网站,可以方便地确定音符的音名,也提供了乐曲的演奏示例,可供熟悉和参考。
// 旋律表
int melody[] = {
NOTE_A4, NOTE_B4, NOTE_C5, NOTE_B4, NOTE_C5, NOTE_E5, NOTE_B4,
NOTE_E4, NOTE_A4, NOTE_G4, NOTE_A4, NOTE_C5, NOTE_G4,
NOTE_E4, NOTE_E4, NOTE_F4, NOTE_E4, NOTE_F4, NOTE_C5, NOTE_E4,
NOTE_C5, NOTE_C5, NOTE_B4, NOTE_F4, NOTE_F4, NOTE_B4, NOTE_B4,
NOTE_A4, NOTE_B4, NOTE_C5, NOTE_B4, NOTE_C5, NOTE_E5, NOTE_B4,
NOTE_E4, NOTE_E4, NOTE_A4, NOTE_G4, NOTE_C5, NOTE_G4, 0,
NOTE_E4, NOTE_F4, NOTE_C5, NOTE_B4, NOTE_C5, NOTE_D5, NOTE_D5, NOTE_E5, NOTE_C5,
NOTE_E5, NOTE_D5, NOTE_C5, NOTE_B4, NOTE_D5, NOTE_B4, NOTE_A4,
NOTE_C5, NOTE_D5, NOTE_E5, NOTE_D5, NOTE_E5, NOTE_G5, NOTE_D5,
NOTE_G4, NOTE_G4, NOTE_C5, NOTE_B4, NOTE_C5, NOTE_E5, NOTE_E5,
NOTE_A4, NOTE_B4, NOTE_C5, NOTE_B4, NOTE_C5, NOTE_D5, NOTE_D5, NOTE_C5, NOTE_G4, NOTE_G4,
NOTE_F5, NOTE_E5, NOTE_D5, NOTE_C5, NOTE_E5, 0,
NOTE_E5, NOTE_A5, NOTE_G5, NOTE_E5, NOTE_D5, NOTE_C5, 0,
NOTE_C5, NOTE_D5, NOTE_C5, NOTE_D5, NOTE_G5, NOTE_E5,
NOTE_E5, NOTE_A5, NOTE_G5, NOTE_E5, NOTE_D5, NOTE_C5, 0,
NOTE_C5, NOTE_D5, NOTE_C5, NOTE_D5, NOTE_D5, NOTE_B4, NOTE_A4, 0
};
// 每个音符对应多少个三十二分音符
unsigned char noteDurations[] = {
4, 4, 12, 4, 8, 8, 24,
8, 12, 4, 8, 8, 24,
4, 4, 12, 4, 8, 8, 24,
4, 4, 12, 4, 8, 8, 24,
4, 4, 12, 4, 8, 8, 24,
4, 4, 12, 4, 8, 8, 24, 8,
4, 8, 4, 12, 8, 4, 8, 4, 16,
4, 4, 4, 4, 8, 4, 24,
4, 4, 12, 4, 8, 8, 24,
4, 4, 12, 4, 8, 8, 32,
4, 4, 8, 4, 4, 4, 4, 12, 4, 12, 8, 8, 8, 8, 48, 0,
8, 16, 16, 4, 4, 16, 4,
4, 8, 4, 12, 8, 24, 8, 16, 16, 4, 4, 16, 4,
4, 8, 4, 12, 8, 16, 8
};
播放乐曲的程序代码如下:
unsigned char duration_per_32 = 50; //每个32分音符所占用时长(ms)
void setup() {
// 按谱子奏曲
for (int pos = 0; pos < sizeof(melody) / sizeof(melody[0]); ++pos) {
int duration = duration_per_32 * noteDurations[pos];
int pauseBetweenNotes = duration * 1.30; // 示例中标注, 延时1.3倍似乎能取得更好的效果
if (melody[pos])
tone(8, melody[pos], duration);
else
delay(melody[pos]);
delay(pauseBetweenNotes);
if (melody[pos])
noTone(8);
}
}
void loop() {
// Nothing to do.
}
实验六 图形化仿真与编程
本实验的内容基于 LinkBoy 的仿真软件来实现。LinkBoy 由 Python 编写实现,实现了硬件环境的仿真和图形化编程,支持组件拖拽,让使用者接触到陌生硬件时可以快速上手使用,并观察到现象。本次实验的目的是熟悉 LinkBoy 环境下的编程,并能在模拟环境中使用 Arduino UNO 进行三色灯显示、DS18B20温度传感器测温、电动机控制。
三色灯显示
从左侧菜单栏的 模块
选项卡中,选择以下模块并放置与模拟图中:
Arduino 主控板系列
中的Arduino UNO
开发板LED灯和数码点阵系列 -> RGB彩灯类
中的三色灯传感输入模块系列 -> 按键输入类
中的红按钮软件模块系列 -> 定时延时类
中的延时器 。
参考下图完成线路连接,光标悬停至组件上的接线端口时也会提示可连接到控制器的那些端口。
接着就是进行图形化编程,根据流程需要,利用图形化模块设置程序功能模块,每个模块完成一个小功能。
操作小技巧:
- 双击空白处可添加新的模块
- 拖动新模块使上方小球重叠于其他模块时可使新模块紧随于该模块之后
- 将模块拖动至右下角垃圾桶可销毁该模块。
点击左上方运行按钮即可开始仿真。当按下按钮时,观察到红灯、绿灯、蓝灯相继一亮一灭,每次亮灭周期为 1 秒。
温度传感器测温
从左侧菜单栏的 模块
选项卡中,选择以下模块并放置与模拟图中:
Arduino 主控板系列
中的Arduino UNO
开发板软件模块系列 -> 定时延时类
中的延时器功能扩展模块系列 -> 图文显示模块
中的信息显示器LED灯和数码点阵系列 -> 数码管类
中的四位数码管TM1637传感输入模块系列 -> 温湿度传感器类
中的温度传感器 ds18b20
参考下图完成接线。
技巧:
- 在左侧菜单的
元素
选项卡可选择创建整数、小数、布尔类型的 全局变量 - 使用信息显示器显示内容前,如果不希望上一次显示的内容被保留下来,就都需要清空信息显示器,然后再重新显示新的数据
在模拟环境运行时,可以观察到四位数码管上显示出了温度传感器接收到的温度数据,可以通过传感器上的数据条调节不同温度,数码管上能在 0.1 秒内更新温度数据。
电动机控制
从左侧菜单栏的 模块
选项卡中,选择以下模块并放置与模拟图中:
Arduino 盾板类
中的Arduino UNO
开发板软件模块系列 -> 定时延时类
中的延时器传感输入模块系列 -> 触发传感器
中的寻迹传感器驱动输出系列 -> 马达和舵机类
中的马达
按照下图完成连线。
技巧:
- 当进入模拟运行状态时,按住左
shift
+字符键,同时对一个传感器点击鼠标左键,就可以将传感器绑定到该字符按键。按下该按键时,相当于传感器设置的条件被触发。
在模拟状态中运行时,当左寻迹感应器被触发时,左马达停转 0.5 秒,随后继续正转。当右寻迹感应器被触发时,右马达停转 0.5 秒,随后继续正转。