回到首页 返回首页
回到顶部 回到顶部
返回上一页 返回上一页

Beetle 树莓派RP2350 - 在PR2350上使用线程 简单

头像 米菲爸爸 2025.05.19 23 0

在上一篇文章里我们使用PWM做了几个实验

Beetle 树莓派RP2350 用PWM做几个小应用: 呼吸灯 播放音乐 失火报警

https://makelog.dfrobot.com.cn/article-316944.html

 

有小伙伴说,他见过有的音箱可以同时播放音乐并切实时有呼吸灯根据节奏来闪烁,那么我们今天就来实现这个效果。为了实现这个效果我们要用到一个概念--线程

 

首先我们要了解什么叫线程?

线程(Thread)是操作系统进行任务调度的最小单位,属于进程内部的执行单元。一个进程可以包含多个线程,这些线程共享进程的资源(如内存空间、文件描述符等),但各自拥有独立的程序计数器、栈空间和寄存器环境。 

 

我们再看树莓派PR2350的wiki中关于双核的描述

RP2350是树莓派新推出的高性能安全微控制器,该微控制器拥有独特的双核双架构设计

 

也就是说在PR2350上我们可以同时运行两个线程,也就是说在主线程外我们还可以在创建一个线程,这样两个线程就可以同时完成两件不同的任务,于是我们把呼吸灯保留在主线程里,给音乐播放再开一个线程。代码编写过程见下面视频:

上一篇文章在这里:Beetle 树莓派RP2350 用PWM做几个小应用: 呼吸灯 播放音乐 失火报警

https://makelog.dfrobot.com.cn/article-316944.html

 

更多RP2350视频请访问

https://www.bilibili.com/video/BV18G55zrEqX/

 

树莓派Pico自虐之旅

https://www.bilibili.com/video/BV1aU4y1H7J5

代码
from machine import Pin, PWM
import utime
import _thread  # 导入线程模块

# 初始化PWM对象
speaker = PWM(Pin(5), 400)
speaker.duty_u16(0)
# 设置LED灯引脚为输出模式,然后打开LED灯(1为启用)。
led_pwm = PWM(4, freq = 1000)
led_pwm.duty_u16(32768)  # 设置PWM占空比为50%(范围为0-65535)

# 音符与对应的频率,使用 dict 类型存储
NOTE_FREQUENCY = {
    "B0": 31,
    "C1": 33,
    "CS1": 35,
    "D1": 37,
    "DS1": 39,
    "E1": 41,
    "F1": 44,
    "FS1": 46,
    "G1": 49,
    "GS1": 52,
    "A1": 55,
    "AS1": 58,
    "B1": 62,
    "C2": 65,
    "CS2": 69,
    "D2": 73,
    "DS2": 78,
    "E2": 82,
    "F2": 87,
    "FS2": 93,
    "G2": 98,
    "GS2": 104,
    "A2": 110,
    "AS2": 117,
    "B2": 123,
    "C3": 131,
    "CS3": 139,
    "D3": 147,
    "DS3": 156,
    "E3": 165,
    "F3": 175,
    "FS3": 185,
    "G3": 196,
    "GS3": 208,
    "A3": 220,
    "AS3": 233,
    "B3": 247,
    "C4": 262,
    "CS4": 277,
    "D4": 294,
    "DS4": 311,
    "E4": 330,
    "F4": 349,
    "FS4": 370,
    "G4": 392,
    "GS4": 415,
    "A4": 440,
    "AS4": 466,
    "B4": 494,
    "C5": 523,
    "CS5": 554,
    "D5": 587,
    "DS5": 622,
    "E5": 659,
    "F5": 698,
    "FS5": 740,
    "G5": 784,
    "GS5": 831,
    "A5": 880,
    "AS5": 932,
    "B5": 988,
    "C6": 1047,
    "CS6": 1109,
    "D6": 1175,
    "DS6": 1245,
    "E6": 1319,
    "F6": 1397,
    "FS6": 1480,
    "G6": 1568,
    "GS6": 1661,
    "A6": 1760,
    "AS6": 1865,
    "B6": 1976,
    "C7": 2093,
    "CS7": 2217,
    "D7": 2349,
    "DS7": 2489,
    "E7": 2637,
    "F7": 2794,
    "FS7": 2960,
    "G7": 3136,
    "GS7": 3322,
    "A7": 3520,
    "AS7": 3729,
    "B7": 3951,
    "C8": 4186,
    "CS8": 4435,
    "D8": 4699,
    "DS8": 4978
}

def play_notes(note_array):
    for note in note_array:
        if note in NOTE_FREQUENCY:
            frequency = NOTE_FREQUENCY[note]
            speaker.freq(frequency)
            speaker.duty_u16(32768)  # 设置音量
            utime.sleep(0.2)  # 播放 0.5 秒
        else:
            # 空
            #print(f"未找到音符 {note} 的频率")
            speaker.duty_u16(0)  # 静音
        utime.sleep(0.1)  # 音符间间隔 0.1 秒

# 示例音符数组
# Mario theme note array
mario = [
    "E7", "E7", 0, "E7", 0, "C7", "E7", 0,
    "G7", 0, 0, 0, "G6", 0, 0, 0,
    "C7", 0, 0, "G6", 0, 0, "E6", 0,
    0, "A6", 0, "B6", 0, "AS6", "A6", 0,
    "G6", "E7", 0, "G7", "A7", 0, "F7", "G7",
    0, "E7", 0, "C7", "D7", "B6", 0, 0,
    "C7", 0, 0, "G6", 0, 0, "E6", 0,
    0, "A6", 0, "B6", 0, "AS6", "A6", 0,
    "G6", "E7", 0, "G7", "A7", 0, "F7", "G7",
    0, "E7", 0, "C7", "D7", "B6", 0, 0,
]

def blink():
    for duty_cycle in range(0, 65536, 2048):  #range(start, stop, step)
        led_pwm.duty_u16(duty_cycle)
        utime.sleep(0.05)
        
    utime.sleep(1)
    
    #reversed()函数的作用是返回一个反转的迭代器(元组、列表、字符串、range)
    for duty_cycle in reversed(range(0, 65536, 4096)):
        led_pwm.duty_u16(duty_cycle)
        utime.sleep(0.05)

    utime.sleep(1)

def play():
    while True :
        play_notes(mario)

def main():
    while True :
        blink()

if __name__ == '__main__':
    # 创建并启动线程
    _thread.start_new_thread(play, ())
    main() 
     
    # 清理资源
    speaker.deinit()

评论

user-avatar