内容介绍
内容介绍
项目介绍
本项目基于硬禾RP2350开发板,实现了通过PWM驱动蜂鸣器发声,能够播放多首不同的曲子,并支持用户通过按键进行曲目切换和控制播放暂停。曲目的名称通过OLED屏幕显示,支持中文汉字显示。系统具备按键消抖功能,保证按键输入稳定可靠。
本项目满足以下功能要求:
- 利用PWM产生不同频率的音调,驱动蜂鸣器发出声音;
- 支持至少三首歌曲的播放与切换;
- 通过板载按键控制歌曲切换与播放状态,含按键消抖处理;
- OLED屏幕显示当前播放曲目的中文名称。
硬件说明
本项目所使用的硬件平台为硬禾RP2350开发板,核心特点包括:
- RP2350 MCU,支持丰富GPIO资源和PWM输出;
- 板载蜂鸣器,通过PWM口驱动;
- 128×32分辨率的OLED显示屏,使用SPI通信;
- 多个模拟输入用于按键电压检测,实现多按键功能;
- 按键连接通过ADC测量电压分压实现多键检测。
方案说明
本项目设计主要包含以下几个模块:
- PWM音频播放模块:通过PWM输出控制蜂鸣器发声,利用预定义音符频率表实现音调变化;
- OLED显示模块:使用SPI驱动SSD1306 OLED屏幕,结合自定义中文字库显示当前曲目名称;
- 按键输入模块:通过ADC读取多级电压检测按键状态,实现多键功能,并做按键消抖;
- 主控制逻辑:根据按键输入实现播放、暂停、上一首、下一首等控制。
软件说明
软件采用CircuitPython在Thonny下开发,使用circuit python官方提供的rp2350固件。
系统框图如下
模块划分及工作流程如下:
PWM与频谱
- 通过pwmio.PWMOut驱动蜂鸣器;
- 预定义音符频率表(NOTE_FREQ字典)对应音符到频率的映射;
- 通过控制PWM频率和占空比发出对应频率的音符,实现歌曲播放。
OLED显示与中文字模
- 采用adafruit_ssd1306库驱动OLED屏幕;
- 中文字模数据以二进制文件存储,16×16像素点阵;
- 读取字模数据,通过自定义函数Decode16x16_Bytes_To_Pixel渲染到屏幕;
- 通过计算字符总宽度实现水平居中显示。
查看原理图可见RP2350与OLED之间的连接使用SPI通信,但是并没有接上硬件SPI的SCK,和MOSI,因此需要使用软件SPI驱动,这一部分cpy也有提供对应的库:
import bitbangio
spi = bitbangio.SPI(microcontroller.pin.GPIO45, MOSI=microcontroller.pin.GPIO44)
SSD1306具体的驱动使用adafruit的库,在adafruit-circuitpython-bundle-9.x-mpy-20250625中有提供绝大多数模块的库与使用示例。
本次任务显示相关的要求只有显示中文的歌曲名字,因此需要自己做字库解码,这里我参考的这个博客树莓派pico入坑笔记,ssd1306使用,他里面提到了一个生成字库的网站,借助博主的解码方式,我实现了中文解码。
但是如果把字库直接当作数组放到代码中的话容易出现爆栈的情况,以及不够灵活,因此我选择将字库放到bin文件,然后读取显示,我这里使用的字体是免费的得意黑。
在电脑的python环境(我这里是3.12)下运行,得到中文字bin。
#pip install pillow
from PIL import Image, ImageFont, ImageDraw
def render_char_to_bytes(ch, font_path="simhei.ttf", size=16):
# 创建黑白图像
img = Image.new("1", (size, size), color=0)
draw = ImageDraw.Draw(img)
font = ImageFont.truetype(font_path, size)
# 计算字符实际的 bbox(左上角、右下角)
bbox = font.getbbox(ch)
text_width = bbox[2] - bbox[0]
text_height = bbox[3] - bbox[1]
# 居中绘制(特别是垂直方向)
x_offset = (size - text_width) // 2 - bbox[0]
y_offset = (size - text_height) // 2 - bbox[1]
draw.text((x_offset, y_offset), ch, fill=1, font=font)
# 提取每行像素,打包成位
data = []
for y in range(size):
for byte_index in range(2): # 每行2字节
byte = 0
for bit in range(8):
x = byte_index * 8 + bit
if x < size and img.getpixel((x, y)) != 0:
byte |= (1 << (7 - bit))
data.append(byte)
return bytes(data)
chars = "硬禾学堂"
font_path = "SmileySans-Oblique.ttf" # 中文字体,需手动提供
with open("yinghexuetang.bin", "wb") as f:
for ch in chars:
bytestr = render_char_to_bytes(ch, font_path)
f.write(bytestr)
随后的读取文件,解码显示比较简单,在附件中可以看到。
按键
- 按键通过ADC采样多级电压值判断按键状态,实现多按键功能;
- 设计了容差范围tol判断电压对应的按键;
- 按键状态变化触发播放控制逻辑;
- 按键输入包含去抖动逻辑,仅在按键释放时触发事件。
效果展示与遇到的问题
效果展示
预定义歌曲:《小星星》、《超级玛丽》、《两只老虎》、《生日快乐》、《送别》具体见视频。
从左到右,按键功能:
1:上一首
2:播放/暂停
3:下一首
4:重播
主界面
开始播放
暂停播放
遇到的问题
- 一开始就是奔着体验circuit python来的,因为之前就没怎么写过python,所以有点无从下手,还好cpy官方网站资料很多,再加上语言上手方便,后面用起来还是挺顺的。
- 实时按键切换曲子本来想像以前那样用中断回调的,但是查阅资料发现cpy好像是不支持中断和多线程的,因此在单个字符之间做了频繁的读取按键。
- 播放的曲子想着直接读取wav文件解释成预定义的音调。但是看看剩下的不到1M的flash空间,只能放弃,或者可以把开发板当作声卡,在电脑这边做解析(还未实现)。
心得体会
- 通过本项目的设计与实现,虽然这次的功能比较简单,还没有完全发挥rp2350的强大,但是终于真正的使用了一回circuit python,体验到了python的快乐。
- 不知道rp2350的双核M33和双核risc-v怎么玩,有时间再看看c-sdk。
软硬件
附件下载
fujian.zip
cpy代码
团队介绍
个人
评论
0 / 100
查看更多
猜你喜欢
基于STEP Pico实现的简单音乐播放器参与电子森林组织的活动 “2023寒假一起练平台(1)- 基于STEP Pico的嵌入式系统学习平台”, 完成了其中的音乐播放器小项目。
kfggww
1432
基于RP2350B实现音乐播放器该项目使用了RP2350B核心板、综合技能训练板,实现了音乐播放器的设计,它的主要功能为:PWM播放音乐,按键切换曲目,OLED屏幕显示歌曲名。
FuShenxiao
6
基于RP2350B实现的音乐播放器该项目使用了CircuitPython,实现了基于RP2350B实现的音乐播放器的设计,它的主要功能为:播放三首不同乐曲,按键切换功能,暂停锁定,音量调节。
Ggbooo
7