ITPub博客

首页 > 嵌入式/内核开发 > 嵌入式/内核开发 > 《STM32H7R7开发指南 V1.1 》第六十九章 USB声卡(Slave)实验

《STM32H7R7开发指南 V1.1 》第六十九章 USB声卡(Slave)实验

原创 嵌入式/内核开发 作者:正点原子 时间:2025-10-25 09:21:03 0 删除 编辑

第六十九章 USB声卡(Slave)实验


       上一章我们向大家介绍了如何利用STM32H7R7的USB接口来做一个USB读卡器,本章我们将利用STM32H7R7的USB来做一个声卡。本章分为如下几个部分:

       69.1 USB声卡简介

       69.2 硬件设计

       69.3 软件设计

       69.4 下载验证


        69.1 USB声卡简介

       USB声卡依赖于USB音频类(USB Audio Class),一个像USB通用的数据接口,可以实现数字音频的数据传输。

       正点原子STM32H7R7开发板板载了一颗高性能CODEC芯片:ES8388,我们可以利用STM32H7R7的SAI接口,控制ES8388播放音乐,同样,如果结合STM32H7R7的USB功能,就可以实现一个USB声卡。

       同上一章一样,我们直接移植官方的USB AUDIO例程,官方例程路径:8,STM32参考资料→1,STM32CubeH7RS固件包 →STM32Cube_FW_H7RS_V1.0.0àProjects →STM32H7S78-DK →Applications →USB_Device →Audio_Standalone,该例程采用USB同步传输来传输音频数据流并且支持某些控制命令(比如静音控制),例程仅支持USB FS模式(不支持HS),同时例程不需要特殊的驱动支持,大多数操作系统直接就可以识别。


        69.2 硬件设计


       1. 例程功能

       本节实验功能简介:先将开发板通过USB_SLAVE连接电脑,开发板开机的时候先提示一些信息,然后开始USB配置,在配置成功之后就可以在电脑上发现多出一个声卡。我们在LCD液晶屏上显示USB的连接状态,如果连接成功,此时,我们可以通过耳机或开发板板载喇叭,可以听到来自电脑电脑的音频信号,还可以通过两个按键调节ES8388音量:按键KEY0可以增大音量,按键KEY2可以减少音量。LED0闪烁,提示程序运行。


       2. 硬件资源


       1)LED灯

               LED0 – PD14

               LED1 – PC0


       2)独立按键

              KEY0 – PE9

              KEY1 – PE8

              KEY2 – PE7

              KEY_UP – PC13  (程序中的宏名:WK_UP)


       3)串口1 (PB14/PB15连接在板载USB转串口芯片CH340上面)


       4)正点原子2.8/3.5/4.3/7/10寸TFTLCD模块(包括MCU屏和RGB屏,都支持)


       5)ES8388音频CODEC芯片,通过SAI驱动


       6)SAI音频接口


       7)USB_SLAVE接口(D-/D+连接在PM5/PM6上)


       8)HyperRAM芯片


        69.3 程序设计


       69.3.1 程序解析

       这里我们只讲解核心代码,详细的源码请大家参考光盘本实验对应源码。

       本章,我们在实验48 音乐播放器实验的基础上修改,把不需要的文件从工程中移除,在Middlewares文件夹所在文件夹下新建一个USB的文件夹,同上一章一样,对照官方AUDIO例子,将相关文件拷贝到USB文件夹下。由于官方提供的USB_APP文件不太好用,我们正点原子团队直接编写了一套USB_APP代码,读者可以直接拷贝我们例程中的USB_APP文件夹,即:实验58 USB声卡(Slave)实验àMiddlewaresàUSBàUSB_APP。

       然后,我们在工程里面去掉一些不必要的代码,并添加USB相关代码,最终得到如图69.3.2.1所示的工程:


图69.3.2.1 USB声卡工程截图


       1. USB驱动代码

       可以看到,USB部分代码,同上一章的在结构上是一模一样的,只是.c文件稍微有些变化。同样,我们移植需要修改以下这几个文件中的代码。

       usbd_conf.c代码,和上一章基本一样,可以用上一章的代码直接替换即可正常使用,但需要注意的是需要在usbd_conf.h中定义音频频率,代码如下。

/* AUDIO Class Config */
/* ST USB库目前只支持这一个采样率, 设置其他采样率无法正常工作 */
#define USBD_AUDIO_FREQ                       48000U

       usbd_desc.c/usbd_desc.h代码,同上一章不一样,上一章描述符是大容量存储设备,本章变成了USB声卡了,所以直接用ST官方USB Audio例程的就行。

       usbd_audio.c代码,从ST官方USB Audio例程的AUDIO文件里直接复制过来。这个文件不需要做太多修改,这里我们就不做介绍了,详见本例程源码。

       usbd_audio_if.h代码,需要添加声明用于更新音频的回调函数,代码如下。

