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 */
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/93068.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/93068.shtml
英文地址,请注明出处:http://en.pswp.cn/bicheng/93068.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

1 JQ6500语音播报模块详解(STM32)

系列文章目录 文章目录系列文章目录前言1 JQ6500简介2 基本参数说明2.1 硬件参数2.2 模块管脚说明3 控制方式3.1 通信格式3.2 通信指令4 硬件设计5 软件设计5.1 main.c5.2 board_config5.2.1board_config.h5.2.2 board_config.c5.3 module_config5.3.1 module_config.h5.3.2 mo…

常用数据分析工具

Tableau丨Power BI丨FineBI丨SQL丨影刀丨Excel丨Python丨 参考视频&#xff1a;【戴师兄】数据分析有哪些必学工具&#xff1f;2023最新版&#xff01;Tableau丨Power BI丨FineBI丨SQL丨影刀丨Excel丨Python丨课程教程自学攻略_哔哩哔哩_bilibili 文档资料&#xff1a; 【戴师兄…

OBOO鸥柏丨智能会议平板教学查询一体机交互式触摸终端招标投标核心标底参数要求

整机参数要求&#xff1a;55寸/65寸/75寸/85-86寸/98寸/100寸/110寸/115寸智能会议平板教学触控一体机/智慧黑板触摸屏招标投标核心标底参数要求1、整机屏幕采用≥采用超高清原厂原包原装工业LCD液晶屏面板&#xff1b;具有高色域&#xff0c;显示动态视频、web及3D动画时&…

无人机在环保监测中的应用:低空经济发展的智能监测与高效治理

一、行业背景与技术革新 随着全球环境问题日益严峻&#xff0c;传统环保监测手段已难以满足现代环境管理的需求。固定监测站点建设成本高、覆盖范围有限&#xff0c;地面巡查效率低下且存在安全风险。在此背景下&#xff0c;无人机技术凭借其独特的空间优势和技术特性&#xff…

PO、BO、VO、DTO、POJO、DAO、DO基本概念

一、图解二、相关概念 1、PO&#xff08;Persistant Object - 持久化对象&#xff09; 核心定位&#xff1a; 直接与数据库表结构一一映射的对象&#xff0c;通常用于 ORM&#xff08;对象关系映射&#xff09;框架&#xff08;如 MyBatis、Hibernate&#xff09;中。 特点&…

todoList清单(HTML+CSS+JavaScript)

&#x1f30f;个人博客主页&#xff1a; 前言&#xff1a; 前段时间学习了JavaScript&#xff0c;然后写了一个todoList小项目&#xff0c;现在和大家分享一下我的清单以及如何实现的&#xff0c;希望对大家有所帮助 &#x1f525;&#x1f525;&#x1f525;文章专题&#xff…

Mac M1探索AnythingLLM+Ollama+知识库问答

AnythingLLM内置 RAG、AI Agent、可视化/无代码的 Agent 编排&#xff0c;支持多家模型与本地/云端向量库&#xff0c;并提供多用户与可嵌入的聊天组件&#xff0c;用来快速验证“知识 模型 工具”拼成的 AI 应用。 1 AnythingLLM、Ollama准备 1&#xff09;AnythingLLM 打…

【 Navicat Premium 17 完全图形化新手指南(从零开始)】

Navicat Premium 17 完全图形化新手指南&#xff08;从零开始&#xff09; 一、准备阶段&#xff1a;清理现有环境 1. 删除已创建的测试数据库&#xff08;如需重新开始&#xff09;打开Navicat Premium 17 双击桌面图标启动程序在左侧连接面板中找到你的MySQL连接&#xff08;…

Web学习笔记5

Javascript概述1、JS简介JS是运行在浏览器的脚本编程语言&#xff0c;最初用于Web表单的校验。现在的作用主要有三个&#xff1a;网页特效、表单验证、数据交互JS由三部分组成&#xff0c;分别是ECMAscript、DOM、BOM&#xff0c;其中ECMAscript规定了JS的基本语法和规则&#…

部署一个开源的证件照系统

以下数据来自官方网站,记录下来,方便自己 项目简介 &#x1f680; 谢谢你对我们的工作感兴趣。您可能还想查看我们在图像领域的其他成果&#xff0c;欢迎来信:zeyi.linswanhub.co. HivisionIDPhoto 旨在开发一种实用、系统性的证件照智能制作算法。 它利用一套完善的AI模型工作…

