Arduino 与 MicroPython 基础入门:PWM、GPIO、ADC、串口、Socket 与计时基础

这篇笔记整理了 Arduino UNOESP32-C3 MicroPython 开发里最常用的基础知识,包括 PWM、时间函数、GPIO、ADC、多线程、串口、Socket 以及 Arduino 常见数值类型。内容尽量保持“短、准、方便查阅”,适合作为入门和速查参考。

一、PWM 输出基础:

Arduino analogWrite() 与 MicroPython PWM

PWM(脉宽调制)通过周期性切换高低电平来调节平均输出效果,本质上不是真正的模拟电压。

核心思想(面积等效原理):在具有惯性的负载(如电机、电感、LED)上,快速开关的高频脉冲会因为惯性而“平滑”成平均值。脉冲宽度越宽,平均能量越大,等效于连续的模拟电压。

数学上,平均电压约等于:电源电压 × 占空比

pwm占空比有效值
pwm占空比有效值

1.1 Arduino PWM 函数原型

analogWrite() - Arduino 官方文档

void analogWrite(uint8_t pin, int value);
  • pin:要输出 PWM 的引脚编号
  • value:占空比设定值,通常为 0~255
  • 官方说明:在支持的引脚上输出 PWM 波

典型写法:

pinMode(pin, OUTPUT);
analogWrite(pin, value);
  • value 范围通常为 0~255
  • 0 表示始终低电平
  • 255 表示始终高电平
  • 128 约等于 50% 占空比

支持 PWM 的引脚在开发板丝印上通常带有 ~ 标记。
analogWrite() 输出的是 PWM 波形,不是真正 DAC 模拟输出。

1.2 MicroPython PWM 构造原型

PWM 类 – 脉宽调制 — MicroPython 中文文档

machine.PWM(dest, *, freq, duty_u16, duty_ns)
  • dest:要绑定为 PWM 输出的引脚对象,通常是 Pin 对象
  • freq:PWM 频率,单位 Hz
  • duty_u16:16 位占空比,范围通常为 0~65535,数值越细,调节越平滑
  • duty_ns:以纳秒表示的高电平持续时间

典型写法:

from machine import Pin, PWM

pin = Pin(2, Pin.OUT)
pwm = PWM(pin, freq=1000, duty_u16=32768)
  • freq 用于设置 PWM 频率
  • duty_u16 用于设置占空比

PWM 输出只有高电平和低电平两种状态,但某些负载或电路会对其进行“平均化”处理:

  • LED 依赖人眼视觉暂留,表现为亮度变化
  • 电机因机械惯性,表现为转速变化
  • 经过 RC 滤波后,可得到较平滑的平均电压

1.3 PWM 常见概念:周期、频率、占空比、分辨率

  • 周期 T:一个完整 PWM 循环所需时间
  • 频率 f:每秒重复的次数,公式:f = 1 / T
  • 高频率通常更不容易让电机、LED 等负载出现明显抖动或可闻噪声
  • 占空比:高电平时间占整个周期的比例,公式:Duty = t_high / T × 100%
  • 例如:f = 1kHz 时,周期 T = 1ms;若高电平持续 0.25ms,则占空比为 25%
  • 分辨率:占空比可调的离散档位数
    • Arduino UNO 的 analogWrite() 常见为 8 位,即 0~255,共 256 级
    • MicroPython 的 duty_u16 常见为 16 位,即 0~65535,共 65536 级
  • 分辨率越高,占空比步进越细,调光、调速通常越平滑
  • PWM 生成方式通常可理解为比较器比较:一个输入是锯齿波/三角波(载波),另一个是调制信号(目标值)。当调制信号高于载波时输出高电平,否则输出低电平,从而形成不同宽度的脉冲
  • Arduino UNO 默认 PWM 频率会随引脚和定时器不同而变化,常见约为 490Hz980Hz
  • 在很多芯片里,PWM 频率分辨率往往互相制约:频率调得越高,可用于细分占空比的计数空间可能越小

