呼吸灯,就是指灯光设备的亮度随着时间由暗到亮逐渐增强,再由亮到暗逐渐衰减,很有节奏感地一起一伏,就像是在呼吸一样。
呼吸的特性是一种类似图指数曲线 中的指数曲线过程,吸气是指数上升过程,呼气是指数下降过程,成年人吸气呼气整个过程持续约 3 秒。
实现过程:
要控制 LED 灯达到呼吸灯的效果,实际上就是要控制 LED 灯的亮度拟合呼吸特性曲线。前面控制全彩 LED 灯时,通过控制脉冲的占空比来调整各个通道 LED 灯的亮度,从而达到混色的效果。若控制脉冲的占空比在 3 秒的时间周期内按呼吸特性曲线变化,那么就可以实现呼吸灯的效果
运用原理:
使用脉冲占空比拟合不同波形的方式称为 PWM(脉冲宽度调制) 控制技术——通过对一系列脉冲的宽度进行调制,来等效地获得所需要波形(含形状和幅值)。
PWM 控制的基本原理为:冲量相等而开头不同的窄脉冲加在具有惯性的环节上时,其效果基本相同。其中冲量指窄脉冲的面积;效果相同指环节输出响应波形基本相同。
PWM等效正弦波:
• 把正弦半波 N 等分,可看成 N 个彼此相连的脉冲序列,宽度相等,但幅值不等;
• 用矩形脉冲代替,各个矩形脉冲等幅,不等宽,中点重合,脉冲宽度按正弦规律变化,脉冲的总面积(冲量)与正弦半波相等。
这种脉冲波形被称为 SPWM 波形——脉冲宽度按正弦规律变化而和正弦波等效的 PWM 波形。
若把拟合的波形改成呼吸特性曲线,即可得到控制呼吸灯使用的 PWM 波形,要生成拟合的 PWM波形,通常使用计算法和调制法:
(1) 计算法:根据拟合波形的频率、幅值和半周期脉冲数,准确计算 PWM 波各脉冲宽度和间隔,据此控制开关器件的通断,就可得到所需 PWM 波形;
(2) 调制法:拟合波形作调制信号,进行调制得到期望的 PWM 波;该方法一般采用等腰三角波为载波,其任一点水平宽度和高度成线性关系且左右对称。载波 (等腰三角波) 与平缓变化的调制信号波 (即要拟合的波形) 相交,在载波与信号波的交点控制器件通断,就得宽度正比于信号波幅值的脉冲,符合 PWM 的要求,见图调制法得到 PWM 波 。相对于计算法,其处理过程计算简单。
使用计算法得到的呼吸曲线 PWM 波和 SPWM 波,并使用 STM32定时器 TIM 的 PWM 功能输出波形控制 LED 灯,达到呼吸灯的效果。
因为本实验的软件将使用 STM32 的定时器控制输出 PWM 脉冲,然而并不是任意 GPIO 都具有 STM32 定时器的输出通道功能,所以在设计硬件时,需要根据《STM32 中文数据手册》中的说明,选择具有定时器输出通道功能的引脚来控制 RGB 灯
• TIMPeriod:定时器的计数周期,它的值必须与 PWM 表中的极大值相等(应用中赋值需要减 1),而 PWM 表的极大值决定了控制的分辨率。例如极大值为 10 时,PWM 占空比只有10 个等级,精确到 0.1,当极大值为 1000 时,PWM 占空比有 1000 个等级,精确到 0.001。
• TIM_Prescaler:定时器时钟分频因子,它控制定时器计数器 CNT 计数加 1 所需要的时间,它的值太大会导致输出的单个 PWM 波周期过长,影响控制的动态特性。如控制 LED 灯时,该值太大会导致 LED 灯开关时间变长,闪烁明显。一般来说,该值越小越好。
• PWM 表的点数:PWM 表的点数即对拟合曲线的采样点数,采样点越多,能更好地还原拟合曲线,采样点太少,可能会导致失真,见图对呼吸特性曲线采样 110 个和 10 个点时的情况 。
• period_class:周期倍数,即 PWM 表中每个元素的循环次数,它影响拟合曲线的周期。当period_class=1 时,可以输出本配置中周期最短的拟合曲线。
• amplitude_class:幅值分级,在后面全彩呼吸灯和 SPWM 实验中,我们还会增加该变量,它可以把拟合曲线的幅值分成 N 个等级,控制时可以选择按某个幅值等级进行输出。本实验中没有配置该参数,所以只能输出最大的等级,即 amplitude_class=1。以上各个参数虽然侧重点不同,但若修改其中的任何一个,最终都会影响到所拟合曲线的周期,所以在实际应用中,通常先设定好 TIMPeriod、TIM_Prescaler、PWM 表的点数以及幅值等级数amplitude_class,得到适合的控制精度、动态特性拟合度以及幅值等级后,然后再调period_class控制拟合曲线的周期,而且 period_class 在程序中动态修改非常方便,不需要重置定时器和PWM表。
最终,我们把本工程配置中的拟合曲线周期计算公式概括如下:
(1) STM32 系统时钟默认频率和周期:f_pclk = 72000000 t_pclk = 1/f_pclk
(2) 定时器 update 事件周期,即定时器中断周期:
t_timer = t_pclk * TIMER_TIM_Prescaler * TIMER_TIM_Period
(3) 每个 PWM 点的时间:T_Point = t_timer * PERIOD_CLASS
(4) 最终,遍历 PWM 表的周期,即拟合曲线的周期:T_PWM = T_Point * POINT_NUM
例如,本工程配置中:
PWM 点数:POINT_NUM = 110
周期倍数:PERIOD_CLASS = 10
定时器定时周期:TIMER_TIM_Period = 1024
定时器分频:TIMER_TIM_Prescaler = 200
代入公式,计算得 T_PWM = 3.128 秒
通过公式的计算可知本工程的配置可使得输出的拟合曲线周期为 3.128 秒,是比较平缓的呼吸周期
main.c
#include "stm32f10x.h"
#include "./breathinglight/bsp_breathing.h"
/**
* @brief 主函数
* @param 无
* @retval 无
*/
int main(void)
{
/* 初始化呼吸灯 */
TIMx_Breathing_Init();
while(1)
{
}
}
/*********************************************END OF FILE**********************/
breathing.h
#ifndef __PWM_BREATHING_H
#define __PWM_BREATHING_H
#include "stm32f10x.h"
/*PWM表中的点数*/
extern uint16_t POINT_NUM ;
//控制输出波形的频率
extern __IO uint16_t period_class ;
#define RED_LIGHT 1
#define GREEN_LIGHT 2
#define BLUE_LIGHT 3
/*要使用什么颜色的呼吸灯,可选RED_LIGHT、GREEN_LIGHT、BLUE_LIGHT*/
#define LIGHT_COLOR RED_LIGHT
/********************定时器通道**************************/
#if LIGHT_COLOR == RED_LIGHT
/************红灯***************/
#define BRE_TIMx TIM3
#define BRE_TIM_APBxClock_FUN RCC_APB1PeriphClockCmd
#define BRE_TIM_CLK RCC_APB1Periph_TIM3
#define BRE_TIM_GPIO_APBxClock_FUN RCC_APB2PeriphClockCmd
#define BRE_TIM_GPIO_CLK (RCC_APB2Periph_GPIOB|RCC_APB2Periph_AFIO)
//红灯的引脚需要重映射
#define BRE_GPIO_REMAP_FUN() GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE);
#define BRE_TIM_LED_PORT GPIOB
#define BRE_TIM_LED_PIN GPIO_Pin_5
#define BRE_TIM_OCxInit TIM_OC2Init //通道选择,1~4
#define BRE_TIM_OCxPreloadConfig TIM_OC2PreloadConfig
#define BRE_CCRx CCR2
#define BRE_TIMx_IRQn TIM3_IRQn //中断
#define BRE_TIMx_IRQHandler TIM3_IRQHandler
#elif LIGHT_COLOR == GREEN_LIGHT
/************绿灯***************/
#define BRE_TIMx TIM3
#define BRE_TIM_APBxClock_FUN RCC_APB1PeriphClockCmd
#define BRE_TIM_CLK RCC_APB1Periph_TIM3
#define BRE_TIM_GPIO_APBxClock_FUN RCC_APB2PeriphClockCmd
#define BRE_TIM_GPIO_CLK (RCC_APB2Periph_GPIOB)
//绿灯不需要重映射
#define BRE_GPIO_REMAP_FUN()
#define BRE_TIM_LED_PORT GPIOB
#define BRE_TIM_LED_PIN GPIO_Pin_0
#define BRE_TIM_OCxInit TIM_OC3Init //通道选择,1~4
#define BRE_TIM_OCxPreloadConfig TIM_OC3PreloadConfig
#define BRE_CCRx CCR3
#define BRE_TIMx_IRQn TIM3_IRQn //中断
#define BRE_TIMx_IRQHandler TIM3_IRQHandler
#elif LIGHT_COLOR == BLUE_LIGHT
/************蓝灯***************/
#define BRE_TIMx TIM3
#define BRE_TIM_APBxClock_FUN RCC_APB1PeriphClockCmd
#define BRE_TIM_CLK RCC_APB1Periph_TIM3
#define BRE_TIM_GPIO_APBxClock_FUN RCC_APB2PeriphClockCmd
#define BRE_TIM_GPIO_CLK (RCC_APB2Periph_GPIOB)
//蓝灯不需要重映射
#define BRE_GPIO_REMAP_FUN()
#define BRE_TIM_LED_PORT GPIOB
#define BRE_TIM_LED_PIN GPIO_Pin_1
#define BRE_TIM_OCxInit TIM_OC4Init //通道选择,1~4
#define BRE_TIM_OCxPreloadConfig TIM_OC4PreloadConfig
#define BRE_CCRx CCR4
#define BRE_TIMx_IRQn TIM3_IRQn //中断
#define BRE_TIMx_IRQHandler TIM3_IRQHandler
#endif
void TIMx_Breathing_Init (void);
#endif /* __PWM_BREATHING_H */
breathing.c
#include "./breathinglight/bsp_breathing.h"
//控制输出波形的频率
__IO uint16_t period_class = 10;
/* 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]);
/**
* @brief 配置TIM复用输出PWM时用到的I/O
* @param 无
* @retval 无
*/
static void TIMx_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
/* clock enable */
RCC_APB2PeriphClockCmd(BRE_TIM_GPIO_CLK, ENABLE);
BRE_TIM_GPIO_APBxClock_FUN ( BRE_TIM_GPIO_CLK, ENABLE );
BRE_GPIO_REMAP_FUN();
/* 配置呼吸灯用到的引脚 */
GPIO_InitStructure.GPIO_Pin = BRE_TIM_LED_PIN ;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // 复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init( BRE_TIM_LED_PORT, &GPIO_InitStructure );
}
/**
* @brief 配置嵌套向量中断控制器NVIC
* @param 无
* @retval 无
*/
static void NVIC_Config_PWM(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
/* Configure one bit for preemption priority */
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
/* 配置TIM3_IRQ中断为中断源 */
NVIC_InitStructure.NVIC_IRQChannel = BRE_TIMx_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
/**
* @brief 配置TIM输出的PWM信号的模式,如周期、极性
* @param 无
* @retval 无
*/
static void TIMx_Mode_Config(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
/* 设置TIM3CLK 时钟 */
BRE_TIM_APBxClock_FUN ( BRE_TIM_CLK, ENABLE );
/* 基本定时器配置 ,配合PWM表点数、中断服务函数中的period_cnt循环次数设置*/
/* 设置使得整个呼吸过程为3秒左右即可达到很好的效果 */
//要求:
//TIM_Period:与PWM表中数值范围一致
//TIM_Prescaler:越小越好,可减轻闪烁现象
//PERIOD_CLASS:中断服务函数中控制单个点循环的次数,调整它可控制拟合曲线的周期
//POINT_NUM:PWM表的元素,它是PWM拟合曲线的采样点数
/*************本实验中的配置***************/
/***********************************************
#python计算脚本 count.py
#PWM点数
POINT_NUM = 110
#周期倍数
PERIOD_CLASS = 10
#定时器定时周期
TIMER_TIM_Period = 2**10
#定时器分频
TIMER_TIM_Prescaler = 200
#STM32系统时钟频率和周期
f_pclk = 72000000
t_pclk = 1/f_pclk
#定时器update事件周期
t_timer = t_pclk*TIMER_TIM_Prescaler*TIMER_TIM_Period
#每个PWM点的时间
T_Point = t_timer * PERIOD_CLASS
#整个呼吸周期
T_Up_Down_Cycle = T_Point * POINT_NUM
print ("呼吸周期:",T_Up_Down_Cycle)
#运行结果:
呼吸周期:3.12888
************************************************************/
/* 基本定时器配置 */
TIM_TimeBaseStructure.TIM_Period = (1024-1);; //当定时器从0计数到 TIM_Period+1 ,为一个定时周期
TIM_TimeBaseStructure.TIM_Prescaler = (200-1); //设置预分频
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1 ; //设置时钟分频系数:不分频(这里用不到)
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上计数模式
TIM_TimeBaseInit(BRE_TIMx, &TIM_TimeBaseStructure);
/* PWM模式配置 */
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //配置为PWM模式1
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //使能输出
TIM_OCInitStructure.TIM_Pulse = 0; //设置初始PWM脉冲宽度为0
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //当定时器计数值小于CCR1_Val时为低电平
BRE_TIM_OCxInit ( BRE_TIMx, &TIM_OCInitStructure ); //使能通道
BRE_TIM_OCxPreloadConfig ( BRE_TIMx, TIM_OCPreload_Enable ); //使能预装载
TIM_ARRPreloadConfig(BRE_TIMx, ENABLE); //使能TIM重载寄存器ARR
/* TIM3 enable counter */
TIM_Cmd(BRE_TIMx, ENABLE); //使能定时器
TIM_ITConfig(BRE_TIMx, TIM_IT_Update, ENABLE); //使能update中断
NVIC_Config_PWM(); //配置中断优先级
}
/**
* @brief TIM 呼吸灯初始化
* 配置PWM模式和GPIO
* @param 无
* @retval 无
*/
void TIMx_Breathing_Init(void)
{
TIMx_GPIO_Config();
TIMx_Mode_Config();
}
/*********************************************END OF FILE**********************/
其他配置:
呼吸灯例程和输出 SPWM 波实验的工程基本一样,只是控制使用的的 PWM 表不同,一个为呼吸特性曲线,另一个为正弦半波曲线。
5.1 编程要点
本工程相对单色呼吸灯例程增加了电压分级 AMPLITUDE_CLASS,结合前面的 TIMPeriod、PWM表的点数、TIM_Prescaler 以及 period_cnt 参数,重新总结如下:
• TIMPeriod:定时器的计数周期,它的值必须与 PWM 表中的极大值相等(应用中赋值需要减 1),而 PWM 表的极大值决定了控制的分辨率。例如极大值为 10 时,PWM 占空比只有10 个等级,精确到 0.1,当极大值为 1000 时,PWM 占空比有 1000 个等级,精确到 0.001。
• TIM_Prescaler:定时器时钟分频因子,它控制定时器计数器 CNT 计数加 1 所需要的时间,它的值太大会导致输出的单个 PWM 波周期过长,影响控制的动态特性。如控制 LED 灯时,该值太大会导致 LED 灯开关时间变长,闪烁明显。一般来说,该值越小越好。
• PWM 表的点数:PWM 表的点数即对拟合曲线的采样点数,采样点越多,能更好地还原拟曲线,采样点太少,可能会导致失真,见图对呼吸特性曲线采样 110 个和 10 个点时的情况 。
• period_class:PWM 表中每个元素的循环次数,它影响拟合曲线的周期。当 period_class=1时,可以输出本配置中周期最短的拟合曲线。
• AMPLITUDE_CLASS:电压分级数,它可以把输出拟合曲线的幅值分成 N 个等级,控制时可以选择按某个幅值等级进行输出,可根据实际情况进行分级,如本实验中分级为与 RGB888各通道一致的 256 等级,若只需要支持 RGB555 格式,那么 AMPLITUDE_CLASS 配置为32 即可。
以上各个参数虽然侧重点不同,但若修改其中的任何一个,最终都会影响到所拟合曲线的周期,所以在实际应用中,通常先设定好 TIMPeriod、TIM_Prescaler、PWM 表的点数以及幅值等级数AMPLITUDE_CLASS,得到适合的控制精度、动态特性拟合度以及幅值等级后,然后再调整 pe-riod_class 控制拟合曲线的周期,而且 period_class 在程序中动态修改非常方便,不需要重置定时器和 PWM 表。
最终,我们把本工程配置中的拟合曲线周期计算公式概括如下:
(5) STM32 系统时钟默认频率和周期:f_pclk = 72000000 t_pclk = 1/f_pclk
(6) 定时器 update 事件周期,即定时器中断周期:
t_timer = t_pclk * TIMER_TIM_Prescaler * TIMER_TIM_Period
(7) 每个 PWM 点的时间:T_Point = t_timer * PERIOD_CLASS* AMPLITUDE_CLASS
(8) 最终,遍历 PWM 表的周期,即拟合曲线的周期:T_PWM = T_Point * POINT_NUM
例如,本工程配置中:
PWM 点数:POINT_NUM = 180
周期倍数:PERIOD_CLASS = 1
电压等级:AMPLITUDE_CLASS = 256
定时器定时周期:TIMER_TIM_Period = 1024
定时器分频:TIMER_TIM_Prescaler = 200
代入公式,计算得 T_PWM = 3.2767 秒
通过公式的计算可知本工程的配置可使得输出的拟合曲线周期为 3.2767 秒,是比较平缓的呼吸周期。
main.c
#include "stm32f10x.h"
#include "./spwm/bsp_spwm.h"
//该变量在定时器中断服务函数中使用,用于控制各通道的输出
//修改该变量的值可直接改变呼吸灯的颜色
//变量格式:RGB888
__IO uint32_t rgb_color = 0xFF00FF;
#define SOFT_DELAY() Delay(0x1FFFFFF);
void Delay(__IO u32 nCount);
/**
* @brief 主函数
* @param 无
* @retval 无
*/
int main(void)
{
/* 初始化呼吸灯 */
TIMx_Breathing_Init();
while(1)
{
//各种颜色的呼吸灯
rgb_color = 0xFF00FF;
SOFT_DELAY();
rgb_color =0x8080ff;
SOFT_DELAY();
rgb_color =0xff8000;
SOFT_DELAY();
rgb_color =0xffc90e;
SOFT_DELAY();
}
}
void Delay(__IO uint32_t nCount) //简单的延时函数
{
for(; nCount != 0; nCount--);
}
/*********************************************END OF FILE**********************/
spwn.h
#ifndef __SPWM_H
#define __SPWM_H
#include "stm32f10x.h"
/*电压幅值等级数*/
#define AMPLITUDE_CLASS 256
//控制输出波形的频率
extern __IO uint16_t period_class ;
/*PWM表中的点数*/
extern uint16_t POINT_NUM;
/********************定时器通道**************************/
#define BRE_TIMx TIM3
#define BRE_TIM_APBxClock_FUN RCC_APB1PeriphClockCmd
#define BRE_TIM_CLK RCC_APB1Periph_TIM3
#define BRE_TIM_GPIO_CLK (RCC_APB2Periph_GPIOB|RCC_APB2Periph_AFIO)
//红灯的引脚需要重映射
#define BRE_GPIO_REMAP_FUN() GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE);
/************红灯***************/
#define BRE_RED_TIM_LED_PORT GPIOB
#define BRE_RED_TIM_LED_PIN GPIO_Pin_5
#define BRE_RED_TIM_OCxInit TIM_OC2Init //通道初始化函数
#define BRE_RED_TIM_OCxPreloadConfig TIM_OC2PreloadConfig //通道重载配置函数
//通道比较寄存器,以TIMx->CCRx方式可访问该寄存器,设置新的比较值,控制占空比
//以宏封装后,使用这种形式:BRE_TIMx->BRE_RED_CCRx ,可访问该通道的比较寄存器
#define BRE_RED_CCRx CCR2
/************绿灯***************/
#define BRE_GREEN_TIM_LED_PORT GPIOB
#define BRE_GREEN_TIM_LED_PIN GPIO_Pin_0
#define BRE_GREEN_TIM_OCxInit TIM_OC3Init //通道初始化函数
#define BRE_GREEN_TIM_OCxPreloadConfig TIM_OC3PreloadConfig //通道重载配置函数
//通道比较寄存器,以TIMx->CCRx方式可访问该寄存器,设置新的比较值,控制占空比
//以宏封装后,使用这种形式:BRE_TIMx->BRE_GREEN_CCRx ,可访问该通道的比较寄存器
#define BRE_GREEN_CCRx CCR3
/************蓝灯***************/
#define BRE_BLUE_TIM_LED_PORT GPIOB
#define BRE_BLUE_TIM_LED_PIN GPIO_Pin_1
#define BRE_BLUE_TIM_OCxInit TIM_OC4Init //通道初始化函数
#define BRE_BLUE_TIM_OCxPreloadConfig TIM_OC4PreloadConfig //通道重载配置函数
//通道比较寄存器,以TIMx->CCRx方式可访问该寄存器,设置新的比较值,控制占空比
//以宏封装后,使用这种形式:BRE_TIMx->BRE_BLUE_CCRx ,可访问该通道的比较寄存器
#define BRE_BLUE_CCRx CCR4
#define BRE_TIMx_IRQn TIM3_IRQn //中断
#define BRE_TIMx_IRQHandler TIM3_IRQHandler
void TIMx_Breathing_Init (void);
#endif /* __SPWM_H */
spwm.c
#include "./spwm/bsp_spwm.h"
//控制输出波形的频率
__IO uint16_t period_class = 1;
/* SPWM表,正弦曲线,此表使用工程目录下的python脚本sin_wave.py生成*/
const uint16_t indexWave[] = {
0, 9, 18, 27, 36, 45, 54, 63, 72, 81, 89, 98,
107, 116, 125, 133, 142, 151, 159, 168, 176,
184, 193, 201, 209, 218, 226, 234, 242, 249,
257, 265, 273, 280, 288, 295, 302, 310, 317,
324, 331, 337, 344, 351, 357, 364, 370, 376,
382, 388, 394, 399, 405, 410, 416, 421, 426,
431, 436, 440, 445, 449, 454, 458, 462, 465,
469, 473, 476, 479, 482, 485, 488, 491, 493,
496, 498, 500, 502, 503, 505, 506, 508, 509,
510, 510, 511, 512, 512, 512, 512, 512, 512,
511, 510, 510, 509, 508, 506, 505, 503, 502,
500, 498, 496, 493, 491, 488, 485, 482, 479,
476, 473, 469, 465, 462, 458, 454, 449, 445,
440, 436, 431, 426, 421, 416, 410, 405, 399,
394, 388, 382, 376, 370, 364, 357, 351, 344,
337, 331, 324, 317, 310, 302, 295, 288, 280,
273, 265, 257, 249, 242, 234, 226, 218, 209,
201, 193, 184, 176, 168, 159, 151, 142, 133,
125, 116, 107, 98, 89, 81, 72, 63, 54, 45, 36,
27, 18, 9, 0
};
//计算PWM表有多少个元素
uint16_t POINT_NUM = sizeof(indexWave)/sizeof(indexWave[0]);
/**
* @brief 配置TIM3复用输出PWM时用到的I/O
* @param 无
* @retval 无
*/
static void TIMx_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
/* GPIO clock enable */
RCC_APB2PeriphClockCmd(BRE_TIM_GPIO_CLK, ENABLE);
/*IO设置*/
BRE_GPIO_REMAP_FUN();
/* 配置LED灯用到的引脚 */
//红
GPIO_InitStructure.GPIO_Pin = BRE_RED_TIM_LED_PIN ;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // 复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(BRE_RED_TIM_LED_PORT, &GPIO_InitStructure);
//绿
GPIO_InitStructure.GPIO_Pin = BRE_GREEN_TIM_LED_PIN ;
GPIO_Init(BRE_GREEN_TIM_LED_PORT, &GPIO_InitStructure);
//蓝
GPIO_InitStructure.GPIO_Pin = BRE_BLUE_TIM_LED_PIN ;
GPIO_Init(BRE_BLUE_TIM_LED_PORT, &GPIO_InitStructure);
}
/**
* @brief 配置嵌套向量中断控制器NVIC
* @param 无
* @retval 无
*/
static void NVIC_Config_PWM(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
/* Configure one bit for preemption priority */
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
/* 配置TIM3_IRQ中断为中断源 */
NVIC_InitStructure.NVIC_IRQChannel = BRE_TIMx_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
/**
* @brief 配置TIM3输出的PWM信号的模式,如周期、极性
* @param 无
* @retval 无
*/
static void TIMx_Mode_Config(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
/* 设置TIMCLK 时钟 */
BRE_TIM_APBxClock_FUN ( BRE_TIM_CLK, ENABLE );
/* 基本定时器配置 ,配合PWM表点数、中断服务函数中的PERIOD_CLASS、AMPLITUDE_CLASS循环次数设置*/
/* 设置使得整个呼吸过程为3秒左右即可达到很好的效果 */
//要求:
//TIM_Period:与PWM表中数值范围一致
//TIM_Prescaler:越小越好,可减轻闪烁现象
//PERIOD_CLASS:中断服务函数中控制单个点循环的次数,调整它可控制拟合曲线的周期
//AMPLITUDE_CLASS:中断服务函数中控制单个点循环的次数,调整它可控制幅值,
// 在本实验中它为LED通道的亮度值,分256个等级,对应RGB888格式各通道的颜色等级
//POINT_NUM:PWM表的元素,它是PWM拟合曲线的采样点数
/*************本实验中的配置***************/
/***********************************************
#python计算脚本 count.py
#PWM点数
POINT_NUM = 180
#幅值(颜色)等级
AMPLITUDE_CLASS = 256
#周期倍数
PERIOD_CLASS = 1
#定时器定时周期
TIMER_TIM_Period = 2**9
#定时器分频
TIMER_TIM_Prescaler = 10
#STM32系统时钟频率和周期
f_pclk = 72000000
t_pclk = 1/f_pclk
#定时器update事件周期
t_timer = t_pclk*TIMER_TIM_Prescaler*TIMER_TIM_Period
#每个PWM点的时间
T_Point = t_timer * RGB_CLASS
#整个呼吸周期
T_Up_Down_Cycle = T_Point * POINT_NUM*PERIOD_CLASS
print ("呼吸周期:",T_Up_Down_Cycle)
#运行结果:
呼吸周期:3.27679
************************************************************/
TIM_TimeBaseStructure.TIM_Period = (512-1); //当定时器从0计数到 TIM_Period+1 ,为一个定时周期
TIM_TimeBaseStructure.TIM_Prescaler = (10-1); //设置预分频
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1 ; //设置时钟分频系数:不分频(这里用不到)
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上计数模式
TIM_TimeBaseInit(BRE_TIMx, &TIM_TimeBaseStructure);
/* PWM模式配置 */
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //配置为PWM模式1
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //使能输出
TIM_OCInitStructure.TIM_Pulse = 0; //设置初始PWM脉冲宽度为0
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //当定时器计数值小于CCR_Val时为低电平
BRE_RED_TIM_OCxInit ( BRE_TIMx, &TIM_OCInitStructure );
BRE_RED_TIM_OCxPreloadConfig ( BRE_TIMx, TIM_OCPreload_Enable ); //使能预装载
BRE_GREEN_TIM_OCxInit ( BRE_TIMx, &TIM_OCInitStructure );
BRE_GREEN_TIM_OCxPreloadConfig ( BRE_TIMx, TIM_OCPreload_Enable ); //使能预装载
BRE_BLUE_TIM_OCxInit ( BRE_TIMx, &TIM_OCInitStructure );
BRE_BLUE_TIM_OCxPreloadConfig ( BRE_TIMx, TIM_OCPreload_Enable ); //使能预装载
TIM_ARRPreloadConfig(BRE_TIMx, ENABLE); //使能TIM重载寄存器ARR
/* TIM3 enable counter */
TIM_Cmd(BRE_TIMx, ENABLE); //使能定时器
TIM_ITConfig(BRE_TIMx, TIM_IT_Update, ENABLE); //使能update中断
NVIC_Config_PWM(); //配置中断优先级
}
/**
* @brief TIM3 呼吸灯初始化
* 配置PWM模式和GPIO
* @param 无
* @retval 无
*/
void TIMx_Breathing_Init(void)
{
TIMx_GPIO_Config();
TIMx_Mode_Config();
}
/*********************************************END OF FILE**********************/
其他配置: