编程实现点灯和串口打印hello world
309.4、编程实现点灯和串口打印 hello world
void setup() { // 定义初始化函数;开发板每次上电或复位后,Arduino 框架会调用它一次。
pinMode(13,OUTPUT); // 将视频使用的 13 号数字引脚配置为输出,用它控制板载 L 灯。
} // setup 函数结束,随后 Arduino 框架开始反复调用 loop。
void loop() { // 定义反复执行的主循环;只要程序正常运行,本函数就会一轮接一轮执行。
digitalWrite(13,LOW); // 把 13 号引脚输出为低电平;在本节 Uno 兼容板上,L 灯熄灭。
delay(2000); // 当前主流程停留 2000 毫秒,即约 2 秒,因而保持熄灭约 2 秒。
digitalWrite(13,HIGH); // 把 13 号引脚输出为高电平;在本节 Uno 兼容板上,L 灯点亮。
delay(2000); // 当前主流程再停留约 2 秒;结束后 loop 从头执行,再把灯熄灭。
} // 本轮循环结束;Arduino 框架会再次调用 loop,形成约 4 秒一个周期的闪烁。
上方是视频约 05:00 编译并上传的第一份独立草图。讲师随后删除点灯语句,将它改成下面的串口打印草图;二者不是同一时刻共同运行的程序,不能把两组 setup()、loop() 原样粘进同一个草图。
void setup() { // 定义初始化函数;每次上电或复位后执行一次。
Serial.begin(115200); // 以视频实际使用的 115200 bit/s 初始化硬件串口。
} // 串口初始化结束,随后进入反复执行的 loop。
void loop() { // 定义反复执行的主循环。
Serial.println("hello world"); // 发送字符串 hello world,并在末尾追加回车符和换行符。
delay(1000); // 当前主流程等待约 1000 毫秒,再开始下一轮打印。
} // 本轮结束;下一轮会再次打印一行 hello world。
第二份草图逐字保留视频最终画面中的 115200、Serial.println("hello world") 和 delay(1000)。代码围栏按项目要求写为 c,但 Arduino 的 .ino 草图实际会按 C++ 流程预处理、编译,并与 Arduino 核心链接;这里没有抄入 Serial、digitalWrite() 等库和核心的内部实现。
一、Arduino 的基本语法
本节在课程的 Arduino Uno R3 CH340G 兼容板上完成两个最小实验:先用数字输出让板载 L 灯按“灭 2 秒、亮 2 秒”循环,再用硬件串口约每秒打印一行 hello world。它们分别验证了数字输出、延时、程序上传和串口监视器这几条最基本的开发链路。
1.1 setup() 函数
- Arduino 草图通常提供
setup()和loop()两个函数。setup()适合放引脚模式、串口、传感器等只需在一轮程序启动时配置一次的初始化代码。本节第一份草图在这里调用pinMode(),第二份草图在这里调用Serial.begin()。 - “只执行一次”是指每次启动过程执行一次,不是烧录后永远只执行一次。重新上电、按复位键、重新上传,或者串口监视器的打开动作触发自动复位时,程序都会从启动流程重新开始,
setup()也会再次执行。 - 视频把
setup()口头类比成 C 语言的main(),只能用来帮助理解“从这里开始做初始化”,不能当成真实调用关系。经典 Uno 的 AVR 核心已经实现了隐藏的main():先完成核心初始化,再调用一次setup(),之后进入无限循环反复调用loop()。因此用户草图里的setup()不是真正的main()。 void表示函数不向调用者返回结果;setup是 Arduino 框架约定的函数名,大小写不能改;圆括号()表示本例没有参数;花括号{}围起函数体。
1.2 loop() 函数
loop()放需要持续重复的业务逻辑。后续追光项目会在这里反复读取光敏传感器、比较光照并控制舵机;本节则反复改变 LED 电平或发送字符串。- 从效果上可以把它理解为“处在一个无限循环里”,但
loop()本身不是while(1)语句。Arduino AVR 核心的main()使用无限for循环调用loop(),每次loop()返回后,框架还可能处理serialEventRun(),然后再调用下一轮loop()。 - 只要开发板保持供电且程序没有复位、卡死或进入其他不会返回的流程,
loop()就会持续被调用。每次调用都从函数第一条语句开始,不会自动记住上次执行到了哪一行。
1.3 编程点灯
1.3.1 板上四类指示灯
视频展示的 Uno 兼容板有 L、TX、RX、ON 四类指示灯,它们的含义不能混为一谈:
| 标识 | 表示什么 | 能证明什么、不能证明什么 |
|---|---|---|
L | 用户可控制的板载 LED;本板随数字引脚 13 的状态变化 | 能验证 D13 输出及程序运行效果;不代表其他引脚都正常 |
TX / RX | USB—串口转换通道上的发送/接收活动 | 上传或串口收发时可能闪烁;它们不是任意数字引脚通信的通用指示灯,也不能证明收到的数据内容正确 |
ON | 电源指示灯 | 常亮说明板上电源轨已上电;不能证明草图已正确编译、上传或运行 |
课程板使用 CH340G,而官方 Uno R3 使用 ATmega16U2 做 USB—串口转换,具体 LED 驱动电路可能不同,所以不要仅凭 TX、RX 的字样武断判断“哪一颗一定表示主控向外发送”。应从整条链路理解:
电脑串口监视器 ⇄ USB ⇄ CH340G USB—串口桥 ⇄ ATmega328P 的 D0/RX、D1/TX
官方 Uno 资料还特别说明,板上 TX/RX 灯反映的是 USB—串口芯片与电脑之间的数据活动;仅在 D0、D1 上与另一设备通信,不一定让这两颗灯按同样方式闪烁。课程兼容板则应以它自己的原理图和实测为准。
1.3.2 D13 为什么能控制 L 灯
- 官方 Uno R3 把
LED_BUILTIN定义为数字引脚 13,本节兼容板也按 Uno 布局让 L 灯受 D13 控制。视频实测为HIGH时亮、LOW时灭;这叫高电平有效。 pinMode(13,OUTPUT)的作用是把 D13 配置为数字输出,不是“给 13 号引脚使能”。随后digitalWrite(13,LOW)或digitalWrite(13,HIGH)才真正选择输出电平。Uno 是 5 V 逻辑板,理想理解可把LOW看作接近 0 V、HIGH看作接近 5 V,但实测电压会受供电和负载影响。- 板载灯的 LED、限流/驱动电路已经做在开发板上,本实验不用另接 LED。若另接裸 LED,则必须确认极性并串联合适的限流电阻,不能把 LED 直接跨接在输出和地之间。
- 视频把 D13 数字写死在代码里,这是本节应保留的原代码。Arduino 还提供随板型变化的
LED_BUILTIN常量,移植到其他开发板时更方便,但本节不擅自用它改写课堂源码。
1.3.3 引脚模式与函数语法
- 对经典 AVR Uno,本节常用的三种
pinMode()模式是:INPUT(高阻输入)、OUTPUT(输出)和INPUT_PULLUP(输入并接通芯片内部上拉电阻)。视频所说“三种输出”是口误,应为“三种常用引脚模式”;本节控制 LED 必须用OUTPUT。 - Arduino 标识符区分大小写:
pinMode中的M、digitalWrite中的W,以及常量OUTPUT、LOW、HIGH都要按代码书写。中文全角括号、逗号、分号也不能代替英文半角符号。 - IDE 把函数或常量显示成某种颜色,只是语法高亮提示,不是“有效函数”的最终证明;所选开发板平台、库或编辑器主题变化时,颜色也可能不同。应以点击“验证/上传”后的编译结果为准。
- 不是“每一行都必须加分号”。这里的函数调用语句
pinMode(...);、digitalWrite(...);、delay(...);需要英文分号;函数定义行以及单独的{、}后面不加分号。
1.3.4 闪烁过程逐步展开
一轮 loop() 的执行顺序如下:
- D13 输出
LOW,L 灯灭。 delay(2000)阻塞当前主流程约 2000 ms,也就是 2 s。- D13 输出
HIGH,L 灯亮。 - 再等待约 2 s。
- 本轮结束,下一轮马上从
LOW开始。
因此完整名义周期约为 2 s + 2 s = 4 s,灭、亮各占约一半时间。视频约 06:09 的实物画面也依次展示“灭 2 秒、亮 2 秒”。delay() 的单位是毫秒,1000 ms = 1 s;它会让草图当前主流程在等待期不能继续做传感器读取等任务,因此适合入门演示,但以后需要多任务并行响应时通常不能大量依赖这种阻塞式等待。
1.3.5 编译、选择端口并上传
视频中的操作顺序为:
- 用可传输数据的 USB Type-B 线连接开发板和电脑,
ON灯亮起。 - 在 Arduino IDE 中选择板型
Arduino Uno。 - 在“工具 → 端口”选择插入此板后出现的端口;视频状态栏显示的是
COM3,这只是当时电脑分配的编号,其他电脑不必相同。 - 点击“上传”,等待编译和写入完成;上传期间
TX/RX灯可能闪烁。 - 上传成功后观察 L 灯是否按预期闪烁。
板型决定编译参数和上传协议,端口决定上传目标,二者都要正确。找不到端口时,可在 Windows 设备管理器中对比插板前后新增的 COMx;课程 CH340G 兼容板还要确认 USB 线确实支持数据、CH340 驱动已正确安装。D0/RX、D1/TX 与 USB—串口桥共用硬件串口,上传异常时应先移除接在这两个引脚上、可能干扰电平的外设。
1.4 串口通信函数
1.4.1 例题:串口打印 hello world
Serial.begin(115200)以 115200 bit/s 初始化 Uno 的硬件串口。转录把讲师提到的另一种常见速率识别成了“960”,结合 Arduino 常见设置应理解为9600;但视频本次真正写入、编译和监视的值始终是 115200,所以精校代码只保留 115200。Serial在这块 Uno 兼容板上通过 ATmega328P 的硬件 UART 和 CH340G 与电脑交换数据。begin()只设置通信速率,不会自动打印内容;真正发送字符串的是Serial.println("hello world")。Serial.print()输出数据后不主动换行;Serial.println()在数据后追加回车\r和换行\n,所以串口监视器中每次hello world各占一行。字符串必须用英文双引号括起来,视频中的字母全为小写,中间有一个空格。- 程序进入第一轮
loop()后会先打印,再执行delay(1000);所以第一行不会先等 1 秒才发送,后续两行的开始时间约相隔 1 秒,再加上很短的发送与循环开销。它是“约每秒一次”,不是严格的实时定时器。
1.4.2 串口监视器的两个下拉框
上传第二份草图后,视频打开串口监视器并把速率选为 115200 baud。监视器和程序必须使用相同速率,持续不一致时通常会显示乱码。画面中第一行开头出现过几个异常字符,但后续 hello world 已连续正确显示;单次残留或打开瞬间的异常字节不能仅凭画面确定具体成因,若乱码持续出现,应优先核对波特率、目标端口、USB 连接及是否频繁复位。
串口监视器右侧另一个“换行和回车”下拉框控制的是从监视器发送文字时,在末尾附加什么结束符。本程序没有调用 Serial.available()、Serial.read() 接收电脑输入,因此该选项不影响 println() 发往电脑的逐行输出;不要把它和 115200 baud 的速率选项混为一谈。
1.4.3 常见现象的判断
| 现象 | 优先检查 |
|---|---|
编译报 not declared 或语法错误 | 函数名大小写、英文括号/逗号/分号、字符串双引号 |
| 编译成功但上传失败 | 是否选对 Arduino Uno 和实际 COMx,数据线、CH340 驱动、D0/D1 外设是否干扰 |
| 串口窗口一直空白 | 是否上传的是第二份草图、是否打开正确端口、板子是否不断复位 |
| 持续乱码 | 草图和监视器是否都为 115200、端口是否选错、连接是否稳定 |
hello world 挤在同一行 | 是否误写成 Serial.print(),或接收软件是否按 CR/LF 显示 |
ON 灯亮但没有输出 | ON 只表示供电,仍需检查程序、上传和串口设置 |
核对依据
- 本地
videos/309.4、编程实现点灯和串口打印hello world.mp4、对应转录与原始 Markdown:逐帧核对两份最终草图、D13 L 灯、COM3、Arduino Uno、115200 baud、实物闪烁和串口输出;源码以视频最终编译/上传画面为准,没有用后续追光项目代码补全。 - Arduino AVR Core 的
main.cpp:核对经典 Uno 中隐藏的main()、一次调用setup()、循环调用loop()和serialEventRun()的真实关系。 - Arduino 官方 Blink 示例与
digitalWrite()参考:核对 Uno 的LED_BUILTIN为 D13、HIGH/LOW控灯以及delay()的毫秒单位和阻塞特性。 - Arduino Uno Rev3 官方资料、Uno R3 引脚图与原理图:核对 D13/L、D0/RX、D1/TX、5 V 逻辑和 USB—串口指示灯的功能;课程 CH340G 兼容板与官方 16U2 版的差异以第 307 节已确认的实物为边界。
- Arduino
Serial.println()参考与Arduino API 概览:核对串口初始化、print/println差异及回车换行行为。 - Arduino 草图构建流程:核对
.ino草图会经过预处理、按 C++ 编译并与 Arduino 核心链接;本文只收录课堂业务代码,不抄录框架或库实现。