定时器简介

基本定时器,计数中断、产生DMA请求。

通用定时器,PWM输出、输入捕获、脉冲计数。

高级定时器,输出比较、互补输出带死区控制、PWM输入。

        中心对齐的计数模式可以生成对称的PWM波形信号。计数可以先增后减。

        这种模式下,PWM 的高电平和低电平时间相等,从而使得 PWM 波形在中心线上对称。 

main.c

int main(void)
{// 初始化FLASH、SYSTIC中断,使能SYSCFG时钟和PWR时钟HAL_Init();// 配置系统时钟,包括PWR等级,各AHB、APB1/2时钟参数SystemClock_Config();// 初始化GPIOMX_GPIO_Init();// 初始化DMAMX_DMA_Init();// 初始化USART1 UARTMX_USART1_UART_Init();// 初始化TIM11定时器MX_TIM11_Init();// 使用DMA接收UART数据,接收50字节HAL_UART_Receive_DMA(&huart1, RXbuf, 50);// 使能UART空闲中断__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);// 启动TIM11定时器中断HAL_TIM_Base_Start_IT(&htim11);// 主循环while (1){// 判断时间计数是否达到500msif (timecount >= 500){// 重置时间计数timecount = 0;// 打印提示信息printf("500ms time up\r\n");// 切换GPIOC_PIN_13的状态(闪烁LED)HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13);}}
}

DMA.c

MX_DMA_Init

        可以看到 DMA的初始化,其实就是

        使能DMA时钟、

        DMA中断优先级、

        使能DMA中断。

void MX_DMA_Init(void)
{// 使能DMA2控制器时钟__HAL_RCC_DMA2_CLK_ENABLE();// 配置DMA中断// 设置DMA2_Stream2中断优先级为最高(0)HAL_NVIC_SetPriority(DMA2_Stream2_IRQn, 0, 0);// 使能DMA2_Stream2中断HAL_NVIC_EnableIRQ(DMA2_Stream2_IRQn);
}

        在串口的MSP_Init中,DMA配置为普通模式。 

        根据 main函数中的

        50字节接收完后,DMA自动停止。 

USART

void MX_USART1_UART_Init(void)
{/* USER CODE BEGIN USART1_Init 0 *//* USER CODE END USART1_Init 0 *//* USER CODE BEGIN USART1_Init 1 *//* USER CODE END USART1_Init 1 */huart1.Instance = USART1;huart1.Init.BaudRate = 115200;huart1.Init.WordLength = UART_WORDLENGTH_8B;huart1.Init.StopBits = UART_STOPBITS_1;huart1.Init.Parity = UART_PARITY_NONE;huart1.Init.Mode = UART_MODE_TX_RX;huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;huart1.Init.OverSampling = UART_OVERSAMPLING_16;if (HAL_UART_Init(&huart1) != HAL_OK){Error_Handler();}/* USER CODE BEGIN USART1_Init 2 *//* USER CODE END USART1_Init 2 */}

        在上面的代码中,调用了下面的UART相关的硬件初始化。 MCU Specific Package Initialization,MCU特定包初始化。

串口硬件(及DMA)配置

void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle)
{GPIO_InitTypeDef GPIO_InitStruct = {0};if(uartHandle->Instance==USART1){/* USER CODE BEGIN USART1_MspInit 0 *//* USER CODE END USART1_MspInit 0 *//* USART1 clock enable */__HAL_RCC_USART1_CLK_ENABLE();__HAL_RCC_GPIOA_CLK_ENABLE();/**USART1 GPIO ConfigurationPA9     ------> USART1_TXPA10     ------> USART1_RX*/GPIO_InitStruct.Pin = GPIO_PIN_9|GPIO_PIN_10;GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;GPIO_InitStruct.Alternate = GPIO_AF7_USART1;HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);/* USART1 DMA Init *//* USART1_RX Init */hdma_usart1_rx.Instance = DMA2_Stream2;hdma_usart1_rx.Init.Channel = DMA_CHANNEL_4;hdma_usart1_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;hdma_usart1_rx.Init.PeriphInc = DMA_PINC_DISABLE;hdma_usart1_rx.Init.MemInc = DMA_MINC_ENABLE;hdma_usart1_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;hdma_usart1_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;hdma_usart1_rx.Init.Mode = DMA_NORMAL;hdma_usart1_rx.Init.Priority = DMA_PRIORITY_LOW;hdma_usart1_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;if (HAL_DMA_Init(&hdma_usart1_rx) != HAL_OK){Error_Handler();}__HAL_LINKDMA(uartHandle,hdmarx,hdma_usart1_rx);/* USART1 interrupt Init */HAL_NVIC_SetPriority(USART1_IRQn, 0, 0);HAL_NVIC_EnableIRQ(USART1_IRQn);/* USER CODE BEGIN USART1_MspInit 1 *//* USER CODE END USART1_MspInit 1 */}
}
hdma_usart1_rx.Instance = DMA2_Stream2;          // 指定使用的 DMA 控制器和流(DMA2 的 Stream2)
hdma_usart1_rx.Init.Channel = DMA_CHANNEL_4;     // 指定 DMA 通道(通道 4,通常对应 USART1 的接收)
hdma_usart1_rx.Init.Direction = DMA_PERIPH_TO_MEMORY; // 数据传输方向:外设到内存
hdma_usart1_rx.Init.PeriphInc = DMA_PINC_DISABLE;    // 外设地址不递增(USART 接收寄存器固定)
hdma_usart1_rx.Init.MemInc = DMA_MINC_ENABLE;       // 内存地址递增(数据存储到连续内存)
hdma_usart1_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; // 外设数据对齐方式:字节对齐
hdma_usart1_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;    // 内存数据对齐方式:字节对齐
hdma_usart1_rx.Init.Mode = DMA_NORMAL;              // DMA 模式:普通模式(非循环)
hdma_usart1_rx.Init.Priority = DMA_PRIORITY_LOW;    // DMA 优先级:低
hdma_usart1_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;// 禁用 FIFO 模式
#define DMA_NORMAL                    0x00000000U                  /*!< Normal mode: 普通模式,传输完成后停止 */
#define DMA_CIRCULAR                  ((uint32_t)DMA_SxCR_CIRC)    /*!< Circular mode: 循环模式,传输完成后自动重新开始 */
#define DMA_PFCTRL                    ((uint32_t)DMA_SxCR_PFCTRL)  /*!< Peripheral flow control mode: 外设流控模式,外设控制传输 */

