| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256 |
- -- LuaTools需要PROJECT和VERSION这两个信息
- PROJECT = "testtts"
- VERSION = "2.0.0"
- --[[
- 本demo当前仅支持ec618系列和ec718系列
- ## 提醒:
- 1. 本demo需要2022.12.21及之后的源码所编译的LuatOS固件
- 2. 本demo若使用外置TTS资源的LuatOS固件, 就必须有外挂的SPI Flash, 起码1M字节, 后面有刷写说明
- 3. 下载脚本时, 把txt也加上一起下载
- 4. 本demo需要音频扩展板, 780E只有I2S输出, 需要codec和PA才能驱动喇叭
- 5. 内置TTS资源的LuatOS最低版本是V1104,且去掉了很多库, 尤其是UI方面的库
- ## 使用本demo前,如果是外置TTS资源的LuatOS固件, 必须先刷tts.binpkg进行SPI Flash的刷写
- 1. 下载链接:
- 618系列:https://gitee.com/openLuat/luatos-soc-2022/attach_files
- 718系列:https://gitee.com/openLuat/luatos-soc-2023/attach_files
- 2. 在LuaTools主界面, 用"下载固件"按钮进行下载.
- 3. 下载前需要接好SPI Flash!!
- 4. 下载前选日志模式 4G USB, luatools版本号2.1.85或以上
- ## SPI Flash布局, 以1M字节为例,供参考:
- -----------------------------------
- 64 k 保留空间, 用户自行分配
- -----------------------------------
- 704k TTS数据
- -----------------------------------
- 剩余空间, 256k,用户自行分配
- -----------------------------------
- ## 基本流程:
- 1. 初始化sfud, 本demo使用SPI0 + GPIO8
- 2. 使用 audio.tts播放文本
- 3. 等待 播放结束事件
- 4. 从第二步重新下一个循环
- ## 接线说明
- 以780E开发板为例, 需要1.5版本或以上,团购版本均为1.5或以上.
- 1.4版本SPI分布有所不同, 注意区分.
- https://wiki.luatos.com/chips/air780e/board.html
- xx脚指开发板pinout图上的顺序编号, 非GPIO编号
- Flash -- 开发板
- GND -- 16脚, GND
- VCC -- 15脚, 3.3V
- CLK -- 14脚, GPIO11/SPI0_CLK/LCD_CLK, 时钟. 如果是1.4版本的开发板, 接05脚的GPIO11/UART2_TXD
- MOSI -- 13脚, GPIO09/SPI0_MOSI/LCD_OUT,主控数据输出
- MISO -- 11脚, GPIO10/SPI0_MISO/LCD_RS,主控数据输入. 如果是1.4版本的开发板, 接06脚的GPIO10/UART2_RXD
- CS -- 10脚, GPIO08/SPI0_CS/LCD_CS,片选.
- 注意: 12脚是跳过的, 接线完毕后请检查好再通电!!
- ]]
- -- sys库是标配
- _G.sys = require("sys")
- _G.sysplus = require("sysplus")
- -- Air780E的AT固件默认会为开机键防抖, 导致部分用户刷机很麻烦
- if rtos.bsp() == "EC618" and pm and pm.PWK_MODE then
- pm.power(pm.PWK_MODE, false)
- end
- function audio_setup()
- local bsp = rtos.bsp()
- if bsp == "EC618" then
- --Air780E开发板配套+音频扩展板. ES7149
- --由于音频扩展板的PA是长供电的,有塔塔声音是正常的,做产品的话有额外的参考设计
- i2s.setup(0, 0, 0, 0, 0, i2s.MODE_I2S)
- --如果用TM8211,打开下面的注释
- -- i2s.setup(0, 0, 0, 0, 0, i2s.MODE_MSB)
- --如果用软件DAC,打开下面的注释
- -- if audio.setBus then
- -- audio.setBus(0, audio.BUS_SOFT_DAC)
- -- end
- audio.config(0, 25, 1, 3, 100)
- elseif string.find(bsp,"EC718") then
- -- CORE+音频小板是这个配置
- pm.power(pm.LDO_CTL, false) --开发板上ES8311由LDO_CTL控制上下电
- sys.wait(100)
- pm.power(pm.LDO_CTL, true) --开发板上ES8311由LDO_CTL控制上下电
- local multimedia_id = 0
- local i2c_id = 0
- local i2s_id = 0
- local i2s_mode = 0
- local i2s_sample_rate = 16000
- local i2s_bits_per_sample = 16
- local i2s_channel_format = i2s.MONO_R
- local i2s_communication_format = i2s.MODE_LSB
- local i2s_channel_bits = 16
-
- local pa_pin = 25
- local pa_on_level = 1
- local pa_delay = 100
- local power_pin = 255
- local power_on_level = 1
- local power_delay = 3
- local power_time_delay = 100
- local voice_vol = 70
- local mic_vol = 80
-
- i2c.setup(i2c_id,i2c.FAST)
- i2s.setup(i2s_id, i2s_mode, i2s_sample_rate, i2s_bits_per_sample, i2s_channel_format, i2s_communication_format,i2s_channel_bits)
-
- audio.config(multimedia_id, pa_pin, pa_on_level, power_delay, pa_delay, power_pin, power_on_level, power_time_delay)
- audio.setBus(multimedia_id, audio.BUS_I2S,{chip = "es8311",i2cid = i2c_id , i2sid = i2s_id}) --通道0的硬件输出通道设置为I2S
- audio.vol(multimedia_id, voice_vol)
- audio.micVol(multimedia_id, mic_vol)
- --带TM8211的云喇叭开发板参考下面的配置
- --[[
- local multimedia_id = 0
- local i2s_id = 0
- local i2s_mode = 0
- local i2s_sample_rate = 0
- local i2s_bits_per_sample = 16
- local i2s_channel_format = i2s.STEREO
- local i2s_communication_format = i2s.MODE_MSB
- local i2s_channel_bits = 16
-
- local pa_pin = 25
- local pa_on_level = 1
- local pa_delay = 100
- local power_pin = nil
- local power_on_level = 1
- local power_delay = 3
- local power_time_delay = 0
- -- local voice_vol = 200 --默认就不放大了
- i2s.setup(i2s_id, i2s_mode, i2s_sample_rate, i2s_bits_per_sample, i2s_channel_format, i2s_communication_format,i2s_channel_bits)
- audio.config(multimedia_id, pa_pin, pa_on_level, power_delay, pa_delay, power_pin, power_on_level, power_time_delay)
- audio.setBus(multimedia_id, audio.BUS_I2S,{chip = "tm8211", i2sid = i2s_id}) --通道0的硬件输出通道设置为I2S
- -- audio.vol(multimedia_id, voice_vol)
- ]]
- elseif bsp == "AIR105" then
- -- Air105开发板支持DAC直接输出
- audio.config(0, 25, 1, 3, 100)
- else
- -- 其他板子未支持
- while 1 do
- log.info("audio", "尚未支持的BSP")
- sys.wait(1000)
- end
- end
- sys.publish("AUDIO_READY")
- end
- -- 配置好audio外设
- sys.taskInit(audio_setup)
- local taskName = "task_audio"
- local MSG_MD = "moreData" -- 播放缓存有空余
- local MSG_PD = "playDone" -- 播放完成所有数据
- audio.on(0, function(id, event)
- --使用play来播放文件时只有播放完成回调
- local succ,stop,file_cnt = audio.getError(0)
- if not succ then
- if stop then
- log.info("用户停止播放")
- else
- log.info("第", file_cnt, "个文件解码失败")
- end
- end
- sysplus.sendMsg(taskName, MSG_PD)
- end)
- local function audio_task()
- local result
- sys.waitUntil("AUDIO_READY")
- -- 初始化spi flash, 如果是极限版TTS_ONCHIP,就不需要初始化
- if sfud then
- spi_flash = spi.deviceSetup(0,8,0,0,8,25600000,spi.MSB,1,0)
- local ret = sfud.init(spi_flash)
- if ret then
- log.info("sfud.init ok")
- else
- log.info("sfud.init error", ret)
- return
- end
- else
- log.info("tts", "TTS_ONCHIP?? skip sfud")
- end
- -- 本例子是按行播放 "千字文", 文本来源自wiki百科
- local fd = nil
- local line = nil
- while true do
- log.info("开始播放")
- line = nil
- if not fd then
- fd = io.open("/luadb/qianzw.txt")
- end
- if fd then
- line = fd:read("*l")
- if line == nil then
- fd:close()
- fd = nil
- end
- end
- if line == nil then
- line = "一二三四五六七八九十一二三四五六七八九十一二三四五六七八九十一二三四五六七八九十一二三四五六七八九十"
- end
- line = line:trim()
- log.info("播放内容", line)
- result = audio.tts(0, line)
- if result then
- --等待音频通道的回调消息,或者切换歌曲的消息
- while true do
- msg = sysplus.waitMsg(taskName, nil)
- if type(msg) == 'table' then
- if msg[1] == MSG_PD then
- log.info("播放结束")
- break
- end
- else
- log.error(type(msg), msg)
- end
- end
- else
- log.debug("解码失败!")
- sys.wait(1000)
- end
- if not audio.isEnd(0) then
- log.info("手动关闭")
- audio.playStop(0)
- end
- if audio.pm then
- audio.pm(0,audio.STANDBY)
- end
- -- audio.pm(0,audio.SHUTDOWN) --低功耗可以选择SHUTDOWN或者POWEROFF,如果codec无法断电用SHUTDOWN
- log.info("mem", "sys", rtos.meminfo("sys"))
- log.info("mem", "lua", rtos.meminfo("lua"))
- sys.wait(1000)
- end
- sysplus.taskDel(taskName)
- end
- sysplus.taskInitEx(audio_task, taskName, task_cb)
- -- 用户代码已结束---------------------------------------------
- -- 结尾总是这一句
- sys.run()
- -- sys.run()之后后面不要加任何语句!!!!!
|