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 */
}
相关推荐
点灯小铭7 小时前
基于单片机的多路热电偶温度监测与报警器
单片机·嵌入式硬件·毕业设计·课程设计·期末大作业
tianyue10011 小时前
STM32G431 ADC 多个channel 采集
stm32·单片机·嵌入式硬件
清风66666613 小时前
基于单片机的水泵效率温差法测量与报警系统设计
单片机·嵌入式硬件·毕业设计·课程设计·期末大作业
z203483152014 小时前
定时器练习报告
单片机·嵌入式硬件
zk0014 小时前
内容分类目录
单片机·嵌入式硬件
安生生申15 小时前
STM32 ESP8266连接ONENET
c语言·stm32·单片机·嵌入式硬件·esp8266
广药门徒15 小时前
电子器件烧毁的底层逻辑与避坑指南
单片机·嵌入式硬件
点灯小铭19 小时前
基于单片机的社区医院小型高压蒸汽灭菌自动控制器设计
单片机·嵌入式硬件·毕业设计·课程设计·期末大作业
youcans_19 小时前
【动手学STM32G4】(3)STM32G431之定时器
stm32·单片机·嵌入式硬件·定时器
悠哉悠哉愿意19 小时前
【嵌入式学习笔记】AD/DA
笔记·单片机·嵌入式硬件·学习