1082|3

81

帖子

0

TA的资源

一粒金砂(中级)

楼主
 

简易MP3播发器 [复制链接]

 

基于RP2的MP3播发器

MP3在90年代非常流行,准备用RP2做个简易MP3,架构如下所示:

实物搭建照片

 

 

数据操作流程框图如下所示:

 

播放音乐数据流框图: 

 

 

  通过上述三个框图,不用谢文字就可以明白MP3的操作过程

主要代码如下:



# import math
# import machine,gc
import os
import time
import ufont
from ssd1306 import *

from machine import SoftI2C,SoftSPI,Pin,Timer,RTC         #从machine模块导入I2C、Pin子模块
from ssd1306 import SSD1306_I2C#从ssd1306模块中导入SSD1306_I2C子模块
from sdcard import SDCard

i2c = SoftI2C(sda=Pin(4), scl=Pin(5))   #I2C初始化:sda--22, scl -->23
oled = SSD1306_I2C(128, 64, i2c, addr=0x3c) #OLED显示屏初始化:128*64分辨率,OLED的I2C地址是0x3c

KEY1=Pin(14,Pin.IN,Pin.PULL_UP) #L构建KEY对象
KEY2=Pin(15,Pin.IN,Pin.PULL_UP) #R构建KEY对象
KEY3=Pin(13,Pin.IN,Pin.PULL_UP) #UP构建KEY对象
KEY4=Pin(12,Pin.IN,Pin.PULL_UP) #UP构建KEY对象

spisd = SoftSPI(-1, miso=Pin(11), mosi=Pin(10), sck=Pin(9))
sd = SDCard(spisd, Pin(8))

# print('未挂载SD之前:{}'.format(os.listdir()))

vfs=os.VfsFat(sd)
os.mount(vfs,'/sd')

# print('挂载SD开之后:{}'.format(os.listdir()))

os.chdir('sd')

# print('SD卡中的文件:{}'.format(os.listdir()))

sd_list = os.listdir()
sd_row = 0
sd_data = []
for i in sd_list:
    if(i[-3:] == 'wav'):
        sd_data.append(i)
print(sd_data)    
sd_len = len(sd_data)
print(sd_len)

class KEY_INST:

    def __init__(self,key_pin=[]):
        self.KEY = []
        for i in range(len(key_pin)):
            self.KEY.append(Pin(key_pin[i],Pin.IN,Pin.PULL_UP))

    def scan(self):
        if self.KEY[0].value() == 0:
            time.sleep_ms(10)
            if self.KEY[0].value() == 0:
                print("key1 is press : Left")
                while not self.KEY[0].value():
                    pass
                print("key1 is unpress : Left")
                return 1
        if self.KEY[1].value() == 0:
            time.sleep_ms(10)
            if self.KEY[1].value() == 0:
                print("key2 is press : Right")
                while not self.KEY[1].value():
                    pass
                print("key2 is unpress : Right")
                return 2
        if self.KEY[2].value() == 0:
            time.sleep_ms(10)
            if self.KEY[2].value() == 0:
                print("key3 is press : Up")
                while not self.KEY[2].value():
                    pass
                print("key1 is unpress : Up")
                return 3
        if self.KEY[3].value() == 0:
            time.sleep_ms(10)
            if self.KEY[3].value() == 0:
                print("key4 is press : Down")
                while not self.KEY[3].value():
                    pass
                print("key4 is unpress : Down")
                return 4
        return 0

list_of_menus = [20,30,40,50,60,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70]
list_of_word =["1.AllLight","2.Cyclle","3.Flicker","4.AloneLight","5.Arbitrarily","6.EXT","7.XXX"]

class MENU_SHOW():

    def __init__(self,handle,name,word):
        self.menu_name = name
        self.oled = handle
        self.menus = word
        self.len = len(word)

    def txt(self):#显示框架
        self.oled.text(self.menu_name,50,4)      #写入第1行内容
        self.oled.line(0,15,128,15,1)
        self.oled.line(0,63,128,63,1)
        self.oled.line(0,16,128,16,1)
        self.oled.rect(115,20,7,7,1)
        self.oled.rect(115,30,7,7,1)
        self.oled.rect(115,40,7,7,1)
        self.oled.rect(115,50,7,7,1)
        self.oled.rect(0,0,128,64,1)

    def word_fixed(self,i):
        if self.len <= 5:
            for i in range(self.len):
                self.oled.text(self.menus[i],   4, list_of_menus[i])
        else:
            if i<=4:
                for j in range(5):
                    self.oled.text(self.menus[j],   4, list_of_menus[j])
            else:
                for j in range(self.len):
                    self.oled.text(self.menus[j],   4, list_of_menus[j + 4 -i])
        self.oled.show()

    def word_allo(self,word,j):
        self.oled.fill(0)
        self.txt()
        for i in range(len(word)):
                self.oled.text(word[i],   4, list_of_menus[i])
        self.choose_word(j)
