本帖子的内容是基于上一贴的继续升级:用STM32F103做个简易无损音乐播放器,软解FLAC/MP3/WAV
http://bbs.mydigit.cn/read.php?tid=1925663建议先看了上一贴再继续阅读本贴内容
之前制作的音乐播放器,使用了STM32f103作为主控。但是有时钟不精确的问题。
STM32在系统主频72mhz 使用44100hz采样率输出且需要提供MCK的时候,因为单片机分频的问题,实际的频率的是46875hz,偏差了6%

实际使用逻辑分析仪看也确实是这样

当时的解决办法是吧主频提到80MHZ
进行7分频得MCK为11.428mhz(接近标准值11.2896)256分频后得到音频播放频率为44.6khz,误差缩小到1%

虽然能在可以接受的范围了,但是作为强迫症的我,还是感觉到不满足。于是决定再改进。
这改进方案呢,一想就想了好久,一直没找到个好的方案。
之前想到的是用stm32f4系列的芯片。stm32f4系列有个好处是集成了音频作用的PLL,可以直接发生能被整除分频的音频专用频率
我手上有两款32f4,一个是pos机拆的stm32f401,比较低端一些;一个是买的stm32f407,高档一点
stm32f4x时钟树如下:
可见I2S模块的时钟有两个来源,一个是内部I2S专用的PLL,一个是外部时钟输入。
看到有时钟专用PLL了,可先别高兴,来看看数据手册上给的参数。jitter(时钟抖动)参数
强迫症还是有些受不了,开始考虑单纯依赖晶振比较好
那就考虑外置时钟输入吧。
查看引脚复用定义表,看到时钟输入脚在PC9
这可就很尴尬了。
pos机拆的stm32f401只有48脚,PC9根本没有引出
如果用STM32f407呢,手上有100脚的,PC9是引出了,仔细一看这个引脚和读取sd卡要用到的SDIO冲突了。
虽然说sd卡是可以支持SPI的,但是首先是SPI读取速度慢,其次是现在新出厂的有些TF卡已经不支持SPI接口了,一个音乐播放器还要挑卡,这也太不爽了吧。另一方面,STM32f407也不便宜,要30+元一片呢,心里有点疼,并且用来音频播放有点杀鸡用牛刀。
想来想去,总觉得不是完美方案啊。遂放弃。
还有其他一堆乱七八糟的方案,都是不太完美,遂放弃。
不是不成熟,就成本太高,或者是难度太大。
最后选用了用CPLD来实现功能。
最终的目标就是音频时钟的稳定度 精度都只唯一与晶振有关,其他电路无任何影响。实现方案
原来是这样的系统框图
现在改成这样的
CPLD(Complex Programmable Logic Device)中文名叫 复杂可编程逻辑器件,我的理解就是要里面有一堆门,一堆触发器之类的东西,然后能通过程序来定义更改他们之间的接线逻辑,来实现想要的电路。
编程我使用的是verilog HDL硬件描述语言来实现。说是编程,实际上就是在设计电路。
芯片方案使用的ALTERA公司的MAX II 系列芯片中的最低档最便宜的EPM240来实现。一开始还担心240个逻辑单元够不够用啊,后来发现要实现的功能也没多复杂,一半的逻辑单元数量都没用完。
开发软件自然是Quartus II ,版本13.0sp1
整个系统分为两个部分,一部分是I2S输出接口,一部分是SPI输入接口。
I2S接口比较简单
I2S输出接口接音频DAC,则需要出生主机时序。按照飞利浦标准的IIS接口来实现。
每一帧数据分为左声道和右声道。每个声道数据位为32位,其中高24位为有效数据,低8为恒为0。高字节在前。
引脚WS用来指示正在发送的数据属于哪个声道(WS=0为左) 左声道总是先于右声道发送数据。
发送方在时钟信号(CK)的下降沿改变数据,接收方在上升沿读取数据。WS信号也在时钟信号的下降沿变化。
时序图如下
代码太占版面就不贴了,见附件
生成的电路图如下
简单的分析一下,左边的ADD和叫CNT的D触发器是用来发生时序的。在主时钟MCLK为11.2896MHz的情况下,256分频为44.1kHz,所以分频器为8位,产生左右片选信号,数据位时钟信号(为4分频),还有数据锁存信号
中间的D触发器为24位数据锁存器,锁存住本次要发送的数据,防止被外部信号更改。
MUX0为数据多选一器,配合计数器来选择要发送的是哪一位,然后按着顺序从高到低发
最右边的SDAT也是D触发器,用来做输出锁存的,使输出值在一个位时钟周期内均保持不变,当在为时钟下降沿的时候,它会吧下一个位的数据锁存进来
仿真验证过程就不贴了,直接贴出实际输出结果
实验中 发送数据(16位)每次加一
仔细对照时序图,可以看到下降沿装填数据,上升沿保持供从机读取,对照标准图看,完美
接着是SPI接口。
spi接口就有意思了。
首先为了保障数据能跟上i2s而不中断,限定spi时钟直接为主时钟速度。并且每帧数据最大长度为24位(目前只用了16位)
同时,为了方便同步,防止出现数据过快丢包或过慢断流,设计由CPLD做主机。控制每次读取时间小与I2S发送时间并且与I2S同步。
为了方便后期拓展其他数据速率,spi时钟为主时钟速度,那么也就是说,在44.1k的情况下,每128个mclk,i2s会发送32位数据,
取前16/24个mclk作为spi的读取时钟,剩下的时间里面为空闲,空闲的时间不发送spi的sclk。
这个安排可难坏了我,想了好久。最终想到了用个使能信号和一个与门来控制是否发送sclk,就是下图中的G1。
生成的电路就稍微复杂一些了。最左边还是计数器,中间是一堆调节判断和其对于的逻辑
右边的倒数第二个d触发器组是数据串入寄存器,倒数第一个是数据锁存器,防止串入的数据影响输出数据(这个结构和74hc595一模一样)
最后形成的时序是这样的 片选cs和数据时钟sck都有cpld发生,stm32做从机,只翻数据给spi总线
顶层模块如图
进行联合测试
测试输入源是stm32作为spi的从机,测试代码很简单,初始化配置spi3->cr1寄存器就ok,主循环中检测如果数据被取走,就自加并填入新数据
调试现场
SPI输入数据延时一个音频周期后,从I2S输出 (逻辑分析仪接入会有一些干扰,无视之)
楼下继续