当前位置: 首页 > news >正文

Cherryusb UAC例程对接STM32内置ADC和DAC播放音乐和录音(中)=>UAC+STM32 ADC+DAC实现录音和播放

1. 程序基本框架

在这里插入图片描述

  • audio_v1_mic_speaker_multichan_template.c文件中,主要usbd_audio_out_callback和usbd_audio_in_callback函数。在usbd_audio_out_callback中添加rt_ringbuffer_put操作,在usbd_audio_in_callback中目前基本不做修改。
  • 在tim_adc_dac_dma_usb.c文件中, 主要实现了2个线程, 一个是adc线程, 一个是dac线程。主要实现adc和dac的dma传输,以及usb的读取和写入。
  • adc线程主要实现了adc的dma buffer读取(通过ringbuffer), 以及usb的发送功能
  • dac线程主要实现了usb的接收数据的读取功能(通过ringbuffer), 以及dac的dma buffer的填充
  • 在整个程序结构中, ringbuffer和信号量起到了非常关键的调节作用。
  • 信号量主要用于控制线程的运行, 比如当ringbuffer中的数据不足时, 就会阻塞线程, 等待数据的到来。
  • ringbuffer主要用于存储数据, 比如usb接收的数据, 或者是dac需要播放的数据。
  • 当usb接收数据时, 会将数据存储到ringbuffer中, dac线程从ringbuffer中读取数据进行播放。那么dac什么时候从ringbuffer中读取数据呢?这取决于dac的传输半完成中断和传输完成中断. 当中断发生的时候,发出信号量通知dac线程填充buffer数据, 那么dac线程被唤醒之后, 根据标志从ringbuffer获取数据,然后填充dma buffer。如果ringbuffer数量不足, 就填充静音数据, 这是dac线程的特别之处.
  • 对于adc线程要简单一些, 因为对于usb来说, 我们的发送方, 我们主动性强很多.

2. audio_v1_mic_speaker_multichan_template.c的修改说明

  • 主要修改了usbd_audio_out_callback函数, 主要添加了rt_ringbuffer_put操作, 用于将usb接收的数据存储到ringbuffer中。
void usbd_audio_out_callback(uint8_t busid, uint8_t ep, uint32_t nbytes)
{// USB_LOG_RAW("actual out len:%d\r\n", nbytes);/* 写入 ring-buffer */extern struct rt_ringbuffer usb_to_dac_ring;rt_ringbuffer_put(&usb_to_dac_ring, read_buffer, 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;
}

3. tim_adc_dac_dma_usb.c主程序的实现说明

  • 首先定义了2个ringbuffer, 一个是usb_to_dac_ring, 一个是adc_to_usb_ring. 分别用于存储usb接收的数据, 以及adc采集的数据.
  • 然后定义了2个信号量, 一个是adc_data_ready_sem, 一个是dac_data_req_sem.
  • 其中adc_data_ready_sem用于控制adc线程的运行, 当ringbuffer中的数据不足时, 就会阻塞线程, 等待数据的到来.
  • 而dac_data_req_sem用于控制dac线程的运行, 当dac发生传输完成或者传输半完成中断时, 就会发出信号量通知dac线程填充buffer数据.
  • 定义了2个线程, 一个是usb_to_dac_thread, 一个是adc_to_usb_thread. 分别用于实现dac需要播放的数据的填充, 以及usb发送需要的数据的读取.
  • 定义了一些dma使用到的buffer, 注意这些buffer放到了USB_NOCACHE_RAM_SECTION区域,并且要对齐.
  • 为了将adc和dac的中断触发频率降低一些, 我们增大了dma buffer的大小, 有320x2个16位样本. 也就是DMA每发送或接收320个数据后, 触发一次中断(传输完成或半传输完成). 由于本例程音频采样频率是16kHz, 这意味着每隔20ms触发一次中断, 这个足够慢了(实际可能不需要这么大buffer).
