ITPub博客

首页 > 嵌入式/内核开发 > 嵌入式/内核开发 > 《STM32H562开发指南 V1.0 》第五十七章 USB声卡(Slave)实验

《STM32H562开发指南 V1.0 》第五十七章 USB声卡(Slave)实验

原创 嵌入式/内核开发 作者:正点原子 时间:2026-01-10 09:18:38 0 删除 编辑

第五十七章 USB声卡(Slave)实验


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

       57.1 USB声卡简介

       57.2 硬件设计

       57.3 软件设计

       57.4 下载验证


        57.1 USB声卡简介

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

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


        57.2 硬件设计


       1. 例程功能

       本节实验功能简介:先将开发板通过USB_SLAVE连接电脑,开发板开机的时候先提示一些信息,然后开始USB配置,在配置成功之后就可以在电脑上发现多出一个声卡。此时,我们可以通过耳机或开发板板载喇叭,听到来自电脑的音频信号。LED0闪烁,提示程序运行。


       2. 硬件资源

       1)LED灯  LED0 - PB4

       2)正点原子2.8/3.5/4.3/7寸TFTLCD模块(仅限MCU屏,16位8080并口驱动)

       3)USB_SLAVE接口(D-/D+连接在PA11/PA12上)

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

       5)SAI音频接口


        57.3 程序设计


       57.3.1 程序流程图


图57.3.1.1 USB声卡实验程序流程图


       57.3.2 程序解析

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

       本章,我们在实验37 音乐播放器实验的基础上修改,把不需要的文件从工程中移除,并移植USB AUDIO相关驱动,最终得到如图57.3.2.1所示的工程:


图57.3.2.1 USB声卡(Slave)实验工程分组截图


       1. USB驱动代码

       app_usbx_device.c代码,和上一章基本一样,具体请查看源码,这里就不贴出来了。

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

       ux_device_audio.c代码,是USB声卡的接口文件,具体源码如下:

/**
 * @brief   AUDIO流读变更
 * @param   stream: Audio流实例
 * @param   alternate_setting: 状态
 * @retval  无
 */
VOID ux_device_audio_stream_read_change(struct 
UX_DEVICE_CLASS_AUDIO_STREAM_STRUCT *stream, ULONG alternate_setting)
{
    if (alternate_setting == 0)
    {
        sai1_play_stop();
    }
    else
    {
        sai1_play_start();
        ux_device_class_audio_reception_start(stream);
    }
}
 
/**
 * @brief   SAI DMA节点传输完成回调函数
 * @param   node: 传输完成的DMA节点编号
 * @retval  无
 */
void ux_device_audio_sai_dma_callback(uint8_t node)
{
    audio_play_buffer_write_index = node + 1;
if (audio_play_buffer_write_index == (sizeof(audio_play_buffer) 
/ sizeof(audio_play_buffer[0])))
    {
        audio_play_buffer_write_index = 0;
    }
}
 
/**
 * @brief   AUDIO流读完成
 * @param   stream: Audio流实例
 * @param   length: 数据长度
 * @retval  无
 */
VOID ux_device_audio_stream_frame_read_done(struct 
UX_DEVICE_CLASS_AUDIO_STREAM_STRUCT *stream, ULONG length)
{
    UCHAR *frame_data;
    ULONG frame_length;
    
    ux_device_class_audio_read_frame_get(stream, &frame_data, &frame_length);
    
    if (length != 0)
    {
        ux_utility_memory_copy(audio_play_buffer[audio_play_buffer_write_index], 
frame_data, frame_length);
    }
    
    ux_device_class_audio_read_frame_free(stream);
}
 
/**
 * @brief   激活AUDIO类
 * @param   audio: AUDIO类实例
 * @retval  无
 */
VOID ux_device_audio_instance_activate(VOID *audio)
{
    UX_PARAMETER_NOT_USED(audio);
    
    /* 配置ES8388 */
    es8388_adda_cfg(1, 0);
    es8388_output_cfg(1, 1);
    es8388_hpvol_set(25);
    es8388_spkvol_set(25);
    es8388_sai_cfg(0, 3);
    
    /* 配置SAI */
sai1_saia_init(SAI_MODEMASTER_TX, SAI_CLOCKSTROBING_RISINGEDGE, 
SAI_DATASIZE_16);
    sai1_samplerate_set(USB_AUDIO_CONFIG_PLAY_SAMPLING_FREQUENCY);
    sai1_tx_callback = ux_device_audio_sai_dma_callback;
sai1_tx_dma_init(audio_play_buffer[0], audio_play_buffer[1], 
AUDIO_PLAY_BUFFER_SIZE / 2, 1);
}
 
/**
 * @brief   停用AUDIO类
 * @param   audio: AUDIO类实例
 * @retval  无
 */