二、时间函数与延时:

millis()micros()ticks_us()

时间函数的原理,本质上是读取芯片内部定时器累计的计数值。程序运行后,计数会按固定节拍不断增加,因此可以换算成毫秒或微秒来做计时;延时函数则是持续等待,直到计数达到设定时长。

2.1 Arduino 计时函数

micros() | Arduino Documentation

unsigned long millis(void);
unsigned long micros(void);
  • micros():返回自 Arduino 主板开始运行当前程序以来的微秒数
  • millis():返回自主板开始运行当前程序以来的毫秒数
  • micros() 达到上限后会回绕:大约 70 分钟后溢出归零,2^32 / 1000000 / 60 ≈ 71.58 分钟
  • millis() 达到上限后会回绕:约 49.7 天

循环耗时示例:

unsigned long start = micros();
// 循环中的代码
unsigned long elapsed = micros() - start;

Arduino 没有像 ticks_diff() 这样的专门函数,但通常可直接用无符号减法计算时间差,即用“当前值 - 起始值”来处理回绕后的计时。

例如:1 - 4294967290 = 7(按 2^32 取模)

static inline unsigned long ticks_diff(unsigned long t1, unsigned long t0) {
    return t1 - t0;
}
// 注意传入顺序:当前时刻 - 起始时刻
  • 适用于 millis() / micros() 返回值的差值计算
  • 依赖 unsigned long 的回绕特性:无符号减法天然按 2^32 运算

2.2 MicroPython 计时函数

time – 时间相关功能 — MicroPython 中文文档

time.time()
time.ticks_ms()
time.ticks_us()
time.ticks_diff(t1, t0)
  • time():获取当前时间戳,表示自 EPOCH 起点开始累计的秒数
  • EPOCH 可理解为时间系统约定的“零点”
  • 若 RTC 未正确设置,则 time() 返回的可能是自上次上电、复位或其他硬件参考点开始累计的秒数
  • ticks_ms():返回毫秒计数,更适合程序运行计时
  • ticks_us():返回微秒计数,更适合更精细的程序运行计时
  • ticks_ms() / ticks_us() 不是无限增大,该计数器达到上限后会回绕
  • ticks_diff():用于处理回绕后的差值计算,因此不建议直接相减

循环耗时示例:

import time

start = time.ticks_us()
# 循环中的代码
elapsed = time.ticks_diff(time.ticks_us(), start)

2.3 Arduino 与 MicroPython 延时函数

Arduino:

delay() | Arduino Documentation

void delay(unsigned long ms);
void delayMicroseconds(unsigned int us);
  • delay(ms):延时指定毫秒
  • delayMicroseconds(us):延时指定微秒

MicroPython:

time.sleep(seconds)
time.sleep_ms(ms)
time.sleep_us(us)
  • sleep(seconds):延时指定秒
  • sleep_ms(ms):延时指定毫秒
  • sleep_us(us):延时指定微秒

三、数字引脚与 GPIO:输入输出最常用的基础

GPIO(General Purpose Input/Output)即通用输入输出引脚,可用于读取高低电平,或输出高低电平控制外设。

3.1 Arduino 数字引脚函数

pinMode() - Arduino 官方文档

digitalWrite() - Arduino 官方文档

digitalRead() - Arduino 官方文档

void pinMode(uint8_t pin, uint8_t mode);
void digitalWrite(uint8_t pin, uint8_t val);
int digitalRead(uint8_t pin);
  • pin:引脚编号
  • mode:引脚模式,常用 INPUTOUTPUTINPUT_PULLUP
  • val:输出电平,常用 LOWHIGH

典型写法:

pinMode(13, OUTPUT);
digitalWrite(13, HIGH);

pinMode(2, INPUT_PULLUP);
int state = digitalRead(2);
  • digitalWrite():输出高低电平
  • digitalRead():读取引脚电平状态

