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
c 复制代码
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)。
  • 实际不推荐这种做法,因为这种"重复样本" 做法会直接引入镜像噪声,听感差。
c 复制代码
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];  // 前半缓冲区
            else
                target_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。
c 复制代码
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。
c 复制代码
   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。
c 复制代码
  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完整代码

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 0x02


void 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;
          
          // 写入ringbuffer
          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_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];  // 前半缓冲区
            else
                target_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 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)
 {
   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 Configuration
     PA2     ------> 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_Stream1
  HAL_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 */
}
相关推荐
云山工作室2 分钟前
基于单片机电子负载的设计(论文+源码)
单片机·嵌入式硬件
GalaxySinCos4 小时前
09 51单片机之LED点阵屏
单片机·嵌入式硬件·51单片机
点灯小铭6 小时前
基于51单片机射频RFID卡考勤上课上班人数计数系统设计
单片机·嵌入式硬件·毕业设计·51单片机·课程设计
苏格拉真没有底6 小时前
MCU 软件断点注意事项!!!
单片机·嵌入式硬件
Shang131130487917 小时前
THCV215一种高速视频数据收发器,采用低电压差分信号(LVDS)技术支持高速串行数据传输,支持1080p/60Hz高分辨率传输
单片机·嵌入式硬件·高速视频收发器·thcv215·高速视频数据收发器
No0d1es16 小时前
202506 电子学会青少年等级考试机器人五级器人理论真题
单片机·嵌入式硬件·青少年编程·机器人·电子学会·五级·理论综合
范纹杉想快点毕业19 小时前
C 语言主控开发与显控开发能力体系及技术栈详解,STM32、QT、嵌入式、边缘系统显示
stm32·单片机·tcp/ip·microsoft·fpga开发·51单片机·wpf
LJWWD20 小时前
AI小智单片机esps32-s3烧录教程
单片机·嵌入式硬件
嵌入式小李1 天前
stm32项目(25)——基于stm32的植物生长箱环境监测系统
stm32·单片机·嵌入式硬件