8266 i2s及音频播放代码实现

jfxbobo
Posts: 2
Joined: Thu Jan 07, 2016 1:50 pm

8266 i2s及音频播放代码实现

Postby jfxbobo » Thu Jan 07, 2016 4:12 pm

最近准备用8266做i2s音频播放器,播放简单的wav格式声音,但是官方的开发板没有i2s输出音频处理芯片,在电子发烧友上看到一款基于8266esp的开发板叫“小e”,正在众筹,有兴趣的可以去看看作为参考,众筹地址http://z.elecfans.com/3?yitongwz。这款开发板上的功能非常完备,包括设计了9023音频芯片,实现了i2s裸流播出声音,买了一块,能播放声音。
8266的i2s资料主要是《8P-ESP8266 I2S Module description__CN_v1.0》文档,但是这个文档没有详细的寄存器讲解,在配置的时候就存在很多问题。
小e开发板实现了i2s,看了下他的代码,他的播放机制是微信发送声音到服务器,小e开发板(8266)下载声音到flash中,然后播放,资料可以参考下:http://pan.baidu.com/s/1sj5TtGp。
小e(8266)i2s的配置流程:
一、slc_init()
scl初始化,8266的i2s有两种模式,第一种是直接读写fifo,第二种方式是采用中断+dma的方式。采用中断+dma方式,寄存器配置如下:
SET_PERI_REG_MASK(SLC_CONF0,(1<<SLC_MODE_S)); //开启中断+DMA模式
ETS_SLC_INTR_ATTACH(slc_isr, NULL); //注册中断函数
ETS_SLC_INTR_ENABLE(); //开中断
二、创建dma缓冲区
创建两个dma缓冲区(可以是4个等)i2s_rx_queue1和i2s_rx_queue2,当音频播放的时候,将数据放入该缓冲区,dma自动将数据发送出去,创建:
creat_one_link(1,1,0,IIS_RX_BUF_LEN,IIS_RX_BUF_LEN,i2s_rx_buff1,&i2s_rx_queue2,&i2s_rx_queue1);
creat_one_link(1,1,0,IIS_RX_BUF_LEN,IIS_RX_BUF_LEN,i2s_rx_buff2,&i2s_rx_queue1,&i2s_rx_queue2);
这个函数实际上是将i2s_rx_buff1和i2s_rx_buff2缓冲区的首地址指向结构i2s_rx_queue1和i2s_rx_queue2的数据指针,并将i2s_rx_queue1和i2s_rx_queue2做成循环链表。
三、启动发送控制
开启slc模块: START_RXLINK();
该函数实际上是调用了SET_PERI_REG_MASK(SLC_RX_LINK, SLC_RXLINK_START),开启scl。
四、i2s模块初始化
初始化i2s本身,包括BCLK、WS、SDATA信号线的配置、时钟配置、通道、采样率、开启中断等。
(1)、8266的i2s与gpio是共用的,配置GPIO12为DATA、GPIO13为BCK,GPIO13为WS;
(2)、开启160M时钟供i2s使用:i2c_writeReg_Mask_def(i2c_bbpll, i2c_bbpll_en_audio_clock_out, 1);
(3)设置i2s模式
根据实际待播放音频的实际情况(可以查看声音的类型),设置为16位和24位,又分half和full模式。这个很重要,如果模式错误,播放的声音混乱。
SET_PERI_REG_MASK(I2S_FIFO_CONF,(I2S_I2S_RX_FIFO_MOD<<I2S_I2S_RX_FIFO_MOD_S)) //16位half模式
(4)声道设置
单、双声道,根据实际待播放音频的实际情况(可以查看声道的类型:
SET_PERI_REG_MASK(I2SCONF_CHAN,(I2S_TX_CHAN_MOD<<I2S_TX_CHAN_MOD_S)) //单声道
(5)设置采样率
根据声音的情况设置相同的采样率。播放器设置为发送主模式、接受从模式,采样率设置为50和25分频倍数(下面代码的数字50和25),BCLK为:160M/50/25=128K/s,由于设置的是16位单声道,因此采样率为128/16=8K/s,如果是其他采样率可计算出分频值(比如24位双声道,采样率44.1k,则160000K/24/2/44.1K=75,75=15*5,则如下设置:15&I2S_BCK_DIV_NUM,5&I2S_CLKM_DIV_NUM,注意前面数字一般比后边数字大,前面数 不超过63)
代码:
//trans master&rece slave mode
SET_PERI_REG_MASK(I2SCONF,I2S_RIGHT_FIRST|I2S_MSB_RIGHT|I2S_RECE_SLAVE_MOD|
I2S_RECE_MSB_SHIFT|I2S_TRANS_MSB_SHIFT|
((50&I2S_BCK_DIV_NUM )<<I2S_BCK_DIV_NUM_S)| //分频数50
((25&I2S_CLKM_DIV_NUM)<<I2S_CLKM_DIV_NUM_S)| //分频数25
(0<<I2S_BITS_MOD_S));
(6)开启中断。
五、中断处理
小e播放的机制:有音频数据到来,将其拷贝至i2s_rx_buff1或者i2s_rx_buff2中,dma自动将音频数据送入i2s总线至音频芯片,当dma发送完成一次数据,中断产生,在拷贝数据,周而复始直至数据播放完成。
中断处理每次拷贝接下来要播放数据,并判断是否播放完成:
void slc_isr(void *para)
{
et_uint32 slc_intr_status;
slc_intr_status = READ_PERI_REG(SLC_INT_STATUS); //读中断状态
if (slc_intr_status == 0)
{
return;
}
WRITE_PERI_REG(SLC_INT_CLR, 0xffffffff); //清中断
if (slc_intr_status & SLC_RX_EOF_INT_ST) //DMA缓冲区空
{
if(READ_PERI_REG(SLC_RX_EOF_DES_ADDR)==(((et_uint32)&i2s_rx_queue1))) //dma缓冲区1
{
if(file_total_size >= IIS_RX_BUF_LEN) //数据拷贝未至最后
{
memcpy(i2s_rx_buff1, buf, IIS_RX_BUF_LEN) //拷贝数据到缓冲区
file_total_size -= IIS_RX_BUF_LEN;
}
else
{
memcpy(i2s_rx_buff1, buf, file_total_size)
file_total_size = 0;
}
}
else if(READ_PERI_REG(SLC_RX_EOF_DES_ADDR)==(((et_uint32)&i2s_rx_queue1))) //dma缓冲区2
{
和缓冲区1相同
}
}

Henry.cui
Posts: 3
Joined: Tue Jan 05, 2016 9:45 pm

Re: 8266 i2s及音频播放代码实现

Postby Henry.cui » Tue Jan 12, 2016 10:35 am

这个很实用啊,找了很久,感谢楼主分享!

isti37
Posts: 3
Joined: Mon Mar 07, 2016 9:55 pm

Re: 8266 i2s及音频播放代码实现

Postby isti37 » Mon Mar 07, 2016 10:20 pm

很不错的开发板。

ESP_Greg
Posts: 49
Joined: Thu Sep 17, 2015 6:10 pm

Re: 8266 i2s及音频播放代码实现

Postby ESP_Greg » Tue Jun 28, 2016 3:57 pm

Great introduction. 使用官方的例证基本设置也是如此,我们也要写一下类似的介绍放到FAQ里面

Who is online

Users browsing this forum: No registered users and 3 guests