3.2 MicroPython GPIO

Pin 类 – 控制 I/O 引脚 — MicroPython 中文文档

machine.Pin(id, mode=-1, pull=-1, *, value, drive, alt)
pin.value([x])
  • id:引脚编号,ESP32 上通常直接写 GPIO 号
  • mode:引脚模式,常用 Pin.INPin.OUT
  • pull:上下拉设置,常用 Pin.PULL_UPPin.PULL_DOWN
  • value([x]):不带参数时读取电平,带参数时设置输出电平

典型写法:

from machine import Pin

led = Pin(2, Pin.OUT)
led.value(1)

key = Pin(4, Pin.IN, Pin.PULL_UP)
state = key.value()
  • Arduino 常用板级引脚号
  • MicroPython 在 ESP32 上通常直接使用 GPIO 编号

四、模拟输入 ADC:把电压读成数字

ADC(Analog to Digital Converter)用于把模拟电压转换为数字量,便于程序读取。

4.1 Arduino 模拟输入

analogRead() - Arduino 官方文档

int analogRead(uint8_t pin);
  • pin:模拟输入引脚,UNO 常用 A0~A5
  • 返回值在 UNO 上通常为 0~1023
  • 默认参考电压常用 5V

典型写法:

int value = analogRead(A0);
  • 0 表示接近 0V
  • 1023 表示接近参考电压

4.2 MicroPython 模拟输入

ADC 类 – 模数转换 — MicroPython 中文文档

machine.ADC(id)
adc.read()
  • id:ADC 引脚对象或通道
  • read():读取 ADC 数值
  • 在 ESP32 上常见返回范围为 0~4095

典型写法:

from machine import ADC, Pin

adc = ADC(Pin(4))
value = adc.read()
  • 0 表示输入电压较低
  • 较大数值表示输入电压较高
  • 不同芯片的 ADC 位数和参考范围可能不同

五、多线程与并发:Arduino UNO 和 ESP32-C3 有什么不同

多线程用于让程序同时处理多个任务。需要注意:同时做多件事不一定等于真正并行执行,还可能只是快速切换执行。

5.1 Arduino UNO 的并发思路

Arduino UNO 本身没有操作系统,也没有官方原生多线程机制,通常只有一个 loop() 主循环。

  • UNO 一般是单线程、单核执行
  • 常见做法不是开线程,而是用 millis() + 状态机 + 非阻塞写法来“并发”处理多个任务
  • 中断可以处理突发事件,但中断不等于多线程

典型思路:

unsigned long t1 = 0;
unsigned long t2 = 0;

void loop() {
    unsigned long now = millis();

    if (now - t1 >= 100) {
        t1 = now;
        // 任务1
    }

    if (now - t2 >= 500) {
        t2 = now;
        // 任务2
    }
}
  • 这种方式常叫“时间片轮询”或“非阻塞任务调度”
  • 对 UNO 来说,这通常比强行做多线程更实用

5.2 ESP32-C3 MicroPython 多线程

ESP32-C3 是单核芯片,但 MicroPython 中通常可以使用 _thread 模块创建线程。

import _thread
  • 可以创建多个线程并发执行任务
  • 由于 ESP32-C3 是单核,多个线程通常是轮流调度执行,不是真正双核并行
  • 线程之间共享变量时要注意同步问题
  • 不同固件版本对 _thread 的支持程度可能略有差异

典型写法:

import _thread
import time

def task():
    # 子线程死循环
    while True:
        # 打印线程状态 + 时间戳(保留2位小数)
        print(f"[{time.time():.2f}] thread running")
        time.sleep_ms(500)

# 启动多线程
_thread.start_new_thread(task, ())

# 主循环
while True:
    # 打印主循环状态 + 时间戳
    print(f"[{time.time():.2f}] main loop")
    time.sleep(2)

