发 帖  
原厂入驻New
[资料] 「ALIENTEK 阿波罗 STM32F767 开发板资料连载」第六十二章 USB 声卡(Slave)实验
2020-5-23 16:13:38  217 STM32
分享
1)实验平台:alientek 阿波罗 STM32F767 开发板
2)摘自《STM32F7 开发指南(HAL 库版)》关注官方微信号公众号,获取更多资料:正点原子



第六十二章 USB 声卡(Slave)实验
上一章我们向大家介绍了如何利用 STM32F767 的 USB 接口来做一个 USB 读卡器,本章我
们将利用 STM32F767 的 USB 来做一个声卡。本章分为如下几个部分:
62.1 USB 声卡简介
62.2 硬件设计
62.3 软件设计
62.4 下载验证
62.1 USB 声卡简介
ALIENTEK 阿波罗 STM32F767 开发板板载了一颗高性能 Codec 芯片wm8978,我们
可以利用 STM32F767 的 SAI 接口,控制 WM8978 播放音乐,同样,如果结合 STM32F767 的
USB 功能,就可以实现一个 USB 声卡。
同上一章一样,我们直接移植官方的 USB audio 例程,官方例程路径:8,STM32 参考
资料STM32 USB 学习资料STM32_USB-Host-Device_Lib_v2.2.0ProjectUSB_De
vice_ExamplesAUDIO,该例程采用 USB 同步传输来传输音频数据流并且支持某些控制命令
(比如静音控制),例程仅支持 USB FS 模式(不支持 HS),同时例程不需要特殊的驱动支持,
大多数操作系统直接就可以识别。
62.2 硬件设计
本节实验功能简介:开机的时候先显示一些提示信息,之后开始 USB 配置,在配置成功
之后就可以在电脑上发现多出一个 USB 声卡。我们用 DS1 来指示 USB 是否连接成功,并在
液晶上显示 USB 连接状况,如果成功连接,DS1 会亮,此时,我们可以将耳机插入开发板的
PHONE 端口,听到来自电脑的音频信号,同时通过两个按键可以调节音量:按 KEY0 可以增
大音量,KEY2 可以减少音量。同样我们还是用 DS0 来指示程序正在运行。
所要用到的硬件资源如下:
1) 指示灯 DS0 、DS1
2) KEY0 和 KEY2 两个按键
3) 串口
4) LCD 模块
5) USB SLAVE 接口
6) WM8978
这几个部分,在之前的实例中都已经介绍过了,我们在此就不多说了。这里再次提醒大家,
P10 的连接,要通过跳线帽连接 Pa11 和 D-以及 PA12 和 D+。
62.3 软件设计
本章,我们在第五十二章 音乐播放器实验(实验 47)的基础上修改,先打开实验 47 的工
程,在 HARDWARE 文件夹所在文件夹下新建一个 USB 的文件夹,同上一章一样,对照官方
AUDIO 例子,将相关文件拷贝到 USB 文件夹下。
然后,我们在工程里面去掉一些不必要的代码,并添加 USB 相关代码,最终得到如图 62.3.1
所示的工程:



图 59.3.1 USB 声卡工程截图
可以看到,USB 部分代码,同上一章的在结构上是一模一样的,只是.c 文件稍微有些变化。
同样,我们移植需要修改的代码,就是 USB_APP 里面的这四个.c 文件了。
其中 usb_bsp.c 和 usbd_usr.c 的代码,和上一章基本一样,可以用上一章的代码直接替换即
可正常使用。
usb_desc.c 代码,同上一章不一样,上一章描述符是大容量存储设备,本章变成了 USB 声
卡了,所以直接用 ST 官方的就行。
最后 stm324xg_usb_audio_codec.c,这里面的代码,是重点要修改的,该文件是配合 USB
声卡的 WM8994 底层驱动相关代码,官方 STM32F7xx 的板子,用的是 WM8994,而我们用的
是 WM8978,需要修改代码,修改后代码如下:
u8 volume=0;

//当前音量

vu8 audiostatus=0;

//bit0:0,暂停播放;1,继续播放

vu8 saiplaybuf=0;

//即将播放的音频帧缓冲编号

vu8 saisavebuf=0;

//当前保存到的音频缓冲编号

#define AUDIO_BUF_NUM

100

//由于采用的是 USB 同步传输数据播放

