1095|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

 

播放器加载失败: 未检测到Flash Player,请到安装
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
点赞 关注
 
 

回复
举报

3279

帖子

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
 
 
 

回复

3279

帖子

0

TA的资源

五彩晶圆(中级)

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

明白了。

 
 
 

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

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 2/10 下一条
TI 处理器AM62L深度讲解,报名直播赢【双肩包、充电器、胶囊伞】
【直播要点】• 如何实现安全节能设计;• 开箱体验和demo;• 软件和硬件
【直播时间】3月21日(周五)上午10:00

查看 »

 
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
快速回复 返回顶部 返回列表