VOID ux_device_audio_instance_deactivate(VOID *audio)
{
    UX_PARAMETER_NOT_USED(audio);
}
 
/**
 * @brief   AUDIO类控制
 * @param   audio: AUDIO类实例
 * @param   transfer: 控制请求
 * @retval  状态
 */
UINT ux_device_audio_control_process(struct UX_DEVICE_CLASS_AUDIO_STRUCT
*audio, UX_SLAVE_TRANSFER *transfer)
{
    UX_DEVICE_CLASS_AUDIO20_CONTROL audio_control[1];
    UX_DEVICE_CLASS_AUDIO20_CONTROL_GROUP group;
    
audio_control[0].ux_device_class_audio20_control_cs_id = 
USB_AUDIO_CONFIG_PLAY_CLOCK_SOURCE_ID;
audio_control[0].ux_device_class_audio20_control_sampling_frequency = 
USB_AUDIO_CONFIG_PLAY_SAMPLING_FREQUENCY;
audio_control[0].ux_device_class_audio20_control_fu_id = 
USB_AUDIO_CONFIG_PLAY_UNIT_FEATURE_ID;
    audio_control[0].ux_device_class_audio20_control_mute[0] = 0;
    audio_control[0].ux_device_class_audio20_control_volume_min[0] = 0;
    audio_control[0].ux_device_class_audio20_control_volume_max[0] = 100;
    audio_control[0].ux_device_class_audio20_control_volume_res[0] = 1;
    audio_control[0].ux_device_class_audio20_control_volume[0] = 70;
    group.ux_device_class_audio20_control_group_controls_nb = 1;
    group.ux_device_class_audio20_control_group_controls = audio_control;
if (ux_device_class_audio20_control_process(audio, transfer, &group) != 
UX_SUCCESS)
    {
        return UX_ERROR;
    }
    
    switch (audio_control[0].ux_device_class_audio20_control_changed)
    {
        case UX_DEVICE_CLASS_AUDIO20_CONTROL_MUTE_CHANGED:
        {
            es8388_output_cfg(0, 0);
            break;
        }
        case UX_DEVICE_CLASS_AUDIO20_CONTROL_VOLUME_CHANGED:
        {
            es8388_output_cfg(1, 1);
            es8388_hpvol_set(audio_control[0]
                      .ux_device_class_audio20_control_volume[0] * 0.3);
            es8388_spkvol_set(audio_control[0]
.ux_device_class_audio20_control_volume[0] * 0.3);
            break;
        }
        case UX_DEVICE_CLASS_AUDIO20_CONTROL_FREQUENCY_CHANGED:
        default:
        {
            break;
        }
    }
    
    return UX_SUCCESS;
}

       该部分代码包括激活AUDIO类、AUDIO类控制和AUDIO流控制等函数。

       2. main.c代码

       下面是main.c的代码,具体如下: 

int main(void)
{
    uint8_t res;
    uint32_t t = 0;
 
    HAL_Init();                          /* HAL库初始化 */
    HAL_ICACHE_Enable();                 /* 使能指令缓存 */
    sys_stm32_clock_init(250, 4, 2, 2); /* 配置时钟,250MHz */
    delay_init(250);                     /* 初始化延时 */
    usart_init(115200);                  /* 初始化串口 */
    led_init();                          /* 初始化LED */
    lcd_init();                          /* 初始化LCD */
    es8388_init();                       /* 初始化ES8388 */
    
    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);
    
    /* USB设备初始化 */
    res = app_usbx_device_init();
    while (res != 0)
    {
        lcd_show_string(30, 110, 200, 16, 16, "USB Device Init Failed!", RED);
        delay_ms(500);
        lcd_show_string(30, 110, 200, 16, 16, "Please Check!          ", RED);
        delay_ms(500);
    }
    
    while (1)
    {
        /* 运行独立模式下的USBX系统任务 */
        ux_system_tasks_run();
        
        if (++t == 100000)
        {
            t = 0;
            LED0_TOGGLE();
        }
    }
}

       该部分代码功能,先进行系统和外设初始化,然后一定时间就运行独立模式下的USBX系统任务。


        57.4 下载验证

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


图57.4.1 USB声卡实验


       此时,LED0闪烁,表示程序在运行中。电脑提示发现新硬件,并自动完成驱动安装。等USB配置成功后,可以在设备管理器→音频输入和输出里面看到多了设备,如图57.4.2所示:


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


       然后,设置STM32 AUDIO20 STREAMING为电脑的默认播放设备,则电脑的所有声音都被切换到开发板输出,将耳机插入Mini STM32H562开发板的PHONE端口,即可听到来自电脑的声音,开发板板载喇叭也可以听到来自电脑的声音。


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

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

注册时间:2024-08-30

  • 博文量
    588
  • 访问量
    764248