随着我们课程的递进,大家会发现,我们之前课程中的例子,虽然功能都能实现,但总觉得体验感不够好,比如按键控制GRB灯珠的时候,很容易出现按键后,灯珠没有反应,还有蓝牙发送指令控制灯珠的时候也会出现延迟等问题,今天,我们就讲解如何改善这类现象。
要想改善这类现象,需要用到Timer、GPIO中断,我们先分别介绍一下这些知识。
一、Timer
在 MicroPython 中,Timer类用于实现硬件定时器功能,可以用于周期性任务、定时中断、PWM 生成等。以下是 Timer 的常见应用场景和示例:
1. 基本概念
定时器 ID: ESP32S3 支持 4 个定时器
定时器模式:
Timer.PERIODIC:周期性执行回调函数。
Timer.ONE_SHOT:单次执行回调函数。
回调函数:定时器触发时调用的函数。 2. 基本用法
2、初始化定时器、停止定时器
from machine import Timer
# 初始化定时器(ID=0,模式为周期性)
tim = Timer(0)
tim.init(period=1000, mode=Timer.PERIODIC, callback=lambda t: print("Timer triggered!"))
tim.deinit() # 停止并关闭定时器
参数说明:
period:定时周期(单位:毫秒)。
mode:Timer.PERIODIC 或 Timer.ONE_SHOT。
callback:定时器触发时调用的函数(需接受一个参数,通常是定时器对象)。
3、应用举例
from machine import Timer, Pin
led = Pin(2, Pin.OUT) # 假设我们用 GPIO2 连接 LED
def toggle_led(t):
led.value(not led.value()) # 切换 LED 状态
# 初始化定时器,每 500ms 触发一次
tim = Timer(0)
tim.init(period=500, mode=Timer.PERIODIC, callback=toggle_led)
4、PWM输出
from machine import Timer, Pin, PWM
pwm = PWM(Pin(2)) # 假设使用 PWM 控制 LED 亮度
pwm.freq(1000) # 设置 PWM 频率
def set_duty(duty):
pwm.duty(duty)
# 定时改变 PWM 占空比
duty = 0
def pwm_callback(t):
global duty
duty = (duty + 10) % 1023
pwm.duty(duty)
tim = Timer(0)
tim.init(period=50, mode=Timer.PERIODIC, callback=pwm_callback)
二、GPIO 中断
在 MicroPython 中配置 GPIO 中断可以实现对外部事件的快速响应(如按键按下、传感器信号触发等)。以下是详细的配置方法和示例:
1. GPIO 中断的基本概念
触发条件:支持多种触发模式,如:
Pin.IRQ_RISING:上升沿触发(低电平 → 高电平)。
Pin.IRQ_FALLING:下降沿触发(高电平 → 低电平)。
Pin.IRQ_LOW_LEVEL:低电平持续触发。
Pin.IRQ_HIGH_LEVEL:高电平持续触发。
Pin.IRQ_CHANGE:电平变化触发(上升沿和下降沿均触发)。(我在s3上测试,好象不支持电平变化触发中断)
中断服务函数(ISR):当触发条件满足时,自动调用绑定的回调函数。
2. 配置 GPIO 中断的步骤
2.1 初始化 GPIO 引脚
设置引脚为输入模式,并启用上拉/下拉电阻(可选):
from machine import Pin
# 初始化引脚为输入模式,启用内部上拉电阻(假设连接按键到 GND)
pbootkey = Pin(0, Pin.IN,Pin.PULL_UP) #配置GPIO0为输入端口
2.2 定义中断回调函数
回调函数需要接收一个参数(触发中断的引脚对象):
def button_handler(pin):
time.sleep_ms(10) #消除抖动
if pin.value() == 0:
led.value(not led.value())
# 此处执行中断处理逻辑
2.3 绑定中断触发条件和回调函数**
# 配置中断:下降沿触发(按键按下时从高电平变为低电平)
pbootkey.irq(trigger = Pin.IRQ_FALLING,handler=button_handler)
3、 多引脚共享中断
多个引脚可以绑定到同一个回调函数:
button1 = Pin(14, Pin.IN, Pin.PULL_UP)
button2 = Pin(15, Pin.IN, Pin.PULL_UP)
def shared_handler(pin):
print("Interrupt from pin:", pin)
button1.irq(trigger=Pin.IRQ_FALLING, handler=shared_handler)
button2.irq(trigger=Pin.IRQ_FALLING, handler=shared_handler)
4. 动态修改中断配置
# 禁用中断
button.irq(handler=None)
# 重新启用中断(例如切换触发模式)
button.irq(trigger=Pin.IRQ_RISING, handler=button_handler)
三、应用实例
接下来,我们利用今天介绍的知识来改善前面课程讲解的实例。
1、通过开发板上的boot按键来控制开发板上的GRB灯珠
def button_handler(pin):
time.sleep_ms(10) #消除抖动
if pin.value() == 0:
wsled.ws2812_Change_Status()
2、通过Timer回调函数来扫描aht20温湿度值
这里我们设定1分钟扫描一次。
想要详细了解aht20及aht10的朋友,可以参考:CH592F /CH582通过硬件IIC读写AHT10 /AHT20,并将数据通过BLE发送给上位机
def monitor(t):
global ahtx_interval
ahtx_interval += 1
if ahtx_interval >= (AHT20_Interval / MOINTOR_Interval):
ahtx_interval = 0
wendu = aht20_sensor.temperature
shidu = aht20_sensor.relative_humidity
tft.showstring("实测温湿度:\n温度 : {:>4.1f} ℃\n湿度 : {:>4.1f} %".format(wendu,shidu),0,42,color(0,0,0xff))
3、通过Timer回调函数监控蓝牙发送控制GRB灯珠的指令
def monitor(t):
command = ble.get_rec_cmd()
if command == 'LED ON':
wsled.ws2812_Power(1)
# print(ble.ble_msg)
ble.send('LED is ON.')
# ble.ble_msg = ''
# 关闭LED
elif command == 'LED OFF':
wsled.ws2812_Power(0)
# print('LED is OFF.')
ble.send('LED is OFF.')
4、通过Timer回调函数改变GRB灯珠状态
def monitor(t):
wsled.ws2812_LED_Cycle()
想要了解ws2812灯珠控制原理及代码介绍的朋友,可以参考:
MicroPython 开发基于ESP32S3控制ws2812灯带的程序
最后给出主程序的完整代码,需要各模块实现代码的朋友请参考:
MicroPythonforESP32S3Timer回调函数、GPIO中断处理函数、aht20温湿度测量及显示、BLE控制ws2812灯珠、st7735TFT显示屏驱动、中文字符支持资源-优快云文库
from machine import I2C,SPI,Pin,Timer
from micropython import const
import time
from esp32_ble import ESP32_BLE
from esp32_wifi import esp32_wifi
from ahtx0 import AHT20,ahtx_interval
from st7735 import ST7735,color
from ws2812 import WS2812
spi = SPI(2,
baudrate=20000000,
polarity=0,
phase=0,
sck=Pin(4),
mosi=Pin(5),
miso=None)
# 定义控制引脚
dc = Pin(7,Pin.OUT)
cs = Pin(15,Pin.OUT)
rst= Pin(6,Pin.OUT)
#bl = Pin(9, Pin.OUT)
# 初始化显示屏 (128x160)
tft = ST7735(128, 160, spi, dc, cs, rst,None,rotate=0)
i2c = I2C(0,scl=Pin(1),sda=Pin(2),freq=200000)
aht20_sensor = AHT20(i2c)
pin = Pin(48, Pin.OUT) #配置GPIO8为输出端口,控制ws2812灯珠的DIN信号
wsled = WS2812(pin,1,3,1)
MOINTOR_Interval = const(100)
AHT20_Interval = const(6000)
def monitor(t):
global ahtx_interval
wsled.ws2812_LED_Cycle()
ahtx_interval += 1
if ahtx_interval >= (AHT20_Interval / MOINTOR_Interval):
ahtx_interval = 0
wendu = aht20_sensor.temperature
shidu = aht20_sensor.relative_humidity
tft.showstring("实测温湿度:\n温度 : {:>4.1f} ℃\n湿度 : {:>4.1f} %".format(wendu,shidu),0,42,color(0,0,0xff))
command = ble.get_rec_cmd()
if command == 'LED ON':
wsled.ws2812_Power(1)
# print(ble.ble_msg)
ble.send('LED is ON.')
# ble.ble_msg = ''
# 关闭LED
elif command == 'LED OFF':
wsled.ws2812_Power(0)
# print('LED is OFF.')
ble.send('LED is OFF.')
# ble.ble_msg = ''
def button_handler(pin):
time.sleep_ms(10) #消除抖动
if pin.value() == 0:
wsled.ws2812_Change_Status()
if __name__ == "__main__":
pbootkey = Pin(0, Pin.IN,Pin.PULL_UP) #配置GPIO0为输入端口
pbootkey.irq(trigger = Pin.IRQ_FALLING,handler=button_handler)
tft.clear()
wendu = aht20_sensor.temperature
shidu = aht20_sensor.relative_humidity
tft.showstring("实测温湿度:\n温度 : {:>4.1f} ℃\n湿度 : {:>4.1f} %".format(wendu,shidu),0,42,color(0,0,0xff))
ble = ESP32_BLE("913 AI App")
tim = Timer(0)
tim.init(period = MOINTOR_Interval,mode=Timer.PERIODIC,callback=monitor)
while True:
time.sleep(1)