H_On个人小站

个人站

这都被你发现了【惊
奖励你一朵小红花~


Pi Pico 中断和模拟信号

目录

前言

话说 上一回 我们了解了如何开始使用 MicroPython 对 Pi Pico 进行一些编程控制。

现在,让我们继续把 Pico 连接电脑,打开 Thonny 开始进行下一环节~

如果 Thonny 的命令行没有显示表示已连接的提示文字,点一下上面的 “停止” 按钮,或许就可以了

中断

作为几乎所有单片机都必备的功能,中断的原理和概念不必赘述,只需要再次明确创建中断的流程:

  1. 定义一个引脚为 中断输入,在 Pico 任何一个 GPIO 引脚都可以设定为中断引脚。
  2. 创建一个 中断处理程序 的函数。
  3. 中断处理程序中断输入 配对。

以下代码声明了一个下拉的输入引脚,且中断定义为上升沿触发,与之前按钮输入的触发方式是一样的。此时你会发现:

  • 板载 LED 是 2 秒闪烁一次
  • 按下按钮后,IDE 中会输出一个提示性文字,并且灯光会快速闪烁 5 次
  • 停顿 4 秒后再次以 2 秒的频率闪烁
from machine import Pin
from utime import sleep

led = Pin(25, Pin.OUT)

but = Pin(16, Pin.IN, Pin.PULL_DOWN)

def int_handler(pin):
    but.irq(handler=None)
    print("Interrupt")
    for i in range(5):
        led.toggle()
        sleep(0.2)
    sleep(4)
    but.irq(handler=int_handler)

but.irq(trigger=Pin.IRQ_RISING, handler=int_handler)

while True:
    led.toggle()
    sleep(2)

有时会发现触发中断之后自动再次触发个 1~2 次,我的做法是这样的:

from machine import Pin
from utime import sleep, time

led = Pin(25, Pin.OUT)

but = Pin(16, Pin.IN, Pin.PULL_DOWN)

st = time()
lt = int(st)

def int_handler(pin):
    global lt
    if time() - lt < 6:
        but.irq(handler=int_handler)
        return
    lt = time()
    but.irq(handler=None)
    sleep(1)
    print(lt - st, end=' - ')
    print("Interrupt")
    for i in range(6):
        led.toggle()
        sleep(0.2)
    sleep(3)
    but.irq(handler=int_handler)

but.irq(trigger=Pin.IRQ_RISING, handler=int_handler)

while True:
    led.toggle()
    sleep(2)

模拟输入

Pi Pico 有三个模拟输入,他们都有 12 位的分辨率。

  • GPIO 26 – ADC0 (Pin 31)
  • GPIO 27 – ADC1 (Pin 32)
  • GPIO 28 – ADC2 (Pin 34)

还有第四个 ADC 用于内部温度传感器。

由于手头没有电位器,所以用土壤湿度传感器的模拟输出来进行模拟输入的测试:

土壤湿度传感器接线

import machine
import utime

earthWater = machine.ADC(28)

i = 1
while True:
    print(f"{i}: {earthWater.read_u16()}")
    utime.sleep(2)
    i += 1

模拟输出

MicroPython machine 库带有 PWM 输出功能,很容易就能实现这一点。将 LED 灯的正极连接 16 引脚,负极接 GND,使用如下代码可以观察到 LED 的亮度随数值变化的发光情况。

import machine
import utime

led = machine.PWM(machine.Pin(16))
led.freq(1000)

while True:
    for i in range(0, 65535, 1000):
        led.duty_u16(i)
        utime.sleep(0.1)
    for i in range(65535, 0, -1000):
        led.duty_u16(i)
        utime.sleep(0.1)

结合上面的模拟输入操作,我们可以使用电位器控制 LED 的亮度。(这里我把湿度传感器插入水里来修改模拟输入的值)

import machine
import utime

earthWater = machine.ADC(26)

led = machine.PWM(machine.Pin(16))
led.freq(1000)

i = 1
while True:
    wet = earthWater.read_u16()
    print(f"{i}: {65535 - wet}")
    led.duty_u16(wet)
    utime.sleep(2)
    i += 1

结合第一节的中断,我们可以做一个还算经典的 “点一下换个亮度” 的小应用。ps:如果是低电平,可以声明的时候 PULL_UP 设置中断的时候再 IRQ_FALLING 就像以下代码做的这样。如果高电平则像 第一节 那样设置。

import machine
import utime
# 用于提示程序没有进入中断的闪烁指示灯
bled = machine.Pin(25, machine.Pin.OUT)
# 中断触发按钮
but = machine.Pin(17, machine.Pin.IN, machine.Pin.PULL_UP)
# 修改亮度的 LED
led = machine.PWM(machine.Pin(21))
led.freq(1000)
# 设置亮度参数的变量
light = 0
li = 1
led.duty_u16(light)
# 防止中断函数误触发的参数
lt = utime.time()
# 中断函数
def int_handler(pin):
    global light
    global li
    global lt
    if utime.time() - lt < 3:
        but.irq(handler=int_handler)
        return
    lt = utime.time()
    but.irq(handler=None)
    utime.sleep(1)
    light += li*1000
    if li * light in [6000, 0]: li = -li
    led.duty_u16(light)
    print(f"now led is {light}")
    utime.sleep(1)
    but.irq(handler=int_handler)
# 指定中断按钮
but.irq(trigger=machine.Pin.IRQ_FALLING, handler=int_handler)
# 主程序闪烁主板上的板载 LED
while True:
    bled.toggle()
    utime.sleep(2)

末尾闲言

掌握到现在,我感觉已经差不多可以做一些简单的控制项目了。但是没有联网终究是不太符合 “物联网”。或者结合语音控制?总之 Pico 真有趣w

第二天我优化了一下代码,现在更灵活了~并且增加了长按 5 直接关灯的功能。

import machine
import utime

bled = machine.Pin(25, machine.Pin.OUT)

but = machine.Pin(17, machine.Pin.IN, machine.Pin.PULL_UP)

led = machine.PWM(machine.Pin(21))
led.freq(1000)

light = 0
li = 1

def led_init():
    global light
    global li
    light = 0
    li = 1
    led.duty_u16(light)

def int_handler(pin):
    global light
    global li

    but.irq(handler=None)
    light += li*1000
    if li * light in [6000, 0]: li = -li
    led.duty_u16(light)
    print(f"now led is {light}")
    utime.sleep(1)
    t1 = utime.time()
    # 抬起检测
    while not but.value():
        # 重置关灯
        if utime.time() - t1 > 5:
            led_init()
            # 抬起检测
            while not but.value(): pass
    but.irq(handler=int_handler)

but.irq(trigger=machine.Pin.IRQ_FALLING, handler=int_handler)

while True:
    bled.toggle()
    utime.sleep(2)