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