整个eCos音频驱动所花费的精力大部分集中于stm32 i2s驱动实现及其调试上。在整个调试过程中,最烦人的就是播放出来的声音有噪声,有噼噼啪啪的杂音。即使是目前提供下载的音频驱动,也还有这些问题,当然,用还是能用的,至少播放驱动包里面所带的wav音频文件是正常的!

本节主要介绍eCos STM32 I2S驱动的实现,侧重点是DMA的使用和DMA中断双缓存机制的实现。

eCos STM32 I2S驱动

eCos STM32 I2S驱动是以eCos STM32 SPI驱动为模板并在此基础上实现。不同的是,SPI驱动除了DMA中断模式外,还支持DMA POLL模式。在本音频驱动中,I2S驱动仅使用DMA中断工作模式。

这里,我们把焦点主要聚集在DMA中断和DMA双缓存机制的实现上。

eCos DMA的使用

在eCos中,中断非常容易使用,DMA亦是如此,它只有简单几个步骤就可以使用。这里以STM32平台为例进行说明,

stm32 DMA的实现位于stm32_dma.c和var_dma.h两源文件中。看下所提供的API接口:

image

所以,DMA的使用只需要以下几个步骤:

  • 调用hal_stm32_dma_init()函数对指定的DMA stream(DMA流)进行初始化,注意:DMA流在初始化之前要预先指定好DMA流对应的描述符、回调函数及其参数数据,如下图所示:

image

  • 调用hal_stm32_dma_configure()函数配置DMA流,如DMA传输bits、POLL模式还是中断模式等,具体详见STM32参考手册第10章节;
  • 调用hal_stm32_dma_start()函数启动DMA传输;
  • 如果有需要,可调用hal_stm32_dma_show()函数展示当前DMA流的配置;

I2S DMA双缓存机制

开始的时候,I2S驱动并未使用双缓存机制,实际上我也没有想到要这样实现。说来可笑,由于I2S驱动是以SPI驱动为模板加以修改的,加之本人并未对DMA进行了解,所以开始时,I2S驱动实际使用的还是DMA POLL工作模式。

正是因为如此,驱动调试前期,播放出来的声音一直有杂音,噼噼啪啪作响。在此期间呢,我也仿照安富莱开发板所带的录音回放例程使用I2S中断方式去实现I2S驱动,有可能是当时心浮气噪的原因,当时也并未认真去调试,反正不管怎么折腾,出来的声音依旧如此!

后面网上搜索,发现遇到此类问题的同学还真不少。下面两个链接对我十分有用,大家不妨也看看:

当然,你也可以参考下面摘录的信息:

zenghuipeng
2012-09-04 22:00

我的噪音可能是DMA发送的速度问题。
主线程读取wav数据,中断喂dma。
主线程分两个buff交替填充,dma吃完一个buffer就吃第二个。
这两者的速度怎么控制得好呢?

zhangdu
2012-09-05 08:29

Quote:

引用第18楼zenghuipeng于2012-09-04 22:00发表的  :
我的噪音可能是DMA发送的速度问题。
主线程读取wav数据,中断喂dma。
主线程分两个buff交替填充,dma吃完一个buffer就吃第二个。
这两者的速度怎么控制得好呢?

dma要用乒乓模式,这样,它自己就会在中断里面轮流续传两个buffer的数据,主线程里面只需要检测哪个buffer里面的数据被用完了,然后更新该buffer就可以了
我用的TI的M3,LM3S9B96,最开始出声音的时候,也是有一点咔咔咔的声音,很规律,间隔很短,当时我开的两个buffer是512字节,然后直接开了两个4096字节的buffer,从SD卡也是直接读出一整个簇的数据直接放里面,中间不经过任何周转,于是声音正常了,这样做的效率和RAM的利用率太低了,以后再慢慢优化吧,但至少可以知道咔咔咔的噪音是因为数据断流引起的,供你参考[s:2]

所以后面我也决定采用DMA双缓存机制来实现。然后在修改代码过程中,发现居然我之前一直使用的是DMA POLL模式。当然,既然改为中断模式,问题依旧!

前面一段都是废话,在本音频I2S驱动中,双缓存机制主要关注3个函数:

  • i2s_transaction_dma():DMA主线程,给DMA缓存喂音频数据;
  • stm32_dma_callback():DMA中断回调函数,用于启动下个DMA缓存数据的传输;
  • dma_channel_setup():DMA通道配置函数。

信号的使用

在DMA中断回调函数和DMA主线程中,通过signal来同步数据。

DMA主线程等待一个DMA帧传输完成,当一个DMA帧传输完成后,产生中断,并在DMA中断DSR中回调DMA的回调函数stm32_dma_callback()。DMA回调函数启动另一个缓存数据传输后,发送信号唤醒DMA主线程,这时,DMA主线程把音频数据流填充到数据已传输完成的DMA缓存里,供下一次DMA传输使用。这个过程周而复始,直到全部音频数据流传输完成。

关于音频驱动配置key

在WM8978驱动和I2S驱动中都用到的配置key,如下图所示:

image

这些KEY的定义按照正常的做法应该是添加到eCos的config_keys.h文件,供用户应用程序和驱动使用。不过,这里我我没有这样做,而是在用户应用程序和驱动程序都要定义这些key。这可是不合理的!

音频采集问题

做录音回放测试时,如果是用耳机回放,则可听到一会儿左耳有声音,一会儿又切换到右耳。扬声器听不出来。然后打印部分采集到声音的数据,发现DMA缓存切换之间有数据错位,如下图所示(注意打紫色那里):

image

我的理解是:DMA双缓存切换时,左右声道数据没有对齐或者说没有实现连续,有可能切换时丢掉了某个声道数据,从而导致声道数据不对齐。但怎么做到双缓存切换时保证声道数据对齐呢?

用户测试程序

http://52ecos.net/thread-558-1-1.html提供的音频驱动源码包中,提供了3种类型的测试程序:

  • 录音和回放的测试,对应sound_record()函数;
  • 播放存储在Nor flash 0x6402 0000位置上wav文件,对应sound_play_wav()函数。wav文件对应03_i2s wav音频文件目录下的谢谢使用 再见.wav文件,测试时,只需要把这个wav文件烧写到or flash 0x6402 0000位置上即可;
  • 播放wave_file.h文件中存储的音频数据(固化在代码中),对应sound_play_wave_data()函数;

驱动声卡的时候,应该按照打开声卡设备、配置声卡参数、播放音频数据流的顺序进行操作。

留个问题给大家思考

I2S驱动的DMA双缓存机制设计时并未考虑:从SD卡读取音频数据,然后播放音频数据流 这种应用情况。因为为了播放流畅,从SD卡读取音频数据必然也是使用DMA机制,那么SD卡DMA缓存和I2S驱动DMA缓存怎样处理呢?在驱动上怎么实现两者DMA缓存的统一??

» 文章出处: reille博客—http://velep.com , 如果没有特别声明,文章均为reille博客原创作品
» 郑重声明: 原创作品未经允许不得转载,如需转载请联系reille#qq.com(#换成@)
分享到:

 Leave a Reply

(必须)

(我会替您保密的)(必须)

*

This site uses Akismet to reduce spam. Learn how your comment data is processed.

   
© 2012 velep.com | reille blog | 管理| 粤ICP备15065318号-2| 谷歌地图| 百度地图| Suffusion theme|Sayontan Sinha

无觅相关文章插件,快速提升流量