串口中断

中断间的调用关系

        USART1_IRQHandler

                HAL_UART_IRQHandler

                        UART_Receive_IT

        DMA2_Stream2_IRQHandler

                HAL_DMA_IRQHandler

                        HAL_UART_RxCpltCallback     

                        HAL_UART_RxHalfCpltCallback

             HAL_UART_ErrorCallback

USART1_IRQHandler

         通用的处理函数中判断了接收中断的使能和接收状态的挂起。

USART1_IRQHandler 的调用时机

USART1_IRQHandler 是 USART1 的中断处理函数,用于处理与 USART1 相关的中断事件。它的调用时机取决于 USART1 的中断标志位是否被置位。以下是具体的触发条件:

触发条件
  • 接收中断(RXNE):当 USART1 的接收缓冲寄存器中有新数据时,接收中断标志位(UART_FLAG_RXNE)会被置位。

  • 发送中断(TC):当 USART1 的发送缓冲寄存器中的数据被发送完成时,发送完成中断标志位(UART_FLAG_TC)会被置位。

  • 空闲中断(IDLE):当 USART1 检测到一个空闲信号(即接收到一个持续的低电平)时,空闲中断标志位(UART_FLAG_IDLE)会被置位。

  • 错误中断(如帧错误、奇偶校验错误等):当 USART1 检测到通信错误时,相应的错误标志位会被置位。

DMA2_Stream2_IRQHandler 的调用时机

DMA2_Stream2_IRQHandler 是 DMA2 Stream2 的中断处理函数,用于处理与 DMA2 Stream2 相关的中断事件。它的调用时机取决于 DMA2 Stream2 的中断标志位是否被置位。以下是具体的触发条件:

触发条件
  • 传输完成中断(TC):当 DMA2 Stream2 完成一次数据传输时,传输完成中断标志位(DMA_FLAG_TCIF2)会被置位。

  • 半传输中断(HT):当 DMA2 Stream2 完成一半数据传输时,半传输中断标志位(DMA_FLAG_HTIF2)会被置位。

  • 传输错误中断(TE):当 DMA2 Stream2 发生传输错误时,传输错误中断标志位(DMA_FLAG_TEIF2)会被置位。

  1. 处理中断事件

    • 如果是传输完成中断(DMA_FLAG_TCIF2),HAL 库会调用 HAL_UART_RxCpltCallback 回调函数。

    • 如果是半传输中断(DMA_FLAG_HTIF2),HAL 库会调用 HAL_UART_RxHalfCpltCallback 回调函数。

    • 如果是传输错误中断(DMA_FLAG_TEIF2),HAL 库会调用 HAL_UART_ErrorCallback 回调函数。

MX_TIM11_Init

        TIM1, TIM8, TIM9, TIM10, TIM11 连接到 APB2 总线。

系统时钟频率的计算

        根据系统时钟配置,系统时钟来源于高速内部晶振

        STM32F4的HSI频率为16Mhz。

         因此 PLL输出频率等于 800Mhz,也即系统时钟频率。

        根据配置,AHB 总线时钟(HCLK)等于 SYSCLK。APB2的时钟等于HCLK的时钟。

        即APB2的时钟频率也为 800Mhz。

定时器周期计算

void MX_TIM11_Init(void)
{// 设置定时器实例为TIM11htim11.Instance = TIM11;// 配置定时器参数htim11.Init.Prescaler = 99;  // 预分频器值为99htim11.Init.CounterMode = TIM_COUNTERMODE_UP;  // 向上计数模式htim11.Init.Period = 999;  // 自动重装载值为999htim11.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;  // 时钟分频因子为1htim11.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;  // 禁用自动重装载寄存器的预装载// 初始化定时器if (HAL_TIM_Base_Init(&htim11) != HAL_OK){Error_Handler();  // 初始化失败则调用错误处理函数}
}

         最后得 0.5s。

HAL_UART_Receive_DMA

        初始化串口的接收DMA。

        主要是初始化 接收数据缓冲区、接收数据大小和各种回调(如传输完成、半完成、结束和终止)。

HAL_StatusTypeDef HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
{/* 检查是否已有接收过程在进行 */if (huart->RxState == HAL_UART_STATE_READY){/* 检查接收数据指针和接收大小是否有效 */if ((pData == NULL) || (Size == 0U)){return HAL_ERROR;}/* 锁定当前处理过程 */__HAL_LOCK(huart);/* 设置接收类型为标准接收 */huart->ReceptionType = HAL_UART_RECEPTION_STANDARD;/* 启动DMA接收 */return (UART_Start_Receive_DMA(huart, pData, Size));}else{/* 如果已有接收过程在进行,则返回忙状态 */return HAL_BUSY;}
}
HAL_StatusTypeDef UART_Start_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
{uint32_t *tmp;// 初始化接收缓冲区指针和接收数据大小huart->pRxBuffPtr = pData;huart->RxXferSize = Size;// 重置错误码和设置接收状态huart->ErrorCode = HAL_UART_ERROR_NONE;huart->RxState = HAL_UART_STATE_BUSY_RX;// 设置DMA传输完成、半完成、错误和中止的回调函数huart->hdmarx->XferCpltCallback = UART_DMAReceiveCplt;huart->hdmarx->XferHalfCpltCallback = UART_DMARxHalfCplt;huart->hdmarx->XferErrorCallback = UART_DMAError;huart->hdmarx->XferAbortCallback = NULL;// 启动DMA接收tmp = (uint32_t *)&pData;HAL_DMA_Start_IT(huart->hdmarx, (uint32_t)&huart->Instance->DR, *(uint32_t *)tmp, Size);// 清除溢出错误标志__HAL_UART_CLEAR_OREFLAG(huart);// 解锁处理__HAL_UNLOCK(huart);// 如果需要,启用奇偶校验错误中断if (huart->Init.Parity != UART_PARITY_NONE){ATOMIC_SET_BIT(huart->Instance->CR1, USART_CR1_PEIE);}// 启用UART错误中断(帧错误、噪声错误、溢出错误)ATOMIC_SET_BIT(huart->Instance->CR3, USART_CR3_EIE);// 通过设置UART CR3寄存器中的DMAR位来启用接收DMA传输ATOMIC_SET_BIT(huart->Instance->CR3, USART_CR3_DMAR);return HAL_OK;
}