//而 STM32 SAI 的速度和 USB 传送过来数据的速度存在差异,比如在 48Khz 下,实
//际 SAI 是低于 48Khz(47.991Khz)的,所以电脑送过来的数据流,会比 STM32 播放
//速度快,缓冲区写位置追上播放位置(saisavebuf==saiplaybuf)时,就会出现
//混叠.设置尽量大的 AUDIO_BUF_NUM 值,可以尽量减少混叠次数.
u8 *saibuf[AUDIO_BUF_NUM]; // 音频缓冲帧 , 占 用 内 存 数
=AUDIO_BUF_NUM*AUDIO_OUT_PACKET 字节

//音频数据 SAI DMA 传输回调函数
void audio_sai_dma_callback(void)
{
IF((saiplaybuf==saisavebuf)&&audiostatus==0)
{
SAI_Play_Stop();
}else
{
saiplaybuf++;
if(saiplaybuf>(AUDIO_BUF_NUM-1))saiplaybuf=0;
if(DMA2_Stream1->CR&(1<<19))
{
DMA2_Stream1->M0AR=(u32)saibuf[saiplaybuf];//指向下一个 buf
}
else
{
DMA2_Stream1->M1AR=(u32)saibuf[saiplaybuf];//指向下一个 buf
}
}
}
//配置音频接口
//OutputDevice:输出设备选择,未用到.
//Volume:音量大小,0~100
//AudioFreq:音频采样率
uint32_t EVAL_AUDIO_Init(uint16_t OutputDevice, uint8_t Volume, uint32_t AudioFreq)
{
u16 t=0;
for(t=0;t<AUDIO_BUF_NUM;t++) //内存申请
{
saibuf[t]=mymalloc(SRAMIN,AUDIO_OUT_PACKET);
}
if(saibuf[AUDIO_BUF_NUM-1]==NULL) //内存申请失败
{
printf("Malloc Error!\r\n");
for(t=0;t<AUDIO_BUF_NUM;t++)myfree(SRAMIN,saibuf[t]);
  return 1;
}
SAIA_Init(SAI_modemASTER_TX,SAI_clockSTROBING_RISINGEDGE,
SAI_DATASIZE_16);//设置 SAI,主发送,16 位数据
SAIA_SampleRate_Set(AudioFreq); //设置采样率
EVAL_AUDIO_VolumeCtl(Volume); //设置音量
SAIA_TX_DMA_Init(saibuf[0],saibuf[1],AUDIO_OUT_PACKET/2,1);
//配置 TX DMA,16 位
sai_tx_callback=audio_sai_dma_callback; //回调函数指向 audio_sai_dma_callback
SAI_Play_Start(); //开启 DMA
printf("EVAL_AUDIO_Init:%d,%d\r\n",Volume,AudioFreq);
return 0;
}
//开始播放音频数据
//pBuffer:音频数据流首地址指针
//Size:数据流大小(单位:字节)
uint32_t EVAL_AUDIO_Play(uint16_t* pBuffer, uint32_t Size)
{
printf("EVAL_AUDIO_Play:%x,%d\r\n",pBuffer,Size);
return 0;
}
//暂停/恢复音频流播放
//Cmd:0,暂停播放;1,恢复播放
//返回值:0,成功
// 其他,设置失败
uint32_t EVAL_AUDIO_PauseResume(uint32_t Cmd)
{
if(Cmd==AUDIO_PAUSE)
{
audiostatus=0;
}else
{
audiostatus=1;
SAI_Play_Start(); //开启 DMA
}
return 0;
}
//停止播放
//Option:控制参数,1/2,详见:CODEC_PDWN_HW 定义
//返回值:0,成功
// 其他,设置失败
uint32_t EVAL_AUDIO_Stop(uint32_t Option)
{
printf("EVAL_AUDIO_Stop:%d\r\n",Option);
audiostatus=0;
return 0;
}
//音量设置
//Volume:0~100
//返回值:0,成功
// 其他,设置失败
uint32_t EVAL_AUDIO_VolumeCtl(uint8_t Volume)
{
volume=Volume;
WM8978_HPvol_Set(volume*0.63,volume*0.63);
WM8978_SPKvol_Set(volume*0.63);
return 0;
}
//静音控制
//Cmd:0,正常
// 1,静音
//返回值:0,正常
// 其他,错误代码
uint32_t EVAL_AUDIO_Mute(uint32_t Cmd)
{
if(Cmd==AUDIO_MUTE_ON)
{
WM8978_HPvol_Set(0,0);
WM8978_SPKvol_Set(0);
}else
{
WM8978_HPvol_Set(volume*0.63,volume*0.63);
WM8978_SPKvol_Set(volume*0.63);
}
return 0;
}
//播放音频数据流
//Addr:音频数据流缓存首地址
//Size:音频数据流大小(单位:haRF word,也就是 2 个字节)
void Audio_MAL_Play(uint32_t Addr, uint32_t Size)
{
u16 i;
u8 t=saisavebuf;
  u8 *p=(u8*)Addr;
u8 curplay=saiplaybuf; //当前正在播放的缓存帧编号
if(curplay)curplay--;
else curplay=AUDIO_BUF_NUM-1;
audiostatus=1;
t++;
if(t>(AUDIO_BUF_NUM-1))t=0;
if(t==curplay) //写缓存碰上了当前正在播放的帧,跳到下一帧
{
t++;
if(t>(AUDIO_BUF_NUM-1))t=0;
printf("bad position:%d\r\n",t);
}
saisavebuf=t;
for(i=0;i<Size*2;i++)
{
saibuf[saisavebuf]=p;
}
SAI_Play_Start(); //开启 DMA这里特别说明一下,USB AUDIO 我们使用的是 USB 同步数据传输,音频采样率固定为:
48Khz(通过 USBD_AUDIO_FREQ 设置,在 usbd_conf.h 里面),这样,USB 传输过来的数据
都是 48Khz 的音频数据流,STM32F767 必须以同样的频率传输数据给 SAI,以同步播放音乐。
但是,STM32F767 我们采用的是外部 25M 时钟倍频后分频作为 SAI 时钟的,在使能主时
钟(MCK)输出的时候,只能以 47.991Khz 频率播放,稍微有点误差,这样,导致 USB 送过
来的数据,会比传输给 SAI 的数据快一点点,如果不做处理,就很容易产生数据混叠,产生噪
音。
因此,我们这里提供了一个简单的解决办法:建立一个类似 FIFO 结构的缓冲数组,USB
传输过来的数据全部存放在这些数组里面,同时通过 SAI DMA 双缓冲机制,播放这些数组里
面的音频数据,当混叠发生时(USB 传过来的数据,赶上 SAI 播放的数据了),直接越过当前
正在播放的数组,继续保存。这样,虽然会导致一些数据丢失(混叠时),但是避免了混叠,保
证了良好的播放效果(听不到噪音),同时,数组个数越多,效果就越好(越不容易混叠)。
以上代码 AUDIO_BUF_NUM 就是我们定义的 FIFO 结构数组的大小,越大,效果越好,
这里我们定义成 100,每个数组的大小由音频采样率和位数决定,计算公式为:
(USBD_AUDIO_FREQ * 2 * 2) /1000
单位为字节,其中 USBD_AUDIO_FREQ 即音频采样率:48Khz,这样,每个数组大小就
是 192 字节。100 个数组,我们总共用了 19200 字节。
audio_sai_dma_callback 函数是 SAI 播放音频的回调函数,完成 SAI 数据流的发送(切换
DMA 源地址),其他函数则基本都是在 usbd_audio_out_if.c 里面被调用,这里就不再详细介绍
了。
最后在 main.c 里面,我们修改 main 函数如下:
USB_OTG_CORE_HANDLE USB_OTG_dev;
extern vu8 bDeviceState; //USB 连接 情况
extern u8 volume; //音量(可通过按键设置)
int main(void)
{
u8 key;
u8 t=0;
u8 Divece_STA=0XFF;

Cache_Enable(); //打开 L1-Cache
HAL_Init(); //初始化 HAL 库
Stm32_Clock_Init(432,25,2,9); //设置时钟,216Mhz
delay_init(216); //延时初始化
uart_init(115200); //串口初始化
LED_Init(); //初始化 LED
KEY_Init(); //初始化按键
SDRAM_Init(); //初始化 SDRAM
LCD_Init(); //初始化 LCD
W25QXX_Init(); //初始化 W25Q256
PCF8574_Init(); //初始化 PCF8574
WM8978_Init(); //初始化 WM8978
WM8978_ADDA_Cfg(1,0); //开启 DAC
WM8978_Input_Cfg(0,0,0); //关闭输入通道
WM8978_Output_Cfg(1,0); //开启 DAC 输出
my_mem_init(SRAMIN); //初始化内部内存池
my_mem_init(SRAMEX); //初始化外部内存池
my_mem_init(SRAMDTCM); //初始化 DTCM 内存池

POINT_COLOR=RED;//设置字体为红色
LCD_ShowString(30,50,200,16,16,"Apollo STM32F4/F7");
LCD_ShowString(30,70,200,16,16,"USB Sound Card TEST");
LCD_ShowString(30,90,200,16,16,"ATOM@ALIENTEK");
LCD_ShowString(30,110,200,16,16,"2016/8/10");
LCD_ShowString(30,130,200,16,16,"KEY2:Vol- KEY0:vol+");
POINT_COLOR=BLUE;//设置字体为蓝色
LCD_ShowString(30,160,200,16,16,"VOLUME:"); //音量显示
LCD_ShowxNum(30+56,160,DEFAULT_VOLUME,3,16,0X80);//显示音量
LCD_ShowString(30,180,200,16,16,"USB Connecting...");//提示正在建立连接
USBD_Init(&USB_OTG_dev,USB_OTG_FS_CORE_ID,&USR_desc,
&AUDIO_cb,&USR_cb);
while(1)
{
key=KEY_Scan(1);//支持连按
if(key)
{
  if(key==KEY0_PRES) //KEY0 按下,音量增加
{
volume++;
if(volume>100)volume=100;
}else if(key==KEY2_PRES)//KEY2 按下,音量减少
{
if(volume)volume--;
else volume=0;
}
EVAL_AUDIO_VolumeCtl(volume);
LCD_ShowxNum(30+56,160,volume,3,16,0X80);//显示音量
delay_ms(20);
}
if(Divece_STA!=bDeviceState)//状态改变了
{
if(bDeviceState==1)
{
LED1(0);
LCD_ShowString(30,180,200,16,16,
"USB Connected ");//提示 USB 连接已经建立
}else
{
LED1(1);
LCD_ShowString(30,180,200,16,16,
"USB DisConnected ");//提示 USB 连接失败
}
Divece_STA=bDeviceState;
}
delay_ms(20);
t++;
if(t>10)
{
t=0;
LED0_Toggle;
} }
}此部分代码比较简单,同上一章一样定义了 USB_OTG_dev 结构体,然后通过 USBD_Init
初始化 USB,不过本章实现的是 USB 声卡功能。本章我们保留了原例程(实验 47)的 USMART
部分,同样可以通过串口 1 设置 WM8978 相关参数。
其他部分我们就不详细介绍了,软件设计部分就为大家介绍到这里。
62.4 下载验证
在代码编译成功之后,我们通过下载代码到阿波罗 STM32 开发板上,在 USB 配置成功后
(注意:USB 数据线,要插在 USB_SLAVE 端口!不是 USB_232 端口!另外,USB_HOST 接
口不要插任何外设!),LCD 显示效果如图 62.4.1 所示:



图 62.4.1 USB 连接成功
此时,电脑提示发现新硬件,并自动完成驱动安装,如图 62.4.2 所示:



图 62.4.2 电脑找到 ALIENTEK USB 声卡
等 USB 配置成功后,DS1 常亮,DS0 闪烁,并且在设备管理器声音、视频和游戏控制器
里面看到多了 ALIENTEK STM32F4/F7 USB AUDIO 设备,如图 62.4.3 所示:



图 62.4.3 设备管理器找到 ALIENTEK USB 声卡
然后,设置 ALIENTEK STM32F4/F7 USB AUDIO 为电脑的默认播放设备,则电脑的所有
声音都被切换到开发板输出,将耳机插入阿波罗 STM32 开发板的 PHONE 端口,即可听到来自
电脑的声音。通过按键 KEY0/KEY2 可以增大/减少音量,默认音量设置的是 65,大家可以自己
调节(范围:0~100)。
0
分享淘帖 显示全部楼层

评论

高级模式
您需要登录后才可以回帖 登录 | 注册

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容图片侵权或者其他问题,请联系本站作侵删。 侵权投诉
发资料
关闭

站长推荐 上一条 /7 下一条

快速回复 返回顶部 返回列表