版主: 51FPGA

分享到:
共1条 1/1 1   

Vivado高效设计案例——Vivado+SDK实现MP3播放

    [您是本帖的第2358位阅读者]
abgh668
我是MM
高级会员

最后登陆时间:2015-01-13 22:13:10

直达楼层
1# 发表于 2015-07-30 22:52:37

本文将给出通过Vivado IDE开发Zynq平台上PS裸机应用程序的流程,读者将看到Vivado开发更高效、快捷。

MP3我们都听过,现在我们可以用ZED-Board来听。板子上有音频芯片ADAU1761,可以实现录音、放音,但不具有MP3解码功能。Zynq 双核ARM9做MP3软件解码应该是可以实现的,但是博主本人有一颗VS1003,可以实现MP3硬件解码,软件将得以简化,对MP3解码原理感兴趣的可以深入研究如何利用CortexA9+ADAU1761实现MP3播放。电路图如下:

利用Zynq MIO实现VS1003控制,这样只和PS有关,PL完全可以丢弃。在本节基础上,读者可以尝试将SPI模块移到PL上实现,这样可以降低PS部分IO读写频率,提高CPU利用率。实物连接图如下:

Zynq板子外接用排母,为了使用杜邦线,需要一个双公排针,可以用普通单排2.54mm排针压制而成

下面介绍软件开发流程。建立Vivado工程,命名为MP3Player,过程遵循上节Vivado建立工程步骤,略。

进入IDE后,点击左侧流程管理器中的IPI Integrator下的Create Block Design。 这个工具是2013.1版本后才出现的,将取代XPS完成系统集成。

在编辑区右键,选择Add IP...,名称保持默认design_1.bd

搜索框中输入zynq,双击第一个,添加IP到电路图中。

添加完成后,自动进行布线连接,点下图中圆圈区域 Run Block Automation。

等待完成,结果如下图所示。

可以看到,DDR和固定IO自动进行了连接。这是因为我们建立工程时选择了ZedBoard DVK,这样就能按照板子描述自动连接引脚到相应外设。

另外看到,默认状态下使能了M_AXI_GP0,可以将PL部分带AXI从接口的IP连接到PS进行控制。本节不需要,所以必须禁用,否则验证设计时会报错。双击方块,见下图

看到了熟悉又陌生的画面,有些像XPS中Zynq视图,但精简了很多。单击左侧“PS-PL Configuration",界面如下:

将AXI GP0接口后的勾取消选择,确认,回到IPI。

验证设计,在空白处右键,点击Validate Design。无误,点确认即可。

在上图位置点Generate Block Design,确认。

在Sources窗口中找到design_1,右键选择生成顶层HDL包装。确认。

直接点左侧流程中的Generate Bitstream,一步到位。完成比特流大约需要5~8min。

完成后,先Open Implementated Design,再导出到SDK。

完成后,先Open Implementated Design,再导出到SDK。如果没有做这一步,上图中第二项会变成灰色。