__HAL_UART_ENABLE_IT

        在UART通信中,空闲中断通常在接收到数据之后,数据线上出现一段空闲时间时触发。这个空闲时间可以由用户配置,用于检测数据包的结束或链接的空闲状态。

/*使能中断*/
__HAL_UART_ENABLE_IT(&huart1,  //串口1UART_IT_IDLE); //空闲中断

HAL_TIM_Base_Start_IT

        启动定时器的基本计数功能,并使能中断。

HAL_StatusTypeDef HAL_TIM_Base_Start_IT(TIM_HandleTypeDef *htim)
{uint32_t tmpsmcr;// 参数检查:确保提供的定时器实例有效assert_param(IS_TIM_INSTANCE(htim->Instance));// 状态检查:确保定时器处于就绪状态if (htim->State != HAL_TIM_STATE_READY){return HAL_ERROR;}// 设置定时器状态为忙碌htim->State = HAL_TIM_STATE_BUSY;// 使能定时器更新中断(溢出中断)__HAL_TIM_ENABLE_IT(htim, TIM_IT_UPDATE);// 使能定时器外设,除非处于触发模式(在触发模式下,使能会在触发时自动完成)if (IS_TIM_SLAVE_INSTANCE(htim->Instance)){// 如果定时器是从模式,检查是否需要手动使能tmpsmcr = htim->Instance->SMCR & TIM_SMCR_SMS;if (!IS_TIM_SLAVEMODE_TRIGGER_ENABLED(tmpsmcr)){__HAL_TIM_ENABLE(htim);}}else{// 如果定时器不是从模式,直接使能__HAL_TIM_ENABLE(htim);}// 返回函数执行状态return HAL_OK;
}

        在从模式下,定时器的计数行为(如计数频率、计数方向和计数周期)可以由外部信号或另一个定时器的输出控制。

        触发从模式:外部触发信号可以启动、停止或重新初始化定时器的计数。

        外部输入从模式:定时器的计数行为(如计数方向、计数值)可以由外部输入信号控制。

        内部触发从模式:定时器可以由内部事件(如另一个定时器的更新或捕获事件)触发。

TIM1_TRG_COM_TIM11_IRQHandler

        中断在xxx_it.c

        当中断源是 TIM1 的触发/换向事件或者 TIM11 的任何事件(如更新事件、输入捕获事件等)时,就会执行这个 ISR。

void TIM1_TRG_COM_TIM11_IRQHandler(void)
{/* USER CODE BEGIN TIM1_TRG_COM_TIM11_IRQn 0 */timecount += 1;/* USER CODE END TIM1_TRG_COM_TIM11_IRQn 0 */HAL_TIM_IRQHandler(&htim11);/* USER CODE BEGIN TIM1_TRG_COM_TIM11_IRQn 1 *//* USER CODE END TIM1_TRG_COM_TIM11_IRQn 1 */
}

TIM1的触发(Trigger)

触发功能允许一个定时器(或外部信号)触发另一个定时器的特定事件。在 TIM1 中,触发事件可以是启动、停止或重新初始化定时器的计数。触发可以由以下来源产生:

  • 内部触发:来自同一微控制器内其他定时器的事件。

  • 外部触发:来自微控制器外部的信号,如 GPIO 引脚上的信号。

        例如,假设我们有两个定时器 TIM1 和 TIM2,我们希望在 TIM2 每次更新(计数溢出)时,启动 TIM1 的计数。这可以通过配置 TIM1 的触发输入来实现,使其响应 TIM2 的更新事件。

TIM1的换向(Commutation)

        换向功能通常用于无刷直流电机(BLDC)控制,它涉及到在正确的时刻改变电机相位的电流方向。在 TIM1 中,换向可以通过配置定时器的互补输出来实现,这些输出可以连接到电机驱动器的开关器件。

        例如,对于一个三相电机,我们需要在特定的时刻改变三相绕组的电流方向。TIM1 可以配置为在检测到特定的转子位置信号时,通过其互补输出改变电流方向,从而实现换向。

举例

        假设我们正在控制一个无刷直流电机,我们使用 TIM1 来生成 PWM 信号,控制电机的三相绕组。以下是如何使用 TIM1 的触发和换向功能的示例:

  1. 配置 TIM1 为 PWM 模式:设置 TIM1 的通道为 PWM 模式,以生成控制电机绕组的信号。

  2. 配置触发输入:如果我们需要 TIM1 的 PWM 输出与另一个定时器(如 TIM2)同步,我们可以配置 TIM1 的触发输入来响应 TIM2 的更新事件。

  3. 配置换向:使用 TIM1 的互补输出输入捕获功能来检测转子的位置。根据转子位置信号,通过软件或硬件逻辑来控制 TIM1 的输出,以在正确的时刻改变电流方向。

  4. 启动 TIM1:使能 TIM1 的更新中断,并启动定时器。在中断服务例程中,根据转子位置更新 TIM1 的捕获比较寄存器,以改变 PWM 输出的占空比,从而控制电机的速度和方向。

定时器知识点

        2 个基本定时器(TIM6 和 TIM7)、

        10 个通用定时 器(TIM2~TIM5,TIM9~TIM14)、

        2 个高级控制定时器(TIM1 和 TIM8)。

定时器的中断服务函数声明在.s文件的中断向量表中

        用的时候可以自己在it.c或者自己的驱动中去实现声明和定义。 

计数中断

        初始化,配置重载值、计数值,到点了执行中断。

        不同定时器的中断函数不同,不同模式的中断函数也不同。

        直接查手册去做。

通用定时器PWM输出

        引脚的复用功能一般在GPIO配置中设置,如

void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim)
{if (htim->Instance == GTIM_TIMX_PWM){GPIO_InitTypeDef gpio_init_struct;GTIM_TIMX_PWM_CHY_GPIO_CLK_ENABLE();                            /* 使能通道y的GPIO时钟 */GTIM_TIMX_PWM_CHY_CLK_ENABLE();                                 /* 使能定时器时钟 */gpio_init_struct.Pin = GTIM_TIMX_PWM_CHY_GPIO_PIN;              /* 使能通道y的GPIO口 */gpio_init_struct.Mode = GPIO_MODE_AF_PP;                        /* 复用推挽输出 */gpio_init_struct.Pull = GPIO_PULLUP;                            /* 上拉 */gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;                  /* 高速 */gpio_init_struct.Alternate = GTIM_TIMX_PWM_CHY_GPIO_AF;         /* 定时器x通道y的GPIO复用 */HAL_GPIO_Init(GTIM_TIMX_PWM_CHY_GPIO_PORT, &gpio_init_struct);}
}

        这里PWM引脚被设置为服用推挽,并设置了复用参数。

        而定时器的PWM初始化设置中,