#         self.oled.show()

    def choose_word(self,j):#方框先择
        if j<=4:
            self.oled.fill_rect(115,list_of_menus[j-1],7,7,1)
            self.oled.rect(2,list_of_menus[j-1]-2,124,12,1)
        else:
            self.oled.fill_rect(115,list_of_menus[3],7,7,1)
            self.oled.rect(2,list_of_menus[3]-2,124,12,1)   
        self.oled.show()

    def up_down(self,a):#先择项目
        self.oled.fill(0)
        self.txt()
        self.word_fixed(a)
        self.choose_word(a)

    def ext_show(self,a):#退出显示
        self.word(a)
        self.choose_word(a)

from machine import Pin, SoftSPI
import os
from machine import I2S

font = ufont.BMFont("/sd/unifont-14-12917-16.v3.bmf")

# ======= I2S CONFIGURATION =======
SCK_PIN = 16
WS_PIN = 17
SD_PIN = 18
I2S_ID = 0
BUFFER_LENGTH_IN_BYTES = 40000
# ======= I2S CONFIGURATION =======

# ======= AUDIO CONFIGURATION =======
WAV_SAMPLE_SIZE_IN_BITS = 16
FORMAT = I2S.STEREO
SAMPLE_RATE_IN_HZ = 16000#16000
# ======= AUDIO CONFIGURATION =======

class MP3_INST(KEY_INST):
    def __init__(self,lcd,key=[],sd_data=[]):
        self.audio_out = I2S(
                    I2S_ID,
                    sck=Pin(SCK_PIN),
                    ws=Pin(WS_PIN),
                    sd=Pin(SD_PIN),
                    mode=I2S.TX,
                    bits=WAV_SAMPLE_SIZE_IN_BITS,
                    format=FORMAT,
                    rate=SAMPLE_RATE_IN_HZ,
                    ibuf=BUFFER_LENGTH_IN_BYTES,
                )
        self.display = lcd
        self.switch = False
        self.playt = False
        self.data = sd_data
        self.num = 0
        super().__init__(key)
    
    def cal_time(self,t):
        t = t.split(':')
        min = int(t[0])
        sec = float(t[1])
        # print("{}:{}".format(min, sec))
        return min * 60 + sec

    def unpack_lrc(self,name):
        print(name)
        file = open("/sd/{}.lrc".format(name), "r", encoding="utf-8")
        lrc_list = file.readlines()
        file.close()
        author = lrc_list[0][4:-3] + '-' + lrc_list[1][4:-3]  #
        lrc_time = []
        lrc_word = []
        lrc_dict = {}
        for i in lrc_list[4:]:
            lrc_sub = i.replace("[", "]").strip().split("]")
            for j in range(len(lrc_sub) - 1):
                if lrc_sub[j] and lrc_sub[j].startswith('0'):
                    lrc_dict[lrc_sub[j]] = lrc_sub[-1]
        for key in sorted(lrc_dict.keys()):
            lrc_time.append(self.cal_time(key))
            lrc_word.append(lrc_dict[key])
        return author, lrc_time, lrc_word

    def play(self,i):
        self.num = i
        wav = open("/sd/{}".format(self.data[self.num]), "rb")
        _ = wav.seek(44)
        # wav_samples_mv = memoryview(bytearray(10000))
        wav_samples_mv = memoryview(bytearray(11025))
        print("==========  START PLAYBACK ==========")
        self.playt = True
        self.switch = True
        lrc_num = 0
        try:
            while True:
                key_state = self.scan()
                if key_state == 1:
                    self.num = self.num - 1
                    if self.num <= 0:
                        self.num = 1
                    self.switch = True
                    wav.close()
                elif key_state == 2:
                    self.num = self.num + 1
                    if self.num >= sd_len:
                        self.num = 1  
                    self.switch = True
                    wav.close()
                elif key_state == 3:
                    self.playt = not self.playt
                elif key_state == 4:
                    return True
                
                if self.playt:
                    if self.switch:
                        self.switch = False
                        print(self.data[self.num])
                        wav = open("/sd/{}".format(self.data[self.num]), "rb")
                        # wav = open("/sd/linjj.wav"), "rb")
                        _ = wav.seek(44)
                        author, lrc_time, lrc_word = self.unpack_lrc(self.data[self.num][:-4])
                        print(author)
                        font.text(self.display, author, 0, 8, show=True, clear=True, auto_wrap=False)
                        now = time.time()
                        lrc_num = 0

                    if time.time() - now >= lrc_time[lrc_num]:
                        lrc_num = lrc_num + 1
                        if lrc_num >= len(lrc_time):
                            lrc_num = len(lrc_time) - 1
                        font.text(self.display, author, 0, 8, show=True, clear=True, auto_wrap=False)
                        font.text(self.display, lrc_word[lrc_num], 0, 24, show=True, clear=False, auto_wrap=True)

                    num_read = wav.readinto(wav_samples_mv)
                    if num_read == 0:
                        self.num = self.num + 1
                        if self.num > sd_len-1:
                            self.num = 0
                        wav = open("/sd/{}".format(self.data[self.num]), "rb")
                        _ = wav.seek(44)
                        # print(self.data[self.num])
                        author, lrc_time, lrc_word = self.unpack_lrc(self.data[self.num])
                        font.text(self.display, author, 0, 8, show=True, clear=True, auto_wrap=False)
                    else:
                        _ = self.audio_out.write(wav_samples_mv[:num_read])
        except (KeyboardInterrupt, Exception) as e:
            print("caught exception {} {}".format(type(e).__name__, e))
        # cleanup
        wav.close()

