文章目录
前言
本文主要讲解自己实现的PWM程序。借鉴了正点原子程序的实现方法。关于正点原子的程序详细讲解,可以参考博客<STM32F429第二十一篇之PWM波实现详解>。
相对于正点原子的实验,本文程序的功能有三处不同:
- 通过高级时钟TIM1的通道1实现。
- 将单通道PWM拓展为双通道PWM。
- 加入刹车(断路)功能。
本文使用的HAL库的版本为:STM32Cube_FW_F4_V1.25.0
本文使用的STM32CubeMX版本为:6.1.1
该工程的下载地址为:
结构体
TIM_BreakDeadTimeConfigTypeDef
/**
* @brief TIM Break input(s) and Dead time configuration Structure definition
* @note 2 break inputs can be configured (BKIN and BKIN2) with configurable
* filter and polarity.
*/
typedef struct
{
uint32_t OffStateRunMode; /*!< TIM off state in run mode
This parameter can be a value of @ref TIM_OSSR_Off_State_Selection_for_Run_mode_state */
uint32_t OffStateIDLEMode; /*!< TIM off state in IDLE mode
This parameter can be a value of @ref TIM_OSSI_Off_State_Selection_for_Idle_mode_state */
uint32_t LockLevel; /*!< TIM Lock level
This parameter can be a value of @ref TIM_Lock_level */
uint32_t DeadTime; /*!< TIM dead Time
This parameter can be a number between Min_Data = 0x00 and Max_Data = 0xFF */
uint32_t BreakState; /*!< TIM Break State
This parameter can be a value of @ref TIM_Break_Input_enable_disable */
uint32_t BreakPolarity; /*!< TIM Break input polarity
This parameter can be a value of @ref TIM_Break_Polarity */
uint32_t BreakFilter; /*!< Specifies the break input filter.
This parameter can be a number between Min_Data = 0x0 and Max_Data = 0xF */
uint32_t AutomaticOutput; /*!< TIM Automatic Output Enable state
This parameter can be a value of @ref TIM_AOE_Bit_Set_Reset */
} TIM_BreakDeadTimeConfigTypeDef;
OffStateRunMode(运行模式关闭状态)
该参数用于设置运行状态下,不使能的通道输出状态,可以选择的参数为:
/** @defgroup TIM_OSSR_Off_State_Selection_for_Run_mode_state TIM OSSR OffState Selection for Run mode state
* @{
*/
#define TIM_OSSR_ENABLE TIM_BDTR_OSSR /*!< When inactive, OC/OCN outputs are enabled (still controlled by the timer) */
#define TIM_OSSR_DISABLE 0x00000000U /*!< When inactive, OC/OCN outputs are disabled (not controlled any longer by the timer) */
/**
* @}
*/
OffStateIDLEMode(空闲状态关闭)
/** @defgroup TIM_OSSI_Off_State_Selection_for_Idle_mode_state TIM OSSI OffState Selection for Idle mode state
* @{
*/
#define TIM_OSSI_ENABLE TIM_BDTR_OSSI /*!< When inactive, OC/OCN outputs are enabled (still controlled by the timer) */
#define TIM_OSSI_DISABLE 0x00000000U /*!< When inactive, OC/OCN outputs are disabled (not controlled any longer by the timer) */
/**
* @}
*/
LockLevel(上锁等级)
/** @defgroup TIM_Lock_level TIM Lock level
* @{
*/
#define TIM_LOCKLEVEL_OFF 0x00000000U /*!< LOCK OFF */
#define TIM_LOCKLEVEL_1 TIM_BDTR_LOCK_0 /*!< LOCK Level 1 */
#define TIM_LOCKLEVEL_2 TIM_BDTR_LOCK_1 /*!< LOCK Level 2 */
#define TIM_LOCKLEVEL_3 TIM_BDTR_LOCK /*!< LOCK Level 3 */
/**
* @}
*/
DeadTime(死区时间)
该参数可以设置范围为0~255。
BreakState(断路状态)
/** @defgroup TIM_Break_Input_enable_disable TIM Break Input Enable
* @{
*/
#define TIM_BREAK_ENABLE TIM_BDTR_BKE /*!< Break input BRK is enabled */
#define TIM_BREAK_DISABLE 0x00000000U /*!< Break input BRK is disabled */
/**
* @}
*/
BreakPolarity(断路极性)
/** @defgroup TIM_Break_Polarity TIM Break Input Polarity
* @{
*/
#define TIM_BREAKPOLARITY_LOW 0x00000000U /*!< Break input BRK is active low */
#define TIM_BREAKPOLARITY_HIGH TIM_BDTR_BKP /*!< Break input BRK is active high */
/**
* @}
*/
BreakFilter(断路滤波器)
该参数的取值范围为:0~15.此参数在目前版本的HAL库中,并没有什么作用。
AutomaticOutput(自动输出使能)
/** @defgroup TIM_AOE_Bit_Set_Reset TIM Automatic Output Enable
* @{
*/
#define TIM_AUTOMATICOUTPUT_DISABLE 0x00000000U /*!< MOE can be set only by software */
#define TIM_AUTOMATICOUTPUT_ENABLE TIM_BDTR_AOE /*!< MOE can be set by software or automatically at the next update event
(if none of the break inputs BRK and BRK2 is active) */
/**
* @}
*/
HAL解析
和正点原子配置相比,本程序只是多用了一个HAL库函数,其定义如下:
/**
* @brief Configures the Break feature, dead time, Lock level, OSSI/OSSR State
* and the AOE(automatic output enable).
* @param htim TIM handle
* @param sBreakDeadTimeConfig pointer to a TIM_ConfigBreakDeadConfigTypeDef structure that
* contains the BDTR Register configuration information for the TIM peripheral.
* @note Interrupts can be generated when an active level is detected on the
* break input, the break 2 input or the system break input. Break
* interrupt can be enabled by calling the @ref __HAL_TIM_ENABLE_IT macro.
* @retval HAL status
*/
HAL_StatusTypeDef HAL_TIMEx_ConfigBreakDeadTime(TIM_HandleTypeDef *htim,
TIM_BreakDeadTimeConfigTypeDef *sBreakDeadTimeConfig)
{
/* Keep this variable initialized to 0 as it is used to configure BDTR register */
uint32_t tmpbdtr = 0U;
/* Check the parameters */
assert_param(IS_TIM_BREAK_INSTANCE(htim->Instance));
assert_param(IS_TIM_OSSR_STATE(sBreakDeadTimeConfig->OffStateRunMode));
assert_param(IS_TIM_OSSI_STATE(sBreakDeadTimeConfig->OffStateIDLEMode));
assert_param(IS_TIM_LOCK_LEVEL(sBreakDeadTimeConfig->LockLevel));
assert_param(IS_TIM_DEADTIME(sBreakDeadTimeConfig->DeadTime));
assert_param(IS_TIM_BREAK_STATE(sBreakDeadTimeConfig->BreakState));
assert_param(IS_TIM_BREAK_POLARITY(sBreakDeadTimeConfig->BreakPolarity));
assert_param(IS_TIM_AUTOMATIC_OUTPUT_STATE(sBreakDeadTimeConfig->AutomaticOutput));
/* Check input state */
__HAL_LOCK(htim);
/* Set the Lock level, the Break enable Bit and the Polarity, the OSSR State,
the OSSI State, the dead time value and the Automatic Output Enable Bit */
/* Set the BDTR bits */
MODIFY_REG(tmpbdtr, TIM_BDTR_DTG, sBreakDeadTimeConfig->DeadTime);
MODIFY_REG(tmpbdtr, TIM_BDTR_LOCK, sBreakDeadTimeConfig->LockLevel);
MODIFY_REG(tmpbdtr, TIM_BDTR_OSSI, sBreakDeadTimeConfig->OffStateIDLEMode);
MODIFY_REG(tmpbdtr, TIM_BDTR_OSSR, sBreakDeadTimeConfig->OffStateRunMode);
MODIFY_REG(tmpbdtr, TIM_BDTR_BKE, sBreakDeadTimeConfig->BreakState);
MODIFY_REG(tmpbdtr, TIM_BDTR_BKP, sBreakDeadTimeConfig->BreakPolarity);
MODIFY_REG(tmpbdtr, TIM_BDTR_AOE, sBreakDeadTimeConfig->AutomaticOutput);
/* Set TIMx_BDTR */
htim->Instance->BDTR = tmpbdtr;
__HAL_UNLOCK(htim);
return HAL_OK;
}
该函数十分简单,将结构体中的成员直接写入到寄存器中即可。不再详细分析。
keil版本
主函数
/**
******************************************************************************
* @file main.c
* @author zhy
* @version 1.0
* @date 2021-02-22
* @brief 用于生成pwm波
******************************************************************************
*/
#include "stm32f4xx_hal.h"
#include "sys.h"
#include "pwm.h"
uint16_t compare = 9000;
int main()
{
/* 1.系统初始化 */
HAL_Init();
SystemClock_Config();
PwmInit();
/* 2.设置占空比 */
while (1)
{
PwmSetCompare(compare);
}
}
与正点原子不同,本文通过调试时,修改变量compare值来修改PWM波的占空比。
PWM配置相关
/**
******************************************************************************
* @file pwm.c
* @author zhy
* @version 1.0
* @date 2021-02-22
* @brief 通过高级定时器配置双通道PWM
******************************************************************************
*/
#include "stm32f4xx_hal.h"
#include "pwm.h"
/**
* @brief PWM配置
* @note 高级寄存器TIM1的通道1
* @param {*}无
* @retval 无
*/
void PwmInit(void)
{
/* 1.使能TIM1的时钟 */
__HAL_RCC_TIM1_CLK_ENABLE();
/* 2.PWM时钟初始化 */
TIM_HandleTypeDef htim1; //定时器1句柄
htim1.Instance = TIM1; //定时器1
htim1.Channel = HAL_TIM_ACTIVE_CHANNEL_1; //活跃通道:通道1.
htim1.State = HAL_TIM_STATE_RESET; //初始化状态
htim1.Lock = HAL_UNLOCKED; //无锁
htim1.Init.Prescaler = 1 - 1; //此处不分频,提高PWM的精度
htim1.Init.Period = 18000 - 1; //PWM的频率为10K
htim1.Init.AutoReloadPreload = TIM_AUTOMATICOUTPUT_ENABLE; //启用ARR影子寄存器
htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; //死区发生器与采样频率的分频数
htim1.Init.RepetitionCounter = 0; //重复次数1
HAL_TIM_PWM_Init(&htim1); //初始化定时器1的通道1
/* 3.PWM输出通道初始化 */
TIM_OC_InitTypeDef ocInit;
ocInit.OCMode = TIM_OCMODE_PWM1; //pwm1模式
ocInit.OCPolarity = TIM_OCPOLARITY_HIGH; //高电平有效
ocInit.OCNPolarity = TIM_OCPOLARITY_HIGH; //高电平有效
ocInit.OCIdleState = TIM_OCIDLESTATE_RESET; //闲置状态:低
ocInit.OCNIdleState = TIM_OCNIDLESTATE_RESET; //闲置状态:低
ocInit.Pulse = 9000; //占空比50%
ocInit.OCFastMode = TIM_OCFAST_DISABLE; //不启动快速响应
HAL_TIM_PWM_ConfigChannel(&htim1, &ocInit, TIM_CHANNEL_1);
/* 4.配置死区与断路 */
TIM_BreakDeadTimeConfigTypeDef timBreakDead;
timBreakDead.OffStateRunMode = TIM_OSSR_DISABLE; //禁止OSSR
timBreakDead.OffStateIDLEMode = TIM_OSSI_ENABLE; //使能OSSI
timBreakDead.LockLevel = TIM_LOCKLEVEL_OFF; //关闭上锁等级
timBreakDead.DeadTime = 0; //死区时间为0
timBreakDead.BreakState = TIM_BREAK_ENABLE; //使能断路
timBreakDead.BreakPolarity = TIM_BREAKPOLARITY_HIGH; //高电平有效
timBreakDead.BreakFilter = 0; //不使用滤波器
timBreakDead.AutomaticOutput = TIM_AUTOMATICOUTPUT_ENABLE; //自动断路使能
HAL_TIMEx_ConfigBreakDeadTime(&htim1, &timBreakDead);
/* 5.启动PWM */
HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);
HAL_TIMEx_PWMN_Start(&htim1, TIM_CHANNEL_1);
}
/**
* @brief PWM底层驱动配置
* @note 1.此函数在HAL_TIM_PWM_Init中调用
* 2.使用PA7与PA8管脚
* @param {*} 定时器句柄
* @retval 无
*/
void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim)
{
/* 1.启动时钟 */
__HAL_RCC_GPIOA_CLK_ENABLE();
/* 2.GPIOA初始化 */
GPIO_InitTypeDef gpioInit;
gpioInit.Pin = GPIO_PIN_6 | GPIO_PIN_7 | GPIO_PIN_8; //pin6,7,8
gpioInit.Pull = GPIO_PULLDOWN; //上拉
gpioInit.Speed = GPIO_SPEED_HIGH; //快速
gpioInit.Mode = GPIO_MODE_AF_PP; //复用推挽
gpioInit.Alternate = GPIO_AF1_TIM1; //复用为TIM1通道接口
HAL_GPIO_Init(GPIOA, &gpioInit); //PA6,PA7,PA8
}
/**
* @brief 设置比较值
* @note 用于Tim1的通道1
* @param {uint16_t} c 比较寄存器的值
* @retval
*/
void PwmSetCompare(uint16_t c)
{
TIM1->CCR1 = c;
}
与正点原子相比,程序中多了刹车功能,通过PA6实现。而且配置互补通道。
CUBE版本
此部分主要介绍与PWM配置相关内容,这部分内容是通过Cube自动生成的,时钟配置的源代码如下:
/**
* @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 */
/* 1.PWM时钟初始化 */
htim1.Instance = TIM1;
htim1.Init.Prescaler = 0;
htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
htim1.Init.Period = 17999;
htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim1.Init.RepetitionCounter = 0;
htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
if (HAL_TIM_PWM_Init(&htim1) != HAL_OK)
{
Error_Handler();
}
/* 2.同步设置 */
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
/* 3.PAM通道初始化 */
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 9000;
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_1) != HAL_OK)
{
Error_Handler();
}
/* 4.PWM死区与断路设置 */
sBreakDeadTimeConfig.OffStateRunMode = TIM_OSSR_DISABLE;
sBreakDeadTimeConfig.OffStateIDLEMode = TIM_OSSI_ENABLE;
sBreakDeadTimeConfig.LockLevel = TIM_LOCKLEVEL_OFF;
sBreakDeadTimeConfig.DeadTime = 0;
sBreakDeadTimeConfig.BreakState = TIM_BREAK_ENABLE;
sBreakDeadTimeConfig.BreakPolarity = TIM_BREAKPOLARITY_HIGH;
sBreakDeadTimeConfig.AutomaticOutput = TIM_AUTOMATICOUTPUT_ENABLE;
if (HAL_TIMEx_ConfigBreakDeadTime(&htim1, &sBreakDeadTimeConfig) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN TIM1_Init 2 */
/* USER CODE END TIM1_Init 2 */
/* 5.PWM输出管脚配置 */
HAL_TIM_MspPostInit(&htim1);
}
通过对比可以发现:
- 该代码多了同步设置的而配置,在注释的第二部分。由于此部分是禁用状态,所以,在keil写代码没有明确配置。
- 定时器句柄部分,只设置了初始化相关的参数。
- 没有启动PWM,该部分需要自己在main函数调用。
- 将PWM输出管脚底层配置通过
HAL_TIM_MspPostInit
实现。底层配置分成两个函数,定义如下:
/**
* @brief TIM_PWM MSP Initialization
* This function configures the hardware resources used in this example
* @param htim_pwm: TIM_PWM handle pointer
* @retval None
*/
void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef* htim_pwm)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(htim_pwm->Instance==TIM1)
{
/* USER CODE BEGIN TIM1_MspInit 0 */
/* USER CODE END TIM1_MspInit 0 */
/* Peripheral clock enable */
__HAL_RCC_TIM1_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
/**TIM1 GPIO Configuration
PA6 ------> TIM1_BKIN
*/
GPIO_InitStruct.Pin = GPIO_PIN_6;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_PULLDOWN;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Alternate = GPIO_AF1_TIM1;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* USER CODE BEGIN TIM1_MspInit 1 */
/* USER CODE END TIM1_MspInit 1 */
}
}
void HAL_TIM_MspPostInit(TIM_HandleTypeDef* htim)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(htim->Instance==TIM1)
{
/* USER CODE BEGIN TIM1_MspPostInit 0 */
/* USER CODE END TIM1_MspPostInit 0 */
__HAL_RCC_GPIOA_CLK_ENABLE();
/**TIM1 GPIO Configuration
PA7 ------> TIM1_CH1N
PA8 ------> TIM1_CH1
*/
GPIO_InitStruct.Pin = GPIO_PIN_7|GPIO_PIN_8;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_PULLDOWN;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF1_TIM1;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* USER CODE BEGIN TIM1_MspPostInit 1 */
/* USER CODE END TIM1_MspPostInit 1 */
}
}
该部分程序与手写部分大同小异,不再详细介绍。