void BSP_AUDIO_OUT_TransferComplete_CallBack(void);

       usbd_audio_if.c代码,是我们重点要修改的,该文件是配合USB声卡的WM8994底层驱动相关代码,官方的板子,用的是WM8994,而我们用的是ES8388, 正点原子团队对该文件进行了重写,大家可直接从例程中复制,代码如下:

/**
  * @brief  Audio_Init
  *         Initializes the AUDIO media low layer
  * @param  None
  * @retval Result of the operation: USBD_OK if all operations are OK else
 USBD_FAIL
  */
static int8_t Audio_Init(uint32_t  AudioFreq, uint32_t Volume, uint32_t options)
{
    UNUSED(options);
    
    es8388_adda_cfg(1, 0);
    es8388_output_cfg(1, 1);
    es8388_hpvol_set(Volume * 0.3);
    es8388_spkvol_set(Volume * 0.3);
    es8388_sai_cfg(0, 3);
sai1_saia_init(SAI_MODEMASTER_TX, SAI_CLOCKSTROBING_RISINGEDGE, 
SAI_DATASIZE_16);
    sai1_samplerate_set(AudioFreq);
    sai1_tx_callback = audio_sai_dma_callback;
    
    return USBD_OK;
}
/**
  * @brief  Audio_AudioCmd
  *         AUDIO command handler
  * @param  Buf: Buffer of data to be sent
  * @param  size: Number of data to be sent (in bytes)
  * @param  cmd: command opcode
  * @retval Result of the operation: USBD_OK if all operations are OK
  else USBD_FAIL
  */
static int8_t Audio_AudioCmd(uint8_t *pbuf, uint32_t size, uint8_t cmd)
{
    switch (cmd)
    {
        case AUDIO_CMD_START:
        {
            sai1_tx_dma_init(pbuf, 0, size, 1);
            break;
        }
        case AUDIO_CMD_PLAY:
        {
            sai1_play_start();
            break;
        }
        case AUDIO_CMD_STOP:
        {
            sai1_play_stop();
            break;
        }
    }
    
    return USBD_OK;
}
 
/**
  * @brief  Audio_VolumeCtl
  * @param  vol: volume level (0..100)
  * @retval Result of the operation: USBD_OK if all operations are OK else
 USBD_FAIL
  */
static int8_t Audio_VolumeCtl(uint8_t vol)
{
    es8388_hpvol_set(vol * 0.3);
    es8388_spkvol_set(vol * 0.3);
    
    return USBD_OK;
}
 
/**
  * @brief  Audio_MuteCtl
  * @param  cmd: vmute command
  * @retval Result of the operation: USBD_OK if all operations are OK else
USBD_FAIL
  */
static int8_t Audio_MuteCtl(uint8_t cmd)
{
    if (cmd != 0)
    {
        es8388_output_cfg(0, 0);
    }
    else
    {
        es8388_output_cfg(1, 1);
    }
    
    return USBD_OK;
}

       本例程工作的时候,当USB连接成功,并完成识别以后,会先调用Audio_Init函数,设置音频接口相关参数,包括:ES8388的SAI配置、音量设置和SAI DMA发送完成中断回调函数等。

       然后,当上电后首 次有音乐输出的时候,会先调用Audio_AudioCmd函数,对SAI的DMA进行配置(使用单次模式,且单缓冲传输),然后启动SAI DMA传输,开始播放电脑的声音。然后,当DMA传输完成后,会自动停止DMA(单次模式),并通过audio_sai_dma_callback函数调用USBD_AUDIO_Sync函数,通知USB数据已经处理完成,等待USB传入新的数据包,经过处理如果有新的音频数据,则会调用Audio_AudioCmd函数,重启DMA传输,开启下一次数据DMA传送,然后DMA传输完成又会进入audio_sai_dma_callback函数,依次循环。

       这样,就实现了USB传过来的音频数据,通过SAI DMA传输出去,最终通过ES8388输出给耳机或开发板板载喇叭,实现USB声卡功能。

       USB AUDIO相关代码,就给大家介绍到这里,详细的介绍,请大家参考:UM1734(STM32Cube USB device library).pdf这个文档。


       2. main.c代码

       下面是main.c的程序,具体如下: 

/* USBD句柄 */
USBD_HandleTypeDef g_usbd_handle = {0};
 