// 定义USB音频参数
#define USB_AUDIO_SAMPLE_RATE    16000
#define USB_AUDIO_CHANNELS       2
#define USB_AUDIO_BYTES_PER_SAMPLE 2  // 16bit
#define USB_AUDIO_PACKET_SIZE    64   // 与USB定义匹配// 定义ringbuffer大小
#define USB_TO_DAC_BUFFER_SIZE   4096  // USB→DAC缓冲
#define ADC_TO_USB_BUFFER_SIZE   4096  // ADC→USB缓冲// 创建ringbuffer
struct rt_ringbuffer usb_to_dac_ring;
static struct rt_ringbuffer adc_to_usb_ring;
static uint8_t usb_to_dac_buf[USB_TO_DAC_BUFFER_SIZE];
static uint8_t adc_to_usb_buf[ADC_TO_USB_BUFFER_SIZE];// 信号量和线程
static rt_sem_t adc_data_ready_sem = RT_NULL;
static rt_sem_t dac_data_req_sem = RT_NULL;
static volatile uint8_t buffer_ready_flag = 0;static rt_thread_t usb_to_dac_thread = RT_NULL;
static rt_thread_t adc_to_usb_thread = RT_NULL;// 修改缓冲区定义
#define DAC_DMA_BUFFER_SIZE   (USB_AUDIO_PACKET_SIZE*10/2)  // 320个16位样本
#define ADC_DMA_BUFFER_SIZE   (USB_AUDIO_PACKET_SIZE*10/2)  // 320个16位样本USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint16_t dac_dma_buffer[DAC_DMA_BUFFER_SIZE * 2];  // 双缓冲
USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint16_t adc_dma_buffer[ADC_DMA_BUFFER_SIZE * 2];  // 双缓冲
USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t usb_send_buffer[USB_AUDIO_PACKET_SIZE];
USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint16_t temp_buffer[DAC_DMA_BUFFER_SIZE];
  • 在ADC的传输完成或半传输完成中断中, 主要填充ringbuffer, 并释放信号量, 通知adc线程接收数据.
  • 因为adc接收的数据是16bits无符号的, 而音频数据是16bits有符号的. 因此我们需要将其转换为16bits有符号的, 所以需要减去32768.
  • 注意ringbuffer是以uint8_t存储数据的
void HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef* hadc)
{if (hadc->Instance == ADC1){// 处理前半部分ADC数据for (int i = 0; i < ADC_DMA_BUFFER_SIZE; i++){// 将12位ADC数据转换为16位音频数据uint16_t adc_value = adc_dma_buffer[i];int16_t audio_sample = adc_value - 32768;// 写入ringbufferrt_ringbuffer_put(&adc_to_usb_ring, (uint8_t *)&audio_sample, 2);}rt_sem_release(adc_data_ready_sem);}
}void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{if (hadc->Instance == ADC1){// 处理后半部分ADC数据for (int i = ADC_DMA_BUFFER_SIZE; i < ADC_DMA_BUFFER_SIZE * 2; i++){uint16_t adc_value = adc_dma_buffer[i];int16_t audio_sample = adc_value - 32768;rt_ringbuffer_put(&adc_to_usb_ring, (uint8_t *)&audio_sample, 2);}rt_sem_release(adc_data_ready_sem);}
}
  • 在dac的传输完成或半传输完成中断中, 主要是释放信号量, 通知dac线程填充数据.
  • 通过设置标志buffer_ready_flag, 来指示dac线程需要填充的是前半缓冲区还是后半缓冲区.
void HAL_DACEx_ConvHalfCpltCallbackCh2(DAC_HandleTypeDef* hdac)
{// 前半缓冲区已传输完成,准备填充后半缓冲区buffer_ready_flag = 1;  // 标记需要填充前半缓冲区rt_sem_release(dac_data_req_sem);
}void HAL_DACEx_ConvCpltCallbackCh2(DAC_HandleTypeDef* hdac)
{// 后半缓冲区已传输完成,准备填充前半缓冲区  buffer_ready_flag = 2;  // 标记需要填充后半缓冲区rt_sem_release(dac_data_req_sem);
}
  • dac线程的处理逻辑是, 会等待信号量dac_data_req_sem, 然后决定从usb_to_dac_ringbuffer中读取数据, 并填充到dac_dma_buffer中.
  • 因为dac_dma_buffer是双缓冲的, 所以需要根据buffer_ready_flag来确定填充的是前半缓冲区还是后半缓冲区.
  • 有一个特别的是, 当数据不足的时候, 会填充静音数据. 因为DMA一旦启动, 就是连续不停的running, 它无法等待,你必须要给它数据, 否则它播放的可能是之前的数据或是乱的, 这是不允许的. 这种情况下, 最好的方式给他填充静音数据.
  • 从ringbuffer取到的数据, 转成int16_t后, 注意是有符号的, 这是从usb uac接收到音频数据. 所以需要加上32768, 才能转换成16bits无符号的. 又由于我们的DAC是12位的, 所以需要右移4bits转成12位。
static void usb_to_dac_thread_entry(void *parameter)
{while (1){// 等待DAC缓冲区需要填充if (rt_sem_take(dac_data_req_sem, RT_WAITING_FOREVER) == RT_EOK){uint16_t *target_buffer;// 根据标志确定填充哪个缓冲区if (buffer_ready_flag == 1)target_buffer = &dac_dma_buffer[0];  // 前半缓冲区elsetarget_buffer = &dac_dma_buffer[DAC_DMA_BUFFER_SIZE];  // 后半缓冲区// 从USB ringbuffer读取数据if (rt_ringbuffer_data_len(&usb_to_dac_ring) >= DAC_DMA_BUFFER_SIZE * 2){size_t read_len = rt_ringbuffer_get(&usb_to_dac_ring, (uint8_t *)temp_buffer, DAC_DMA_BUFFER_SIZE * 2);// 数据格式转换并填充目标缓冲区for (int i = 0; i < read_len/2; i++){int16_t audio_sample = ((int16_t *)temp_buffer)[i];target_buffer[i] = (audio_sample + 32768) >> 4;}} else{// 数据不够时填充静音memset(target_buffer, 0x80, DAC_DMA_BUFFER_SIZE * 2);}}}
}
  • adc线程的处理逻辑是, 会等待信号量adc_data_ready_sem, 然后从adc_to_usb_ringbuffer中读取数据, 并填充到usb_send_buffer中.
  • 然后开启usb的发送, 等待发送完成.
  • usb发送完成后, 会在usbd_audio_in_callback中设置标志ep_tx_busy_flag为0, 于是adc线程可以发送下一批数据.
static void adc_to_usb_thread_entry(void *parameter)
{extern volatile bool tx_flag;while (1){if (tx_flag) {while (rt_ringbuffer_data_len(&adc_to_usb_ring) < sizeof(usb_send_buffer)){rt_sem_take(adc_data_ready_sem, RT_WAITING_FOREVER);}size_t read_len = rt_ringbuffer_get(&adc_to_usb_ring, usb_send_buffer, sizeof(usb_send_buffer));ep_tx_busy_flag = 1;usbd_ep_start_write(0, AUDIO_IN_EP, usb_send_buffer, read_len);while(ep_tx_busy_flag){}}else {rt_thread_delay(10);}}
}
  • 初始化的过程, 主要是初始化dma, adc, dac, 定时器. 然后初始化ringbuffer, 信号量. 最后创建线程.
  • 然后启动定时器, adc_dma, dac_dma.
  • 这个初始化与前面的TIM + DAC +DAC的回环试验基本是一样的. 前面的文章是这一篇的基础.
int ADC_DAC_DMA_Init(void)
{MX_DMA_Init();MX_ADC1_Init();MX_DAC1_Init();MX_TIM2_Init();rt_ringbuffer_init(&usb_to_dac_ring, usb_to_dac_buf, sizeof(usb_to_dac_buf));rt_ringbuffer_init(&adc_to_usb_ring, adc_to_usb_buf, sizeof(adc_to_usb_buf));adc_data_ready_sem = rt_sem_create("adc_ready", 0, RT_IPC_FLAG_FIFO);dac_data_req_sem = rt_sem_create("dac_buf", 0, RT_IPC_FLAG_FIFO);memset(dac_dma_buffer, 0x80, sizeof(dac_dma_buffer));usb_to_dac_thread = rt_thread_create("usb2dac", usb_to_dac_thread_entry, RT_NULL,2048, 15, 10);adc_to_usb_thread = rt_thread_create("adc2usb", adc_to_usb_thread_entry, RT_NULL,2048, 15, 10);if (usb_to_dac_thread && adc_to_usb_thread){rt_thread_startup(usb_to_dac_thread);rt_thread_startup(adc_to_usb_thread);}HAL_TIM_Base_Start(&htim2);HAL_DAC_Start_DMA(&hdac1, DAC_CHANNEL_2, (uint32_t *)dac_dma_buffer, DAC_DMA_BUFFER_SIZE * 2, DAC_ALIGN_12B_R);HAL_ADC_Start_DMA(&hadc1, (uint32_t *)adc_dma_buffer, ADC_DMA_BUFFER_SIZE * 2);return 0;
}INIT_APP_EXPORT(ADC_DAC_DMA_Init);

4. 效果演示

  • 播放效果。上抖音搜索仙剑奇侠传主题曲《此生不换》,通过板子播放。最初是用PA5脚直推一个喇叭, 发现推不动,声音非常非常小。后来换了一个自带功放的喇叭,效果不错。
    在这里插入图片描述
    在这里插入图片描述

  • 录音效果。打开录音机, 录一段我说话的声音, 然后播放出来, 效果不错.
    在这里插入图片描述

  • 播放的同时录音。在通过板子播放《此生不换》的时候,打开录音机也同步录音。录音完毕,用板子再播放,杂音较大(这个喇叭好像不太给力); 用电脑播放, 声音很不错, 这说明录音的文件应该没有问题.
    在这里插入图片描述

5.附录1: 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 == 0AUDIO_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)),
#elseAUDIO_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)),
#endifAUDIO_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 == 0AUDIO_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)),
#elseAUDIO_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)),
#endifAUDIO_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,
#endif0x00
};
#endifUSB_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);/* 写入 ring-buffer */extern struct rt_ringbuffer usb_to_dac_ring;rt_ringbuffer_put(&usb_to_dac_ring, read_buffer, 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);
}
#endifstatic 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
};
#endifstruct 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_DESCusbd_desc_register(busid, &audio_v1_descriptor);
#elseusbd_desc_register(busid, audio_v1_descriptor);
#endifusbd_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 == 1usbd_add_endpoint(busid, &audio_out_feedback_ep);
#endifusbd_initialize(busid, reg_base, usbd_event_handler);
}

