1、回顾、原理
前面文章利用,ARR,CCRx制作了呼吸灯。
void motor_task (void * arg)
{
osStatus_t status;
bsp_InitPWM(); /*pwm PB1*/
while(1)
{
#ifdef BLN_ //呼吸灯
uint16_t pwmValue;
while (pwmValue < _TIM3_ARR)
{
pwmValue ++;
bsp_SetPWM_CCRx(pwmValue);
osDelay(1);
}
while (pwmValue > _TIM3_ARR/2)
{
pwmValue --;
bsp_SetPWM_CCRx(pwmValue);
osDelay(1);
}
#endif
osThreadYield();
}
}
从上面代码以及实验现象,我们发现呼吸灯实际上跟人的呼吸是差距很大的。代码上PWM占空比是线性变化的,但人是呼吸实际不是如此。
2、呼吸灯与 PWM 控制原理
呼吸的特性是一种类似图 40-1 中的指数曲线过程,吸气是指数上升过程,呼气是指数下降过程,成年人吸气呼气整个过程持续约 3 秒:
要控制 LED 灯达到呼吸灯的效果,实际上就是要控制 LED 灯的亮度拟合呼吸特性曲
线。前面控制全彩 LED 灯时,通过控制脉冲的占空比来调整各个通道 LED 灯的亮度,从而达到混色的效果。若控制脉冲的占空比在 3 秒的时间周期内按呼吸特性曲线变化,那么就可以实现呼吸灯的效果了。
这种使用脉冲占空比拟合不同波形的方式称为 PWM(脉冲宽度调制)控制技术——通过对一系列脉冲的宽度进行调制,来等效地获得所需要波形(含形状和幅值)。PWM 控制的基本原理为:冲量相等而开头不同的窄脉冲加在具有惯性的环节上时,其效果基本相同。其中冲量指窄脉冲的面积;效果相同指环节输出响应波形基本相同。
例如:可以用一系列等幅不用一系列等幅不等宽的脉冲来代替一个正弦半波,见图40-2。
把正弦半波 N 等分,可看成 N 个彼此相连的脉冲序列,宽度相等,但幅值不等;
用矩形脉冲代替,各个矩形脉冲等幅,不等宽,中点重合,脉冲宽度按正弦规律变化,脉冲的总面积(冲量)与正弦半波相等。
这种脉冲波形被称为 SPWM 波形——脉冲宽度按正弦规律变化而和正弦波等效的PWM 波形。SPWM 是一种非常典型的 PWM 波形,它在数字电路控制中应用非常广泛,如果使用低通滤波器,可以由 SPWM 波得到其等效的连续正弦半波。
要改变等效输出正弦波幅值,按同一比例改变各脉冲宽度即可。 若把拟合的波形改成呼吸特性曲线,即可得到控制呼吸灯使用的 PWM 波形,要生成拟合的 PWM 波形,通常使用计算法和调制法:
(1) 计算法:根据拟合波形的频率、幅值和半周期脉冲数,准确计算 PWM 波各脉冲宽度和间隔,据此控制开关器件的通断,就可得到所需 PWM 波形;
(2) 调制法:拟合波形作调制信号,进行调制得到期望的 PWM 波;该方法一般采用等腰三角波为载波,其任一点水平宽度和高度成线性关系且左右对称。载波(等腰三角波)与平缓变化的调制信号波(即要拟合的波形)相交,在载波与信号波的交点控制器件通断,就得宽度正比于信号波幅值的脉冲,符合 PWM 的要求,见图 40-3。相对于计算法,其处理过程计算简单。
在本章的各个实验中,演示如何使用计算法得到的呼吸曲线 PWM 波和 SPWM 波,并
使用 STM32 定时器 TIM 的 PWM 功能输出波形控制 LED 灯,达到呼吸灯的效果。
原理:
使用脉冲占空比拟合呼吸特性曲线,生成LED亮度等级 PWM表,指数曲线 。根据PWM表,每隔固定周期,修改亮度即占空比。
LED低电平点亮,配置时1024uS周期,PWM1模式,工作极性低,初始化低电平占空比0即输出全高。
2、配置
3、代码
bsp_pwm.c
/*************本实验中的配置***************/
/***********************************************
#PWM点数
POINT_NUM = 110
#周期倍数
PERIOD_CLASS = 27
#定时器定时周期
TIMER_TIM_Period = 1024
#定时器分频
TIMER_TIM_Prescaler = 84
#STM32系统时钟频率和周期
f_hclk = 168000000
#定时器计时频率
f_pclk1 = f_hclk/2
t_pclk1 = 1/f_pclk1
#定时器update事件周期
t_timer = t_pclk1*TIMER_TIM_Prescaler*TIMER_TIM_Period
t_timer = 1024uS
#每个PWM点的时间
T_Point = t_timer * PERIOD_CLASS
T_Point = 27648uS
#整个呼吸周期
T_Up_Down_Cycle = T_Point * POINT_NUM
T_Up_Down_Cycle = 27648uS*110=3041280uS
print ("呼吸周期:",T_Up_Down_Cycle)
#运行结果:
呼吸周期:3.041280S
************************************************************/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include "bsp_pwm.h"
//控制输出波形的频率
uint16_t period_class = 27;
/* LED亮度等级 PWM表,指数曲线 ,此表使用工程目录下的python脚本index_wave.py生成*/
uint16_t indexWave[] = {
1, 1, 1, 1, 2, 2, 2, 2, 3, 3,
4, 4, 5, 5, 6, 7, 8, 9, 10,
11, 13, 15, 17, 19, 22, 25,
28, 32, 36, 41, 47, 53, 61,
69, 79, 89, 102, 116, 131,
149, 170, 193, 219, 250, 284,
323, 367, 417, 474, 539, 613,
697, 792, 901, 1024, 1024, 901,
792, 697, 613, 539, 474, 417,
367, 323, 284, 250, 219, 193,
170, 149, 131, 116, 102, 89, 79,
69, 61, 53, 47, 41, 36, 32, 28,
25, 22, 19, 17, 15, 13, 11, 10,
9, 8, 7, 6, 5, 5, 4, 4, 3, 3, 2,
2, 2, 2, 1, 1, 1, 1
};
//计算PWM表有多少个元素
uint16_t POINT_NUM = sizeof(indexWave)/sizeof(indexWave[0]);
/*
*********************************************************************************************************
* 函 数 名: PWM_TIM3_CH4_EN
* 功能说明: 使能PWM
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
void PWM_TIM3_CH4_EN(void)
{
HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_4);
}
/*
*********************************************************************************************************
* 函 数 名: bsp_SetPWM_ARR
* 功能说明: 设置PWM频率 即ARR自动重装载值
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
void bsp_SetPWM_ARR(uint16_t _ARR)
{
__HAL_TIM_SET_AUTORELOAD(&htim3,_ARR-1);
// TIM3->ARR = _ARR-1;
}
/*
*********************************************************************************************************
* 函 数 名: bsp_InitPWM
* 功能说明: 使能PWM
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
void bsp_InitPWM(void)
{
HAL_TIM_Base_Start_IT(&htim3);
PWM_TIM3_CH4_EN();
bsp_SetPWM_ARR(_TIM3_ARR);//周期
bsp_SetPWM_CCRx(0);//占空比
}
/*
*********************************************************************************************************
* 函 数 名: bsp_SetPWM_CCRx
* 功能说明: 设置占空比 即ARR
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
void bsp_SetPWM_CCRx(uint16_t duty)
{
if(duty <= _TIM3_ARR)
{
__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_4, duty);
// TIM3->CCR4 = dutyCycle;
}
}
//周期运行回调,配置定时进入中断 到达ARR
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
static uint16_t pwm_index = 0; //用于PWM查表
static uint16_t period_cnt = 0; //用于计算周期数
if (htim->Instance == htim3.Instance)
{
period_cnt++;
bsp_SetPWM_CCRx(indexWave[pwm_index]);
//每个PWM表中的每个元素使用period_class次
if(period_cnt > period_class) //根据PWM表修改定时器的比较寄存器值
{
pwm_index++; //标志PWM表指向下一个元素
//若PWM表已到达结尾,重新指向表头
if( pwm_index >= POINT_NUM)
{
pwm_index=0;
}
period_cnt=0; //重置周期计数标志
}
else
{
}
}
}
bsp_pwm.h
#ifndef _BSP_PWM_H
#define _BSP_PWM_H
#include "stm32f4xx_hal.h"
#include "main.h"
#include "pub_gpio.h"
#include "tim.h"
#include "bsp_led.h"
/*PWM周期 单位us 84M/84 Prescaler*/
#define _TIM3_ARR 1024
void bsp_InitPWM(void);
void bsp_SetPWM_CCRx(uint16_t duty);
void bsp_BLE_Control(uint8_t repeat);
#endif
1、回顾、原理
前面文章利用,ARR,CCRx制作了呼吸灯。
void motor_task (void * arg)
{
osStatus_t status;
bsp_InitPWM(); /*pwm PB1*/
while(1)
{
#ifdef BLN_ //呼吸灯
uint16_t pwmValue;
while (pwmValue < _TIM3_ARR)
{
pwmValue ++;
bsp_SetPWM_CCRx(pwmValue);
osDelay(1);
}
while (pwmValue > _TIM3_ARR/2)
{
pwmValue --;
bsp_SetPWM_CCRx(pwmValue);
osDelay(1);
}
#endif
osThreadYield();
}
}
从上面代码以及实验现象,我们发现呼吸灯实际上跟人的呼吸是差距很大的。代码上PWM占空比是线性变化的,但人是呼吸实际不是如此。
2、呼吸灯与 PWM 控制原理
呼吸的特性是一种类似图 40-1 中的指数曲线过程,吸气是指数上升过程,呼气是指数下降过程,成年人吸气呼气整个过程持续约 3 秒:
要控制 LED 灯达到呼吸灯的效果,实际上就是要控制 LED 灯的亮度拟合呼吸特性曲
线。前面控制全彩 LED 灯时,通过控制脉冲的占空比来调整各个通道 LED 灯的亮度,从而达到混色的效果。若控制脉冲的占空比在 3 秒的时间周期内按呼吸特性曲线变化,那么就可以实现呼吸灯的效果了。
这种使用脉冲占空比拟合不同波形的方式称为 PWM(脉冲宽度调制)控制技术——通过对一系列脉冲的宽度进行调制,来等效地获得所需要波形(含形状和幅值)。PWM 控制的基本原理为:冲量相等而开头不同的窄脉冲加在具有惯性的环节上时,其效果基本相同。其中冲量指窄脉冲的面积;效果相同指环节输出响应波形基本相同。
例如:可以用一系列等幅不用一系列等幅不等宽的脉冲来代替一个正弦半波,见图40-2。
把正弦半波 N 等分,可看成 N 个彼此相连的脉冲序列,宽度相等,但幅值不等;
用矩形脉冲代替,各个矩形脉冲等幅,不等宽,中点重合,脉冲宽度按正弦规律变化,脉冲的总面积(冲量)与正弦半波相等。
这种脉冲波形被称为 SPWM 波形——脉冲宽度按正弦规律变化而和正弦波等效的PWM 波形。SPWM 是一种非常典型的 PWM 波形,它在数字电路控制中应用非常广泛,如果使用低通滤波器,可以由 SPWM 波得到其等效的连续正弦半波。
要改变等效输出正弦波幅值,按同一比例改变各脉冲宽度即可。 若把拟合的波形改成呼吸特性曲线,即可得到控制呼吸灯使用的 PWM 波形,要生成拟合的 PWM 波形,通常使用计算法和调制法:
(1) 计算法:根据拟合波形的频率、幅值和半周期脉冲数,准确计算 PWM 波各脉冲宽度和间隔,据此控制开关器件的通断,就可得到所需 PWM 波形;
(2) 调制法:拟合波形作调制信号,进行调制得到期望的 PWM 波;该方法一般采用等腰三角波为载波,其任一点水平宽度和高度成线性关系且左右对称。载波(等腰三角波)与平缓变化的调制信号波(即要拟合的波形)相交,在载波与信号波的交点控制器件通断,就得宽度正比于信号波幅值的脉冲,符合 PWM 的要求,见图 40-3。相对于计算法,其处理过程计算简单。
在本章的各个实验中,演示如何使用计算法得到的呼吸曲线 PWM 波和 SPWM 波,并
使用 STM32 定时器 TIM 的 PWM 功能输出波形控制 LED 灯,达到呼吸灯的效果。
原理:
使用脉冲占空比拟合呼吸特性曲线,生成LED亮度等级 PWM表,指数曲线 。根据PWM表,每隔固定周期,修改亮度即占空比。
LED低电平点亮,配置时1024uS周期,PWM1模式,工作极性低,初始化低电平占空比0即输出全高。
2、配置
3、代码
bsp_pwm.c
/*************本实验中的配置***************/
/***********************************************
#PWM点数
POINT_NUM = 110
#周期倍数
PERIOD_CLASS = 27
#定时器定时周期
TIMER_TIM_Period = 1024
#定时器分频
TIMER_TIM_Prescaler = 84
#STM32系统时钟频率和周期
f_hclk = 168000000
#定时器计时频率
f_pclk1 = f_hclk/2
t_pclk1 = 1/f_pclk1
#定时器update事件周期
t_timer = t_pclk1*TIMER_TIM_Prescaler*TIMER_TIM_Period
t_timer = 1024uS
#每个PWM点的时间
T_Point = t_timer * PERIOD_CLASS
T_Point = 27648uS
#整个呼吸周期
T_Up_Down_Cycle = T_Point * POINT_NUM
T_Up_Down_Cycle = 27648uS*110=3041280uS
print ("呼吸周期:",T_Up_Down_Cycle)
#运行结果:
呼吸周期:3.041280S
************************************************************/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include "bsp_pwm.h"
//控制输出波形的频率
uint16_t period_class = 27;
/* LED亮度等级 PWM表,指数曲线 ,此表使用工程目录下的python脚本index_wave.py生成*/
uint16_t indexWave[] = {
1, 1, 1, 1, 2, 2, 2, 2, 3, 3,
4, 4, 5, 5, 6, 7, 8, 9, 10,
11, 13, 15, 17, 19, 22, 25,
28, 32, 36, 41, 47, 53, 61,
69, 79, 89, 102, 116, 131,
149, 170, 193, 219, 250, 284,
323, 367, 417, 474, 539, 613,
697, 792, 901, 1024, 1024, 901,
792, 697, 613, 539, 474, 417,
367, 323, 284, 250, 219, 193,
170, 149, 131, 116, 102, 89, 79,
69, 61, 53, 47, 41, 36, 32, 28,
25, 22, 19, 17, 15, 13, 11, 10,
9, 8, 7, 6, 5, 5, 4, 4, 3, 3, 2,
2, 2, 2, 1, 1, 1, 1
};
//计算PWM表有多少个元素
uint16_t POINT_NUM = sizeof(indexWave)/sizeof(indexWave[0]);
/*
*********************************************************************************************************
* 函 数 名: PWM_TIM3_CH4_EN
* 功能说明: 使能PWM
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
void PWM_TIM3_CH4_EN(void)
{
HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_4);
}
/*
*********************************************************************************************************
* 函 数 名: bsp_SetPWM_ARR
* 功能说明: 设置PWM频率 即ARR自动重装载值
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
void bsp_SetPWM_ARR(uint16_t _ARR)
{
__HAL_TIM_SET_AUTORELOAD(&htim3,_ARR-1);
// TIM3->ARR = _ARR-1;
}
/*
*********************************************************************************************************
* 函 数 名: bsp_InitPWM
* 功能说明: 使能PWM
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
void bsp_InitPWM(void)
{
HAL_TIM_Base_Start_IT(&htim3);
PWM_TIM3_CH4_EN();
bsp_SetPWM_ARR(_TIM3_ARR);//周期
bsp_SetPWM_CCRx(0);//占空比
}
/*
*********************************************************************************************************
* 函 数 名: bsp_SetPWM_CCRx
* 功能说明: 设置占空比 即ARR
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
void bsp_SetPWM_CCRx(uint16_t duty)
{
if(duty <= _TIM3_ARR)
{
__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_4, duty);
// TIM3->CCR4 = dutyCycle;
}
}
//周期运行回调,配置定时进入中断 到达ARR
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
static uint16_t pwm_index = 0; //用于PWM查表
static uint16_t period_cnt = 0; //用于计算周期数
if (htim->Instance == htim3.Instance)
{
period_cnt++;
bsp_SetPWM_CCRx(indexWave[pwm_index]);
//每个PWM表中的每个元素使用period_class次
if(period_cnt > period_class) //根据PWM表修改定时器的比较寄存器值
{
pwm_index++; //标志PWM表指向下一个元素
//若PWM表已到达结尾,重新指向表头
if( pwm_index >= POINT_NUM)
{
pwm_index=0;
}
period_cnt=0; //重置周期计数标志
}
else
{
}
}
}
bsp_pwm.h
#ifndef _BSP_PWM_H
#define _BSP_PWM_H
#include "stm32f4xx_hal.h"
#include "main.h"
#include "pub_gpio.h"
#include "tim.h"
#include "bsp_led.h"
/*PWM周期 单位us 84M/84 Prescaler*/
#define _TIM3_ARR 1024
void bsp_InitPWM(void);
void bsp_SetPWM_CCRx(uint16_t duty);
void bsp_BLE_Control(uint8_t repeat);
#endif
举报