time 模块管三件事:延时、计时、取当前时间。
2021-01-01 起算,要准时间得配 RTC 或联网对时
import time就能用。MicroPython 里也能写import utime,两者是同一个模块(u是兼容 CPython 的老写法),初学统一写time就好。
# ============ 接线 ============
# 演示 time 模块的用法,只用到串口输出,不用接线
# =============================
import time
# 延时(秒)
time.sleep(1) # 停 1 秒
# 延时(毫秒)
time.sleep_ms(500) # 停 500 毫秒
# 延时(微秒)
time.sleep_us(100) # 停 100 微秒
# 取当前时间戳(自 2000-01-01 起算的秒数)
print(time.time())
# 取当前时间元组,按年/月/日/时/分/秒顺序取
t = time.localtime()
print(t)
| 函数 | 说明 | 精度 |
|---|---|---|
sleep(seconds) |
延时指定秒数 | 秒 |
sleep_ms(ms) |
延时指定毫秒数 | 毫秒 |
sleep_us(us) |
延时指定微秒数 | 微秒 |
# ============ 接线 ============
# 只用到串口输出,不用接线
# =============================
from machine import Pin
import time
led = Pin(25, Pin.OUT) # 板载 LED(Pico W 写 Pin('LED'))
while True:
led.on() # 点亮
time.sleep(1) # 亮 1 秒
led.off() # 熄灭
time.sleep_ms(500) # 灭 0.5 秒
# 想闪得更快,把上面两个延时一起改小
三个函数单位不同,用错就差 1000 倍:想停 0.5 秒,写
time.sleep(0.5),别写time.sleep_ms(0.5)(那等于没停)。
sleep 是"停多久",ticks_* 是"过了多久"。*计时一律用 `ticks_,不要用time.time()`**,原因见下面表格。
| 函数 | 返回什么 | 单位 |
|---|---|---|
ticks_ms() |
开机到现在的毫秒数 | 毫秒 |
ticks_us() |
开机到现在的微秒数 | 微秒 |
ticks_diff(t2, t1) |
两个时刻之间差多少 | 跟传入值同单位 |
# ============ 接线 ============
# 只用到串口输出,不用接线
# =============================
import time
# 毫秒计时:量一段代码跑多久
start = time.ticks_ms() # 记下起点(毫秒)
time.sleep(0.2) # 假装干点活(这里用延时占用 200ms)
cost = time.ticks_diff(time.ticks_ms(), start) # 终点减起点
print('耗时', cost, '毫秒') # 大约 200 毫秒
# 微秒计时:量更短的动作
start = time.ticks_us() # 记下起点(微秒)
time.sleep_us(500) # 占用 500 微秒
cost = time.ticks_us()
print('耗时', time.ticks_diff(cost, start), '微秒')
为什么不用 time.time() 计时? 看这张表就明白了:
time.time() |
ticks_ms() / ticks_us() |
|
|---|---|---|
| 返回什么 | 日期时间(秒,带小数) | 开机以来经过的毫秒/微秒 |
| 受系统调时影响 | 会(对时后可能倒退) | 不会,一直向前走 |
| 算时间差 | 自己写减法,浮点数 | 用 ticks_diff(),结果精确到整毫秒 |
| 典型用途 | 读日期、钟点 | 防抖、测转速、倒计时、超时判断 |
ticks_diff(t2, t1) 的写法是「新时刻减旧时刻」,顺序写反会得到一个很大的数(或者负数),这是最常见的错。
# ============ 接线 ============
# 只用到串口输出,不用接线
# =============================
import time
start = time.ticks_ms() # 开始计时
# 待测的代码:累加 1 到 100000
total = 0
for i in range(100001):
total += i
elapsed = time.ticks_diff(time.ticks_ms(), start) # 已经过去多少毫秒
print('累加结果:', total)
print('执行耗时:', elapsed, '毫秒')
想量更短的动作就换
ticks_us(),写法一样,把单位换成微秒即可。
真实场景里最常用的形态:记下上次的时刻,和当前时刻比一比,够久了才算新动作。
# ============ 接线 ============
# 按键一脚 → Pico GP14
# 按键另一脚 → Pico GND(用内部上拉,无需外接电阻)
# =============================
from machine import Pin
import time
key = Pin(14, Pin.IN, Pin.PULL_UP) # 内部上拉:松开读 1,按下读 0
DEBOUNCE = 50 # 防抖时间:50 毫秒内的抖动都算同一次
last_ms = 0 # 上一次有效按下的时刻
last_state = 1 # 上一次读到的状态
print('按键防抖检测')
while True:
now = time.ticks_ms() # 当前时刻(毫秒)
state = key.value() # 当前读到的高低电平
# 状态变了,并且距上次动作超过 50ms,才算一次有效动作
if state != last_state and time.ticks_diff(now, last_ms) > DEBOUNCE:
if state == 0:
print('按键按下')
else:
print('按键释放')
last_ms = now # 记下这一次的时刻,供下次比
last_state = state # 更新状态
time.sleep_ms(10) # 主循环节奏
例子里的
last_ms = 0是"刚开机的时刻":第一次按下时ticks_diff(now, 0)就是开机至今的毫秒数,肯定大于 50,所以第一次一定能触发。
# ============ 接线 ============
# 数码管 VCC → Pico 3V3
# 数码管 GND → Pico GND
# 数码管 CLK → Pico GP16
# 数码管 DIO → Pico GP17
# 模块要上传 tm1637.py
# =============================
from machine import Pin
import tm1637
import time
tm = tm1637.TM1637(clk=Pin(16), dio=Pin(17)) # 建数码管对象
TOTAL = 30000 # 倒计时总长(毫秒),这里 30 秒
start = time.ticks_ms() # 记下开始时刻
while True:
# 已经过去多少毫秒
elapsed = time.ticks_diff(time.ticks_ms(), start)
# 还剩多少秒(整除 1000,不足 1 秒就进位)
left = (TOTAL - elapsed + 999) // 1000
if left <= 0:
tm.show('0000') # 时间到
break
tm.number(left) # 显示剩余秒数
time.sleep_ms(100) # 每 100ms 刷新一次
print('倒计时结束')
tm.number(30000)和tm.show('0000')是tm1637库的两个常用方法。 这里的+ 999是"不足一秒也算一秒",让显示从 30 开始往下走。
# ============ 接线 ============
# 只用到串口输出,不用接线
# =============================
import time
# 时间戳:自 2000-01-01 起算的秒数
t = time.time()
print('时间戳:', t)
# time() 和 ticks_ms() 的关系:毫秒 = 秒 × 1000 + 毫秒余数
print('等价毫秒:', time.time() * 1000)
# 本地时间(元组):(年, 月, 日, 时, 分, 秒, 星期, 一年第几天)
t = time.localtime()
print('本地时间:', t)
print('年: %d, 月: %d, 日: %d' % (t[0], t[1], t[2]))
print('时: %d, 分: %d, 秒: %d' % (t[3], t[4], t[5]))
print('星期: %d(0=周一)' % t[6])
时间元组的顺序要记牢:前 6 位是
年 月 日 时 分 秒,第 7 位是星期(0 表示周一),第 8 位是"一年中的第几天"。 别写成t[0]=日这种想当然的顺序,取错了不报错,只是数字莫名其妙。
# ============ 接线 ============
# 只用到串口输出,不用接线
# =============================
import time
t = time.localtime()
# %Y 年 %m 月 %d 日 %H 时 %M 分 %S 秒
print(time.strftime('%Y-%m-%d', t)) # 2021-01-01
print(time.strftime('%H:%M:%S', t)) # 00:00:01
print(time.strftime('%Y-%m-%d %H:%M:%S', t)) # 2021-01-01 00:00:01
print(time.strftime('%m月%d日 %H:%M', t)) # 01月01日 00:00
不传第二个参数也是用"当前时间":
time.strftime('%H:%M')等于time.strftime('%H:%M', time.localtime())。
# ============ 接线 ============
# LCD1602(I2C)VCC → Pico 3V3
# LCD1602 GND → Pico GND
# LCD1602 SDA → Pico GP8
# LCD1602 SCL → Pico GP9
# 模块要上传 lcd_api.py 和 pico_i2c_lcd.py
# =============================
from machine import I2C, Pin
from lcd_api import LcdApi
from pico_i2c_lcd import I2cLcd
import time
i2c = I2C(0, scl=Pin(9), sda=Pin(8), freq=400000) # I2C0 固定用 GP8/GP9
lcd = I2cLcd(i2c, 0x27, 2, 16) # 地址 0x27,2 行 16 字
while True:
t = time.localtime() # 取当前时间
lcd.clear() # 清屏
lcd.putstr(time.strftime('%H:%M:%S', t)) # 第一行显示钟点
lcd.move_to(0, 1) # 光标挪到第二行
lcd.putstr(time.strftime('%Y-%m-%d', t)) # 第二行显示日期
time.sleep(1) # 每秒刷新
sleep(0.5) 是半秒,sleep_ms(500) 才是 500 毫秒;sleep_ms(0.5) 等于没停**:time.time()` 是日期时间(浮点秒),对时后会跳变,不适合测间隔ticks_diff 顺序:写法是 ticks_diff(新时刻, 旧时刻),写反得负数或大数ticks_ms() 约 6 天多就会溢出回绕,短项目够用;跨天计时用 time.time()Q: ticks_diff 算出来是负数,或者是个特别大的数?
A: 参数顺序写反了。正确写法是 time.ticks_diff(现在, 过去)——从旧时刻减去新时刻必然为负。
Q: 想一次停 0.5 秒,写成 time.sleep_ms(0.5) 没反应?
A: sleep_ms 收的是毫秒整数,0.5 会被当成 0。要么写 time.sleep_ms(500),要么写 time.sleep(0.5)。
Q: time.localtime() 读出来年份是 2021,日期不对?
A: 正常现象。Pico 没有电池供电的实时时钟,断电重启后从固件默认时间(2021-01-01)重新起算。要显示真实日期得接 RTC 模块或联网对时。
Q: 按键一次被识别成好几次?
A: 机械按键按下、松开的瞬间会抖几下,用「用法二」的 ticks_diff 防抖:50ms 内的抖动只算一次。
Q: 中断函数里能用 time.sleep_ms() 吗?
A: 不要。中断里不刷屏、不 sleep;中断只适合记录时刻、改计数这类立刻返回的操作(可参考滚珠开关页的做法)。