def donghua():
    for i in range(2,269):
        file = open("/sd/text_img_{}.dat".format(i), "rb")
        buffer = file.read(128*8)
        file.close()
        fb_buffer = framebuf.FrameBuffer(bytearray(buffer), 128, 64, framebuf.MONO_HMSB)

        oled.blit(fb_buffer, 0, 0)
        oled.show()

if __name__ == "__main__":
    menu_show = MENU_SHOW(oled,"SD",sd_data[0:4])
    key_press = KEY_INST([14,15,13,12])
    menu_num = 1
    donghua()
    menu_show.word_allo(sd_data[0:4],1)
    while True:
        key_state = key_press.scan()
        if key_state == 1:
            menu_num = menu_num - 1
            if menu_num <= 0:
                menu_num = 1
            if menu_num > 4:
                menu_show.word_allo(sd_data[menu_num-4:menu_num],menu_num)
            else:
                menu_show.word_allo(sd_data[0:4],menu_num)
        elif key_state == 2:
            menu_num = menu_num + 1
            if menu_num > sd_len:
                menu_num = 1
            if menu_num > 4:
                menu_show.word_allo(sd_data[menu_num-4:menu_num],menu_num)
            else:
                menu_show.word_allo(sd_data[0:4],menu_num)
        elif key_state == 3:
            pass
            #进入下一级目录,
            print(sd_data[menu_num - 1])
            audio_wav = MP3_INST(oled,[14,15,13,12],sd_data)
            audio_wav.play(menu_num - 1)
            # menu_show.choose_word(1)
        elif key_state == 4:
            #返回上一级目录
            menu_show.word_allo(sd_data[0:4],1)
            





            







 

MP3功能演示操作:链接如下所示:

https://training.eeworld.com.cn/uploadcourse/68027/lesson

 

MP3

 

源码如下:

MP3.rar (17.37 MB, 下载次数: 0)

 

 

项目总结:

    非常感谢得捷电子与EEWORLD提供设备与平台,本次开发非常愉快,开发MP3功能的过程中,熟悉了音频的处理相关知识与工具,提升了自身知识

另外项目缺陷是EP9023的晶振是8M,因此只能支持22.05k以下、24bit的音频文件,另外就是打开双核处理的时候,即写音频文件与刷新OLED同时工作,RP2就奔溃了,但是如何简单的led、oled刷新开双核,RP2可以正常运行,这一点没有解决,所以项目任然采用的是单核运行,这就导致播发音乐与刷新OLED的歌词不连贯,这点可惜了,等后续用c实现试试。

欢迎大家交流。

 

 

其他:

任务1:熟悉micropython的基本语法
最简单的莫过于电灯了

from machine import Pin
import time

led = Pin(25,Pin.OUT)
led.value(1)

def led_work():
    led.value(1)
    time.sleep(1)
    led.value(0)
    time.sleep(1)
    
while True:
#     led.toggle()
    time.sleep(1)
    print("led work...")

 

任务2:驱动外设
驱动蜂鸣器,源码如下:

from mic import *
from machine import Pin,PWM
import time

