之前在学习STM32的基本定时器的输出PWM的相关调试、使用DMA接收串口数据的相关内容,今天和大家分享一下利用DMA和定时器中断请求,来修改定时器的比较值的相关调试内容。
学习内容:使用DMA从内存到外设的传输功能,完成定时器输出PWM的脉冲宽度的改变。
软件编程学习调试步骤:
目标是配置 TIM1 通道 3 以生成频率等于 1 KHz 的互补 PWM(脉宽调制)信号,以及一个可变占空比,该占空比在特定数量的更新 DMA 请求后由 DMA 更改。
此重复请求的数量由 TIM1 重复计数器定义,每 4 个更新请求,TIM1 通道 3 占空比更改为 aCCValue_Buffer定义的下一个新值。
在主程序开始时,调用 HAL_Init() 函数来重置所有外围设备,
初始化 Flash 接口和系统。
SystemClock_Config() 函数用于配置 STM32U083RCTx 设备的系统时钟:CPU 频率为 48 MHz
定时器配置图如下:
软件代码如下:
/* Compute the value of ARR register to generate signal frequency at 17.57 Khz */ uwTimerPeriod = (uint32_t)((SystemCoreClock / 1000) - 1); /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_DMA_Init(); MX_TIM1_Init(); /* USER CODE BEGIN 2 */ /* Compute CCR1 value to generate a duty cycle at 75% */ aCCValue_Buffer[0] = (uint32_t)(((uint32_t) 75 * (uwTimerPeriod - 1)) / 100); /* Compute CCR2 value to generate a duty cycle at 50% */ aCCValue_Buffer[1] = (uint32_t)(((uint32_t) 50 * (uwTimerPeriod - 1)) / 100); /* Compute CCR3 value to generate a duty cycle at 25% */ aCCValue_Buffer[2] = (uint32_t)(((uint32_t) 25 * (uwTimerPeriod - 1)) / 100); /*## Start PWM signal generation in DMA mode ############################*/ if (HAL_TIM_PWM_Start_DMA(&htim1, TIM_CHANNEL_3, aCCValue_Buffer, 3) != HAL_OK) { /* Starting Error */ Error_Handler(); }
定时器1的初始化部分:
/** * @brief TIM1 Initialization Function * @param None * @retval None */ static void MX_TIM1_Init(void) { /* USER CODE BEGIN TIM1_Init 0 */ /* USER CODE END TIM1_Init 0 */ TIM_MasterConfigTypeDef sMasterConfig = {0}; TIM_OC_InitTypeDef sConfigOC = {0}; TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig = {0}; /* USER CODE BEGIN TIM1_Init 1 */ /* USER CODE END TIM1_Init 1 */ htim1.Instance = TIM1; htim1.Init.Prescaler = 0; htim1.Init.CounterMode = TIM_COUNTERMODE_UP; htim1.Init.Period = uwTimerPeriod; htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; htim1.Init.RepetitionCounter = 3; htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; if (HAL_TIM_PWM_Init(&htim1) != HAL_OK) { Error_Handler(); } sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET; sMasterConfig.MasterOutputTrigger2 = TIM_TRGO2_RESET; sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE; if (HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig) != HAL_OK) { Error_Handler(); } sConfigOC.OCMode = TIM_OCMODE_PWM1; sConfigOC.Pulse = 0; sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH; sConfigOC.OCNPolarity = TIM_OCNPOLARITY_HIGH; sConfigOC.OCFastMode = TIM_OCFAST_DISABLE; sConfigOC.OCIdleState = TIM_OCIDLESTATE_RESET; sConfigOC.OCNIdleState = TIM_OCNIDLESTATE_RESET; if (HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_3) != HAL_OK) { Error_Handler(); } sBreakDeadTimeConfig.OffStateRunMode = TIM_OSSR_DISABLE; sBreakDeadTimeConfig.OffStateIDLEMode = TIM_OSSI_DISABLE; sBreakDeadTimeConfig.LockLevel = TIM_LOCKLEVEL_OFF; sBreakDeadTimeConfig.DeadTime = 0; sBreakDeadTimeConfig.BreakState = TIM_BREAK_DISABLE; sBreakDeadTimeConfig.BreakPolarity = TIM_BREAKPOLARITY_HIGH; sBreakDeadTimeConfig.BreakFilter = 0; sBreakDeadTimeConfig.BreakAFMode = TIM_BREAK_AFMODE_INPUT; sBreakDeadTimeConfig.Break2State = TIM_BREAK2_DISABLE; sBreakDeadTimeConfig.Break2Polarity = TIM_BREAK2POLARITY_HIGH; sBreakDeadTimeConfig.Break2Filter = 0; sBreakDeadTimeConfig.Break2AFMode = TIM_BREAK_AFMODE_INPUT; sBreakDeadTimeConfig.AutomaticOutput = TIM_AUTOMATICOUTPUT_DISABLE; if (HAL_TIMEx_ConfigBreakDeadTime(&htim1, &sBreakDeadTimeConfig) != HAL_OK) { Error_Handler(); } /* USER CODE BEGIN TIM1_Init 2 */ /* USER CODE END TIM1_Init 2 */ HAL_TIM_MspPostInit(&htim1); }
DMA的初始化如下:
/** * Enable DMA controller clock */ static void MX_DMA_Init(void) { /* DMA controller clock enable */ __HAL_RCC_DMA1_CLK_ENABLE(); /* DMA interrupt init */ /* DMA1_Ch4_7_DMA2_Ch1_5_DMAMUX_OVR_IRQn interrupt configuration */ HAL_NVIC_SetPriority(DMA1_Ch4_7_DMA2_Ch1_5_DMAMUX_OVR_IRQn, 0, 0); HAL_NVIC_EnableIRQ(DMA1_Ch4_7_DMA2_Ch1_5_DMAMUX_OVR_IRQn); }
使用HAL库操作DMA时候,我们只需要在cube中配置DMA的传输方向就可以。不像标准库的底层驱动一样
使用DMA更新脉宽的学习过程中的记录如下:
上面提到的 PWM 信号频率值是理论值(当系统时钟频率正好为 48 MHz 时获得)。由于生成的系统 clock frequency 可能因板而异,因此观察到的 PWM 信号频率可能略有不同。
使用 HAL_Delay() 时必须小心,此函数根据 SysTick ISR 中递增的变量提供准确的延迟(以毫秒为单位)。这意味着,如果 HAL_Delay() 是从外围 ISR 进程调用的,则 SysTick 中断必须具有更高的优先级(数值较低)。
此示例需要确保 SysTick 时基始终设置为 1 毫秒,才能执行正确的 HAL 操作。
PWM输出可变脉宽的波形如下图所示: