rt-thread audio框架移植stm32 adc+dac,对接cherryusb uac,进行录音和播放

发布于:2025-08-19 ⋅ 阅读:(15) ⋅ 点赞:(0)

D1 参考

rt-thread官方sdk中,正点原子stm32f429-atk-appollo的board中有audio文件夹,包括了mic/play的程序,wm8978的库文件
在这里插入图片描述

因为我们基于stm32h750内置adc+dac设计,所以不需要wm8978.c/h。只需要移植drv_sound.c和drv_mic.c

D2 工程名字和路径

在这里插入图片描述

D3 程序中所用到的关键c文件在工程中的路径

在这里插入图片描述
在这里插入图片描述

D4 drv_sound.c所作修改简单说明

  • 总的内容与前一篇文章完全一样,这里只说不同的地方
  • 定义了一个播放线程,用于与cherryusb uac交互。通过mailbox与play_thread_entry交互,通过uac中的usbd_audio_out_callback回调函数,将录音数据的nbytes发送给这里的play_thread_entry线程。
  • 在play_thread_entry线程中,通过rt_mb_recv函数,接收usbd_audio_out_callback发送来的nbytes,read_buffer以全局变量的方式访问,进行数据处理后,然后通过rt_device_write函数,将数据写入到sound0设备中。
  • 因为uac发送的双声道立体声,16bits signed。我们取左右声道的平均值,然后转换为12bits unsigned,再写入到sound0设备中。因为我们的dac player只有一个,而且是12bits的。
  • 注意!我们将usbd_ep_start_read的读,放到了play_thread_entry线程中(原来是在usbd_audio_out_callback), 从生产者与消费者模型角度上说,这样可以实现更好的同步效果。
/****************************************************************************
* @brief 录音线程, 与usb交互
****************************************************************************/
#include "usbd_core.h"
void play_thread_entry(void *parameter)
{

  #define AUDIO_OUT_PACKET 64
  #define AUDIO_OUT_EP 0x02
  extern uint8_t read_buffer[AUDIO_OUT_PACKET];
  uint16_t *pdata =rt_malloc(AUDIO_OUT_PACKET);

  rt_device_t sound_dev;
  rt_size_t written;
  
  // generate_sin_table();
  sound_dev = rt_device_find("sound0");
  rt_device_open(sound_dev, RT_DEVICE_OFLAG_WRONLY);


  while(1){
    if (rt_mb_recv(dac_mbox, &written, RT_WAITING_FOREVER) == RT_EOK)
    {
      for(int i=0; i<written/2; i++){
        /*取左右声道的平均值*/
        int32_t v=(((int16_t *)read_buffer)[2*i]+((int16_t*)read_buffer)[2*i+1])/2 + 32768;
        /*16bits signed => 12bits unsigned*/
        pdata[i]=(uint16_t)v>>4;
      }

      written = rt_device_write(sound_dev, 0, pdata, written/2);
      usbd_ep_start_read(0, AUDIO_OUT_EP, read_buffer, AUDIO_OUT_PACKET); /*在线程中启动下次读,同步效果更好*/
    }
  }
}

int play_thread()
{
    rt_thread_t thread;
    thread = rt_thread_create("play_thread", play_thread_entry, RT_NULL, 2048, 2, 10);
    if (thread != RT_NULL)
    {
        rt_thread_startup(thread);
    }
    return 0;
}
INIT_APP_EXPORT(play_thread);

D5 drv_mic.c所作修改简单说明

  • 总的内容与前一篇文章完全一样,这里只说不同的地方
  • 定义了一个录音线程,用于与cherryusb uac交互。通过rt_device_read函数,从mic0设备中读取数据,经过数据处理后,通过usbd_ep_start_write发送给usb。线程发送与usb的中断回调,目前直接通过标志变量ep_tx_busy_flag进行同步。其实更高级的做法是用rt-thread的完成量来进行同步。
  • 因为uac需要的双声道立体声,16bits signed。而我们的adc只有1个,而且是16bits unsigned。所以需要做转换。通过复制填充的方式,让左右声道填充相同的数据。
/****************************************************************************
* @brief 录音线程, 与usb交互
****************************************************************************/
#include "usbd_core.h"
static void record_thread_entry(void *parameter)
{
  extern volatile bool tx_flag;
  #define AUDIO_IN_PACKET 64
  #define AUDIO_IN_EP  0x81 
  extern volatile uint8_t ep_tx_busy_flag;
  extern uint8_t write_buffer[AUDIO_IN_PACKET];    


  rt_device_t mic_dev = rt_device_find("mic0");
  rt_device_open(mic_dev, RT_DEVICE_OFLAG_RDONLY);


  struct rt_audio_caps caps;
  caps.main_type = AUDIO_TYPE_INPUT;
  caps.sub_type  = AUDIO_DSP_PARAM;
  caps.udata.config.samplerate = 16000;
  caps.udata.config.channels = 1;
  caps.udata.config.samplebits = 16;
  rt_device_control(mic_dev, AUDIO_CTL_CONFIGURE, &caps); 


  uint16_t *buffer = (uint16_t *)rt_malloc(AUDIO_IN_PACKET/2);
  int16_t *pwbuffer=(int16_t *)write_buffer;
  while (1)
  {
    if (tx_flag) {
        rt_size_t size =  rt_device_read(mic_dev, 0, buffer, AUDIO_IN_PACKET/2); /*因为要复制扩倍, 因此只需要读一半的数量*/
        if (size)
        {
            for(int i = 0; i < size/2; i++) {
                pwbuffer[2*i] = (int16_t)(buffer[i] - 32768);
                pwbuffer[2*i+1] = (int16_t)(buffer[i] - 32768); /*2个声道, repeat填充*/
            }

            ep_tx_busy_flag = 1;
            usbd_ep_start_write(0, AUDIO_IN_EP, write_buffer, size*2);
            while(ep_tx_busy_flag){
            }
        }
    }
    else {
      rt_thread_delay(10);
    }
  }
}

int record_thread()
{
    rt_thread_t thread;
    thread = rt_thread_create("record_thread_entry", record_thread_entry, RT_NULL, 2048, 2, 10);

    if (thread != RT_NULL)
    {
        rt_thread_startup(thread);
    }
    return 0;
}
INIT_APP_EXPORT(record_thread);

D6 audio_v1_mic_speaker_multichan_template.c所作修改简单说明

  • 在usbd_audio_out_callback中,通过rt_mb_send发送nbytes变量值给play_thread_entry线程
  • 在usbd_audio_out_callback中,注意!我们注释了usbd_ep_start_read,因为我们在play_thread_entry线程中启动了下一次读取。这种同步效果更好。
  • usbd_audio_in_callback没有做修改,它的功能只是简单的设置ep_tx_busy_flag为false,用于同步。(其实可以考虑用rt-thread的完成量)
void usbd_audio_out_callback(uint8_t busid, uint8_t ep, uint32_t nbytes)
{
    // USB_LOG_RAW("actual out len:%d\r\n", nbytes);

    extern rt_mailbox_t dac_mbox;
    rt_mb_send(dac_mbox, nbytes);

    /* 继续启动下一次 USB 读取 */
    // usbd_ep_start_read(busid, AUDIO_OUT_EP, read_buffer, AUDIO_OUT_PACKET);
}

void usbd_audio_in_callback(uint8_t busid, uint8_t ep, uint32_t nbytes)
{
    // USB_LOG_RAW("actual in len:%d\r\n", nbytes);
    ep_tx_busy_flag = false;
}

D7 wavplayer的修改

  • 跟前一篇文章一样, 有2个地方是bug要做修改, 有2个地方要做数据转换处理修改
  • 请参考前一篇文章

D8 测试

在这里插入图片描述
在这里插入图片描述

  • 播放仙剑奇侠传主题曲,效果不错

在这里插入图片描述

  • 录音后再播放,效果不错。录音清晰。

  • 下面是打印的log


[D/drv.sdram] sdram init success, mapped at 0xC0000000, size is 33554432 bytes, data width is 16

 \ | /
- RT -     Thread Operating System
 / | \     5.2.0 build Aug 16 2025 09:59:24
 2006 - 2024 Copyright by RT-Thread team
samplerate: 16000
samplebits: 16
channels: 1
ramdisk0 device found
[E/app.port_sdcard] SD card device not found
[I/USB] ========== dwc2 udc params ==========
[I/USB] CID:00002300
[I/USB] GSNPSID:4f54330a
[I/USB] GHWCFG1:00000000
[I/USB] GHWCFG2:229fe190
[I/USB] GHWCFG3:03b8d2e8
[I/USB] GHWCFG4:e3f00030
[I/USB] dwc2 fsphy type:1, hsphy type:2, dma support:2
[I/USB] dwc2 has 9 endpoints and dfifo depth(32-bit words) is 952, default config: 9 endpoints
[I/USB] =================================
msh />[I/USB] fifo0 size:0010, offset:0100
[I/USB] fifo1 size:0200, offset:0110
[I/USB] fifo2 size:0010, offset:0310
[I/USB] fifo3 size:0010, offset:0320
[I/USB] fifo4 size:0010, offset:0330
[I/USB] fifo5 size:0010, offset:0340
[I/USB] fifo6 size:0010, offset:0350
[I/USB] fifo7 size:0010, offset:0360
[I/USB] fifo8 size:0010, offset:0370
[32m[I/SDIO] SD card capacity 31166976 KB.
CLOSE1
CLOSE2
CLOSE1
CLOSE2
OPEN1
OPEN2
CLOSE2

D9 附录1:drv_sound.c

/*
 * Copyright (c) 2006-2021, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author         Notes
 * 2019-07-28     Ernest         the first version
 */

#include "board.h"
#include "drv_sound.h"

#define DBG_TAG              "drv.sound"
#define DBG_LVL              DBG_INFO
#include <rtdbg.h>

#define TX_DMA_FIFO_SIZE (2048)

struct stm32_audio
{
    struct rt_audio_device audio;
    struct rt_audio_configure replay_config;
    int replay_volume;
    rt_uint8_t *tx_fifo;
    rt_bool_t startup;
};
struct stm32_audio _stm32_audio_play = {0};




/****************************************************************************
* @brief 与cubeMX硬件初始化相关的变量定义
****************************************************************************/
#define USB_NOCACHE_RAM_SECTION __attribute__((section(".noncacheable")))
#define USB_MEM_ALIGNX __attribute__((aligned(32)))

DAC_HandleTypeDef hdac1;
DMA_HandleTypeDef hdma_dac1_ch2;

TIM_HandleTypeDef htim2;

rt_mailbox_t dac_mbox = RT_NULL;

#define TONE_FREQ (440)
#define SIN_TABLE_SIZE (16000 / TONE_FREQ)  /*16kHz/440Hz=36 pts*/
#define M_PI 3.14159265358979323846
uint16_t sin_table[SIN_TABLE_SIZE];

USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t dac_buffer[TX_DMA_FIFO_SIZE]; // Nocache buffer for DMA, important!

// static void MX_ADC1_Init(void);
static void MX_DAC1_Init(void);
static void MX_TIM2_Init(void);
static void MX_DMA_Init(void);

static void generate_sin_table(void)
{
    for (int i = 0; i < SIN_TABLE_SIZE; i++)
    {
        float angle = 2 * M_PI * i / SIN_TABLE_SIZE;
        sin_table[i] = (uint16_t)((sin(angle) + 1.0f) * 2047.5f); // 12-bit DAC, 0-4095 range
    }
}








/****************************************************************************
* @brief 要实现的几个dev_audio接口函数
****************************************************************************/
void HAL_DACEx_ConvHalfCpltCallbackCh2(DAC_HandleTypeDef* hdac)
{
    if (hdac->Instance == DAC1)
    {
        rt_audio_tx_complete(&_stm32_audio_play.audio);
    }
}


void HAL_DACEx_ConvCpltCallbackCh2(DAC_HandleTypeDef* hdac)
{
    if (hdac->Instance == DAC1)
    {
        rt_audio_tx_complete(&_stm32_audio_play.audio);
    }
}


static rt_err_t stm32_player_getcaps(struct rt_audio_device *audio, struct rt_audio_caps *caps)
{
    rt_err_t result = RT_EOK;
    struct stm32_audio *st_audio = (struct stm32_audio *)audio->parent.user_data;

    LOG_D("%s:main_type: %d, sub_type: %d", __FUNCTION__, caps->main_type, caps->sub_type);

    return result;
}

static rt_err_t  stm32_player_configure(struct rt_audio_device *audio, struct rt_audio_caps *caps)
{
    rt_err_t result = RT_EOK;
    struct stm32_audio *st_audio = (struct stm32_audio *)audio->parent.user_data;

    LOG_D("%s:main_type: %d, sub_type: %d", __FUNCTION__, caps->main_type, caps->sub_type);

    switch (caps->main_type)
    {
    case AUDIO_TYPE_MIXER:
    {
        switch (caps->sub_type)
        {
        case AUDIO_MIXER_VOLUME:
        {
            int volume = caps->udata.value;
            st_audio->replay_volume = volume;

            break;
        }

        default:
            result = -RT_ERROR;
            break;
        }

        break;
    }
  
    case AUDIO_TYPE_OUTPUT:
    {
        switch (caps->sub_type)
        {
        case AUDIO_DSP_PARAM:
        {
            struct rt_audio_configure config = caps->udata.config;

            st_audio->replay_config.samplerate = config.samplerate;
            st_audio->replay_config.samplebits = config.samplebits;
            st_audio->replay_config.channels = config.channels;
            /*只添加调整采样率的功能*/
            __HAL_TIM_SET_AUTORELOAD(&htim2, 240000000 / st_audio->replay_config.samplerate - 1);
            rt_kprintf("samplerate: %d\n", st_audio->replay_config.samplerate);
            rt_kprintf("samplebits: %d\n", st_audio->replay_config.samplebits);
            rt_kprintf("channels: %d\n", st_audio->replay_config.channels);

            break;
        }

        default:
            result = -RT_ERROR;
            break;
        }
        break;
    }

    default:
        break;
    }

    return result;
}

static rt_err_t stm32_player_init(struct rt_audio_device *audio)
{
    MX_DMA_Init();
    MX_DAC1_Init();
    MX_TIM2_Init();
    dac_mbox = rt_mb_create("dac_mbox", 100, RT_IPC_FLAG_FIFO);

    return RT_EOK;
}

static rt_err_t stm32_player_start(struct rt_audio_device *audio, int stream)
{
    if (stream == AUDIO_STREAM_REPLAY)
    {
        HAL_TIM_Base_Start(&htim2);
        HAL_DAC_Start_DMA(&hdac1, DAC_CHANNEL_2, (uint32_t *) _stm32_audio_play.tx_fifo, TX_DMA_FIFO_SIZE / 2, DAC_ALIGN_12B_R);
    }

    return RT_EOK;
}

static rt_err_t stm32_player_stop(struct rt_audio_device *audio, int stream)
{
    if (stream == AUDIO_STREAM_REPLAY)
    {
        HAL_DAC_Stop_DMA(&hdac1, DAC_CHANNEL_2);
        HAL_TIM_Base_Stop(&htim2);
    }

    return RT_EOK;
}

static void stm32_player_buffer_info(struct rt_audio_device *audio, struct rt_audio_buf_info *info)
{
    /**
     *               TX_FIFO
     * +----------------+----------------+
     * |     block1     |     block2     |
     * +----------------+----------------+
     *  \  block_size  /
     */
    info->buffer = _stm32_audio_play.tx_fifo;
    info->total_size = TX_DMA_FIFO_SIZE;
    info->block_size = TX_DMA_FIFO_SIZE / 2;
    info->block_count = 2;
}
static struct rt_audio_ops _p_audio_ops =
{
    .getcaps     = stm32_player_getcaps,
    .configure   = stm32_player_configure,
    .init        = stm32_player_init,
    .start       = stm32_player_start,
    .stop        = stm32_player_stop,
    .transmit    = RT_NULL,
    .buffer_info = stm32_player_buffer_info,
};

int rt_hw_sound_init(void)
{
    rt_uint8_t *tx_fifo;

    /* player */
    tx_fifo = /*rt_malloc(TX_DMA_FIFO_SIZE)*/ dac_buffer; /*替换为nocache的dac_buffer, 非常重要! 折腾了几个小时!*/
    if (tx_fifo == RT_NULL)
    {
        return -RT_ENOMEM;
    }
    rt_memset(tx_fifo, 0, TX_DMA_FIFO_SIZE);
    _stm32_audio_play.tx_fifo = tx_fifo;

    /* register sound device */
    _stm32_audio_play.audio.ops = &_p_audio_ops;
    rt_audio_register(&_stm32_audio_play.audio, "sound0", RT_DEVICE_FLAG_WRONLY, &_stm32_audio_play);

    return RT_EOK;
}

INIT_DEVICE_EXPORT(rt_hw_sound_init);











/****************************************************************************
* @brief 录音线程, 与usb交互
****************************************************************************/
#include "usbd_core.h"
void play_thread_entry(void *parameter)
{

  #define AUDIO_OUT_PACKET 64
  #define AUDIO_OUT_EP 0x02
  extern uint8_t read_buffer[AUDIO_OUT_PACKET];
  uint16_t *pdata =rt_malloc(AUDIO_OUT_PACKET);

  rt_device_t sound_dev;
  rt_size_t written;
  
  // generate_sin_table();
  sound_dev = rt_device_find("sound0");
  rt_device_open(sound_dev, RT_DEVICE_OFLAG_WRONLY);


  while(1){
    if (rt_mb_recv(dac_mbox, &written, RT_WAITING_FOREVER) == RT_EOK)
    {
      for(int i=0; i<written/2; i++){
        /*取左右声道的平均值*/
        int32_t v=(((int16_t *)read_buffer)[2*i]+((int16_t*)read_buffer)[2*i+1])/2 + 32768;
        /*16bits signed => 12bits unsigned*/
        pdata[i]=(uint16_t)v>>4;
      }

      written = rt_device_write(sound_dev, 0, pdata, written/2);
      usbd_ep_start_read(0, AUDIO_OUT_EP, read_buffer, AUDIO_OUT_PACKET);
    }
  }
}

int play_thread()
{
    rt_thread_t thread;
    thread = rt_thread_create("play_thread", play_thread_entry, RT_NULL, 2048, 2, 10);
    if (thread != RT_NULL)
    {
        rt_thread_startup(thread);
    }
    return 0;
}
INIT_APP_EXPORT(play_thread);









/****************************************************************************
* @brief CubeMX底层硬件初始化, 包括xx_MSPInit(), xx_Init(), xx_IRQHandler()
****************************************************************************/
 /**
 * @brief DAC MSP Initialization
 * This function configures the hardware resources used in this example
 * @param hdac: DAC handle pointer
 * @retval None
 */
void HAL_DAC_MspInit(DAC_HandleTypeDef* hdac)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(hdac->Instance==DAC1)
  {
    /* USER CODE BEGIN DAC1_MspInit 0 */

    /* USER CODE END DAC1_MspInit 0 */
    /* Peripheral clock enable */
    __HAL_RCC_DAC12_CLK_ENABLE();

    __HAL_RCC_GPIOA_CLK_ENABLE();
    /**DAC1 GPIO Configuration
   PA5     ------> DAC1_OUT2
    */
    GPIO_InitStruct.Pin = GPIO_PIN_5;
    GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    /* DAC1 DMA Init */
    /* DAC1_CH2 Init */
    hdma_dac1_ch2.Instance = DMA1_Stream1;
    hdma_dac1_ch2.Init.Request = DMA_REQUEST_DAC2;
    hdma_dac1_ch2.Init.Direction = DMA_MEMORY_TO_PERIPH;
    hdma_dac1_ch2.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_dac1_ch2.Init.MemInc = DMA_MINC_ENABLE;
    hdma_dac1_ch2.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
    hdma_dac1_ch2.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
    hdma_dac1_ch2.Init.Mode = DMA_CIRCULAR;
    hdma_dac1_ch2.Init.Priority = DMA_PRIORITY_LOW;
    hdma_dac1_ch2.Init.FIFOMode = DMA_FIFOMODE_ENABLE;
    hdma_dac1_ch2.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_HALFFULL;
    hdma_dac1_ch2.Init.MemBurst = DMA_MBURST_INC4;
    hdma_dac1_ch2.Init.PeriphBurst = DMA_PBURST_SINGLE;
    if (HAL_DMA_Init(&hdma_dac1_ch2) != HAL_OK)
    {
      Error_Handler();
    }

    __HAL_LINKDMA(hdac,DMA_Handle2,hdma_dac1_ch2);

    /* USER CODE BEGIN DAC1_MspInit 1 */

    /* USER CODE END DAC1_MspInit 1 */

  }

}
 
 /**
 * @brief DAC MSP De-Initialization
 * This function freeze the hardware resources used in this example
 * @param hdac: DAC handle pointer
 * @retval None
 */
void HAL_DAC_MspDeInit(DAC_HandleTypeDef* hdac)
{
  if(hdac->Instance==DAC1)
  {
    /* USER CODE BEGIN DAC1_MspDeInit 0 */

    /* USER CODE END DAC1_MspDeInit 0 */
    /* Peripheral clock disable */
    __HAL_RCC_DAC12_CLK_DISABLE();

    /**DAC1 GPIO Configuration
   PA5     ------> DAC1_OUT2
    */
    HAL_GPIO_DeInit(GPIOA, GPIO_PIN_5);

    /* DAC1 DMA DeInit */
    HAL_DMA_DeInit(hdac->DMA_Handle2);
    /* USER CODE BEGIN DAC1_MspDeInit 1 */

    /* USER CODE END DAC1_MspDeInit 1 */
  }

}

 
/**
  * @brief TIM_Base MSP Initialization
  * This function configures the hardware resources used in this example
  * @param htim_base: TIM_Base handle pointer
  * @retval None
  */
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* htim_base)
{
  if(htim_base->Instance==TIM2)
  {
    /* USER CODE BEGIN TIM2_MspInit 0 */

    /* USER CODE END TIM2_MspInit 0 */
    /* Peripheral clock enable */
    __HAL_RCC_TIM2_CLK_ENABLE();
    /* USER CODE BEGIN TIM2_MspInit 1 */

    /* USER CODE END TIM2_MspInit 1 */

  }

}

/**
  * @brief TIM_Base MSP De-Initialization
  * This function freeze the hardware resources used in this example
  * @param htim_base: TIM_Base handle pointer
  * @retval None
  */
void HAL_TIM_Base_MspDeInit(TIM_HandleTypeDef* htim_base)
{
  if(htim_base->Instance==TIM2)
  {
    /* USER CODE BEGIN TIM2_MspDeInit 0 */

    /* USER CODE END TIM2_MspDeInit 0 */
    /* Peripheral clock disable */
    __HAL_RCC_TIM2_CLK_DISABLE();
    /* USER CODE BEGIN TIM2_MspDeInit 1 */

    /* USER CODE END TIM2_MspDeInit 1 */
  }

}
 

/**
  * @brief DAC1 Initialization Function
  * @param None
  * @retval None
  */
static void MX_DAC1_Init(void)
{

  /* USER CODE BEGIN DAC1_Init 0 */

  /* USER CODE END DAC1_Init 0 */

  DAC_ChannelConfTypeDef sConfig = {0};

  /* USER CODE BEGIN DAC1_Init 1 */

  /* USER CODE END DAC1_Init 1 */

  /** DAC Initialization
  */
  hdac1.Instance = DAC1;
  if (HAL_DAC_Init(&hdac1) != HAL_OK)
  {
    Error_Handler();
  }

  /** DAC channel OUT2 config
  */
  sConfig.DAC_SampleAndHold = DAC_SAMPLEANDHOLD_DISABLE;
  sConfig.DAC_Trigger = DAC_TRIGGER_T2_TRGO;
  sConfig.DAC_OutputBuffer = DAC_OUTPUTBUFFER_ENABLE;
  sConfig.DAC_ConnectOnChipPeripheral = DAC_CHIPCONNECT_DISABLE;
  sConfig.DAC_UserTrimming = DAC_TRIMMING_FACTORY;
  if (HAL_DAC_ConfigChannel(&hdac1, &sConfig, DAC_CHANNEL_2) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN DAC1_Init 2 */

  /* USER CODE END DAC1_Init 2 */

}


/**
* @brief TIM2 Initialization Function
* @param None
* @retval None
*/
void MX_TIM2_Init(void)
{

  /* USER CODE BEGIN TIM2_Init 0 */

  /* USER CODE END TIM2_Init 0 */

  TIM_ClockConfigTypeDef sClockSourceConfig = {0};
  TIM_MasterConfigTypeDef sMasterConfig = {0};

  /* USER CODE BEGIN TIM2_Init 1 */

  /* USER CODE END TIM2_Init 1 */
  htim2.Instance = TIM2;
  htim2.Init.Prescaler = 0;
  htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim2.Init.Period = 240e6/16e3;
  htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
  if (HAL_TIM_Base_Init(&htim2) != HAL_OK)
  {
    Error_Handler();
  }
  sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
  if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK)
  {
    Error_Handler();
  }
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN TIM2_Init 2 */

  /* USER CODE END TIM2_Init 2 */

}

/**
  * @brief This function handles DMA1 stream1 global interrupt.
  */
void DMA1_Stream1_IRQHandler(void)
{
  /* USER CODE BEGIN DMA1_Stream1_IRQn 0 */

  /* USER CODE END DMA1_Stream1_IRQn 0 */
  rt_base_t level = rt_hw_interrupt_disable();
  HAL_DMA_IRQHandler(&hdma_dac1_ch2);
  rt_hw_interrupt_enable(level);
  /* USER CODE BEGIN DMA1_Stream1_IRQn 1 */

  /* USER CODE END DMA1_Stream1_IRQn 1 */
}

static void MX_DMA_Init(void)
{

  /* DMA controller clock enable */
  __HAL_RCC_DMA1_CLK_ENABLE();

  /* DMA interrupt init */
  /* DMA1_Stream1_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA1_Stream1_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA1_Stream1_IRQn);
}

D10 附录2:drv_mic.c

/*
 * Copyright (c) 2006-2021, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author         Notes
 * 2019-07-28     Ernest         the first version
 */

#include "board.h"
#include "drv_mic.h"

#define DBG_TAG              "drv.mic"
#define DBG_LVL              DBG_INFO
#include <rtdbg.h>

#define RX_DMA_FIFO_SIZE (2048)

struct stm32_mic
{
    struct rt_audio_device audio;
    struct rt_audio_configure config;
    rt_uint8_t *rx_fifo;
    rt_bool_t startup;
};
static struct stm32_mic _stm32_audio_record = {0};



/****************************************************************************
* @brief 与cubeMX硬件初始化相关的变量定义
****************************************************************************/
#define USB_NOCACHE_RAM_SECTION __attribute__((section(".noncacheable")))
#define USB_MEM_ALIGNX __attribute__((aligned(32)))

ADC_HandleTypeDef hadc1;
DMA_HandleTypeDef hdma_adc1;

extern TIM_HandleTypeDef htim2;

USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t adc_buffer[RX_DMA_FIFO_SIZE]; // Nocache buffer for DMA, important!

static void MX_ADC1_Init(void);
static void MX_TIM2_Init(void);
static void MX_DMA_Init(void);




/****************************************************************************
* @brief 要实现的几个dev_audio接口函数
****************************************************************************/
void HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef* hadc)
{
    if (hadc->Instance == ADC1)
    {
        rt_audio_rx_done(&(_stm32_audio_record.audio), &_stm32_audio_record.rx_fifo[RX_DMA_FIFO_SIZE / 2], RX_DMA_FIFO_SIZE / 2);
    }
}

void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
    if (hadc->Instance == ADC1)
    {
        rt_audio_rx_done(&(_stm32_audio_record.audio), &_stm32_audio_record.rx_fifo[0], RX_DMA_FIFO_SIZE / 2);
    }
}

static rt_err_t stm32_mic_getcaps(struct rt_audio_device *audio, struct rt_audio_caps *caps)
{
    rt_err_t result = RT_EOK;

    LOG_D("%s:main_type: %d, sub_type: %d", __FUNCTION__, caps->main_type, caps->sub_type);

    return result;
}

static rt_err_t  stm32_mic_configure(struct rt_audio_device *audio, struct rt_audio_caps *caps)
{
    rt_err_t result = RT_EOK;

    LOG_D("%s:main_type: %d, sub_type: %d", __FUNCTION__, caps->main_type, caps->sub_type);

    switch (caps->main_type)
    {
    case AUDIO_TYPE_INPUT:
    {
        switch (caps->sub_type)
        {

        case AUDIO_DSP_PARAM:
        {
            _stm32_audio_record.config.samplerate = caps->udata.config.samplerate;
            _stm32_audio_record.config.channels   = caps->udata.config.channels;
            _stm32_audio_record.config.samplebits = caps->udata.config.samplebits;
            /*只添加调整采样率的功能*/
            __HAL_TIM_SET_AUTORELOAD(&htim2, 240000000 / _stm32_audio_record.config.samplerate - 1);
            rt_kprintf("samplerate: %d\n", _stm32_audio_record.config.samplerate);
            rt_kprintf("samplebits: %d\n", _stm32_audio_record.config.samplebits);
            rt_kprintf("channels: %d\n", _stm32_audio_record.config.channels);

            break;
        }

        default:
            result = -RT_ERROR;
            break;
        }
        /* After set config, MCLK will stop */
        break;
    }

    default:
        break;
    }

    return result;
}

static rt_err_t stm32_mic_init(struct rt_audio_device *audio)
{
    MX_DMA_Init();
    MX_ADC1_Init();
    MX_TIM2_Init();

    return RT_EOK;
}

static rt_err_t stm32_mic_start(struct rt_audio_device *audio, int stream)
{
    rt_err_t result = RT_EOK;

    if (stream == AUDIO_STREAM_RECORD)
    {
        HAL_TIM_Base_Start(&htim2);
        HAL_ADC_Start_DMA(&hadc1, (uint32_t *) _stm32_audio_record.rx_fifo, RX_DMA_FIFO_SIZE/2);
    }

    return result;
}

static rt_err_t stm32_mic_stop(struct rt_audio_device *audio, int stream)
{
    if (stream == AUDIO_STREAM_RECORD)
    {
        HAL_ADC_Stop_DMA(&hadc1);
        HAL_TIM_Base_Stop(&htim2);
    }

    return RT_EOK;
}

static struct rt_audio_ops _mic_audio_ops =
{
    .getcaps     = stm32_mic_getcaps,
    .configure   = stm32_mic_configure,
    .init        = stm32_mic_init,
    .start       = stm32_mic_start,
    .stop        = stm32_mic_stop,
    .transmit    = RT_NULL,
    .buffer_info = RT_NULL,
};

int rt_hw_mic_init(void)
{
    struct rt_audio_device *audio = &_stm32_audio_record.audio;
    /* mic default */
    _stm32_audio_record.rx_fifo = /*rt_calloc(1, RX_DMA_FIFO_SIZE)*/ adc_buffer; /*替换为nocache的adc_buffer, 非常重要! 折腾了几个小时!*/
    if (_stm32_audio_record.rx_fifo == RT_NULL)
    {
        return -RT_ENOMEM;
    }

    _stm32_audio_record.config.channels = 1;
    _stm32_audio_record.config.samplerate = 16000;
    _stm32_audio_record.config.samplebits = 16;

    /* register mic device */
    audio->ops = &_mic_audio_ops;
    rt_audio_register(audio, "mic0", RT_DEVICE_FLAG_RDONLY, &_stm32_audio_record);

    return RT_EOK;
}

INIT_DEVICE_EXPORT(rt_hw_mic_init);







/****************************************************************************
* @brief 录音线程, 与usb交互
****************************************************************************/
#include "usbd_core.h"
static void record_thread_entry(void *parameter)
{
  extern volatile bool tx_flag;
  #define AUDIO_IN_PACKET 64
  #define AUDIO_IN_EP  0x81 
  extern volatile uint8_t ep_tx_busy_flag;
  extern uint8_t write_buffer[AUDIO_IN_PACKET];    


  rt_device_t mic_dev = rt_device_find("mic0");
  rt_device_open(mic_dev, RT_DEVICE_OFLAG_RDONLY);


  struct rt_audio_caps caps;
  caps.main_type = AUDIO_TYPE_INPUT;
  caps.sub_type  = AUDIO_DSP_PARAM;
  caps.udata.config.samplerate = 16000;
  caps.udata.config.channels = 1;
  caps.udata.config.samplebits = 16;
  rt_device_control(mic_dev, AUDIO_CTL_CONFIGURE, &caps); 


  uint16_t *buffer = (uint16_t *)rt_malloc(AUDIO_IN_PACKET/2);
  int16_t *pwbuffer=(int16_t *)write_buffer;
  while (1)
  {
    if (tx_flag) {
        rt_size_t size =  rt_device_read(mic_dev, 0, buffer, AUDIO_IN_PACKET/2); /*因为要复制扩倍, 因此只需要读一半的数量*/
        if (size)
        {
            for(int i = 0; i < size/2; i++) {
                pwbuffer[2*i] = (int16_t)(buffer[i] - 32768);
                pwbuffer[2*i+1] = (int16_t)(buffer[i] - 32768); /*2个声道, repeat填充*/
            }

            ep_tx_busy_flag = 1;
            usbd_ep_start_write(0, AUDIO_IN_EP, write_buffer, size*2);
            while(ep_tx_busy_flag){
            }
        }
    }
    else {
      rt_thread_delay(10);
    }
  }
}

int record_thread()
{
    rt_thread_t thread;
    thread = rt_thread_create("record_thread_entry", record_thread_entry, RT_NULL, 2048, 2, 10);

    if (thread != RT_NULL)
    {
        rt_thread_startup(thread);
    }
    return 0;
}
INIT_APP_EXPORT(record_thread);







/****************************************************************************
* @brief CubeMX底层硬件初始化, 包括xx_MSPInit(), xx_Init(), xx_IRQHandler()
****************************************************************************/
/**
  * @brief ADC MSP Initialization
  * This function configures the hardware resources used in this example
  * @param hadc: ADC handle pointer
  * @retval None
  */
 void HAL_ADC_MspInit(ADC_HandleTypeDef* hadc)
 {
   if(hadc->Instance==ADC1)
   {
     /* USER CODE BEGIN ADC1_MspInit 0 */
 
     /* USER CODE END ADC1_MspInit 0 */
     /* Peripheral clock enable */
     __HAL_RCC_ADC12_CLK_ENABLE();
 
     __HAL_RCC_GPIOA_CLK_ENABLE();
     /**ADC1 GPIO Configuration
     PA1_C     ------> ADC1_INP1
     */
     HAL_SYSCFG_AnalogSwitchConfig(SYSCFG_SWITCH_PA1, SYSCFG_SWITCH_PA1_OPEN);
 
     /* ADC1 DMA Init */
     /* ADC1 Init */
     hdma_adc1.Instance = DMA1_Stream0;
     hdma_adc1.Init.Request = DMA_REQUEST_ADC1;
     hdma_adc1.Init.Direction = DMA_PERIPH_TO_MEMORY;
     hdma_adc1.Init.PeriphInc = DMA_PINC_DISABLE;
     hdma_adc1.Init.MemInc = DMA_MINC_ENABLE;
     hdma_adc1.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
     hdma_adc1.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
     hdma_adc1.Init.Mode = DMA_CIRCULAR;
     hdma_adc1.Init.Priority = DMA_PRIORITY_LOW;
     hdma_adc1.Init.FIFOMode = DMA_FIFOMODE_ENABLE;
     hdma_adc1.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_HALFFULL;
     hdma_adc1.Init.MemBurst = DMA_MBURST_SINGLE;
     hdma_adc1.Init.PeriphBurst = DMA_PBURST_INC4;
     if (HAL_DMA_Init(&hdma_adc1) != HAL_OK)
     {
       Error_Handler();
     }
 
     __HAL_LINKDMA(hadc,DMA_Handle,hdma_adc1);
 
     /* ADC1 interrupt Init */
     HAL_NVIC_SetPriority(ADC_IRQn, 0, 0);
     HAL_NVIC_EnableIRQ(ADC_IRQn);
     /* USER CODE BEGIN ADC1_MspInit 1 */
 
     /* USER CODE END ADC1_MspInit 1 */
 
   }
 
 }
 
 /**
   * @brief ADC MSP De-Initialization
   * This function freeze the hardware resources used in this example
   * @param hadc: ADC handle pointer
   * @retval None
   */
 void HAL_ADC_MspDeInit(ADC_HandleTypeDef* hadc)
 {
   if(hadc->Instance==ADC1)
   {
     /* USER CODE BEGIN ADC1_MspDeInit 0 */
 
     /* USER CODE END ADC1_MspDeInit 0 */
     /* Peripheral clock disable */
     __HAL_RCC_ADC12_CLK_DISABLE();
 
     /* ADC1 DMA DeInit */
     HAL_DMA_DeInit(hadc->DMA_Handle);
 
     /* ADC1 interrupt DeInit */
     HAL_NVIC_DisableIRQ(ADC_IRQn);
     /* USER CODE BEGIN ADC1_MspDeInit 1 */
 
     /* USER CODE END ADC1_MspDeInit 1 */
   }
 
 }

// /**
//   * @brief TIM_Base MSP Initialization
//   * This function configures the hardware resources used in this example
//   * @param htim_base: TIM_Base handle pointer
//   * @retval None
//   */
// void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* htim_base)
// {
//   if(htim_base->Instance==TIM2)
//   {
//     /* USER CODE BEGIN TIM2_MspInit 0 */

//     /* USER CODE END TIM2_MspInit 0 */
//     /* Peripheral clock enable */
//     __HAL_RCC_TIM2_CLK_ENABLE();
//     /* USER CODE BEGIN TIM2_MspInit 1 */

//     /* USER CODE END TIM2_MspInit 1 */

//   }

// }

// /**
//   * @brief TIM_Base MSP De-Initialization
//   * This function freeze the hardware resources used in this example
//   * @param htim_base: TIM_Base handle pointer
//   * @retval None
//   */
// void HAL_TIM_Base_MspDeInit(TIM_HandleTypeDef* htim_base)
// {
//   if(htim_base->Instance==TIM2)
//   {
//     /* USER CODE BEGIN TIM2_MspDeInit 0 */

//     /* USER CODE END TIM2_MspDeInit 0 */
//     /* Peripheral clock disable */
//     __HAL_RCC_TIM2_CLK_DISABLE();
//     /* USER CODE BEGIN TIM2_MspDeInit 1 */

//     /* USER CODE END TIM2_MspDeInit 1 */
//   }

// }
 

/**
 * @brief ADC1 Initialization Function
 * @param None
 * @retval None
 */
static void MX_ADC1_Init(void)
{

/* USER CODE BEGIN ADC1_Init 0 */

/* USER CODE END ADC1_Init 0 */

ADC_MultiModeTypeDef multimode = {0};
ADC_ChannelConfTypeDef sConfig = {0};

/* USER CODE BEGIN ADC1_Init 1 */

/* USER CODE END ADC1_Init 1 */

/** Common config
 */
hadc1.Instance = ADC1;
hadc1.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV2;
hadc1.Init.Resolution = ADC_RESOLUTION_16B;
hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;
hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
hadc1.Init.LowPowerAutoWait = DISABLE;
hadc1.Init.ContinuousConvMode = DISABLE;
hadc1.Init.NbrOfConversion = 1;
hadc1.Init.DiscontinuousConvMode = DISABLE;
hadc1.Init.ExternalTrigConv = ADC_EXTERNALTRIG_T2_TRGO;
hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING;
hadc1.Init.ConversionDataManagement = ADC_CONVERSIONDATA_DMA_CIRCULAR;
hadc1.Init.Overrun = ADC_OVR_DATA_PRESERVED;
hadc1.Init.LeftBitShift = ADC_LEFTBITSHIFT_NONE;
hadc1.Init.OversamplingMode = DISABLE;
hadc1.Init.Oversampling.Ratio = 1;
if (HAL_ADC_Init(&hadc1) != HAL_OK)
{
    Error_Handler();
}

/** Configure the ADC multi-mode
 */
multimode.Mode = ADC_MODE_INDEPENDENT;
if (HAL_ADCEx_MultiModeConfigChannel(&hadc1, &multimode) != HAL_OK)
{
    Error_Handler();
}

/** Configure Regular Channel
 */
sConfig.Channel = ADC_CHANNEL_1;
sConfig.Rank = ADC_REGULAR_RANK_1;
sConfig.SamplingTime = ADC_SAMPLETIME_8CYCLES_5;
sConfig.SingleDiff = ADC_SINGLE_ENDED;
sConfig.OffsetNumber = ADC_OFFSET_NONE;
sConfig.Offset = 0;
sConfig.OffsetSignedSaturation = DISABLE;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
    Error_Handler();
}
/* USER CODE BEGIN ADC1_Init 2 */

/* USER CODE END ADC1_Init 2 */

}

/**
 * @brief TIM2 Initialization Function
 * @param None
 * @retval None
 */
static void MX_TIM2_Init(void)
{

/* USER CODE BEGIN TIM2_Init 0 */

/* USER CODE END TIM2_Init 0 */

TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};

/* USER CODE BEGIN TIM2_Init 1 */

/* USER CODE END TIM2_Init 1 */
htim2.Instance = TIM2;
htim2.Init.Prescaler = 0;
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.Period = 240e6/16e3;
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim2) != HAL_OK)
{
    Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK)
{
    Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
{
    Error_Handler();
}
/* USER CODE BEGIN TIM2_Init 2 */

/* USER CODE END TIM2_Init 2 */

}
 
/**
  * @brief This function handles DMA1 stream0 global interrupt.
  */
void DMA1_Stream0_IRQHandler(void)
{
  /* USER CODE BEGIN DMA1_Stream0_IRQn 0 */

  /* USER CODE END DMA1_Stream0_IRQn 0 */
  rt_base_t level = rt_hw_interrupt_disable();
  HAL_DMA_IRQHandler(&hdma_adc1);
  rt_hw_interrupt_enable(level);
  /* USER CODE BEGIN DMA1_Stream0_IRQn 1 */

  /* USER CODE END DMA1_Stream0_IRQn 1 */
}

static void MX_DMA_Init(void)
{

  /* DMA controller clock enable */
  __HAL_RCC_DMA1_CLK_ENABLE();

  /* DMA interrupt init */
  /* DMA1_Stream0_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA1_Stream0_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA1_Stream0_IRQn);

}

D11 附录3:audio_v1_mic_speaker_multichan_template.c

/*
 * Copyright (c) 2024, sakumisu
 *
 * SPDX-License-Identifier: Apache-2.0
 */
#include "usbd_core.h"
#include "usbd_audio.h"
#include "trace_log.h"
#include <rtthread.h>
#include <rtdevice.h> 

#define USING_FEEDBACK 0

#define USBD_VID           0xffff
#define USBD_PID           0xffff
#define USBD_MAX_POWER     100
#define USBD_LANGID_STRING 1033

#ifdef CONFIG_USB_HS
#define EP_INTERVAL               0x04
#define FEEDBACK_ENDP_PACKET_SIZE 0x04
#else
#define EP_INTERVAL               0x01
#define FEEDBACK_ENDP_PACKET_SIZE 0x03
#endif

#define AUDIO_IN_EP  0x81
#define AUDIO_OUT_EP 0x02
#define AUDIO_OUT_FEEDBACK_EP 0x83

#define AUDIO_IN_FU_ID  0x02
#define AUDIO_OUT_FU_ID 0x05

/* AUDIO Class Config */
#define AUDIO_SPEAKER_FREQ            16000U
#define AUDIO_SPEAKER_FRAME_SIZE_BYTE 2u
#define AUDIO_SPEAKER_RESOLUTION_BIT  16u
#define AUDIO_MIC_FREQ                16000U
#define AUDIO_MIC_FRAME_SIZE_BYTE     2u
#define AUDIO_MIC_RESOLUTION_BIT      16u

#define AUDIO_SAMPLE_FREQ(frq) (uint8_t)(frq), (uint8_t)((frq >> 8)), (uint8_t)((frq >> 16))

/* AudioFreq * DataSize (2 bytes) * NumChannels (Stereo: 2) */
#define AUDIO_OUT_PACKET ((uint32_t)((AUDIO_SPEAKER_FREQ * AUDIO_SPEAKER_FRAME_SIZE_BYTE * 2) / 1000))
/* 16bit(2 Bytes) 双声道(Mono:2) */
#define AUDIO_IN_PACKET ((uint32_t)((AUDIO_MIC_FREQ * AUDIO_MIC_FRAME_SIZE_BYTE * 2) / 1000))

#if USING_FEEDBACK == 0
#define USB_AUDIO_CONFIG_DESC_SIZ (unsigned long)(9 +                                       \
                                                  AUDIO_AC_DESCRIPTOR_INIT_LEN(2) +         \
                                                  AUDIO_SIZEOF_AC_INPUT_TERMINAL_DESC +     \
                                                  AUDIO_SIZEOF_AC_FEATURE_UNIT_DESC(2, 1) + \
                                                  AUDIO_SIZEOF_AC_OUTPUT_TERMINAL_DESC +    \
                                                  AUDIO_SIZEOF_AC_INPUT_TERMINAL_DESC +     \
                                                  AUDIO_SIZEOF_AC_FEATURE_UNIT_DESC(2, 1) + \
                                                  AUDIO_SIZEOF_AC_OUTPUT_TERMINAL_DESC +    \
                                                  AUDIO_AS_DESCRIPTOR_INIT_LEN(1) +         \
                                                  AUDIO_AS_DESCRIPTOR_INIT_LEN(1))
#else
#define USB_AUDIO_CONFIG_DESC_SIZ (unsigned long)(9 +                                       \
                                                  AUDIO_AC_DESCRIPTOR_INIT_LEN(2) +         \
                                                  AUDIO_SIZEOF_AC_INPUT_TERMINAL_DESC +     \
                                                  AUDIO_SIZEOF_AC_FEATURE_UNIT_DESC(2, 1) + \
                                                  AUDIO_SIZEOF_AC_OUTPUT_TERMINAL_DESC +    \
                                                  AUDIO_SIZEOF_AC_INPUT_TERMINAL_DESC +     \
                                                  AUDIO_SIZEOF_AC_FEATURE_UNIT_DESC(2, 1) + \
                                                  AUDIO_SIZEOF_AC_OUTPUT_TERMINAL_DESC +    \
                                                  AUDIO_AS_DESCRIPTOR_INIT_LEN(1) +         \
                                                  AUDIO_AS_FEEDBACK_DESCRIPTOR_INIT_LEN(1))
#endif

#define AUDIO_AC_SIZ (AUDIO_SIZEOF_AC_HEADER_DESC(2) +          \
                      AUDIO_SIZEOF_AC_INPUT_TERMINAL_DESC +     \
                      AUDIO_SIZEOF_AC_FEATURE_UNIT_DESC(2, 1) + \
                      AUDIO_SIZEOF_AC_OUTPUT_TERMINAL_DESC +    \
                      AUDIO_SIZEOF_AC_INPUT_TERMINAL_DESC +     \
                      AUDIO_SIZEOF_AC_FEATURE_UNIT_DESC(2, 1) + \
                      AUDIO_SIZEOF_AC_OUTPUT_TERMINAL_DESC)

#ifdef CONFIG_USBDEV_ADVANCE_DESC
static const uint8_t device_descriptor[] = {
    USB_DEVICE_DESCRIPTOR_INIT(USB_2_0, 0xef, 0x02, 0x01, USBD_VID, USBD_PID, 0x0001, 0x01)
};