buzzer = 16
scale=[G,A,EE,A,G,A,G,A,EE,A,G,
            A,EE,A,G,A,E,
            G,D,E,G,A,B,
            A,EE,A,G,A,G,
            A,EE,B,CC,B,CC,B,A,E,
            D,E,G,A,B,A,EE,A,G,A,
            G,A,EE,A,G,A,EE,A,G,A,
            E,G,D,E,G,A,B,A,EE,A,G,A,
            G,A,EE,B,CC,B,CC,DD,EE,AA]  #旋律

duration=[2,1,1,1,1,7,1,1,1,1,1,
                1,1,1,1,3,1,
                3,1,1,1,1,1,
                1,1,1,1,7,1,
                1,1,1,1,1,1,1,1,6,
                1,1,1,1,1,1,1,1,1,7,
                1,1,1,1,1,1,1,1,1,3,
                1,3,1,1,1,1,1,1,1,1,1,7,
                1,1,1,1,1,1,1,1,1,4] #音符时值 

scale_len=len(scale)  #该变量后面用于统计音符个数 
buzzer_phy = Pin(buzzer,Pin.OUT)
pwm_phy = PWM(buzzer_phy)
interval = 100

while True:
    for i in range(len(scale)):
        pwm_phy.freq(scale[i])
        pwm_phy.duty_u16(duration[i]*250)
        time.sleep_ms(400)
        pwm_phy.duty_u16(0)
        time.sleep_ms(interval)
    time.sleep(2)

驱动OLED,利用OLED 实现简易示波器

from ssd1306 import SSD1306_I2C
from machine import Pin,I2C
import math

i2c = I2C(1)
print(i2c)
print(i2c.scan())
display = SSD1306_I2C(128,64,i2c)
display.fill(0)
display.text("abcdef",0,0)
display.text("1234",0,8)
display.text("123456789",0,16)
display.text("123456789",0,24)
display.text("abcdef",10,40)
display.text("1234",20,54)

display.show()
osci_hight = 50
osci_width = 100
freq = 5
amp = 25
freq_div = 1
amp_div = 1
display.show()
display.fill(0)
display.hline(0,31,128,1)
display.vline(64,0,64,1)
y = []
a_last = 0
for i in range(osci_width):
    a = 25+amp*math.sin(2*math.pi*freq*i/osci_width)
    y.append(int(a))
#     display.pixel(i,int(a),1)
    if i == 0:
       display.line(i,int(a),i,int(a),1)
    else:
        display.line(i-1,int(a_last),i,int(a),1)
    a_last = a
display.text("5v/div",8,56)
display.show()

显示如下所示:

任务3:同步网络时间
 


import network
import urequests
import ujson
import utime
import time
import ssd1306
import machine,gc
from machine import RTC, I2C, Pin
from ssd1306 import SSD1306_I2C

ssid = "HAHA" # wifi路由器名称
pw = "4r8,52L8" # wifi路由器密码
 

print("Connecting to wifi...")
# i2c = SoftI2C(sda=Pin(4), scl=Pin(5))   #I2C初始化:sda--22, scl -->23
i2c = I2C(1)
oled = SSD1306_I2C(128, 64, i2c, addr=0x3c) #OLED显示屏初始化:128*64分辨率,OLED的I2C地址是0x3c
oled.fill(0)
oled.text("Connecting", 0, 5)
oled.text(" to wifi...", 0, 16)
oled.show()
time.sleep(1) 
# wifi connection#链接WiFi
wifi = network.WLAN(network.STA_IF) # station mode
wifi.active(True)
if wifi.isconnected():
    print("wifi connected")
else:
    wifi.connect(ssid, pw)
    # wait for connection#等待WiFi链接上
    i = 0
    while not wifi.isconnected():
        time.sleep_ms(1000)
        oled.text("wait {}s".format(int(i)), 0, 24)
        i = i + 1
        if i % 20 == 0:
            wifi.connect(ssid, pw)
        if i == 60:
            print("wifi connect failed..")
            oled.text("wifi connect failed..",0,32)
        oled.show()
 