后面就是SDK开发了,和本系列教程(三)中相同。建立Application工程,C工程,模板helloworld。将代码改为下面:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
#include
#include "platform.h"
#define MIO_BASE 0xE000A000
#define DATA0 0x40
#define DATA0_RO 0x60
#define DIRM_0 0x204
#define OEN_0 0x208
void delay(unsigned int t)
{
unsigned int i,j;
for(j=0;j {
for(i=0;i<600;i++);
}
}
/*---------------------------------------------------------------------------------------------------------*/
/* MAINfunction*/
/*---------------------------------------------------------------------------------------------------------*/
 
#define VS_XRESET_0 DrvGPIO_ClrBit(MIO_BASE + DATA0,12)
#define VS_XRESET_1 DrvGPIO_SetBit(MIO_BASE + DATA0,12)
#define VS_DREQ DrvGPIO_GetBit(MIO_BASE + DATA0_RO,11)
#define VS_XDCS_0 DrvGPIO_ClrBit(MIO_BASE + DATA0,10)
#define VS_XDCS_1 DrvGPIO_SetBit(MIO_BASE + DATA0,10)
#define VS_XCS_0 DrvGPIO_ClrBit(MIO_BASE + DATA0,13)
#define VS_XCS_1 DrvGPIO_SetBit(MIO_BASE + DATA0,13)
#define SPI_MOSI_0 DrvGPIO_ClrBit(MIO_BASE + DATA0,0)
#define SPI_MOSI_1 DrvGPIO_SetBit(MIO_BASE + DATA0,0)
#define SPI_SCL_0 DrvGPIO_ClrBit(MIO_BASE + DATA0,9)
#define SPI_SCL_1 DrvGPIO_SetBit(MIO_BASE + DATA0,9)
 
void DrvGPIO_ClrBit(volatile unsigned int * p,int idx);
void DrvGPIO_SetBit(volatile unsigned int * p,int idx);
unsigned char DrvGPIO_GetBit(volatile unsigned int * p,int idx);
void init_vs1003(void);
void VS_Reset(void);//VS1003软复位及初始化
void VS_Write_Reg(unsigned char addr,unsigned char hdat,unsigned char ldat);//向VS1003的功能寄存器写入一个字
unsigned int VS_Read_Reg(unsigned char addr);//从VS1003的功能寄存器读取一个字
void VS_Send_Dat(unsigned char dat);//向VS1003发送音频数据
void VS_Flush_Buffer(void);//清空VS1003的数据缓冲区
void VS_sin_test(unsigned char x);//正弦测试
void LoadPatch(void);//为VS1003打补丁
void SPI_WriteByte(unsigned char x);
 
#include "mp3.h"
 
void print(char *str);
 
int main()
{
init_platform();
 
print("Hello World\n\r");
unsigned int i;
 
init_vs1003();
 
VS_Reset();//VS1003复位初始化
VS_sin_test(200);//正弦测试,可以听到一声滴
VS_Flush_Buffer();
for(i = 0;i {
VS_Send_Dat(mp3_table[i]);
}
while(1)
{
DrvGPIO_ClrBit(MIO_BASE + DATA0,7);
delay(40000);
DrvGPIO_SetBit(MIO_BASE + DATA0,7);
delay(40000);
}
return0;
}
void DrvGPIO_ClrBit(volatile unsigned int * p,int idx)
{
(*p) &= ~(1< }
void DrvGPIO_SetBit(volatile unsigned int * p,int idx)
{
(*p) |= (1< }
unsigned char DrvGPIO_GetBit(volatile unsigned int * p,int idx)
{
return(((*p)&(1<>idx);
}
void init_vs1003(void)
{
DrvGPIO_SetBit(MIO_BASE + OEN_0,7);
DrvGPIO_SetBit(MIO_BASE + DIRM_0,7);
DrvGPIO_SetBit(MIO_BASE + OEN_0,0);
DrvGPIO_SetBit(MIO_BASE + DIRM_0,0);
DrvGPIO_SetBit(MIO_BASE + OEN_0,9);
DrvGPIO_SetBit(MIO_BASE + DIRM_0,9);
DrvGPIO_SetBit(MIO_BASE + OEN_0,10);
DrvGPIO_SetBit(MIO_BASE + DIRM_0,10);
DrvGPIO_SetBit(MIO_BASE + OEN_0,12);
DrvGPIO_SetBit(MIO_BASE + DIRM_0,12);
DrvGPIO_SetBit(MIO_BASE + OEN_0,13);
DrvGPIO_SetBit(MIO_BASE + DIRM_0,13);
}
 
void SPI_WriteByte(unsigned char x)
{
unsigned char i=0;
for(i=0;i<8;i++)
{
if(x&0x80)
{
SPI_MOSI_1;
}
else
{
SPI_MOSI_0;
}
 
SPI_SCL_0;
 
SPI_SCL_1;
 
x<<=1;
}
}
/******************************************************************
- 功能描述:向VS1003的功能寄存器中写入数据(一个字,即两个字节)
- 隶属模块:VS1003B模块
- 函数属性:外部,用户可调用
- 参数说明:addr是功能寄存器的地址
hdat是要写入的高字节
ldat是要写入的低字节
- 返回说明:无返回
******************************************************************/
 
void VS_Write_Reg(unsigned char addr,unsigned char hdat,unsigned char ldat)
{
while(!VS_DREQ);//VS1003的DREQ为高电平时才接收数据
VS_XCS_0;//打开片选,SCI有效,这样才能对功能寄存器进行读写
SPI_WriteByte(0x02);//写入操作码0x02 00000010 (功能寄存器写操作)
SPI_WriteByte(addr);//写入寄存器地址
SPI_WriteByte(hdat);//写入高字节
SPI_WriteByte(ldat);//写入低字节
VS_XCS_1;//关闭片选,SCI无效
}
/******************************************************************
- 功能描述:VS1003软复位及初始化(设置时钟频率及音量)
- 隶属模块:VS1003B模块
- 函数属性:外部,用户可调用
- 参数说明:无
- 返回说明:无
******************************************************************/
 
void VS_Reset(void)
{
VS_XRESET_1;
delay(100);
VS_XRESET_0;
delay(100);
VS_XRESET_1;//硬件复位,XRESET低电平有效
delay(100);
 
VS_Write_Reg(0x00,0x08,0x04);//软件复位,向0号寄存器写入0x0804 SM_SDINEW为1 SM_RESET为1
VS_Write_Reg(0x03,0x98,0x00);//时钟设置,向3号寄存器写入0x9800 SC_MULT 为4 SC_ADD 为3 SC_FREQ为0
VS_Write_Reg(0x0b,0x00,0x00);//音量设置,左右声道均最大音量
 
VS_XDCS_0;//打开数据片选,注意此时XCS(片选)为高电平,SDI有效
SPI_WriteByte(0);//写入数据,这里写入4个0,是无关数据,用来启动数据传输
SPI_WriteByte(0);
SPI_WriteByte(0);
SPI_WriteByte(0);
VS_XDCS_1;//关闭数据片选,SDI无效
}
 
/******************************************************************
- 功能描述:向VS1003写入一个字节的音频数据(即用于播放的数据)
注:调用前先将VS_XDCS置为0,打开数据片选
- 隶属模块:VS1003B模块
- 函数属性:外部,用户可调用
- 参数说明:dat是要写入的字节
- 返回说明:无
******************************************************************/
 
void VS_Send_Dat(unsigned char dat)
{
VS_XDCS_0;//打开SDI,此时可以向VS1003写入音频数据
while(!VS_DREQ);//VS1003的DREQ为高才能写入数据
SPI_WriteByte(dat);//通过SPI向VS1003写入一个字节的音频数据
VS_XDCS_1;//关闭SDI
}
 
/******************************************************************
- 功能描述:向VS1003写入2048个0,用于清空VS1003的数据缓冲区
注:在播放完一个完整的音频(如一首完整的MP3)后,调用
此函数,清空VS1003数据缓冲区,为下面的音频数据(如下
一首MP3)作准备。
- 隶属模块:VS1003B模块
- 函数属性:外部,用户可调用
- 参数说明:无
- 返回说明:无
******************************************************************/
 
void VS_Flush_Buffer(void)
{
unsigned int i;
VS_XDCS_0;//打开数据片选,即开启SDI传输
for(i=0;i<2048;i++)
{
VS_Send_Dat(0);
}
VS_XDCS_1;//关闭数据片选
}
 
/******************************************************************
- 功能描述:正弦测试,这是测试VS1003芯片是否正常的有效手段!!
- 隶属模块:VS1003B模块
- 函数属性:外部,用户可调用
- 参数说明:x决定了正弦测试中产生的正弦波的频率,直接影响听到的
声音的频率
- 返回说明:无
******************************************************************/
 
void VS_sin_test(unsigned char x)
{
VS_Write_Reg(0x00,0x08,0x20);//启动测试,向0号寄存器写入0x0820 SM_SDINEW为1 SM_TEST为1
while(!VS_DREQ);//等待DREQ变为高电平
VS_XDCS_0;//打开数据片选 SDI有效
SPI_WriteByte(0x53);//写入以下8个字节,进入正弦测试
SPI_WriteByte(0xef);
SPI_WriteByte(0x6e);
SPI_WriteByte(x);//参数x用来调整正弦测试中正弦波的频率 FsIdx (b7~b5):采样率表索引 S (b4~b0):正弦波的跃速 频率F=Fs X S / 128
SPI_WriteByte(0);//比如x=126 (0b 011 11110) FsIdx=011=3 Fs=22050Hz S=11110=30 F=22050Hz X 30/128=5168 Hz
SPI_WriteByte(0);
SPI_WriteByte(0);
SPI_WriteByte(0);
delay(6000);//这里延时一段时间,为了听到“正弦音”
SPI_WriteByte(0x45);//写入以下8个字节,退出正弦测试
SPI_WriteByte(0x78);
SPI_WriteByte(0x69);
SPI_WriteByte(0x74);
SPI_WriteByte(0);
SPI_WriteByte(0);
SPI_WriteByte(0);
SPI_WriteByte(0);
VS_XDCS_1;//关闭数据片选 ,SDI无效
}
 
音频文件需要转换为C头文件,可以用matlab实现:
clear;
clc;
close all;
 
f = fopen('222.mp3','rb');
a = fread(f,'uint8');
fclose(f);
fb = fopen('D:\Tutor_My\MP3Player\MP3Player.sdk\SDK\SDK_Export\mp3\src\mp3.h','w');
fprintf(fb,'const unsigned char mp3_table[] = {\r\n');
fprintf(fb,'0x%02x,\r\n',a(1:end));
fprintf(fb,'\r\n};');
fclose(fb);



下载比特流,运行。通过耳机可以听到你转换的mp3。

完成上述工程,只需要10min,操作完全由Vivado+SDK完成,操作十分简单集中。

此帖由abgh668于2015-07-30 22:59:48最后编辑
共1条 1/1 1   
快速回复主题
  • 匿名不能发帖!请先 [ 登陆 注册 ]