Cherryusb UAC例程对接STM32内置ADC和PWM播放音乐和录音(下)=>UAC+STM32 ADC+PWM实现录音和播放
1. 程序基本框架
整个程序框架, 与之前的一篇文章《Cherryusb UAC例程对接STM32内置ADC和DAC播放音乐和录音(中)=>UAC+STM32 ADC+DAC实现录音和播放》基本一致, 只是这次将DAC替换成了PWM。因此这里不再赘述了。
2. audio_v1_mic_speaker_multichan_template.c的修改说明(略)
参考《Cherryusb UAC例程对接STM32内置ADC和DAC播放音乐和录音(中)=>UAC+STM32 ADC+DAC实现录音和播放》
3. tim_adc_tim5_dma_usb.c主程序的实现说明
- 因为是移植的之前的程序, 程序中仍然保持dac这个词语,不过表示的是pwm。
- 注意dac_dma_buffer使用的是32位的, 而不是16位的。因为pwm是用tim5产生的,tim5是32bits计数器。
- temp_buffer仍然是16位的, 因为是用来接收usb发过来的音频数据, 仍然是16bits
USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint32_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];
- dac线程: 用于将usb接收的数据并转换为pwm的ccr数据, 并填充到dac_dma_buffer中。
- 因为dac_dma_buffer是32位的, 因此需要将指针target_buffer改为32bits
- 最核心的是将audio_sample转换为pwm的ccr数据。转换公式: ccr = (audio_sample + 32768) * 1499/65535
audio_sample是16bits有符号的, (audio_sample + 32768)转为16bits无符号的
其中1499是pwm的period, 65535是16bits的最大值。
(audio_sample + 32768)/65535表示进行归一化[0, 1)范围,然后乘以1499,转化为pwm的满量程范围[0,1499)
因此, 转换后的ccr值范围是0~1499。 - 从这里的代码可以看出,我们并没有区分左右声道,而是把数据都填充到了dma_buffer。这种做法相当于是用一个pwm,同时播放了左声道和右声道的数据。需要的播放采样率加倍了(16kHzx2=32kHz)。
- 实际不推荐这种做法,因为这种“重复样本” 做法会直接引入镜像噪声,听感差。
static void usb_to_dac_thread_entry(void *parameter)
{my_sprintf("tim_adc_dac.c", __LINE__, __func__);while (1){// 等待DAC缓冲区需要填充my_sprintf2("tim_adc_dac.c", __LINE__, __func__, -2, "go=> rt_sem_take");if (rt_sem_take(dac_data_req_sem, RT_WAITING_FOREVER) == RT_EOK){uint32_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读取数据my_sprintf2("tim_adc_dac.c", __LINE__, __func__, -2, "go=> rt_ringbuffer_data_len");if (rt_ringbuffer_data_len(&usb_to_dac_ring) >= DAC_DMA_BUFFER_SIZE * 2){my_sprintf2("tim_adc_dac.c", __LINE__, __func__, -2, "go=> rt_ringbuffer_get");size_t read_len = rt_ringbuffer_get(&usb_to_dac_ring, (uint8_t *)temp_buffer, DAC_DMA_BUFFER_SIZE * 2);my_sprintf2("tim_adc_dac.c", __LINE__, __func__, read_len, "<= read_len"); // 数据格式转换并填充目标缓冲区for (int i = 0; i < read_len/2; i++){int16_t audio_sample = ((int16_t *)temp_buffer)[i];target_buffer[i] = ((uint16_t)((int16_t)audio_sample + 32768) * 1499)/65535;}} else{// 数据不够时填充静音my_sprintf2("tim_adc_dac.c", __LINE__, __func__, -2, "go=> memset");for(int i = 0; i < DAC_DMA_BUFFER_SIZE; i++){target_buffer[i] = 1499/2;}// memset(target_buffer, 0x00, DAC_DMA_BUFFER_SIZE * 4);}}}
}
- PWM的DMA传输完成和半传输完成回调函数。这个与之前的DAC是一样的。用于发信号量,通知dac线程填充dma_buffer。
void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim)
{my_sprintf("tim_adc_dac.c", __LINE__, __func__);rt_pin_write(LED0_PIN, PIN_HIGH);if (htim->Instance == TIM5){// 后半缓冲区已传输完成,准备填充前半缓冲区 buffer_ready_flag = 2; // 标记需要填充后半缓冲区my_sprintf2("tim_adc_dac.c", __LINE__, __func__, -2, "go=> rt_sem_release");rt_sem_release(dac_data_req_sem);}
}void HAL_TIM_PWM_PulseFinishedHalfCpltCallback(TIM_HandleTypeDef *htim)
{my_sprintf("tim_adc_dac.c", __LINE__, __func__);rt_pin_write(LED0_PIN, PIN_LOW);if (htim->Instance == TIM5){// 前半缓冲区已传输完成,准备填充后半缓冲区buffer_ready_flag = 1; // 标记需要填充前半缓冲区my_sprintf2("tim_adc_dac.c", __LINE__, __func__, -2, "go=> rt_sem_release");rt_sem_release(dac_data_req_sem);}
}
- 需要特别注意,TIM12的频率需要设置为16kHzx2=32kHz,因为UAC的数据是16kHz双声道立体声。
- 效果,相当于是用32kHz来播放,用一个DAC来播放,同时播放了左声道和右声道的数据。
- 咨询Kimi-K2,说这种方式可以播放,但是效果不太好。“重复样本” 这种粗暴做法会直接引入镜像噪声,听感差。
- 优化的话,建议tim12仍然是用16kHz,在dac线程中,将audio_sample转换为pwm的ccr数据时,只抽取奇数(或偶数)序号的数据填充dma_buffer。
htim12.Instance = TIM12;htim12.Init.Prescaler = 0;htim12.Init.CounterMode = TIM_COUNTERMODE_UP;htim12.Init.Period = 7499; /*注意, 需要240e6/7500=32kHz*/htim12.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;htim12.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
- 需要注意,ADC的ClockPrescaler必须大于等于ADC_CLOCK_ASYNC_DIV2。这可能是一个hal库的bug。
hadc1.Instance = ADC1;hadc1.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV2; /*注意: 这里ADC_CLOCK_ASYNC_DIV1输出异常, 必须用至少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();}
4. 效果演示
- 播放效果。上抖音搜索仙剑奇侠传主题曲《此生不换》,通过板子播放。
- 用PA2脚直推一个喇叭, 可以推动,声音稍微小一点,不过音质感觉很不错。后来换了一个自带功放的喇叭,效果也不错。
- 总的感觉,用PWM播放,似乎比用DAC播放音质效果还要好一些。(注意,DAC输出没有加任何RC滤波,纯PWM直驱)
5.附录1: audio_v1_mic_speaker_multichan_template.c 完整代码(略)
参考《Cherryusb UAC例程对接STM32内置ADC和DAC播放音乐和录音(中)=>UAC+STM32 ADC+DAC实现录音和播放》
6.附录2: tim_adc_tim5_dma_usb.c完整代码
#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;TIM_HandleTypeDef htim2;
TIM_HandleTypeDef htim12;
TIM_HandleTypeDef htim5;
DMA_HandleTypeDef hdma_tim5_ch3;#define LED0_PIN GET_PIN(G, 11)// 定义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 uint32_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_TIM2_Init(void);
static void MX_TIM5_Init(void);
static void MX_TIM12_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)
{my_sprintf("tim_adc_dac.c", __LINE__, __func__);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);}my_sprintf2("tim_adc_dac.c", __LINE__, __func__, -2, "go=> rt_sem_release");rt_sem_release(adc_data_ready_sem);}
}void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{my_sprintf("tim_adc_dac.c", __LINE__, __func__);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);}my_sprintf2("tim_adc_dac.c", __LINE__, __func__, -2, "go=> rt_sem_release");rt_sem_release(adc_data_ready_sem);}
}void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim)
{my_sprintf("tim_adc_dac.c", __LINE__, __func__);rt_pin_write(LED0_PIN, PIN_HIGH);if (htim->Instance == TIM5){// 后半缓冲区已传输完成,准备填充前半缓冲区 buffer_ready_flag = 2; // 标记需要填充后半缓冲区my_sprintf2("tim_adc_dac.c", __LINE__, __func__, -2, "go=> rt_sem_release");rt_sem_release(dac_data_req_sem);}
}void HAL_TIM_PWM_PulseFinishedHalfCpltCallback(TIM_HandleTypeDef *htim)
{my_sprintf("tim_adc_dac.c", __LINE__, __func__);rt_pin_write(LED0_PIN, PIN_LOW);if (htim->Instance == TIM5){// 前半缓冲区已传输完成,准备填充后半缓冲区buffer_ready_flag = 1; // 标记需要填充前半缓冲区my_sprintf2("tim_adc_dac.c", __LINE__, __func__, -2, "go=> rt_sem_release");rt_sem_release(dac_data_req_sem);}
}static void usb_to_dac_thread_entry(void *parameter)
{my_sprintf("tim_adc_dac.c", __LINE__, __func__);while (1){// 等待DAC缓冲区需要填充my_sprintf2("tim_adc_dac.c", __LINE__, __func__, -2, "go=> rt_sem_take");if (rt_sem_take(dac_data_req_sem, RT_WAITING_FOREVER) == RT_EOK){uint32_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读取数据my_sprintf2("tim_adc_dac.c", __LINE__, __func__, -2, "go=> rt_ringbuffer_data_len");if (rt_ringbuffer_data_len(&usb_to_dac_ring) >= DAC_DMA_BUFFER_SIZE * 2){my_sprintf2("tim_adc_dac.c", __LINE__, __func__, -2, "go=> rt_ringbuffer_get");size_t read_len = rt_ringbuffer_get(&usb_to_dac_ring, (uint8_t *)temp_buffer, DAC_DMA_BUFFER_SIZE * 2);my_sprintf2("tim_adc_dac.c", __LINE__, __func__, read_len, "<= read_len"); // 数据格式转换并填充目标缓冲区for (int i = 0; i < read_len/2; i++){int16_t audio_sample = ((int16_t *)temp_buffer)[i];target_buffer[i] = ((uint16_t)((int16_t)audio_sample + 32768) * 1499)/65535;}} else{// 数据不够时填充静音my_sprintf2("tim_adc_dac.c", __LINE__, __func__, -2, "go=> memset");for(int i = 0; i < DAC_DMA_BUFFER_SIZE; i++){target_buffer[i] = 1499/2;}// memset(target_buffer, 0x00, DAC_DMA_BUFFER_SIZE * 4);}}}
}static void adc_to_usb_thread_entry(void *parameter)
{my_sprintf("tim_adc_dac.c", __LINE__, __func__); extern volatile bool tx_flag;while (1){if (tx_flag) {my_sprintf2("tim_adc_dac.c", __LINE__, __func__, -2, "go=> rt_ringbuffer_data_len");while (rt_ringbuffer_data_len(&adc_to_usb_ring) < sizeof(usb_send_buffer)){my_sprintf2("tim_adc_dac.c", __LINE__, __func__, -2, "go=> rt_sem_take");rt_sem_take(adc_data_ready_sem, RT_WAITING_FOREVER);}my_sprintf2("tim_adc_dac.c", __LINE__, __func__, -2, "go=> rt_ringbuffer_get");size_t read_len = rt_ringbuffer_get(&adc_to_usb_ring, usb_send_buffer, sizeof(usb_send_buffer));my_sprintf2("tim_adc_dac.c", __LINE__, __func__, read_len, "<= read_len"); ep_tx_busy_flag = 1;my_sprintf2("tim_adc_dac.c", __LINE__, __func__, -2, "go=> usbd_ep_start_write");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_TIM5_DMA_Init(void)
{MX_DMA_Init();MX_ADC1_Init();MX_TIM2_Init();MX_TIM5_Init();MX_TIM12_Init();rt_pin_mode(LED0_PIN, PIN_MODE_OUTPUT);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, 1, 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_TIM_Base_Start(&htim12);HAL_ADC_Start_DMA(&hadc1, (uint32_t *)adc_dma_buffer, ADC_DMA_BUFFER_SIZE * 2);HAL_TIM_PWM_Start_DMA(&htim5, TIM_CHANNEL_3, (uint32_t *)dac_dma_buffer, DAC_DMA_BUFFER_SIZE * 2);return 0;
}INIT_APP_EXPORT(ADC_TIM5_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 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){HAL_DMA_MuxSyncConfigTypeDef pSyncConfig;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 */}else if(htim_base->Instance==TIM5){/* USER CODE BEGIN TIM5_MspInit 0 *//* USER CODE END TIM5_MspInit 0 *//* Peripheral clock enable */__HAL_RCC_TIM5_CLK_ENABLE();/* TIM5 DMA Init *//* TIM5_CH3 Init */hdma_tim5_ch3.Instance = DMA1_Stream1;hdma_tim5_ch3.Init.Request = DMA_REQUEST_TIM5_CH3;hdma_tim5_ch3.Init.Direction = DMA_MEMORY_TO_PERIPH;hdma_tim5_ch3.Init.PeriphInc = DMA_PINC_DISABLE;hdma_tim5_ch3.Init.MemInc = DMA_MINC_ENABLE;hdma_tim5_ch3.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;hdma_tim5_ch3.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;hdma_tim5_ch3.Init.Mode = DMA_CIRCULAR;hdma_tim5_ch3.Init.Priority = DMA_PRIORITY_LOW;hdma_tim5_ch3.Init.FIFOMode = DMA_FIFOMODE_ENABLE;hdma_tim5_ch3.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;hdma_tim5_ch3.Init.MemBurst = DMA_MBURST_INC4;hdma_tim5_ch3.Init.PeriphBurst = DMA_PBURST_SINGLE;if (HAL_DMA_Init(&hdma_tim5_ch3) != HAL_OK){Error_Handler();}pSyncConfig.SyncSignalID = HAL_DMAMUX1_SYNC_TIM12_TRGO;pSyncConfig.SyncPolarity = HAL_DMAMUX_SYNC_RISING;pSyncConfig.SyncEnable = ENABLE;pSyncConfig.EventEnable = DISABLE;pSyncConfig.RequestNumber = 1;if (HAL_DMAEx_ConfigMuxSync(&hdma_tim5_ch3, &pSyncConfig) != HAL_OK){Error_Handler();}__HAL_LINKDMA(htim_base,hdma[TIM_DMA_ID_CC3],hdma_tim5_ch3);/* TIM5 interrupt Init */HAL_NVIC_SetPriority(TIM5_IRQn, 0, 0);HAL_NVIC_EnableIRQ(TIM5_IRQn);/* USER CODE BEGIN TIM5_MspInit 1 *//* USER CODE END TIM5_MspInit 1 */}else if(htim_base->Instance==TIM12){/* USER CODE BEGIN TIM12_MspInit 0 *//* USER CODE END TIM12_MspInit 0 *//* Peripheral clock enable */__HAL_RCC_TIM12_CLK_ENABLE();/* USER CODE BEGIN TIM12_MspInit 1 *//* USER CODE END TIM12_MspInit 1 */}}void HAL_TIM_MspPostInit(TIM_HandleTypeDef* htim){GPIO_InitTypeDef GPIO_InitStruct = {0};if(htim->Instance==TIM5){/* USER CODE BEGIN TIM5_MspPostInit 0 *//* USER CODE END TIM5_MspPostInit 0 */__HAL_RCC_GPIOA_CLK_ENABLE();/**TIM5 GPIO ConfigurationPA2 ------> TIM5_CH3*/GPIO_InitStruct.Pin = GPIO_PIN_2;GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;GPIO_InitStruct.Alternate = GPIO_AF2_TIM5;HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);/* USER CODE BEGIN TIM5_MspPostInit 1 *//* USER CODE END TIM5_MspPostInit 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 */}else if(htim_base->Instance==TIM5){/* USER CODE BEGIN TIM5_MspDeInit 0 *//* USER CODE END TIM5_MspDeInit 0 *//* Peripheral clock disable */__HAL_RCC_TIM5_CLK_DISABLE();/* TIM5 DMA DeInit */HAL_DMA_DeInit(htim_base->hdma[TIM_DMA_ID_CC3]);/* TIM5 interrupt DeInit */HAL_NVIC_DisableIRQ(TIM5_IRQn);/* USER CODE BEGIN TIM5_MspDeInit 1 *//* USER CODE END TIM5_MspDeInit 1 */}else if(htim_base->Instance==TIM12){/* USER CODE BEGIN TIM12_MspDeInit 0 *//* USER CODE END TIM12_MspDeInit 0 *//* Peripheral clock disable */__HAL_RCC_TIM12_CLK_DISABLE();/* USER CODE BEGIN TIM12_MspDeInit 1 *//* USER CODE END TIM12_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; /*注意: 这里ADC_CLOCK_ASYNC_DIV1输出异常, 必须用至少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 = 14999;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 TIM5 Initialization Function* @param None* @retval None*/static void MX_TIM5_Init(void){/* USER CODE BEGIN TIM5_Init 0 *//* USER CODE END TIM5_Init 0 */TIM_ClockConfigTypeDef sClockSourceConfig = {0};TIM_MasterConfigTypeDef sMasterConfig = {0};TIM_OC_InitTypeDef sConfigOC = {0};/* USER CODE BEGIN TIM5_Init 1 *//* USER CODE END TIM5_Init 1 */htim5.Instance = TIM5;htim5.Init.Prescaler = 0;htim5.Init.CounterMode = TIM_COUNTERMODE_UP;htim5.Init.Period = 1499;htim5.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;htim5.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;if (HAL_TIM_Base_Init(&htim5) != HAL_OK){Error_Handler();}sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;if (HAL_TIM_ConfigClockSource(&htim5, &sClockSourceConfig) != HAL_OK){Error_Handler();}if (HAL_TIM_PWM_Init(&htim5) != HAL_OK){Error_Handler();}sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;if (HAL_TIMEx_MasterConfigSynchronization(&htim5, &sMasterConfig) != HAL_OK){Error_Handler();}sConfigOC.OCMode = TIM_OCMODE_PWM1;sConfigOC.Pulse = 749;sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;if (HAL_TIM_PWM_ConfigChannel(&htim5, &sConfigOC, TIM_CHANNEL_3) != HAL_OK){Error_Handler();}/* USER CODE BEGIN TIM5_Init 2 *//* USER CODE END TIM5_Init 2 */HAL_TIM_MspPostInit(&htim5);}/*** @brief TIM12 Initialization Function* @param None* @retval None*/static void MX_TIM12_Init(void){/* USER CODE BEGIN TIM12_Init 0 *//* USER CODE END TIM12_Init 0 */TIM_ClockConfigTypeDef sClockSourceConfig = {0};TIM_MasterConfigTypeDef sMasterConfig = {0};/* USER CODE BEGIN TIM12_Init 1 *//* USER CODE END TIM12_Init 1 */htim12.Instance = TIM12;htim12.Init.Prescaler = 0;htim12.Init.CounterMode = TIM_COUNTERMODE_UP;htim12.Init.Period = 7499; /*注意, 需要240e6/7500=32kHz*/htim12.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;htim12.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;if (HAL_TIM_Base_Init(&htim12) != HAL_OK){Error_Handler();}sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;if (HAL_TIM_ConfigClockSource(&htim12, &sClockSourceConfig) != HAL_OK){Error_Handler();}sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;if (HAL_TIMEx_MasterConfigSynchronization(&htim12, &sMasterConfig) != HAL_OK){Error_Handler();}/* USER CODE BEGIN TIM12_Init 2 *//* USER CODE END TIM12_Init 2 */}/*** Enable DMA controller clock*/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);/* DMAMUX1_OVR_IRQn interrupt configuration */HAL_NVIC_SetPriority(DMAMUX1_OVR_IRQn, 0, 0);HAL_NVIC_EnableIRQ(DMAMUX1_OVR_IRQn);}/**
* @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_tim5_ch3);/* USER CODE BEGIN DMA1_Stream1_IRQn 1 *//* USER CODE END DMA1_Stream1_IRQn 1 */
}/*** @brief This function handles TIM5 global interrupt.*/
void TIM5_IRQHandler(void)
{/* USER CODE BEGIN TIM5_IRQn 0 *//* USER CODE END TIM5_IRQn 0 */HAL_TIM_IRQHandler(&htim5);/* USER CODE BEGIN TIM5_IRQn 1 *//* USER CODE END TIM5_IRQn 1 */
}/**
* @brief This function handles DMAMUX1 overrun interrupt.
*/
void DMAMUX1_OVR_IRQHandler(void)
{/* USER CODE BEGIN DMAMUX1_OVR_IRQn 0 *//* USER CODE END DMAMUX1_OVR_IRQn 0 */// Handle DMA1_Stream1HAL_DMAEx_MUX_IRQHandler(&hdma_tim5_ch3);/* USER CODE BEGIN DMAMUX1_OVR_IRQn 1 *//* USER CODE END DMAMUX1_OVR_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 */
}