USB异步音频设备制作的一点总结

    几年前自己就很想DIY一个音频播放设备,那时候也申请了一些音频DAC以及运放,前前后后做了一些耳放,也用PCM1793和51做过一个CD-ROM播放器(遥控器控制)。但逐渐感觉没兴趣了,自己也没什么仪器可以测试,所以也就是简单地按照一些典型电路自己焊板子调试,之后很长一段时间都没有做过相关的东西。不过之前剩下的一些元件放着也可惜,于是决定再次动手!

    这次的计划是做一个USB异步音频声卡,用STM32做USB接口,fpga(用的是EP2C5T144)缓存数据并合成I2S信号给PCM1796。9月开学前花了几天画了PCB,然后国庆这几天了实现了初步的USB声卡功能。先上图:

         

 

    

 

     音质感觉不错,具体就不评论了,现在对调试经验进行一点总结。

 

1.  USB异步音频传输,主要需要2个同步端点:

     1) 同步IN端点,该端点负责实时反馈设备的速度(可以理解为传快传慢)。这个反馈值以USB上的SOF为参考,代表每一帧需要消耗的数据量,在USB全速设备中(full speed),该值以10.10的格式放在3byte中(对齐MSB),这前后各10位分别代表实际消耗速度的整数和分数部分;如果是高速设备则情况稍有不同,是以微帧为单位,反馈的格式也不一样。 在此我主要参考了USB Audio Class Spec V1.0以及USB Device Class Definition for Audio Data Formats的内容,其中反馈(feedback)的一些情况在USB Specification  Rev.2.0里也有描述。

     2) 同步OUT端点,音频数据流就在这个端点上传输,每一帧(Frame)都会有数据传过来。

     我的系统是WIN7,在实际测试后发现,反馈值并不影响接收的音频采样率,即:PC端根据反馈值会少发或多发一些采样值,但不会因为的设备反馈值变化而去SRC原始音频数据,换句话说,原始的音频数据会始终保持自身的采样值,不会被SRC而去匹配设备的实际回放采样率。因此,我认为这个反馈值不能理解为设备采样率,可以简单的理解成传快和传慢。不过反馈值的只在一定的范围内起作用,例如:设备被设定在44.1KHZ采样率以及16BIT的模式下工作,则反馈值是以44.1*2*2 = 176.4为基准,在一定的范围内才有效,假设一种极端情况,如果我反馈值为44.1+10 = 54.1(表明我每一帧需要消耗超过200 byte的数据),但实际接收的一帧数据包大小甚至不会超过200。因此,这个反馈值起到一个微调作用,设备可以根据缓存的情况实时反馈传输速度。

 

2.  关于I2S:

    因为目前手上只有16.36767MHZ的晶振,可以通过该晶振产生42.626KHZ的I2S信号(因为暂时只有这个晶振,只能勉强代替44.1KHZ采样率,这样的确会使得播放速度变慢,音调变低)。

    为了获得I2S首先将16.36767MHZ进行8分频得到2.046MHZ的BCK,每24个BCK组成一个声道的数据。我用的是16bit Right Jusitified的格式,因此16bit的音频数据就在每24个BCK的后16个上发送,48个BCK传输的就是一个双声道音频的一个双声道采样值,同时,对BCK进行48分频就能得到42.626KHZ的LRCK。注意LRCK和BCK以及DATA需要按时序对齐。

    主要参考PCM1796手册里的这张时序图:

   

 3.  在STM32有关USB的设计中一定要小心控制中断函数里的执行时间,不然会出现麻烦。为了分析MCU里开辟的缓存使用情况,我在SOF的回调函数(在SOF中断中调用)里用串口打印调试信息,结果这段代码导致了音乐顿卡,最后花了不少时间才终于找到导致问题的原因。

     具体情况是这样的:我在SOF回调函数中加入一段每几百次SOF就printf一次的代码,由于串口配置的波特率是9600,且是忙等的方式输出字符,可想而知输出一次信息就会占用好几个SOF的时间,导致MCU卡在了中断函数中,同步端点的输出不能按时接收,结果就导致音乐大概每一秒都会严重断流一次。

 

 4.  经测试发现,在系统没有声音的时候(例如没有音乐播放),在用于传输音频数据的OUT ENDPOINT上是不会有数据包的。

 

 5.  整个制作过程中最棘手的一个问题是STM32会随机”死掉”,在DEBUG下才发现产生了HARD FAULT,那问题就来了,这个HARD FAULT是怎么来的?因为是随机产生  的,比如说可能设备刚连上PC过几秒就HARD FAULT,也可能是听了几分钟后再HARD FAULT,所以我在定位问题代码时花了不少时间,具体过程我在另外写一篇随笔里介绍。直接说结果吧,最终找到问题来源于usb_ini.c文件中的HP中断处理过程中,在这个端点IO处理的函数中,获取可能会现端点号为0的情况(HP处理的是同步端点和块传输端点,因此不可能会有0号端点,因为0号端点是默认的控制端点),而HP处理0号端点会导致数组越界,跳转到一个错误的入口,从而导致hard fault。出现这样情况的原因暂不明,目前认为可能是传输错误导致的。

 

 6.  对FSMC总线上的某个地址直接读写时,一定要对指针加volatile关键字,例如: *(volatile uint16_t*)(FIFO_ADDR) = streamBuff[i]。这就如同库文件在定义寄存器时会加 __IO,其实这就是关键字volatile。如果不加volatile,编译器可能会进行优化,例如对某个地址连写4次,编译后的程序却只执行最后一次。

 

 7.  quartus中调用的异步FIFO的IP,其rdusedw、rdempty和wrusedw、wrfull等信号是分别在rdclk和wrclk下改变的,并且有一定延时,具体情况可以查看FIFO说明文档。这意味着,如果长时间没有输入wrclk(或rdclk),则相应的读写状态信号是不确定的。

 

暂时就写到这里,以后再整理。最后再上两张图。

 

转载于:https://www.cnblogs.com/Ilmen/p/3356039.html

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值