• 第 40 章 呼吸灯与 SPWM 波


    40.1 呼吸灯简介

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

    40.2 呼吸灯与 PWM 控制原理

    呼吸的特性是一种类似图指数曲线 中的指数曲线过程,吸气是指数上升过程,呼气是指数下降过程,成年人吸气呼气整个过程持续约 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 灯,达到呼吸灯的效果。

    40.3 硬件设计

    在这里插入图片描述

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

    在这里插入图片描述

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

    在这里插入图片描述

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

    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 */
    
    
    • 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
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97

    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**********************/
    
    
    • 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
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174

    其他配置:

    在这里插入图片描述

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

    在这里插入图片描述

    • 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**********************/
    
    
    
    • 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
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51

    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 */
    
    
    • 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
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71

    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**********************/
    
    
    
    • 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
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201

    其他配置:

    在这里插入图片描述

  • 相关阅读:
    C# LINQ源码分析之Count
    数据结构之时间复杂度与空间复杂度
    [E2E Test] Python Behave Selenium 一文学会自动化测试
    FPGA-VGA成像原理与时序
    下载安装nvm教程(附带下载切换node.js版本实操)
    idea2023帅气的jpa函数生成辅助工具
    一文搞懂CAN总线协议
    七夕特制:《牛郎会织女》
    Java【并发】面试题
    【Linux】进程控制与进程调度
  • 原文地址:https://blog.csdn.net/C_say_easy_to_me/article/details/126473993