1.任务解析
2.PWM原理
3. 通用定时器TIM4配置原理
4. 图形化配置对应的HAL库软件代码
5. 补充代码
1.任务解析
1.1:LED灯效果显示为逐渐变量,然后逐渐变暗;
1.2:系统刚上电,LED灯为关闭状态,第奇数次按键按下,LED灯显示呼吸灯效果,第偶数次按下按键,LED灯关闭;
目标:先实现任务1.1。要实现LED灯逐渐变量,逐渐变暗的交替效果;定位到硬件原理图,观察LED9发光二极管直接连接PB8_PWM,于是我们可以直接使用GPIOB_8引脚,实现PWM脉冲输出;
2.PWM解析
脉冲宽度调制(PWM),是英文“Pulse Width Modulation”的缩写,简称脉宽调制,是利用数字输出来对模拟电路进行控制的一种技术。
PWM 信号的生成样式与计数器寄存器(TIMx_CNT)、自动重载寄存器(TIMx_ARR)以及捕获/比较寄存器(TIMx_CCRy)有关。
假定定时器工作在向上计数 PWM模式,且当 CNT
改变 CCRx 的值,就可以改变 PWM 输出的占空比,
改变 ARR 的值,就可以改变 PWM 输出的频率,这就是 PWM 输出的原理。<本小节参考《正点原子HAL库开发指南》>
3.通用定时器TIM4配置原理
3.1 TMI4通用定时器功能框图
补充功能框图的解释:
3.2 关键寄存器介绍
之前定时器控制LED闪灭1秒,我们使用了三个寄存器,详情请看
https://www.jianshu.com/p/8403dedd1785
本文重点介绍捕获/比较模式寄存器(TIMx_CCMR1/2)、捕获/比较使能寄存器(TIMx_CCER)、捕获/比较寄存器(TIMx_CCR1~4)。
模式寄存器(TIMx_CCMR1/2)
捕获/比较模式寄存器(TIMx_CCMR1/2)该寄存器总共有 2 个,TIMx _CCMR1和 TIMx _CCMR2。TIMx_CCMR1 控制 CH1 和 2,而 TIMx_CCMR2 控制 CH3 和 4;
该寄存器分了 2层,上面一层对应输出而下面的则对应输入。可用于输入(捕获模式)或输出(比较模式),通道的方向由相应的CCxS定义;当使用的是 PWM 模式时,OCxM 3 位必须设置为 110/111;
PWM1模式:OCxM=110,在向上计数时,一旦TIMx_CNT 捕获/比较使能寄存器(TIMx_CCER) 捕获/比较寄存器(TIMx_CCR1~4) 1)开启 TIM4 和 GPIO 时钟;使能GPIO8寄存器; 2)初始化 TIM4,设置 TIM4 的 ARR 和 PSC 等参数; 其中,这些参数的配置对应图形化配置为: HAL 库为我们提供了一个独立的定时器初始化函数 HAL_TIM_PWM_Init,该函数,作用是初始化定时 3)设置 TIM4_CH3 的 PWM 比较初值;调用函数实现: PWM 模式,输出比较极性的设置: 4)使能 TIM4,使能 TIM4 的 CH3 输出。该函数第二个入口参数 Channel 是用来设置要使能输出的通道号; 5)修改 TIM4_CCR3 来控制占空比。 为了实现任务1.2,按键按下奇数次灯逐渐变亮,按键按下偶数次变灭的效果;需要用到全局变量 keydown_flag,pwm_enable来做判断按键是否按下,是否使能PWM模式;注意pwm_value的时函数__HAL_TIM_SET_COMPARE(&htim4, TIM_CHANNEL_3, pwm_value);
PWM2模式:OCxM=111: 在向上计数时,一旦TIMx_CNT
该寄存器控制着各个输入输出通道的开关。该寄存器的各位描述如图7所示:
我们这里只用到了 CC3E 位,该位是输入/捕获 3 输出使能位,要想PWM 从 IO 口输出,这个位必须设置为 1,所以我们需要设置该位为 1;
最后,我们介绍一下捕获/比较寄存器(TIMx_CCR1~4),该寄存器总共有 4 个,对应 4 个输通道 CH1~4。
该寄存器总共有 4 个,对应 4 个输通道 CH1~4。因为这 4 个寄存器都差不多,我们仅以 TIMx_CCR1 为例介绍;
在输出模式下,该寄存器的值与 CNT 的值比较,根据比较结果产生相应动作。利用这点,我们通过修改这个寄存器的值,就可以控制 PWM 的输出脉宽度了。本章,我们使用的是 TIM4的通道 3,所以我们需要修改 TIM4_CCR3 以实现脉宽控制 LED9 的亮度。
4.软件代码的功能实现顺序
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* tim_baseHandle)
{
if(tim_baseHandle->Instance==TIM4)
{
/* TIM4 clock enable */
__HAL_RCC_TIM4_CLK_ENABLE();
}
}
void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
}
设置 TIM4_CH3 的 PWM 模式,输出比较极性,比较值等参数。void MX_TIM4_Init(void)
{
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
TIM_OC_InitTypeDef sConfigOC = {0};
htim4.Instance = TIM4;
htim4.Init.Prescaler = 71;
htim4.Init.CounterMode = TIM_COUNTERMODE_UP;
htim4.Init.Period = 99;
htim4.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim4.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim4) != HAL_OK)
{
Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim4, &sClockSourceConfig) != HAL_OK)
{
Error_Handler();
}
if (HAL_TIM_PWM_Init(&htim4) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim4, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 0;
sConfigOC.OCPolarity = TIM_OCPOLARITY_LOW;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
if (HAL_TIM_PWM_ConfigChannel(&htim4, &sConfigOC, TIM_CHANNEL_3) != HAL_OK)
{
Error_Handler();
}
HAL_TIM_MspPostInit(&htim4);
}
htim4.Instance = TIM4; 使能定时器4
htim4.Init.Prescaler = 71; 预分频系数71
htim4.Init.CounterMode = TIM_COUNTERMODE_UP;
htim4.Init.Period = 99; 重载计数器初值99;
sConfigOC.OCMode = TIM_OCMODE_PWM1; PWM1模式为1;
sConfigOC.Pulse = 0;捕捉比较寄存器TIMx_CCR1初始值为0;
sConfigOC.OCPolarity = TIM_OCPOLARITY_LOW;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
器 的 ARR 和 PSC 等参 数;它调用了 MSP 回 调 函 数
HAL_TIM_PWM_MspInit;HAL_StatusTypeDef HAL_TIM_PWM_Init(TIM_HandleTypeDef *htim)
{
/* Check the TIM handle allocation */
if (htim == NULL)
{
return HAL_ERROR;
}
/* Check the parameters */
assert_param(IS_TIM_INSTANCE(htim->Instance));
assert_param(IS_TIM_COUNTER_MODE(htim->Init.CounterMode));
assert_param(IS_TIM_CLOCKDIVISION_DIV(htim->Init.ClockDivision));
assert_param(IS_TIM_AUTORELOAD_PRELOAD(htim->Init.AutoReloadPreload));
if (htim->State == HAL_TIM_STATE_RESET)
{
/* Allocate lock resource and initialize it */
htim->Lock = HAL_UNLOCKED;
#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)
/* Reset interrupt callbacks to legacy weak callbacks */
TIM_ResetCallback(htim);
if (htim->PWM_MspInitCallback == NULL)
{
htim->PWM_MspInitCallback = HAL_TIM_PWM_MspInit;
}
/* Init the low level hardware : GPIO, CLOCK, NVIC */
htim->PWM_MspInitCallback(htim);
#else
/* Init the low level hardware : GPIO, CLOCK, NVIC and DMA */
HAL_TIM_PWM_MspInit(htim);
#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */
}
/* Set the TIM state */
htim->State = HAL_TIM_STATE_BUSY;
/* Init the base time for the PWM */
TIM_Base_SetConfig(htim->Instance, &htim->Init);
/* Initialize the DMA burst operation state */
htim->DMABurstState = HAL_DMA_BURST_STATE_READY;
/* Initialize the TIM channels state */
TIM_CHANNEL_STATE_SET_ALL(htim, HAL_TIM_CHANNEL_STATE_READY);
TIM_CHANNEL_N_STATE_SET_ALL(htim, HAL_TIM_CHANNEL_STATE_READY);
/* Initialize the TIM state*/
htim->State = HAL_TIM_STATE_READY;
return HAL_OK;
}
__HAL_TIM_SET_COMPARE(&htim4, TIM_CHANNEL_3, 0);
sConfigOC.Pulse = 0;捕捉比较寄存器TIMx_CCR1初始值为0;
sConfigOC.OCPolarity = TIM_OCPOLARITY_LOW; 比较极性
HAL_TIM_PWM_Start(&htim4, TIM_CHANNEL_3);
static uint16_t pwm_value = 0;
__HAL_TIM_SET_COMPARE(&htim4, TIM_CHANNEL_3, pwm_value);
5.补充代码
的参数,用于给寄存器TIM_CCRx赋值,修改比较寄存器的值。用于调整脉冲宽度; typedef enum
{
count_up = 0x01,
count_down,
} pwm_mode_enum_TypeDef;
pwm_mode_enum_TypeDef pwm_mode = count_up;
uint8_t keydown_flag = 0;
uint8_t pwm_enable = 0;
static uint16_t pwm_value = 0;
int main(void)
{
while (1)
{
if (keydown_flag == 1)
{
HAL_Delay(300);
if (keydown_flag == 1)
{
keydown_flag = 0;
if (pwm_enable == 0)
pwm_enable = 1;
else if (pwm_enable == 1)
pwm_enable = 0;
}
}
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
if (pwm_enable == 1)
{
if (pwm_value == 0)
{
pwm_mode = count_up;
}
else if (pwm_value == 30)
{
pwm_mode = count_down;
}
if (pwm_mode == count_up)
{
pwm_value++;
}
if (pwm_mode == count_down)
{
pwm_value--;
}
__HAL_TIM_SET_COMPARE(&htim4, TIM_CHANNEL_3, pwm_value);
}
else if (pwm_enable == 0)
{
__HAL_TIM_SET_COMPARE(&htim4, TIM_CHANNEL_3, 0);
}
HAL_Delay(50);
}
/* USER CODE END 3 */
}