普冉(PUYA)单片机开发笔记(6): 呼吸灯
概述
上一篇的实验中,分别正确地配置了 TIM16 和 TIM1,TIM16 的中断服务程序中每隔 500ms 翻转板载 LED 一次;TIM1 的 CHANNEL_1 用于输出一个固定占空比的 PWM 信号。这一次我们进一小步:使用 TIM16 的中断设置 TIM1 CHANNEL_1 的 PWM 输出呈现从小到大,再从大到小的循环,即实现呼吸灯效果。
为此,PWM 输出改用千分之一精度的调节,TIM16 的定时周期设置为 8 毫秒,PWM 从 0% 到 100%,耗时 8秒。
对于 TIM1,PWM_PERIOD 定义为 2400,PWM 调节步长设置为 2.4F,和 1000 步调节到位相对应。PWM_PERIOD 参数用于 TIM1 的 ARR 装填,PWM 输出频率为 10KHz。
实现代码
修改 main.h 文件
将 PWM 分频参数 PWM_PERIOD 搬到 mian.h 中
新增一个千分之一 PWM 输出的函数:
void TIM1_PWM_Output_Permill(const uint16_t duty_permill);
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __MAIN_H
#define __MAIN_H
#ifdef __cplusplus
extern "C"
{
#endif
#ifndef PWM_PERIOD
#define PWM_PERIOD 2400
#endif
/* Includes ------------------------------------------------------------------*/
#include "py32f0xx_hal.h"
#include "py32f003xx_Start_Kit.h"
#include <stdbool.h>
/* Exported functions prototypes ---------------------------------------------*/
HAL_StatusTypeDef SystemClock_Config(void);
HAL_StatusTypeDef GPIO_Config(void);
HAL_StatusTypeDef USART_Config(void);
HAL_StatusTypeDef DBG_UART_Start(void);
HAL_StatusTypeDef TIM16_Config(void);
HAL_StatusTypeDef TIM16_Start(void);
HAL_StatusTypeDef TIM1_PWM_Config(void);
HAL_StatusTypeDef TIM1_PWM_Start(uint32_t duty);
HAL_StatusTypeDef TIM1_PWM_Stop(void);
void TIM1_PWM_Output(const uint8_t duty_percent);
void TIM1_PWM_Output_Permill(const uint16_t duty_permill);
void Debug_Info(const char* msg);
...
...
在 app_pwm.c 中实现 TIM1_PWM_Output_Permill 函数
void TIM1_PWM_Output_Permill(const uint16_t duty_permill)
{
uint16_t tmp_duty = 0;
uint32_t duty = 0;
tmp_duty = duty_permill;
if(duty_permill > 1000) tmp_duty = 999;
duty = (uint32_t)(tmp_duty * PWM_PERIOD / 1000.0F + 0.5F) + 1;
TIM1_PWM_Start(duty);
}
计算 duty 时,加的那个 0.5F 是处理四舍五入用的。
修改 app_timer.c 文件
在原有代码上增加三个全局变量
#include "main.h"
TIM_HandleTypeDef htim16;
#define gPwmStep 2.4F
uint16_t gCurrentDutyPermill = 0;
int8_t gPwmDir = 1;
...
...
初始化代码修改如下,Period 和 Prescaler 的设定值使 TIM16 的定时周期为 0.008s ,即8毫秒。
HAL_StatusTypeDef TIM16_Config(void)
{
HAL_StatusTypeDef conf_res = HAL_OK;
htim16.Instance = TIM16; // 选择 TIM16
htim16.Init.Period = 12000 - 1; // 自动重装载值 500us
htim16.Init.Prescaler = 16 - 1; // 预分频为 2-1,两者确定定时器中断周期为1ms
htim16.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; // 时钟不分频
htim16.Init.CounterMode = TIM_COUNTERMODE_UP; // 向上计数
htim16.Init.RepetitionCounter = 1 - 1; // 不重复计数
htim16.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE; // TIMx ARR 缓冲
conf_res = HAL_TIM_Base_Init(&htim16); // TIMx 初始化
if ( conf_res != HAL_OK)
return conf_res;
return HAL_OK;
}
在 HAL_TIM_PeriodElapsedCallback 函数中计算出呼吸灯的调节量,并调用 TIM1_PWM_Output_Permill 函数执行 PWM 输出。
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance != TIM16) return;
TIM1_PWM_Output_Permill(gCurrentDutyPermill);
gCurrentDutyPermill = (uint16_t)(gCurrentDutyPermill + gPwmDir * gPwmStep);
if(gPwmDir == 1)
{
if(gCurrentDutyPermill >= 1000)
{
gPwmDir = -1;
gCurrentDutyPermill = 1000;
}
}
else {
if(gCurrentDutyPermill <= (uint16_t)(gPwmStep + 0.5))
{
gPwmDir = 1;
gCurrentDutyPermill = 0;
}
}
}
代码中原有的翻转 LED 灯的那一句去掉了:闪烁频率太高,看着不舒服了。
稍微修改一下 main() 函数
注意要将 TIM16 的初始化函数放到 TIM1_PWM 初始化函数前面。
int main(void)
{
HAL_Init(); // systick初始化
SystemClock_Config(); // 配置系统时钟
if(USART_Config() != HAL_OK) Error_Handler();
printf("[SYS_INIT] Debug port initilaized.\r\n");
if(GPIO_Config() != HAL_OK) Error_Handler();
printf("[SYS_INIT] Board LED initilaized.\r\n");
if(TIM16_Config() != HAL_OK) Error_Handler();
printf("[SYS_INIT] Timer initialized.\r\n");
if(TIM1_PWM_Config() != HAL_OK) Error_Handler();
printf("[SYS_INIT] PWM initialized.\r\n");
if (TIM16_Start() != HAL_OK) Error_Handler();
printf("[SYS_INIT] Timer started.\r\n");
printf("\r\n+---------------------------------------+"
"\r\n| PY32F003 MCU is ready. |"
"\r\n+---------------------------------------+"
"\r\n");
if (DBG_UART_Start() != HAL_OK) Error_Handler();
// TIM1_PWM_Output_Permill(250);
while (1) { }
}
总结
示波器的输出波形已通过视频上传,达到设计预期。
要点:
- 根据 PWM 输出精度的要求设置 TIM1 对应通道 PWM 的频率,例如,要实现千分之一的逐级调节,PWM_PERIOD 就要设置得大于1000才好。
- 根据 PWM 调节时长限制确定 TIM16 的定时周期,例如,要实现 8秒完成一轮调节,每一轮调节需要 1000 次递增/递减,则需要将 TIM16 的定时周期设置为 8 毫秒。
- TIM16 的初始化和启动应放在 TIM1 的初始化之后。
- 在 TIM16 的中断服务程序中,以最少的时间代价完成 TIM1 PWM 通道的输出。
问题点:在 PWM 波形变化过程中,会出现少许凹陷的毛刺,还没有找到原因。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!