int main(void)
{
    uint8_t t = 0;
    uint8_t key;
    uint8_t volume;
    uint8_t conn_sta;
    
    sys_mpu_config();                   /* 配置MPU */
    sys_cache_enable();                 /* 使能Cache */
    HAL_Init();                         /* 初始化HAL库 */
    sys_stm32_clock_init(300, 6, 2);    /* 配置时钟,600MHz */
    delay_init(600);                    /* 初始化延时 */
    usart_init(115200);                 /* 初始化串口 */
    led_init();                         /* 初始化LED */
    hyperram_init();                    /* 初始化HyperRAM */
    lcd_init();                         /* 初始化LCD */
    key_init();                         /* 初始化按键 */
    es8388_init();                      /* 初始化ES8388 */
    my_mem_init(SRAMIN);                /* 初始化AXI-SRAM1~4内存池 */
    my_mem_init(SRAMEX);                /* 初始化XSPI2 HyperRAM内存池 */
    my_mem_init(SRAM12);                /* 初始化AHB-SRAM1~2内存池 */
    my_mem_init(SRAMDTCM);              /* 初始化DTCM内存池 */
    my_mem_init(SRAMITCM);              /* 初始化ITCM内存池 */
    
    lcd_show_string(30, 50, 200, 16, 16, "STM32", RED);
    lcd_show_string(30, 70, 200, 16, 16, "USB Audio Card TEST", RED);
    lcd_show_string(30, 90, 200, 16, 16, "ATOM@ALIENTEK", RED);
    lcd_show_string(30, 110, 200, 16, 16, "KEY0: Vol+", RED);
    lcd_show_string(30, 130, 200, 16, 16, "KEY2: Vol-", RED);
    
    volume = AUDIO_DEFAULT_VOLUME;
    lcd_show_string(30, 150, 200, 16, 16, "Volume: ", RED);
    lcd_show_num(94, 150, AUDIO_DEFAULT_VOLUME, 3, 16, BLUE);
    lcd_show_string(30, 170, 200, 16, 16, "USB Disconnected", BLUE);
    
    USBD_Init(&g_usbd_handle, &AUDIO_Desc, DEVICE_FS);
    USBD_RegisterClass(&g_usbd_handle, USBD_AUDIO_CLASS);
    USBD_AUDIO_RegisterInterface(&g_usbd_handle, &USBD_AUDIO_fops);
    USBD_Start(&g_usbd_handle);
    
    while (1)
    {
        key = key_scan(1);
        if (key != NONE_PRES)
        {
            if (key == KEY0_PRES)
            {
                if (++volume > 100)
                {
                    volume = 100;
                }
            }
            else if (key == KEY2_PRES)
            {
                if (volume != 0)
                {
                    volume--;
                }
            }
            USBD_AUDIO_fops.VolumeCtl(volume);
            lcd_show_num(94, 150, volume, 3, 16, BLUE);
        }
        
        if (conn_sta != g_usb_conn_state)
        {
            conn_sta = g_usb_conn_state;
            
            if (g_usb_conn_state != 0)
            {
                lcd_show_string(30, 170, 200, 16, 16, "USB Connected  ", BLUE);
            }
            else
            {
                lcd_show_string(30, 170, 200, 16, 16, "USB Disconnected",BLUE);
            }
        }
        
        if (++t == 20)
        {
            t = 0;
            LED0_TOGGLE();
        }
        
        delay_ms(10);
    }
}

       此部分代码比较简单,同上一章一样定义了USBD_Device结构体,然后通过USBD_Init等函数初始化USB,不过本章实现的是USB声卡功能。

       其他部分我们就不详细介绍了,软件设计部分就为大家介绍到这里。


        69.4 下载验证

       在代码编译成功之后,我们通过下载代码到STM32H7R7开发板上,在USB配置成功后(注意:USB数据线,要插在USB_SLAVE端口!不是USB_UART端口!另外,USB_HOST接口不要插任何外设!),LCD显示效果如图69.4.1所示:


图69.4.1 USB连接成功


       此时,电脑提示发现新硬件,并自动完成驱动安装。

       等USB配置成功后,LED1常亮,LED0闪烁,并且在设备管理器à声音、视频和游戏控制器里面看到多了设备,如图69.4.2所示:


图69.4.2 设备管理器找到STM32 USB声卡


       然后,设置STM32 AUDIO Steaming in FS Mode为电脑的默认播放设备,则电脑的所有声音都被切换到开发板输出,将耳机插入STM32H7R7开发板的PHONE端口,即可听到来自电脑的声音,开发板板载喇叭也可以听到来自电脑的声音。通过按键KEY0/KEY2可以增大/减少音量,默认音量设置的是70,大家可以自己调节(范围:0~100)。


来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/70040860/viewspace-3098603/ ,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论

注册时间:2024-08-30

  • 博文量
    588
  • 访问量
    764568