static const uint8_t config_descriptor[] = {
    USB_CONFIG_DESCRIPTOR_INIT(USB_AUDIO_CONFIG_DESC_SIZ, 0x03, 0x01, USB_CONFIG_BUS_POWERED, USBD_MAX_POWER),
    AUDIO_AC_DESCRIPTOR_INIT(0x00, 0x03, AUDIO_AC_SIZ, 0x00, 0x01, 0x02),
    AUDIO_AC_INPUT_TERMINAL_DESCRIPTOR_INIT(0x01, AUDIO_INTERM_MIC, 0x02, 0x0003),
    AUDIO_AC_FEATURE_UNIT_DESCRIPTOR_INIT(0x02, 0x01, 0x01, 0x03, 0x00, 0x00),
    AUDIO_AC_OUTPUT_TERMINAL_DESCRIPTOR_INIT(0x03, AUDIO_TERMINAL_STREAMING, 0x02),
    AUDIO_AC_INPUT_TERMINAL_DESCRIPTOR_INIT(0x04, AUDIO_TERMINAL_STREAMING, 0x02, 0x0003),
    AUDIO_AC_FEATURE_UNIT_DESCRIPTOR_INIT(0x05, 0x04, 0x01, 0x03, 0x00, 0x00),
    AUDIO_AC_OUTPUT_TERMINAL_DESCRIPTOR_INIT(0x06, AUDIO_OUTTERM_SPEAKER, 0x05),
#if USING_FEEDBACK == 0
    AUDIO_AS_DESCRIPTOR_INIT(0x01, 0x04, 0x02, AUDIO_SPEAKER_FRAME_SIZE_BYTE, AUDIO_SPEAKER_RESOLUTION_BIT, AUDIO_OUT_EP, 0x09, AUDIO_OUT_PACKET,
                             EP_INTERVAL, AUDIO_SAMPLE_FREQ_3B(AUDIO_SPEAKER_FREQ)),
#else
    AUDIO_AS_FEEDBACK_DESCRIPTOR_INIT(0x01, 0x04, 0x02, AUDIO_SPEAKER_FRAME_SIZE_BYTE, AUDIO_SPEAKER_RESOLUTION_BIT, AUDIO_OUT_EP, AUDIO_OUT_PACKET,
                             EP_INTERVAL, AUDIO_OUT_FEEDBACK_EP, AUDIO_SAMPLE_FREQ_3B(AUDIO_SPEAKER_FREQ)),
#endif
    AUDIO_AS_DESCRIPTOR_INIT(0x02, 0x03, 0x02, AUDIO_MIC_FRAME_SIZE_BYTE, AUDIO_MIC_RESOLUTION_BIT, AUDIO_IN_EP, 0x05, AUDIO_IN_PACKET,
                             EP_INTERVAL, AUDIO_SAMPLE_FREQ_3B(AUDIO_MIC_FREQ))
};

static const uint8_t device_quality_descriptor[] = {
    ///////////////////////////////////////
    /// device qualifier descriptor
    ///////////////////////////////////////
    0x0a,
    USB_DESCRIPTOR_TYPE_DEVICE_QUALIFIER,
    0x00,
    0x02,
    0x00,
    0x00,
    0x00,
    0x40,
    0x00,
    0x00,
};

static const char *string_descriptors[] = {
    (const char[]){ 0x09, 0x04 }, /* Langid */
    "CherryUSB",                  /* Manufacturer */
    "CherryUSB UAC DEMO",         /* Product */
    "2022123456",                 /* Serial Number */
};

static const uint8_t *device_descriptor_callback(uint8_t speed)
{
    return device_descriptor;
}

static const uint8_t *config_descriptor_callback(uint8_t speed)
{
    return config_descriptor;
}

static const uint8_t *device_quality_descriptor_callback(uint8_t speed)
{
    return device_quality_descriptor;
}

static const char *string_descriptor_callback(uint8_t speed, uint8_t index)
{

    if (index > 3) {
        return NULL;
    }
    return string_descriptors[index];
}

const struct usb_descriptor audio_v1_descriptor = {
    .device_descriptor_callback = device_descriptor_callback,
    .config_descriptor_callback = config_descriptor_callback,
    .device_quality_descriptor_callback = device_quality_descriptor_callback,
    .string_descriptor_callback = string_descriptor_callback
};
#else
const uint8_t audio_v1_descriptor[] = {
    USB_DEVICE_DESCRIPTOR_INIT(USB_2_0, 0xef, 0x02, 0x01, USBD_VID, USBD_PID, 0x0001, 0x01),
    USB_CONFIG_DESCRIPTOR_INIT(USB_AUDIO_CONFIG_DESC_SIZ, 0x03, 0x01, USB_CONFIG_BUS_POWERED, USBD_MAX_POWER),
    AUDIO_AC_DESCRIPTOR_INIT(0x00, 0x03, AUDIO_AC_SIZ, 0x00, 0x01, 0x02),
    AUDIO_AC_INPUT_TERMINAL_DESCRIPTOR_INIT(0x01, AUDIO_INTERM_MIC, 0x02, 0x0003),
    AUDIO_AC_FEATURE_UNIT_DESCRIPTOR_INIT(0x02, 0x01, 0x01, 0x03, 0x00, 0x00),
    AUDIO_AC_OUTPUT_TERMINAL_DESCRIPTOR_INIT(0x03, AUDIO_TERMINAL_STREAMING, 0x02),
    AUDIO_AC_INPUT_TERMINAL_DESCRIPTOR_INIT(0x04, AUDIO_TERMINAL_STREAMING, 0x02, 0x0003),
    AUDIO_AC_FEATURE_UNIT_DESCRIPTOR_INIT(0x05, 0x04, 0x01, 0x03, 0x00, 0x00),
    AUDIO_AC_OUTPUT_TERMINAL_DESCRIPTOR_INIT(0x06, AUDIO_OUTTERM_SPEAKER, 0x05),
#if USING_FEEDBACK == 0
    AUDIO_AS_DESCRIPTOR_INIT(0x01, 0x04, 0x02, AUDIO_SPEAKER_FRAME_SIZE_BYTE, AUDIO_SPEAKER_RESOLUTION_BIT, AUDIO_OUT_EP, 0x09, AUDIO_OUT_PACKET,
                             EP_INTERVAL, AUDIO_SAMPLE_FREQ_3B(AUDIO_SPEAKER_FREQ)),
#else
    AUDIO_AS_FEEDBACK_DESCRIPTOR_INIT(0x01, 0x04, 0x02, AUDIO_SPEAKER_FRAME_SIZE_BYTE, AUDIO_SPEAKER_RESOLUTION_BIT, AUDIO_OUT_EP, AUDIO_OUT_PACKET,
                             EP_INTERVAL, AUDIO_OUT_FEEDBACK_EP, AUDIO_SAMPLE_FREQ_3B(AUDIO_SPEAKER_FREQ)),
#endif
    AUDIO_AS_DESCRIPTOR_INIT(0x02, 0x03, 0x02, AUDIO_MIC_FRAME_SIZE_BYTE, AUDIO_MIC_RESOLUTION_BIT, AUDIO_IN_EP, 0x05, AUDIO_IN_PACKET,
                             EP_INTERVAL, AUDIO_SAMPLE_FREQ_3B(AUDIO_MIC_FREQ)),
    ///////////////////////////////////////
    /// string0 descriptor
    ///////////////////////////////////////
    USB_LANGID_INIT(USBD_LANGID_STRING),
    ///////////////////////////////////////
    /// string1 descriptor
    ///////////////////////////////////////
    0x14,                       /* bLength */
    USB_DESCRIPTOR_TYPE_STRING, /* bDescriptorType */
    'C', 0x00,                  /* wcChar0 */
    'h', 0x00,                  /* wcChar1 */
    'e', 0x00,                  /* wcChar2 */
    'r', 0x00,                  /* wcChar3 */
    'r', 0x00,                  /* wcChar4 */
    'y', 0x00,                  /* wcChar5 */
    'U', 0x00,                  /* wcChar6 */
    'S', 0x00,                  /* wcChar7 */
    'B', 0x00,                  /* wcChar8 */
    ///////////////////////////////////////
    /// string2 descriptor
    ///////////////////////////////////////
    0x26,                       /* bLength */
    USB_DESCRIPTOR_TYPE_STRING, /* bDescriptorType */
    'C', 0x00,                  /* wcChar0 */
    'h', 0x00,                  /* wcChar1 */
    'e', 0x00,                  /* wcChar2 */
    'r', 0x00,                  /* wcChar3 */
    'r', 0x00,                  /* wcChar4 */
    'y', 0x00,                  /* wcChar5 */
    'U', 0x00,                  /* wcChar6 */
    'S', 0x00,                  /* wcChar7 */
    'B', 0x00,                  /* wcChar8 */
    ' ', 0x00,                  /* wcChar9 */
    'U', 0x00,                  /* wcChar10 */
    'A', 0x00,                  /* wcChar11 */
    'C', 0x00,                  /* wcChar12 */
    ' ', 0x00,                  /* wcChar13 */
    'D', 0x00,                  /* wcChar14 */
    'E', 0x00,                  /* wcChar15 */
    'M', 0x00,                  /* wcChar16 */
    'O', 0x00,                  /* wcChar17 */
    ///////////////////////////////////////
    /// string3 descriptor
    ///////////////////////////////////////
    0x16,                       /* bLength */
    USB_DESCRIPTOR_TYPE_STRING, /* bDescriptorType */
    '2', 0x00,                  /* wcChar0 */
    '0', 0x00,                  /* wcChar1 */
    '2', 0x00,                  /* wcChar2 */
    '2', 0x00,                  /* wcChar3 */
    '1', 0x00,                  /* wcChar4 */
    '2', 0x00,                  /* wcChar5 */
    '3', 0x00,                  /* wcChar6 */
    '4', 0x00,                  /* wcChar7 */
    '5', 0x00,                  /* wcChar8 */
#if USING_FEEDBACK == 0
    '1', 0x00,                  /* wcChar9 */
#else
    '2', 0x00,                  /* wcChar9 */
#endif
#ifdef CONFIG_USB_HS
    ///////////////////////////////////////
    /// device qualifier descriptor
    ///////////////////////////////////////
    0x0a,
    USB_DESCRIPTOR_TYPE_DEVICE_QUALIFIER,
    0x00,
    0x02,
    0x00,
    0x00,
    0x00,
    0x40,
    0x00,
    0x00,
#endif
    0x00
};
#endif

USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t read_buffer[AUDIO_OUT_PACKET];
USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t write_buffer[AUDIO_IN_PACKET];
USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t s_speaker_feedback_buffer[4];

volatile bool tx_flag = 0;
volatile bool rx_flag = 0;
volatile bool ep_tx_busy_flag = false;

static void usbd_event_handler(uint8_t busid, uint8_t event)
{
    switch (event) {
        case USBD_EVENT_RESET:
            break;
        case USBD_EVENT_CONNECTED:
            break;
        case USBD_EVENT_DISCONNECTED:
            break;
        case USBD_EVENT_RESUME:
            break;
        case USBD_EVENT_SUSPEND:
            break;
        case USBD_EVENT_CONFIGURED:
            break;
        case USBD_EVENT_SET_REMOTE_WAKEUP:
            break;
        case USBD_EVENT_CLR_REMOTE_WAKEUP:
            break;

        default:
            break;
    }
}

void usbd_audio_open(uint8_t busid, uint8_t intf)
{

    if (intf == 1) {
        rx_flag = 1;
        /* setup first out ep read transfer */
        usbd_ep_start_read(busid, AUDIO_OUT_EP, read_buffer, AUDIO_OUT_PACKET);
        uint32_t feedback_value = AUDIO_FREQ_TO_FEEDBACK_FS(AUDIO_SPEAKER_FREQ);
        AUDIO_FEEDBACK_TO_BUF_FS(s_speaker_feedback_buffer, feedback_value); /* uac1 can only use 10.14 */

        usbd_ep_start_write(busid, AUDIO_OUT_FEEDBACK_EP, s_speaker_feedback_buffer, FEEDBACK_ENDP_PACKET_SIZE);
        printf("OPEN1\r\n");
    } else {
        tx_flag = 1;
        ep_tx_busy_flag = false;
        printf("OPEN2\r\n");
    }
}

void usbd_audio_close(uint8_t busid, uint8_t intf)
{
    if (intf == 1) {
        rx_flag = 0;
        printf("CLOSE1\r\n");
    } else {
        tx_flag = 0;
        ep_tx_busy_flag = false;
        printf("CLOSE2\r\n");
    }
}


void usbd_audio_out_callback(uint8_t busid, uint8_t ep, uint32_t nbytes)
{
    // USB_LOG_RAW("actual out len:%d\r\n", nbytes);

    extern rt_mailbox_t dac_mbox;
    rt_mb_send(dac_mbox, nbytes);

    /* 继续启动下一次 USB 读取 */
    // usbd_ep_start_read(busid, AUDIO_OUT_EP, read_buffer, AUDIO_OUT_PACKET);
}

void usbd_audio_in_callback(uint8_t busid, uint8_t ep, uint32_t nbytes)
{
    // USB_LOG_RAW("actual in len:%d\r\n", nbytes);
    ep_tx_busy_flag = false;
}

#if USING_FEEDBACK == 1
void usbd_audio_iso_out_feedback_callback(uint8_t busid, uint8_t ep, uint32_t nbytes)
{
    USB_LOG_RAW("actual feedback len:%d\r\n", nbytes);
    uint32_t feedback_value = AUDIO_FREQ_TO_FEEDBACK_FS(AUDIO_SPEAKER_FREQ);
    AUDIO_FEEDBACK_TO_BUF_FS(s_speaker_feedback_buffer, feedback_value);
    usbd_ep_start_write(busid, AUDIO_OUT_FEEDBACK_EP, s_speaker_feedback_buffer, FEEDBACK_ENDP_PACKET_SIZE);
}
#endif

static struct usbd_endpoint audio_in_ep = {
    .ep_cb = usbd_audio_in_callback,
    .ep_addr = AUDIO_IN_EP
};

static struct usbd_endpoint audio_out_ep = {
    .ep_cb = usbd_audio_out_callback,
    .ep_addr = AUDIO_OUT_EP
};

#if USING_FEEDBACK == 1
static struct usbd_endpoint audio_out_feedback_ep = {
    .ep_cb = usbd_audio_iso_out_feedback_callback,
    .ep_addr = AUDIO_OUT_FEEDBACK_EP
};
#endif

struct usbd_interface intf0;
struct usbd_interface intf1;
struct usbd_interface intf2;

struct audio_entity_info audio_entity_table[] = {
    { .bEntityId = AUDIO_IN_FU_ID,
      .bDescriptorSubtype = AUDIO_CONTROL_FEATURE_UNIT,
      .ep = AUDIO_IN_EP },
    { .bEntityId = AUDIO_OUT_FU_ID,
      .bDescriptorSubtype = AUDIO_CONTROL_FEATURE_UNIT,
      .ep = AUDIO_OUT_EP },
};

void audio_v1_init(uint8_t busid, uintptr_t reg_base)
{

#ifdef CONFIG_USBDEV_ADVANCE_DESC
    usbd_desc_register(busid, &audio_v1_descriptor);
#else
    usbd_desc_register(busid, audio_v1_descriptor);
#endif
    usbd_add_interface(busid, usbd_audio_init_intf(busid, &intf0, 0x0100, audio_entity_table, 2));
    usbd_add_interface(busid, usbd_audio_init_intf(busid, &intf1, 0x0100, audio_entity_table, 2));
    usbd_add_interface(busid, usbd_audio_init_intf(busid, &intf2, 0x0100, audio_entity_table, 2));
    usbd_add_endpoint(busid, &audio_in_ep);
    usbd_add_endpoint(busid, &audio_out_ep);
#if USING_FEEDBACK == 1
    usbd_add_endpoint(busid, &audio_out_feedback_ep);
#endif
    usbd_initialize(busid, reg_base, usbd_event_handler);
}


网站公告

今日签到

点亮在社区的每一天
去签到