micropython多线程运行截图
micropython多线程运行截图

  • _thread.start_new_thread(function, args):启动新线程
  • 更适合把网络、串口、传感器轮询等任务拆开处理
  • 如果任务很简单,很多时候 uasyncio 也比多线程更常用

六、串口输入输出:Arduino Serial 与 MicroPython UART

串口通信常用于与电脑、传感器、模块或其他单片机交换数据。常见操作包括:初始化波特率、发送数据、判断是否收到数据、读取数据。

6.1 Arduino 串口

Serial | Arduino Documentation

Serial.begin(baud);
Serial.print(val);
Serial.println(val);
int Serial.available();
int Serial.read();
  • Serial.begin(baud):初始化串口,常见如 9600115200
  • print():发送文本或数字,不自动换行
  • println():发送后自动换行
  • available():返回接收缓冲区中可读取的字节数
  • read():读取 1 个字节,没有数据时通常返回 -1

典型写法:

void setup() {
    Serial.begin(115200);
}

void loop() {
    if (Serial.available()) {
        int ch = Serial.read();
        Serial.print("recv: ");
        Serial.println(ch);
    }
}
  • Arduino UNO 上硬件串口主要是 D0(RX) / D1(TX)
  • 它通常和 USB 串口共用,因此下载程序、串口监视器、外设通信之间可能互相影响

6.2 MicroPython 串口

UART 类——双工串行通信总线 — MicroPython中文 1.17 文档

from machine import UART
machine.UART(id, baudrate=9600, tx=..., rx=...)
uart.write(data)
uart.any()
uart.read([n])
  • UART(id, baudrate=..., tx=..., rx=...):创建串口对象并指定波特率、TX、RX 引脚
  • write(data):发送数据
  • any():返回接收缓冲区中可读取的字节数
  • read([n]):读取数据,不传参数时尽量读取当前全部可读数据

典型写法:

from machine import UART, Pin

uart = UART(1, baudrate=115200, tx=Pin(0), rx=Pin(1))
uart.write("hello\n")

if uart.any():
    data = uart.read()
    print(data)
  • 在 ESP32-C3 上,串口号和默认引脚会因固件或板卡而异,实际项目里常手动指定 tx / rx
  • read() 返回的通常是 bytes,如需文本可再做解码

七、MicroPython Socket 基础:联网后怎么收发数据

Socket 可理解为网络通信的“接口端点”,常用于 TCP / UDP 通信。在 MicroPython 里,通常先联网,再通过 socket 模块创建套接字。

7.1 常见导入方式

import socket

7.2 常见流程

import network
import socket

wlan = network.WLAN(network.STA_IF)
wlan.active(True)
wlan.connect("WiFi名", "密码")

addr = socket.getaddrinfo("example.com", 80)[0][-1]
s = socket.socket()
s.connect(addr)
s.send(b"GET / HTTP/1.0\r\nHost: example.com\r\n\r\n")
data = s.recv(1024)
s.close()
  • socket.getaddrinfo(host, port):把域名和端口解析成可连接地址
  • socket.socket():创建 socket 对象
  • connect(addr):连接远端主机
  • send() / recv():发送和接收数据
  • close():关闭连接
  • 如果是网页请求,常见是 TCP;如果是局域网广播、简单数据包,也可能用 UDP

7.3 常见说明

  • Socket 更像底层网络接口,HTTP、MQTT 等协议通常都建立在它之上
  • recv(n) 不一定一次返回完整数据,实际项目中常需要循环接收
  • 不同固件版本提供的方法可能略有差异
  • 如果网络不稳定,建议配合超时、异常处理一起使用

八、Arduino UNO 常见数值类型:范围、字节大小与区别

下表以 Arduino UNO / ATmega328P 为参考,不同架构开发板(如 ESP32、ARM)上的大小可能不同。