void gtim_timx_pwm_chy_init(uint16_t arr, uint16_t psc)
{TIM_OC_InitTypeDef timx_oc_pwm_chy = {0};                                               /* 定时器输出句柄 */g_timx_pwm_chy_handle.Instance = GTIM_TIMX_PWM;                                         /* 定时器x */g_timx_pwm_chy_handle.Init.Prescaler = psc;                                             /* 预分频系数 */g_timx_pwm_chy_handle.Init.CounterMode = TIM_COUNTERMODE_UP;                            /* 递增计数模式 */g_timx_pwm_chy_handle.Init.Period = arr;                                                /* 自动重装载值 */HAL_TIM_PWM_Init(&g_timx_pwm_chy_handle);                                               /* 初始化PWM */timx_oc_pwm_chy.OCMode = TIM_OCMODE_PWM1;                                               /* PWM1模式 */timx_oc_pwm_chy.Pulse = arr / 2;                                                        /* 设置比较值,此值用来确定占空比 */timx_oc_pwm_chy.OCPolarity = TIM_OCPOLARITY_LOW;                                        /* 输出比较极性为低 */HAL_TIM_PWM_ConfigChannel(&g_timx_pwm_chy_handle,  //定时器配置句柄&timx_oc_pwm_chy,   //GTIM_TIMX_PWM_CHY); /* 配置TIMx通道y */HAL_TIM_PWM_Start(&g_timx_pwm_chy_handle, GTIM_TIMX_PWM_CHY);                           /* 开启对应PWM通道 */
}

       PWM模式1PWM模式2的区别在于,

        PWM 模式 1:简单直接,适用于大多数基本 PWM 应用,其中输出引脚在计数器匹配到 CC 寄存器值时切换状态。

        PWM 模式 2:提供更复杂的控制,允许更灵活的 PWM 波形生成,适用于需要特殊 PWM 波形的应用,如中心对齐模式。

        输出比较极性决定了当定时器的计数器匹配到捕获/比较(CC)寄存器的值时,输出引脚是置高还是置低。

        再就是定时器通道和PWM引脚的关系,定时器一般4个PWM通道,每个通道可以配置为不同的功能。定时器通道可以配置一个具备该功能的复用引脚,来进行输出

1. 多任务处理

多个通道允许定时器同时执行多个任务。例如,在一个通道上生成一个PWM信号来控制电机速度,同时在另一个通道上进行输入捕获来测量旋转编码器的脉冲。

2. 同步操作

在需要同步多个事件的应用中,多个通道可以协调工作。例如,在电机控制中,可能需要同时控制多个相位的电流,每个通道控制一相。

3. 复杂的PWM控制

对于需要复杂PWM控制的应用,如无刷直流电机(BLDC)控制,多个通道可以用于生成相位差的PWM信号,以实现电机的换向。

通用定时器输入捕获

        输入捕获模式可以用来测量脉冲宽度或者测量频率

        先配置通道为上升沿触发中断, 触发后开始计时,同时配置下降沿触发中断,触发后计时结束。时间差就是我们测量的脉冲宽度。

void gtim_timx_cap_chy_init(uint16_t arr, uint16_t psc)
{TIM_IC_InitTypeDef timx_ic_cap_chy = {0};g_timx_cap_chy_handle.Instance = GTIM_TIMX_CAP;                                        /* 定时器5 */g_timx_cap_chy_handle.Init.Prescaler = psc;                                            /* 预分频系数 */g_timx_cap_chy_handle.Init.CounterMode = TIM_COUNTERMODE_UP;                           /* 递增计数模式 */g_timx_cap_chy_handle.Init.Period = arr;                                               /* 自动重装载值 */HAL_TIM_IC_Init(&g_timx_cap_chy_handle);                                               /* 初始化定时器 */timx_ic_cap_chy.ICPolarity = TIM_ICPOLARITY_RISING;                                    /* 上升沿捕获 */timx_ic_cap_chy.ICSelection = TIM_ICSELECTION_DIRECTTI;                                /* 映射到TI1上 */timx_ic_cap_chy.ICPrescaler = TIM_ICPSC_DIV1;                                          /* 配置输入分频,不分频 */timx_ic_cap_chy.ICFilter = 0;                                                          /* 配置输入滤波器,不滤波 */HAL_TIM_IC_ConfigChannel(&g_timx_cap_chy_handle, &timx_ic_cap_chy, GTIM_TIMX_CAP_CHY); /* 配置TIM5通道1 */__HAL_TIM_ENABLE_IT(&g_timx_cap_chy_handle, TIM_IT_UPDATE);                            /* 使能更新中断 */HAL_TIM_IC_Start_IT(&g_timx_cap_chy_handle, GTIM_TIMX_CAP_CHY);                        /* 开始捕获TIM5的通道1 */
}

        备注的映射到TI1上,这里的TI1是Timer Input 1的缩写,

        也就是输入捕获通道1,是一个特定引脚。(在GPIO中配置复用)

定时器中断服务函数

         HAL_tim.c中,定时器共用处理函数,根据配置情况执行了不同配置下的回调。

捕获比较中断函数

通用定时器脉冲计数

        脉冲计数,需要将定时器配置为从模式,外部触发模式1。

/* 从模式:外部触发模式 1 */tim_slave_config.SlaveMode = TIM_SLAVEMODE_EXTERNAL1;/* 外部触发模式 1 */tim_slave_config.InputTrigger = TIM_TS_TI1FP1;/* TI1FP1 作为触发输入源 */tim_slave_config.TriggerPolarity = TIM_TRIGGERPOLARITY_RISING;/* 上升沿 */tim_slave_config.TriggerPrescaler = TIM_TRIGGERPRESCALER_DIV1;/* 不分频 */tim_slave_config.TriggerFilter = 0x0; /* 滤波:本例中不需要任何滤波 */
HAL_TIM_SlaveConfigSynchronization(&g_timx_cnt_chy_hand

        TIM_SLAVEMODE_EXTERNAL1 是定时器的最基本的从模式,允许定时器通过外部输入(如 TI1FP1)来触发更新事件。

        TIM_TS_TI1FP1 表示定时器的触发输入源是来自定时器的输入捕获通道 1(TI1)的滤波输入(FP1)。这里的 FP 指的是滤波器(Filter),它用于减少噪声或干扰对触发信号的影响。数字 1 表示这是第一个滤波输入。 

        GPIO也要配置为输入捕获通道。

    gpio_init_struct.Alternate = GTIM_TIMX_CNT_CHY_GPIO_AF;                /* 复用为捕获TIMx的通道 */HAL_GPIO_Init(GTIM_TIMX_CNT_CHY_GPIO_PORT, &gpio_init_struct);
TIM的复用引脚在数据手册上能查到,N是代表互补输出的

        在实际应用中,TIM1_CH1N 与 TIM1_CH1 是一对互补通道。

        如果死区时间(Deadtime)为0,则 TIM1_CH1N 是 TIM1_CH1 的反相信号;如果死区时间不为0,则会在 TIM1_CH1N 上插入死区时间,以防止上下功率管同时导通 。

        死区时间内,两个互补信号不同时导通。

        中断逻辑程序的逻辑代码是 放在更新中断回调函数里面的,这是 HAL 库回调机制标准的做法

        因为我们在通用定时器输 入捕获实验中使用过 HAL_TIM_PeriodElapsedCallback 更新中断回调函数,所以本实验我们不 使用 HAL 库这套回调机制,而是直接将中断处理写在定时器中断服务函数中。(标准做法是在定时器中断服务函数中调用更新中断回调函数,这里把那个函数删了)

高级定时器输出比较模式

        高级定时器输出比较模式下翻转功能,通过定时器 4 个通道分别输 出 4 个 50%占空比、不同相位的 PWM。

        输出比较模式下翻转功能作用是:当计数器的值等于捕获/比较寄存器影子寄存器的值时,OC1REF 发生翻转,进而控制通道输出(OCx)翻转。

        通过翻转功能实现输出 PWM 的具体原理如下:

        PWM 频率由自动重载寄存器(TIMx_ARR)的值决定,在这个过程中,只要自动重 载寄存器的值不变,那么 PWM 占空比就固定为 50%。

        我们可以通过捕获/比较寄存器 (TIMx_CCRx)的值改变 PWM 的相位。

输出比较模式的初始化函数

 

高级定时器互补输出带死区控制

main函数

例程功能

        1,利用 TIM1_CH1(PE9)输出 70%占空比的 PWM,它的互补输出通道(PE8)则是输出 30% 占空比的 PWM。

        2,刹车功能,当给刹车输入引脚(PE15)输入低电平时,进行刹车,即 PE9 和 PE8 停止输 出 PWM。

使能时钟

时钟使能,后面仨代码其实重复了,但为了便于理解使用了DEFINE,其实就是开了E口的使能

复用和初始化GPIO

GPIO端口复用和初始化
复用了一个刹车,两个TIM通道

        定时器的 PWM初始化,

        这里 arr = 1000-1,psc = 180-1。

初始化定时器模式和周期

g_timx_cplm_pwm_handle.Instance = ATIM_TIMX_CPLM;                               /* 定时器x */
g_timx_cplm_pwm_handle.Init.Prescaler = psc;                                    /* 预分频系数 */
g_timx_cplm_pwm_handle.Init.CounterMode = TIM_COUNTERMODE_UP;                   /* 递增计数模式 */
g_timx_cplm_pwm_handle.Init.Period = arr;                                       /* 自动重装载值 */
g_timx_cplm_pwm_handle.Init.ClockDivision = TIM_CLOCKDIVISION_DIV4;             /* CKD[1:0] = 10, tDTS = 4 * tCK_INT = Ft / 4 = 45Mhz */
g_timx_cplm_pwm_handle.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;  /* 使能影子寄存器TIMx_ARR */
HAL_TIM_PWM_Init(&g_timx_cplm_pwm_handle) ;

         这里用的定时器1,挂载在APB2总线。

        虽然主通道和互补通道的极性都设置为低电平有效,但它们的输出信号是互补的。

  • 当主通道(OCy)为低电平时,互补通道(OCyN)为高电平。

  • 当主通道(OCy)为高电平时,互补通道(OCyN)为低电平。

        这种互补关系是通过定时器的硬件逻辑实现的,而不是通过软件直接设置的。即使它们的极性设置相同,硬件会自动确保它们的输出信号是互补的

PWM通道配置

        输出通道(OCx)和互补输出通道(OCxN)在主输出使能(MOE,Master Output Enable)为0时的空闲状态(Idle State),

        TIM_OCIDLESTATE_SET:

        当MOE=0时,主输出通道(OCx)的输出电平被设置为高电平。

        主输出使能(Master Output Enable,MOE)通常位于定时器的基本死区和锁存器寄存器(TIMx_BDTR)中。通过设置或清除MOE位,可以启用或禁用定时器的所有输出信号,包括主输出通道(OCx)和互补输出通道(OCxN)。

设置定时器死区参数

设置定时器的死区时间
死区参数解释

        TIM_OSSR_DISABLE(禁用运行模式下的关闭输出状态

        运行模式下的关闭输出状态(Off-State in Run Mode)。

        那么刹车信号被触发时,定时器的输出不会被强制关闭。相反,输出信号的状态将由其他配置决定。

        TIM_OSSI_DISABLE(禁用空闲模式下的关闭输出状态)

        空闲模式下的关闭输出状态(Off-State in Idle Mode)

        在定时器处于空闲模式(如定时器停止计数)时,输出不会被强制关闭。输出信号的状态由其他配置决定

    寄存器锁就是锁住寄存器,禁止写入。

    TIM_BREAK_ENABLE(使能刹车功能)

        刹车输入信号的有效极性

        自动输出功能。刹车事件结束后,定时器的输出将自动恢复到正常状态。

开启PWM和PWMN信号输出

开始输出PWM信号
开始输出PWM互补信号

设置定时器死区时间

带死区互补输出情况

        死区时间内,两互补输出不同时导通。

        由于开关器件的开关速度和寄生电容的影响,Q1的漏极和源极之间的电压不会立即下降到0,可能需要一定时间才能完全关闭。如果Q2在Q1完全关闭之前就导通,可能会导致Q1和Q2同时导通,从而产生直通电流,导致断路。

        因此设置死区,确保一方完全关闭后,另一方才导通

        由上到下分别是 PE9 输出 70%占空比的 PWM 波和 PE8 互补输出 30%占空 比的 PWM 波。

        互补输出的 PWM 波的正脉宽减去正常的 PWM 的负脉宽的值除以 2 就是死区 时间,也可以是正常的 PWM 的正脉宽减去互补输出的 PWM 波的负脉宽的值除以 2。

        我们使用第一种方法得到:死区时间 =(702–698)/2 us= 2us。与我们理论计算得到的值 2.22us 差不多,这样的误差是正常的。

高级定时器PWM输入

例程:

        首先通过 TIM3_CH4(PB1)输出 PWM 波。然后把 PB1 输出的 PWM 波用杜邦线接入 PC6 (定时器 8 通道 1),最后通过串口打印 PWM 波的脉宽和频率等信息。

/*定时器初始化和通道配置*/
gtim_timx_pwm_chy_init(10 - 1, 90 - 1); /* 1Mhz的计数频率, 100Khz的PWM */
atim_timx_pwmin_chy_init();             /* 初始化PWM输入捕获 */
void gtim_timx_pwm_chy_init(uint16_t arr, uint16_t psc)
{TIM_OC_InitTypeDef timx_oc_pwm_chy = {0};                       /* 定时器输出句柄 */g_timx_pwm_chy_handle.Instance = GTIM_TIMX_PWM;                 /* 定时器x */g_timx_pwm_chy_handle.Init.Prescaler = psc;                     /* 预分频系数 */g_timx_pwm_chy_handle.Init.CounterMode = TIM_COUNTERMODE_UP;    /* 递增计数模式 */g_timx_pwm_chy_handle.Init.Period = arr;                        /* 自动重装载值 */HAL_TIM_PWM_Init(&g_timx_pwm_chy_handle);                       /* 初始化PWM */timx_oc_pwm_chy.OCMode = TIM_OCMODE_PWM1;                       /* 选择PWM1模式 */timx_oc_pwm_chy.Pulse = arr / 2;                                /* 设置比较值,此值用来确定占空比 */timx_oc_pwm_chy.OCPolarity = TIM_OCPOLARITY_LOW;                                        /* 输出比较极性为低 */HAL_TIM_PWM_ConfigChannel(&g_timx_pwm_chy_handle, &timx_oc_pwm_chy, GTIM_TIMX_PWM_CHY); /* 配置TIMx通道y */HAL_TIM_PWM_Start(&g_timx_pwm_chy_handle, GTIM_TIMX_PWM_CHY);                           /* 开启对应PWM通道 */
}
void atim_timx_pwmin_chy_init(void)
{GPIO_InitTypeDef gpio_init_struct = {0};TIM_SlaveConfigTypeDef slave_config = {0};TIM_IC_InitTypeDef tim_ic_pwmin_chy = {0};ATIM_TIMX_PWMIN_CHY_CLK_ENABLE();ATIM_TIMX_PWMIN_CHY_GPIO_CLK_ENABLE();gpio_init_struct.Pin = ATIM_TIMX_PWMIN_CHY_GPIO_PIN;gpio_init_struct.Mode = GPIO_MODE_AF_PP; gpio_init_struct.Pull = GPIO_PULLDOWN;gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;gpio_init_struct.Alternate = ATIM_TIMX_PWMIN_CHY_GPIO_AF;HAL_GPIO_Init(ATIM_TIMX_PWMIN_CHY_GPIO_PORT, &gpio_init_struct);g_timx_pwmin_chy_handle.Instance = ATIM_TIMX_PWMIN;             /* 定时器8 */g_timx_pwmin_chy_handle.Init.Prescaler = 0;                     /* 定时器预分频系数 */g_timx_pwmin_chy_handle.Init.CounterMode = TIM_COUNTERMODE_UP;  /* 递增计数模式 */g_timx_pwmin_chy_handle.Init.Period = 65535;                    /* 自动重装载值 */HAL_TIM_IC_Init(&g_timx_pwmin_chy_handle);/* 从模式配置,IT1触发更新 */slave_config.SlaveMode = TIM_SLAVEMODE_RESET;                   /* 从模式:复位模式 */slave_config.InputTrigger = TIM_TS_TI1FP1;                      /* 定时器输入触发源:TI1FP1 */slave_config.TriggerPolarity = TIM_INPUTCHANNELPOLARITY_RISING; /* 上升沿检测 */slave_config.TriggerFilter = 0;                                 /* 不滤波 */HAL_TIM_SlaveConfigSynchro(&g_timx_pwmin_chy_handle, &slave_config);/* IC1捕获:上升沿触发TI1FP1 */tim_ic_pwmin_chy.ICPolarity = TIM_INPUTCHANNELPOLARITY_RISING;  /* 上升沿检测 *///表示输入捕获的信号来源为一个直接信号tim_ic_pwmin_chy.ICSelection = TIM_ICSELECTION_DIRECTTI;        /* 选择输入端IC1映射到TI1 */tim_ic_pwmin_chy.ICPrescaler = TIM_ICPSC_DIV1;                  /* 不分频 */tim_ic_pwmin_chy.ICFilter = 0;                                  /* 不滤波 */HAL_TIM_IC_ConfigChannel(&g_timx_pwmin_chy_handle, &tim_ic_pwmin_chy, TIM_CHANNEL_1);/* IC2捕获:上升沿触发TI1FP2 */tim_ic_pwmin_chy.ICPolarity = TIM_INPUTCHANNELPOLARITY_FALLING; /* 下降沿检测 */tim_ic_pwmin_chy.ICSelection = TIM_ICSELECTION_INDIRECTTI;      /* 选择输入端IC2映射到TI1 */HAL_TIM_IC_ConfigChannel(&g_timx_pwmin_chy_handle, &tim_ic_pwmin_chy, TIM_CHANNEL_2);HAL_NVIC_SetPriority(ATIM_TIMX_PWMIN_IRQn, 1, 3);               /* 设置中断优先级,抢占优先级1,子优先级3 */HAL_NVIC_EnableIRQ( ATIM_TIMX_PWMIN_IRQn );                     /* 开启TIMx中断 *//* TIM1/TIM8有独立的输入捕获中断服务函数 */if ( ATIM_TIMX_PWMIN == TIM1 || ATIM_TIMX_PWMIN == TIM8){HAL_NVIC_SetPriority(ATIM_TIMX_PWMIN_CC_IRQn, 1, 3);        /* 设置中断优先级,抢占优先级1,子优先级3 */HAL_NVIC_EnableIRQ(ATIM_TIMX_PWMIN_CC_IRQn);                /* 开启TIMx中断 */}__HAL_TIM_ENABLE_IT(&g_timx_pwmin_chy_handle, TIM_IT_UPDATE);HAL_TIM_IC_Start_IT(&g_timx_pwmin_chy_handle, TIM_CHANNEL_1);HAL_TIM_IC_Start_IT(&g_timx_pwmin_chy_handle, TIM_CHANNEL_2);
}

输入捕获通道映射 

直接映射 TIM_ICSELECTION_DIRECTTI

  • 含义:表示输入捕获通道直接连接到对应的输入引脚。

  • 具体映射

    • 对于通道1(IC1),信号来源是通道1的输入引脚(TI1)。

    • 对于通道2(IC2),信号来源是通道2的输入引脚(TI2)。

    • 对于通道3(IC3),信号来源是通道3的输入引脚(TI3)。

    • 对于通道4(IC4),信号来源是通道4的输入引脚(TI4)。

间接映射 TIM_ICSELECTION_INDIRECTTI

  • 含义:表示输入捕获通道的信号来源于另一个通道的间接输入信号。

  • 具体映射

    • 对于通道2(IC2),信号来源是通道1的滤波输入信号(TI1FP1)。

    • 对于通道3(IC3),信号来源是通道2的滤波输入信号(TI2FP2)。

    • 对于通道4(IC4),信号来源是通道3的滤波输入信号(TI3FP3)。

这里通道配置的时候,把定时器8配置成了 从模式:复位模式

并给出了输入触发源TI1FP1,意思就是通道1(TI1)的输入信号经过内部过滤(FP1)后的版本。

        进行定时器的输入捕获配置的时候,给通道1配置了直接映射

        表示输入捕获通道直接连接到对应的输入引脚,因此通道1的信号来源就是通道1,即TI1

        通道2配置了间接映射,因此通道2的信号来源是TI1FP1

TIM1/TIM8独立的捕获中断服务函数

 /* TIM1/TIM8 有独立的捕获中断服务函数,需要单独定义,对于TIM2~5等,则不需要以下定义 */
#define ATIM_TIMX_PWMIN_CC_IRQn                 TIM8_CC_IRQn
#define ATIM_TIMX_PWMIN_CC_IRQHandler           TIM8_CC_IRQHandler

         设置优先级,开启TIM中断,

/*启用定时器的更新中断*/
__HAL_TIM_ENABLE_IT(&g_timx_pwmin_chy_handle, TIM_IT_UPDATE);/*开启通道1的输入捕获模式*/
HAL_TIM_IC_Start_IT(&g_timx_pwmin_chy_handle, TIM_CHANNEL_1);/*开启通道2的输入捕获模式*/
HAL_TIM_IC_Start_IT(&g_timx_pwmin_chy_handle, TIM_CHANNEL_2);

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

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

相关文章

利用不坑盒子的Copilot,快速排值班表

马上放暑假了&#xff0c;有多少人拼命排值班表的&#xff1f; 今天用我亲身制作值班表的一些Excel操作&#xff0c;给大家分享一些在Excel中的小技巧&#xff0c;需要的及时收藏&#xff0c;有一天用得上~ 值班表全貌 先给大家看看我制作的值班表的样子&#xff0c;应该大家…

Linux 面试知识(附常见命令)

目录结构与重要文件 Linux 中一切皆文件&#xff0c;掌握目录结构有助于理解系统管理与配置。 目录说明/根目录&#xff0c;所有文件起点/bin基本命令的可执行文件&#xff0c;如 ls, cp/sbin系统管理员用的命令&#xff0c;如 shutdown/etc配置文件目录&#xff0c;如 /etc/…

Lua 安装使用教程

一、Lua 简介 Lua 是一门轻量级、高性能的脚本语言&#xff0c;具有简洁语法、嵌入性强、可扩展性高等特点。广泛应用于游戏开发&#xff08;如 Roblox、World of Warcraft&#xff09;、嵌入式开发、配置脚本、Nginx 扩展&#xff08;OpenResty&#xff09;等领域。 二、Lua …

SPAD像素概念理解

SPAD(Single Photon Avalanche Diode,单光子雪崩二极管)像素是一种能够检测单个光子的超灵敏光电探测器,其核心原理是通过雪崩倍增效应将单个光子产生的微弱电流信号放大到可观测水平。 一、工作原理 雪崩倍增效应 当SPAD反向偏压超过其击穿电压时,进入盖革模式(Geiger M…

SSSSS

#include <iostream> void LineOf(bool** n1, bool** n2, int column, int raw, int* result) { for (int i 0; i < column; i) { int d -1, n -1; // 反向遍历&#xff0c;找最后一个 true for (int j raw - 1; j > 0; j--) { …

【AI智能体】社交娱乐-智能助教

智能助教是扣子官方提供的教育类智能体模板。助教模板分为学习陪伴和作业批改两种场景&#xff0c;分别适用于学生角色和教师角色&#xff0c;你可以根据需求选择对应的模板&#xff0c;并将其改造为其他学科或其他教育阶段的智能助教。 模板介绍 在智能学伴/助教的落地过程中…

自动化保护 AWS ECS Fargate 服务:使用 Prisma Cloud 实现容器安全

引言 在云原生时代,容器化技术已成为现代应用部署的标准方式。AWS ECS Fargate 作为一种无服务器容器服务,让开发者能够轻松运行容器化应用而无需管理底层基础设施。然而,随着容器技术的普及,安全问题也日益突出。本文将介绍如何通过 Python 脚本自动化地为 ECS Fargate 服…

Kafka Controller 元数据解析与故障恢复实战指南

#作者&#xff1a;张桐瑞 文章目录 1 生产案例&#xff1a;Controller 选举在故障恢复中的关键作用1.1 问题背景1.2 核心操作原理&#xff1a; 2 Controller 元数据全景&#xff1a;从 ZooKeeper 到内存的数据镜像2.1元数据核心载体&#xff1a;ControllerContext 类2.2核心元…

《寻北技术的全面剖析与应用前景研究报告》

一、引言 1.1 研究背景与意义 寻北&#xff0c;作为确定地理北极方向的关键技术&#xff0c;在众多领域中扮演着举足轻重的角色。在军事领域&#xff0c;精确的寻北对于武器系统的瞄准、导弹的精确制导以及部队的战略部署都至关重要。例如&#xff0c;火炮在发射前需要精确寻…

深入比较 Gin 与 Beego:Go Web 框架的两大选择

引言 在 Go 语言生态系统中&#xff0c;Gin 和 Beego 是两个非常受欢迎的 Web 框架。它们各自有着不同的设计理念和目标用户群体。本文将对这两个框架进行深入比较&#xff0c;并帮助你理解它们之间的区别&#xff0c;以便根据项目需求做出合适的选择。 一、Gin 概述 Gin是一…

全新大模型开源,腾讯(int4能打DeepSeek) Vs 谷歌(2GB运行多模态)

大家好&#xff0c;我是 Ai 学习的老章 最近除了阿里 Qwen3 模型更新了图片生成和处理能力&#xff0c;大家都可以玩转吉卜力风格 还有几个最近发布的大模型值得关注 1 是腾讯开源了 80B 混元 A13B 模型&#xff0c;亮点是精度无损的 int4 很能打 2 是谷歌开源的小参数 Gemm…

向量数据库milvus中文全文检索取不到数据的处理办法

​检查中文分词配置​ Milvus 2.5 支持原生中文全文检索&#xff0c;但需显式配置中文分词器&#xff1a; 创建集合时指定分词器类型为 chinese python schema.add_field(field_name"text", datatypeDataType.VARCHAR, max_length65535, enable_analyzerTrue, an…

Stable Diffusion 项目实战落地:从0到1 掌握ControlNet 第一篇 打造光影字形的创意秘技

大家好呀,欢迎来到 AI造字工坊! 在这篇文章中,我们将带领你走进一个神奇的世界——ControlNet。你可能听说过它,但可能还没摸清它的深奥之处。 今天,我们就来揭开它神秘的面纱,轻松带你玩转字形设计! 话说回来,相信大家对图片生成、提示词、放大操作、抽卡这些基本操…

从零用java实现 小红书 springboot vue uniapp (12)实现分类筛选与视频笔记功能

移动端演示 http://8.146.211.120:8081/#/ 管理端演示 http://8.146.211.120:8088/#/ 项目整体介绍及演示 前言 在前面的系列文章中&#xff0c;我们已经基本完成了小红书项目的核心框架搭建和图文笔记的发布、展示流程。为了丰富App的功能和用户体验&#xff0c;今天我们将在…

Python与Web3.py库交互实践

目录 Python与Web3.py库交互实践引言:连接Python与区块链的桥梁1. 环境配置与基础连接1.1 安装Web3.py1.2 连接以太坊节点2. 基础区块链交互2.1 账户与余额查询2.2 创建并发送交易3. 智能合约交互3.1 加载和部署合约3.2 与已部署合约交互4. 高级功能实践4.1 事件监听4.2 与ERC…

《汇编语言:基于X86处理器》第6章 条件处理(2)

本章向程序员的汇编语言工具箱中引入一个重要的内容&#xff0c;使得编写出来的程序具备作决策的功能。几乎所有的程序都需要这种能力。首先&#xff0c;介绍布尔操作&#xff0c;由于能影响CPU状态标志&#xff0c;它们是所有条件指令的核心。然后&#xff0c;说明怎样使用演绎…

深度剖析NumPy核心函数reshape()

深度剖析NumPy核心函数reshape reshape()函数基础概念reshape()函数语法与参数详解reshape()函数使用示例基本的形状重塑使用-1自动计算维度多维数组的形状重塑不同order参数的效果 reshape()函数的应用场景数据预处理机器学习模型输入算法实现 当我们使用np.array()创建好数组…

Linux平台MinGW32/MinGW64交叉编译完全指南:原理、部署与组件详解

一、MinGW是什么&#xff1f;为什么需要交叉编译&#xff1f; MinGW&#xff08;Minimalist GNU for Windows&#xff09;是一套在Linux上构建Windows应用程序的完整工具链。它允许开发者&#xff1a; 在Linux环境下编译Windows可执行文件&#xff08;.exe/.dll&#xff09;避…

为什么我画的频谱图和audacity、audition不一样?

文章目录 系列文章目录 目录 文章目录 前言 一、问题引入 二、使用步骤 三、分析和改进 总结 前言 我们知道audacity和audition都有频谱分析这个窗口&#xff0c;一般过程肯定是分帧加窗&#xff0c;fft变换然后呈现&#xff0c; 大体这个过程是没问题的&#xff0c;但为什…

责任链模式 Go 语言实战

责任链模式&#xff08;Chain of Responsibility&#xff09; 责任链模式是一种行为设计模式&#xff0c;它允许将请求沿着处理者链进行传递&#xff0c;直到有一个处理者能够处理它。这个模式的主要目的是解耦请求的发送者和接收者&#xff0c;使得多个对象都有机会处理这个请…