Linux客户端利用MinIO对服务器数据进行同步

接上篇 Windows客户端利用MinIO对服务器数据进行同步 本篇为Linux下 操作&#xff0c;先看下我本地的系统版本 所以我这里下载的话&#xff0c;是AMD64 文档在这 因为我这里只是需要用到客户端&#xff0c;获取数据而已&#xff0c;所以我只需要下载个MC工具用来数据获取就可以…

Docker 中部署 MySQL 5.7 并远程连接 Navicat 的完整指南

个人名片 &#x1f393;作者简介&#xff1a;java领域优质创作者 &#x1f310;个人主页&#xff1a;码农阿豪 &#x1f4de;工作室&#xff1a;新空间代码工作室&#xff08;提供各种软件服务&#xff09; &#x1f48c;个人邮箱&#xff1a;[2435024119qq.com] &#x1f4f1…

自己动手造个球平衡机器人

你是否曾对那些能够精妙地保持平衡的机器设备感到好奇&#xff1f; 从无人机到独轮平衡车&#xff0c;背后都蕴藏着复杂的控制系统。 今天&#xff0c;我们来介绍一个充满挑战与乐趣的项目——制作一个球平衡机器人。这不仅是一个酷炫的摆件&#xff0c;更是一次深入学习机器…

21.Linux HTTPS服务

Linux : HTTPS服务协议传输方式端口安全性HTTP明文传输80无加密&#xff0c;可被窃听HTTPS加密传输443HTTP SSL/TLS 数据加密&#xff08;防窃听&#xff09;身份认证&#xff08;防伪装&#xff09;完整性校验&#xff08;防篡改&#xff09;OpenSSL 证书操作核心命令命令选项…

SqlSugar 跨方法 操作临时表

.net项目中时长会有用到临时表的操作结果如下所示但是在SqlSugar中可能因为会话问题导致临时表访问受限 搜索到的方式var conn (SqlConnection)sugarClient.Ado.Connection;if (conn.State ! System.Data.ConnectionState.Open) {conn.Open();}using (var cmd new SqlCommand…

怎么用飞算javaAI实现视频逐帧截图并保存

相信很多朋友都遇到过这样的需求&#xff1a;想从视频中截取特定帧作为素材&#xff0c;却苦于没有简单易用的工具&#xff0c;要么操作复杂难以精准定位&#xff0c;要么导出的图片质量不佳。市面上的视频处理软件要么功能冗余&#xff0c;要么需要付费才能使用逐帧截取功能&a…

【2】Transformers快速入门:统计语言模型是啥?

一句话看懂统计语言模型核心任务&#xff1a;教电脑判断一句话 “像不像人话” &#xff08;比如“我爱吃苹果”✅ vs “苹果吃爱我”❌&#xff09;1. 早期&#xff1a;死磕语法规则 → 失败&#xff01; 科学家思路&#xff08;1970年前&#xff09;&#xff1a; 像语文老师一…

[激光原理与应用-230]:物理学主要分支、研究对象、衍生技术及职业方向解析

物理学作为自然科学的核心学科&#xff0c;其分支体系覆盖从微观粒子到宏观宇宙的广阔领域&#xff0c;并通过交叉融合衍生出众多前沿技术。以下从经典与现代物理学分支、交叉学科、技术转化及职业方向四个维度展开分析&#xff1a;一、经典物理学分支&#xff1a;宏观世界的基…

北京JAVA基础面试30天打卡08

RocketMQ、RabbitMQ与Kafka对比及常见问题解决方案 一、概述 消息队列&#xff08;Message Queue, MQ&#xff09;是企业IT系统内部通信的核心手段&#xff0c;用于提升性能、实现系统解耦和流量削峰。它具有低耦合、可靠投递、广播、流量控制、最终一致性等功能&#xff0c;是…

【CSS 变量】让你的 CSS “活”起来:深入理解 CSS 自定义属性与主题切换

【CSS 变量】让你的 CSS “活”起来&#xff1a;深入理解 CSS 自定义属性与主题切换 所属专栏&#xff1a; 《前端小技巧集合&#xff1a;让你的代码更优雅高效》 上一篇&#xff1a; 【CSS 视觉】无需JS&#xff0c;纯 CSS 实现酷炫视觉效果&#xff08;clip-path, filter, b…