第 40 章 呼吸灯与 SPWM 波

40.1 呼吸灯简介

呼吸灯,就是指灯光设备的亮度随着时间由暗到亮逐渐增强,再由亮到暗逐渐衰减,很有节奏感地一起一伏,就像是在呼吸一样。

40.2 呼吸灯与 PWM 控制原理

呼吸的特性是一种类似图指数曲线 中的指数曲线过程,吸气是指数上升过程,呼气是指数下降过程,成年人吸气呼气整个过程持续约 3 秒。

第 40 章 呼吸灯与 SPWM 波_第1张图片

实现过程

要控制 LED 灯达到呼吸灯的效果,实际上就是要控制 LED 灯的亮度拟合呼吸特性曲线。前面控制全彩 LED 灯时,通过控制脉冲的占空比来调整各个通道 LED 灯的亮度,从而达到混色的效果。若控制脉冲的占空比在 3 秒的时间周期内按呼吸特性曲线变化,那么就可以实现呼吸灯的效果

运用原理

使用脉冲占空比拟合不同波形的方式称为 PWM(脉冲宽度调制) 控制技术——通过对一系列脉冲的宽度进行调制,来等效地获得所需要波形(含形状和幅值)。

PWM 控制的基本原理为:冲量相等而开头不同的窄脉冲加在具有惯性的环节上时,其效果基本相同。其中冲量指窄脉冲的面积;效果相同指环节输出响应波形基本相同。

PWM等效正弦波

• 把正弦半波 N 等分,可看成 N 个彼此相连的脉冲序列,宽度相等,但幅值不等;

• 用矩形脉冲代替,各个矩形脉冲等幅,不等宽,中点重合,脉冲宽度按正弦规律变化,脉冲的总面积(冲量)与正弦半波相等。
第 40 章 呼吸灯与 SPWM 波_第2张图片

这种脉冲波形被称为 SPWM 波形——脉冲宽度按正弦规律变化而和正弦波等效的 PWM 波形。

若把拟合的波形改成呼吸特性曲线,即可得到控制呼吸灯使用的 PWM 波形,要生成拟合的 PWM波形,通常使用计算法调制法

(1) 计算法:根据拟合波形的频率、幅值和半周期脉冲数,准确计算 PWM 波各脉冲宽度和间隔,据此控制开关器件的通断,就可得到所需 PWM 波形;

(2) 调制法:拟合波形作调制信号,进行调制得到期望的 PWM 波;该方法一般采用等腰三角波为载波,其任一点水平宽度和高度成线性关系且左右对称。载波 (等腰三角波) 与平缓变化的调制信号波 (即要拟合的波形) 相交,在载波与信号波的交点控制器件通断,就得宽度正比于信号波幅值的脉冲,符合 PWM 的要求,见图调制法得到 PWM 波 。相对于计算法,其处理过程计算简单。
第 40 章 呼吸灯与 SPWM 波_第3张图片

使用计算法得到的呼吸曲线 PWM 波和 SPWM 波,并使用 STM32定时器 TIM 的 PWM 功能输出波形控制 LED 灯,达到呼吸灯的效果。

40.3 硬件设计

第 40 章 呼吸灯与 SPWM 波_第4张图片

因为本实验的软件将使用 STM32 的定时器控制输出 PWM 脉冲,然而并不是任意 GPIO 都具有 STM32 定时器的输出通道功能,所以在设计硬件时,需要根据《STM32 中文数据手册》中的说明,选择具有定时器输出通道功能的引脚来控制 RGB 灯

第 40 章 呼吸灯与 SPWM 波_第5张图片

40.4 单色呼吸灯实验

4.1 编程要点

  1. 初始化 PWM 输出通道,初始化 PWM 工作模式;
  2. 计算获取 PWM 数据表;
  3. 编写中断服务函数,在中断服务函数根据 PWM 数据表切换比较寄存器的值;

4.2 代码分析

4.2.1 LED 灯硬件相关宏定义
4.2.2 初始化 GPIO
4.2.3 定义 PWM 表
4.2.4 定时器 PWM 配置
4.2.5 定时器中断服务函数
4.2.6 计算拟合波形的周期

• 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 个点时的情况 。

第 40 章 呼吸灯与 SPWM 波_第6张图片

• 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 秒,是比较平缓的呼吸周期

4.2.7 主函数

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**********************/

其他配置:

第 40 章 呼吸灯与 SPWM 波_第7张图片

40.5 呼吸灯及输出 SPWM 波实验

呼吸灯例程和输出 SPWM 波实验的工程基本一样,只是控制使用的的 PWM 表不同,一个为呼吸特性曲线,另一个为正弦半波曲线。

5.1 编程要点

  1. 在单色呼吸灯的基础上,增加 PWM 输出通道,三个通道分别控制红绿蓝颜色;
  2. 编写中断服务函数,增加对拟合波形幅值的控制;
  3. 计算获取新的 PWM 数据表;

5.2 代码分析

5.2.1 LED 灯硬件相关宏定义
5.2.2 初始化 GPIO
5.2.3 定义 PWM 表
5.2.4 定时器 PWM 配置
5.2.5 定时器中断服务函数
5.2.6 计算拟合波形的周期

本工程相对单色呼吸灯例程增加了电压分级 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 个点时的情况 。

第 40 章 呼吸灯与 SPWM 波_第8张图片

• 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 秒,是比较平缓的呼吸周期。

5.2.7 主函数

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**********************/


其他配置:

第 40 章 呼吸灯与 SPWM 波_第9张图片

你可能感兴趣的:(stm32,单片机,嵌入式硬件)