类型 字节数 位数 常见范围 说明
bool 1 8 false / true 布尔类型
char 1 8 -128 ~ 127 有符号字符
unsigned char 1 8 0 ~ 255 无符号字符
byte 1 8 0 ~ 255 Arduino 常用字节类型
int 2 16 -32768 ~ 32767 UNO 上是 16 位
unsigned int 2 16 0 ~ 65535 无符号整型
word 2 16 0 ~ 65535 Arduino 常用无符号 16 位
long 4 32 -2147483648 ~ 2147483647 长整型
unsigned long 4 32 0 ~ 4294967295 常用于 millis() / micros()
float 4 32 ±3.4E38 单精度浮点数
double 4 32 ±3.4E38 在 UNO 上通常与 float 相同
  • int 在 UNO 上是 16 位,不像 32 位电脑环境里是 32 位
  • unsigned long 常用于时间计数、位运算和较大范围数值
  • floatdouble 在 UNO 上通常都是 4 字节

8.1 floatdouble 的区别

  • 在很多电脑和高性能平台上,float 通常是 4 字节单精度double 通常是 8 字节双精度
  • 但在 Arduino UNO / ATmega328P 上,double 通常与 float 相同,都是 4 字节
  • 因此在 UNO 上使用 double,通常不会比 float 获得更高精度
  • 如果代码要移植到 ESP32、STM32、PC 等平台,double 的大小和精度可能会不同

九、常见误区与速查结论

  • analogWrite() 输出的是 PWM 波形,不是严格意义上的模拟电压
  • millis()micros()ticks_ms()ticks_us() 都会回绕,做差值时要用正确方法
  • Arduino UNO 没有官方原生多线程,常见做法是 主循环 + 非阻塞调度
  • MicroPython 的 UART.read() 和 Socket 的 recv() 读到的通常是 bytes
  • Arduino UNO 上 double 往往和 float 一样,别默认它一定更高精度

如果你只是想快速查用法,优先记住这几个关键词:PWM、GPIO、ADC、串口、Socket、计时回绕、float/double 差异

如果你正在学习 Arduino UNO 或 ESP32-C3 MicroPython,那么上面这些 PWM、GPIO、ADC、串口、Socket 与计时函数,就是最值得优先掌握的一批基础能力。后续继续深入时,可以再从中断、定时器、I2C、SPI、滤波、PID 控制与网络协议逐步扩展。


十、WordPress 发布与 SEO 信息

10.1 SEO 标题(Title)

Arduino 与 MicroPython 入门扫盲:PWM、GPIO、ADC、串口、Socket 与计时基础

10.2 SEO 描述(Meta Description)

本文系统整理 Arduino UNO 与 ESP32-C3 MicroPython 的基础知识,包括 PWM、GPIO、ADC、串口、Socket、millis、micros、ticks_us 等核心内容,适合入门学习与开发速查。

10.3 文章摘要(Excerpt)

这是一篇面向入门与查阅场景的 Arduino / MicroPython 基础笔记,涵盖 PWM 输出、时间函数、GPIO、ADC、串口通信、Socket 网络通信、多线程以及 Arduino UNO 常见数值类型,适合作为 ESP32-C3 与 Arduino UNO 开发的基础速查表。

10.4 推荐 Slug

arduino-micropython-pwm-gpio-adc-uart-socket-guide

10.5 推荐分类(Category)

  • 嵌入式开发
  • Arduino
  • MicroPython

10.6 推荐标签(Tags)

  • Arduino
  • Arduino UNO
  • MicroPython
  • ESP32-C3
  • PWM
  • GPIO
  • ADC
  • UART
  • Socket
  • millis
  • micros
  • ticks_us
  • 单片机入门

10.7 SEO 关键词参考

  • Arduino PWM
  • MicroPython PWM
  • Arduino GPIO
  • ESP32-C3 MicroPython
  • Arduino ADC
  • MicroPython UART
  • Arduino 串口通信
  • MicroPython Socket
  • millis 和 micros 区别
  • Arduino UNO 数据类型