6.附录2: tim_adc_dac_dma_usb.c完整代码

  • 这个代码包含了完整的tim+adc+dac+dma的初始化代码, 不再需要在stm32h7xx_hal_msp.c中进行初始化.
  • 注意需要在rt-thread的menuconfig中开启cherryusb, 以及adc和dac和硬件timer的驱动框架 (只有开启了相应的驱动框架, stm32h7xx_hal_adc.h, stm32h7xx_hal_dac.h等HAL库文件, 才会被纳入到编译中)
#include <drv_common.h>
#include "usbd_core.h"
#include "trace_log.h"#define USB_NOCACHE_RAM_SECTION __attribute__((section(".noncacheable")))
#define USB_MEM_ALIGNX __attribute__((aligned(32)))ADC_HandleTypeDef hadc1;
DMA_HandleTypeDef hdma_adc1;DAC_HandleTypeDef hdac1;
DMA_HandleTypeDef hdma_dac1_ch2;TIM_HandleTypeDef htim2;// 定义USB音频参数
#define USB_AUDIO_SAMPLE_RATE    16000
#define USB_AUDIO_CHANNELS       2
#define USB_AUDIO_BYTES_PER_SAMPLE 2  // 16bit
#define USB_AUDIO_PACKET_SIZE    64   // 与USB定义匹配// 定义ringbuffer大小
#define USB_TO_DAC_BUFFER_SIZE   4096  // USB→DAC缓冲
#define ADC_TO_USB_BUFFER_SIZE   4096  // ADC→USB缓冲// 创建ringbuffer
struct rt_ringbuffer usb_to_dac_ring;
static struct rt_ringbuffer adc_to_usb_ring;
static uint8_t usb_to_dac_buf[USB_TO_DAC_BUFFER_SIZE];
static uint8_t adc_to_usb_buf[ADC_TO_USB_BUFFER_SIZE];// 信号量和线程
static rt_sem_t adc_data_ready_sem = RT_NULL;
static rt_sem_t dac_data_req_sem = RT_NULL;
static volatile uint8_t buffer_ready_flag = 0;static rt_thread_t usb_to_dac_thread = RT_NULL;
static rt_thread_t adc_to_usb_thread = RT_NULL;// 修改缓冲区定义
#define DAC_DMA_BUFFER_SIZE   (USB_AUDIO_PACKET_SIZE*10/2)  // 320个16位样本
#define ADC_DMA_BUFFER_SIZE   (USB_AUDIO_PACKET_SIZE*10/2)  // 320个16位样本USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint16_t dac_dma_buffer[DAC_DMA_BUFFER_SIZE * 2];  // 双缓冲
USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint16_t adc_dma_buffer[ADC_DMA_BUFFER_SIZE * 2];  // 双缓冲
USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t usb_send_buffer[USB_AUDIO_PACKET_SIZE];
USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint16_t temp_buffer[DAC_DMA_BUFFER_SIZE];static void MX_ADC1_Init(void);
static void MX_DAC1_Init(void);
static void MX_TIM2_Init(void);
static void MX_DMA_Init(void);extern volatile uint8_t ep_tx_busy_flag;
#define AUDIO_IN_EP  0x81
#define AUDIO_OUT_EP 0x02void HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef* hadc)
{if (hadc->Instance == ADC1){// 处理前半部分ADC数据for (int i = 0; i < ADC_DMA_BUFFER_SIZE; i++){// 将12位ADC数据转换为16位音频数据uint16_t adc_value = adc_dma_buffer[i];int16_t audio_sample = adc_value - 32768;// 写入ringbufferrt_ringbuffer_put(&adc_to_usb_ring, (uint8_t *)&audio_sample, 2);}rt_sem_release(adc_data_ready_sem);}
}void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{if (hadc->Instance == ADC1){// 处理后半部分ADC数据for (int i = ADC_DMA_BUFFER_SIZE; i < ADC_DMA_BUFFER_SIZE * 2; i++){uint16_t adc_value = adc_dma_buffer[i];int16_t audio_sample = adc_value - 32768;rt_ringbuffer_put(&adc_to_usb_ring, (uint8_t *)&audio_sample, 2);}rt_sem_release(adc_data_ready_sem);}
}void HAL_DACEx_ConvHalfCpltCallbackCh2(DAC_HandleTypeDef* hdac)
{// 前半缓冲区已传输完成,准备填充后半缓冲区buffer_ready_flag = 1;  // 标记需要填充前半缓冲区rt_sem_release(dac_data_req_sem);
}void HAL_DACEx_ConvCpltCallbackCh2(DAC_HandleTypeDef* hdac)
{// 后半缓冲区已传输完成,准备填充前半缓冲区  buffer_ready_flag = 2;  // 标记需要填充后半缓冲区rt_sem_release(dac_data_req_sem);
}static void usb_to_dac_thread_entry(void *parameter)
{while (1){// 等待DAC缓冲区需要填充if (rt_sem_take(dac_data_req_sem, RT_WAITING_FOREVER) == RT_EOK){uint16_t *target_buffer;// 根据标志确定填充哪个缓冲区if (buffer_ready_flag == 1)target_buffer = &dac_dma_buffer[0];  // 前半缓冲区elsetarget_buffer = &dac_dma_buffer[DAC_DMA_BUFFER_SIZE];  // 后半缓冲区// 从USB ringbuffer读取数据if (rt_ringbuffer_data_len(&usb_to_dac_ring) >= DAC_DMA_BUFFER_SIZE * 2){size_t read_len = rt_ringbuffer_get(&usb_to_dac_ring, (uint8_t *)temp_buffer, DAC_DMA_BUFFER_SIZE * 2);// 数据格式转换并填充目标缓冲区for (int i = 0; i < read_len/2; i++){int16_t audio_sample = ((int16_t *)temp_buffer)[i];target_buffer[i] = (audio_sample + 32768) >> 4;}} else{// 数据不够时填充静音memset(target_buffer, 0x80, DAC_DMA_BUFFER_SIZE * 2);}}}
}static void adc_to_usb_thread_entry(void *parameter)
{extern volatile bool tx_flag;while (1){if (tx_flag) {while (rt_ringbuffer_data_len(&adc_to_usb_ring) < sizeof(usb_send_buffer)){rt_sem_take(adc_data_ready_sem, RT_WAITING_FOREVER);}size_t read_len = rt_ringbuffer_get(&adc_to_usb_ring, usb_send_buffer, sizeof(usb_send_buffer));ep_tx_busy_flag = 1;usbd_ep_start_write(0, AUDIO_IN_EP, usb_send_buffer, read_len);while(ep_tx_busy_flag){}}else {rt_thread_delay(10);}}
}int ADC_DAC_DMA_Init(void)
{MX_DMA_Init();MX_ADC1_Init();MX_DAC1_Init();MX_TIM2_Init();rt_ringbuffer_init(&usb_to_dac_ring, usb_to_dac_buf, sizeof(usb_to_dac_buf));rt_ringbuffer_init(&adc_to_usb_ring, adc_to_usb_buf, sizeof(adc_to_usb_buf));adc_data_ready_sem = rt_sem_create("adc_ready", 0, RT_IPC_FLAG_FIFO);dac_data_req_sem = rt_sem_create("dac_buf", 0, RT_IPC_FLAG_FIFO);memset(dac_dma_buffer, 0x80, sizeof(dac_dma_buffer));usb_to_dac_thread = rt_thread_create("usb2dac", usb_to_dac_thread_entry, RT_NULL,2048, 15, 10);adc_to_usb_thread = rt_thread_create("adc2usb", adc_to_usb_thread_entry, RT_NULL,2048, 15, 10);if (usb_to_dac_thread && adc_to_usb_thread){rt_thread_startup(usb_to_dac_thread);rt_thread_startup(adc_to_usb_thread);}HAL_TIM_Base_Start(&htim2);HAL_DAC_Start_DMA(&hdac1, DAC_CHANNEL_2, (uint32_t *)dac_dma_buffer, DAC_DMA_BUFFER_SIZE * 2, DAC_ALIGN_12B_R);HAL_ADC_Start_DMA(&hadc1, (uint32_t *)adc_dma_buffer, ADC_DMA_BUFFER_SIZE * 2);return 0;
}INIT_APP_EXPORT(ADC_DAC_DMA_Init);/*** @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 ConfigurationPA1_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 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 ConfigurationPA5     ------> 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 ConfigurationPA5     ------> 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 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 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*/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 = 11 /*240MHz/12=20MHz*/;htim2.Init.CounterMode = TIM_COUNTERMODE_UP;htim2.Init.Period = 625 /*20MHz/16kHz/2=625, here 2 is stereo*/;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 */HAL_DMA_IRQHandler(&hdma_adc1);/* USER CODE BEGIN DMA1_Stream0_IRQn 1 *//* USER CODE END DMA1_Stream0_IRQn 1 */
}/*** @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 */HAL_DMA_IRQHandler(&hdma_dac1_ch2);/* USER CODE BEGIN DMA1_Stream1_IRQn 1 *//* USER CODE END DMA1_Stream1_IRQn 1 */
}/*** @brief This function handles ADC1 and ADC2 global interrupts.*/void ADC_IRQHandler(void){/* USER CODE BEGIN ADC_IRQn 0 *//* USER CODE END ADC_IRQn 0 */HAL_ADC_IRQHandler(&hadc1);/* USER CODE BEGIN ADC_IRQn 1 *//* USER CODE END ADC_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);/* DMA1_Stream1_IRQn interrupt configuration */HAL_NVIC_SetPriority(DMA1_Stream1_IRQn, 0, 0);HAL_NVIC_EnableIRQ(DMA1_Stream1_IRQn);}
http://www.xdnf.cn/news/1270261.html

相关文章:

  • traceroute命令调试网络
  • C++高频知识点(十七)
  • 《Resolving tissue complexity by multimodal spatial omics modeling with MISO》
  • 9. 堆和栈有什么区别
  • Vitalik谈以太坊:ETH财库储备策略“有益且有价值”
  • Kotlin 协程线程切换机制详解
  • AG32cpld实现一个UartTx“外设”
  • 智慧能源设备巡检缺陷漏检率↓76%:陌讯多模态融合算法实战解析
  • Android适配最新SplashScreen方案:让启动页不再“翻车“
  • webrtc弱网-BandwidthQualityScaler 源码分析与算法原理
  • 视图是什么?有什么用?什么时候用?MySQL中的视图
  • Android MediaCodec 音视频编解码技术详解
  • linux php版本降级,dnf版本控制
  • Amazon Linux 训练lora模型的方式
  • Web自动化技术选择
  • 回答“http协议 ,js组件化,工程化, seo优化策略 ,针对不同平台终端适配 web标注和兼容性”
  • 基于遗传优化的智能灌溉系统控制策略matlab仿真
  • Beelzebub靶机通关教程
  • 【工具】Python多环境管理
  • 【Java基础】字符串不可变性、string的intern原理
  • 【李宏毅-2024】第六讲 大语言模型的训练过程1——预训练(Pre-training)
  • 搭建若依前后端分离版本的开发环境
  • 鸿蒙分布式任务调度深度剖析:跨设备并行计算的最佳实践
  • 在nodejs中使用Java方法
  • windows、linux应急响应入侵排查
  • React中实现完整的登录鉴权与权限控制系统
  • 云服务器--阿里云OSS(2)【Springboot使用阿里云OSS】
  • 原生Vim操作大全
  • Python映射合并技术:多源数据集成的高级策略与工程实践
  • Jmeter性能测试之安装及启动Jmeter