# wifi connected#WiFi链接上以后显示的内容
print("IP: " + str(wifi.ifconfig()[0]) + "\n")
oled.text("Cd. IP: ", 0, 35)
oled.text(" " + str(wifi.ifconfig()[0]), 0, 45)
oled.show()
time.sleep(1)
url = 'https://api.seniverse.com/v3/weather/daily.json?key=Sm2zhT9bLvDz_Hfzb&location=wuhan&language=en&unit=c&start=0&days=5'
oled.fill(0)#根据网址更换密钥和城市(key=)(location=)
oled.show()
 

 
def weather_():
    result = urequests.get(url)
    if not wifi.isconnected():
        machine.reset()
    j = ujson.loads(result.text)
    for i in range(3):
        oled.fill(0)
        city=j['results'][0]['location']['name']
        oled.text(city+" weather", 0, 0)
        
        date=j['results'][0]['daily'][i]['date']
        oled.text("Date "+date, 0, 9)
        
        tianqi='D/N:'+j['results'][0]['daily'][i]['text_day']+'/'+j['results'][0]['daily'][i]['text_night']
        oled.text(tianqi, 0, 20)
        
        high=j['results'][0]['daily'][i]['high']+'  deg'
        low=j['results'][0]['daily'][i]['low']+'  deg'
        
        oled.text('Low:'+low, 0, 29)
        oled.text('High:'+high, 0, 37)
        
        shidu=j['results'][0]['daily'][i]["humidity"]
        oled.text('Humidity:'+shidu+'%', 0, 50)
        oled.show()
        utime.sleep(3)
        oled.show()
    
def weather_icon():
    result = urequests.get(url)
    if not wifi.isconnected():
        machine.reset()
    j = ujson.loads(result.text)
    weatherday=j['results'][0]['daily'][0]['text_day']
    return weatherday 
 
web_query_delay = 60000 # interval time of web JSON query
retry_delay = 5000 # interval time of retry after a failed Web query
url2 = 'http://worldtimeapi.org/api/timezone/Asia/Hong_Kong'
rtc = RTC()
def webtime_():

            response = urequests.get(url2)
        
            if response.status_code == 200: # query success
                # parse JSON
                parsed = ujson.loads(response.text) 
                # you can also use parsed = response.json()
                datetime_str = str(parsed["datetime"])
                year = int(datetime_str[0:4])
                month = int(datetime_str[5:7])
                day = int(datetime_str[8:10])
                hour = int(datetime_str[11:13])
                minute = int(datetime_str[14:16])
                second = int(datetime_str[17:19])
                subsecond = int(round(int(datetime_str[20:26]) / 10000))
            
                # update internal RTC
                rtc.datetime((year, month, day, 0, hour, minute, second, subsecond))
                update_time = utime.ticks_ms()
                print("wifi of RTC updated success\n")
       
            else: # query failed, retry retry_delay ms later
                update_time = utime.ticks_ms() - web_query_delay + retry_delay
        
rtc = RTC()
oled.text('wifiLinkTo RTC', 0, 4)  
oled.show()
webtime_()
oled.fill(0)
oled.text('updated', 35, 20)
oled.text('success', 35, 30)
oled.show()
time.sleep(1)

weather_()
time.sleep(10)
webtime_()

    
    

 

任务4:实现定位功能

import time
from machine import  Pin, UART
from micropyGPS import MicropyGPS

if __name__ == '__main__':

    uart_gps = UART(1, baudrate=9600, tx=Pin(4), rx=Pin(5)) # uart1
    gps = MicropyGPS() #GPS
    
    while True:
        if uart_gps.any() > 0:
            status = gps.update(uart_gps.read(1).decode("ascii"))
            if status:
                print("latitude:", gps.latitude_string())
                print("Longitude:", gps.longitude_string())
                print("Speed:",gps.speed_string("kph"),"or",gps.speed_string("mph"),"or", gps.speed_string("knot"),)  #读取速度信息
                print("Date (Long Format):", gps.date_string("long"))#读取时间信息
                print("Date (Short D/M/Y Format):", gps.date_string("s_dmy"))
                print("timestamp (Short [H,M,S] Format):", gps.timestamp)

 

 

 

 

最新回复

明白了。   详情 回复 发表于 2023-7-18 16:41
点赞 关注
 
 

回复
举报

3243

帖子

0

TA的资源

五彩晶圆(中级)

沙发
 

不错,RP2是什么???

点评

Raspberry Pi Pico W简写RPP,缩写RP2  详情 回复 发表于 2023-7-18 15:16
 
 
 

回复

81

帖子

0

TA的资源

一粒金砂(中级)

板凳
 
tagetage 发表于 2023-7-17 09:20 不错,RP2是什么???

Raspberry Pi Pico W简写RPP,缩写RP2

点评

明白了。  详情 回复 发表于 2023-7-18 16:41
 
 
 

回复

3243

帖子

0

TA的资源

五彩晶圆(中级)

4
 
冒险武者 发表于 2023-7-18 15:16 Raspberry Pi Pico W简写RPP,缩写RP2

明白了。

 
 
 

回复
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/8 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表