C语言实现呼吸灯(HAL库)

article/2025/10/27 21:13:35

1. 呼吸灯原理

呼吸灯的实现可以通过控制灯的亮度连续变化,当变化的频率大于24帧时,肉眼看上去就会逐渐变暗,逐渐变亮。

2. PWM控制亮度

PWM通过设置亮度在一段时间内的占空比,亮的百分比多,人眼看到的就亮,反之就是暗。
关于PWM的块不打算展开说,这里针对呼吸灯的PWM详细说明。

/** 描述  :呼吸灯PWM初始化* 参数  :*        无* 返回  :*        无*/
void bspBreathLedTIMInit(void)
{TIM_ClockConfigTypeDef sClockSourceConfig = {0};TIM_MasterConfigTypeDef sMasterConfig = {0};TIM_OC_InitTypeDef sConfigOC = {0};g_breathled_tim_handle.Instance = TIM2;g_breathled_tim_handle.Init.Prescaler = 83;g_breathled_tim_handle.Init.CounterMode = TIM_COUNTERMODE_UP;g_breathled_tim_handle.Init.Period = (BREATHLED_PWM_VALUE - 1);g_breathled_tim_handle.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;g_breathled_tim_handle.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;if (HAL_TIM_Base_Init(&g_breathled_tim_handle) != HAL_OK){Error_Handler();}sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;if (HAL_TIM_ConfigClockSource(&g_breathled_tim_handle, &sClockSourceConfig) != HAL_OK){Error_Handler();}if (HAL_TIM_PWM_Init(&g_breathled_tim_handle) != HAL_OK){Error_Handler();}sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;if (HAL_TIMEx_MasterConfigSynchronization(&g_breathled_tim_handle, &sMasterConfig) != HAL_OK){Error_Handler();}sConfigOC.OCMode = TIM_OCMODE_PWM1;sConfigOC.Pulse = 0;sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;if (HAL_TIM_PWM_ConfigChannel(&g_breathled_tim_handle, &sConfigOC, TIM_CHANNEL_4) != HAL_OK){Error_Handler();}GPIO_InitTypeDef GPIO_InitStruct;BREATHLED_GPIO_CLK_ENABLE();/* TIM2 GPIO Configuration* PA3     ------> TIM2_CH4*/GPIO_InitStruct.Pin = BREATHLED_GPIO_GREEN_PIN;GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;GPIO_InitStruct.Alternate = GPIO_AF1_TIM2;HAL_GPIO_Init(BREATHLED_GPIO_TYPE, &GPIO_InitStruct);HAL_TIM_Base_Start_IT(&g_breathled_tim_handle);
}

我这里使用了PA3引脚,定时器2做实验。时钟频率是84MHz,Prescaler设置(84 - 1) = 83,Period这里设置是(10000 - 1) = 9999,所以定时器是 (83 + 1) * (9999 + 1) / 84000000 = 0.01s = 10ms一个周期。
这个频率各位可以根据实际情况去计算,尽量设置成看上去平滑,而且对系统也不会造成产生过多中断。

/** 描述  :定时器底层回调初始化* 参数  :*        无* 返回  :*        无*/
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim_base)
{if (htim_base->Instance == TIM2){__HAL_RCC_TIM2_CLK_ENABLE();HAL_NVIC_SetPriority(TIM2_IRQn, 5, 0);HAL_NVIC_EnableIRQ(TIM2_IRQn);}
}/** 描述  :定时器中断处理函数* 参数  :*        无* 返回  :*        无*/
void TIM2_IRQHandler(void)
{HAL_TIM_IRQHandler(&g_breathled_tim_handle);	HAL_TIM_PeriodElapsedCallback(&g_breathled_tim_handle);	
}void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{/* USER CODE BEGIN Callback 0 *//* USER CODE END Callback 0 */if (htim->Instance == TIM11) {HAL_IncTick();}//呼吸灯else if (htim->Instance == TIM2){//注意由于产生中断过快,反转实现不了//LED电平反转HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_3);}/* USER CODE BEGIN Callback 1 *//* USER CODE END Callback 1 */
}

上面代码是中断处理函数,对呼吸灯的引脚电平反转。是通过设置占空比的参数,定时器自动触发中断后反转电平。

3. 呼吸灯亮度曲线

//PWM设置的Period值
#define BREATHLED_PWM_VALUE                10000
//呼吸灯x轴变暗或变亮步数
#define BREATHLED_X_DIVIDE                 100
//呼吸灯x轴总步数
#define BREATHLED_X_TOTAL                 (BREATHLED_X_DIVIDE * 2)
//呼吸灯y轴比例
#define BREATHLED_Y_RATIO                 (BREATHLED_PWM_VALUE / BREATHLED_X_DIVIDE)
//亮度变化频率,也就是帧数,单位毫秒
#define BREATHLED_FRAMES_MS											10

一些宏定义,BREATHLED_PWM_VALUE值是上面PWM设置的Period值。
BREATHLED_X_DIVIDE是x轴,变暗或变亮时,x轴的步数。
BREATHLED_X_TOTAL是x轴变暗加上变亮一个周期的总步数。
BREATHLED_Y_RATIO是y轴比例。
BREATHLED_FRAMES_MS亮度变化频率,注意这个值必须大于24帧,也就是小于40毫秒的时间,为了更加平滑,可以适量提高帧数,这样看起来呼吸灯更加流畅。

3.1 线性折线

在这里插入图片描述

y = 10000时代表占空比100%,x轴为时间。

/** 描述  :呼吸灯函数,由x轴得出y轴值* 参数  :*        [in]  x     x轴,时间* 返回  :*        y轴值,PWM值*/
static unsigned int breathLedCurve(unsigned int x)
{unsigned int y;if (x < BREATHLED_X_DIVIDE){y = BREATHLED_Y_RATIO * x;}else{y = -BREATHLED_Y_RATIO * (x - BREATHLED_X_TOTAL);}return y;
}/** 描述  :呼吸灯过程* 参数  :*        无* 返回  :*        无*/
void breathLedProgress(void)
{static unsigned int time_x = 0;unsigned int pwm_value_y = 0;while (1){time_x = (time_x + 1) % BREATHLED_X_TOTAL;pwm_value_y = breathLedCurve(time_x);//修改占空比__HAL_TIM_SetCompare(&g_breathled_tim_handle, TIM_CHANNEL_4, pwm_value_y);sysDelay(BREATHLED_FRAMES_MS);if (time_x % BREATHLED_X_DIVIDE == 0){sysDelay(500);}}
}

实现方式可以参考上面的,但是人眼感受的却不是线性的,是由于在灯光微亮区,很小的光通量改变也让人眼感到光通量变化很大,而在光通量比较大的区域,很大的光通量跳跃,人眼感觉到的光通量变化不大,简单理解为,人眼对亮度暗的比较敏感,而对亮度量的不敏感。
所以你尝试后发现,当由亮变暗时感觉时间长,由暗变亮时时间短,会有突然变亮的感觉。这个曲线是有缺陷的。

3.2 抛物线曲线

在这里插入图片描述
由上图可知,当亮度由暗变亮时,程序的呼吸灯曲线需要如同图3。
为了方便我这里使用一元二次曲线,有条件的可以使用对数函数,但是效果或许看起来会差不多。但是对数函数计算量会比一元二次方程大的多,所以衡量之下,选择通过一元二次曲线来实现。
在这里插入图片描述
通过这个曲线,你可能感觉到和线性折线差不多的效果。所以还需要降低由暗变亮时的亮度变化频率,即可让人眼看上去平滑。

3.3 伽马曲线

人眼的特征曲线大致为对数。伽马校正的目的是用来对人类视觉的特性进行补偿。在实践中,这意味着我们必须在亮度上呈现出来的现象满足我们的眼睛,以便我们能够感觉到这是亮度是线性增加的。更科学地说,这意味着我们必须通过将眼睛的对数特征曲线与指数特征曲线连接起来,实现感觉上的线性亮度变化。
关于LED伽马曲线
有兴趣的可以点击这链接研究。
我这里说明一下代码,我选择的使用了PWM值为1024,分成64步,所以PWM的Period值10000改成1024。一开始我使用了65536的Period值,但是实际上因为中断频率太低,导致LED灯会闪烁的问题。

static const unsigned short breath_led_pwmtable[BREATH_LED_X_TOTAL] =
{0, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 5, 5, 6, 6, 7, 8, 9, 10,11, 12, 13, 15, 17, 19, 21, 23, 26, 29, 32, 36, 40, 44, 49, 55,61, 68, 76, 85, 94, 105, 117, 131, 146, 162, 181, 202, 225, 250,279, 311, 346, 386, 430, 479, 534, 595, 663, 739, 824, 918, 1023
};

PWM值是使用了公式计算出来,为了程序能够减少计算量,我们把计算出来的值用数组来存储。公式想要了解的也可以点击上面的连接,里面有说明。我们只需要在呼吸灯过程遍历这个数组,从0开始到63,然后63又到0,重新设置PWM的占空比,即可实现呼吸灯效果。
想要完整代码的也可以移步呼吸灯伽马曲线代码下载。


http://chatgpt.dhexx.cn/article/30Ux8xYI.shtml

相关文章

二、15【FPGA】呼吸灯实现

前言 学习说明此文档为本人的学习笔记&#xff0c;注重实践&#xff0c;关于理论部分会给出相应的学习链接。 学习视频&#xff1a;是根据野火FPGA视频教程——第十八讲 https://www.bilibili.com/video/BV1nQ4y1Z7zN?p3 实战演练 一、设计规划 1.1 实验目标 在开发板上…

基于FPGA实践之呼吸灯(含程序)

呼吸灯是指灯光在微电脑的控制之下完成由亮到暗的逐渐变化&#xff0c;感觉好像是人在呼吸。 在单片机中我们调节PWM波的占空比可以实现一个周期内高电平占百分比&#xff0c;这个百分比固定就可以调节亮度&#xff0c;这个百分比是动态的&#xff0c;那么灯的亮度也是动态的&…

Verilog实现呼吸灯效果

呼吸灯的效果采用PWM调波的形式&#xff0c;即快速的改变每个周期的占空比&#xff08;一个周期内高电平时间占一个周期时间的比值&#xff09;来实现点亮到熄灭的效果。示意如下图 而关于整个波形图&#xff0c;用50MHz的晶振&#xff0c;从0开始计数到49则为1us。 而1ms是1u…

呼吸灯

呼吸灯 呼吸灯&#xff0c;就是控制led灯的亮度从弱变强、从强变弱的循环往复&#xff0c;从而实现像呼吸一样的效果。改变电压即可改变led灯的亮度&#xff0c;但是用代码控制led灯两端电压显然是不现实的&#xff0c;我们可以用控制脉冲宽度的方式来控制led灯点亮的时间&…

C51单片机实现呼吸灯

呼吸灯的效果是灯的亮度由暗缓慢变亮再缓慢变暗。 实现思路是改变小灯亮与暗在单位周期的占空比。如下图 具体代码如下&#xff1a; #include<reg52.h> typedef unsigned int u16; typedef unsigned char u8;sbit LEDP2^0; void delay(u16 i) {while(i--); }void ma…

Arduino程序设计(一) 流水灯+呼吸灯

LED灯程序设计 前言一、LED灯的程序设计1 —— 频闪灯二、LED灯的程序设计2 —— 流水灯三、LED灯的程序设计3 —— 呼吸灯总结参考文献 前言 本文主要介绍三种LED灯的程序设计&#xff0c;即频闪灯、流水灯和呼吸灯。本文使用的MCU芯片为ESP8266EX&#xff0c;程序编写使用Ar…

51单片机呼吸灯的实现

1.PWM介绍 PWM&#xff08;Pulse Width Modulation&#xff09;即脉冲宽度调制&#xff08;如电机&#xff1a;转5ms停1ms还是转5ms停2ms&#xff09;&#xff0c;在具有惯性的系统中&#xff0c;可以通过对一系列脉冲的宽度进行调制&#xff0c;来等效地获得所需要的模拟参量…

呼吸灯——FPGA

文章目录 前言一、呼吸灯是什么&#xff1f;1、介绍2、占空比调节示意图 二、系统设计1、系统框图2、RTL视图 三、源码四、效果五、总结六、参考资料 前言 环境&#xff1a; 1、Quartus18.0 2、vscode 3、板子型号&#xff1a;EP4CE6F17C8 要求&#xff1a; 将四个LED灯实现循环…

51单片机——实现呼吸灯程序代码

实现效果如图&#xff1a; 注&#xff1a;该51单片机led灯引脚为P2^ 0 - P2^7,实际代码按照引脚图实现。 代码如下&#xff1a; #include<reg52.h> typedef unsigned int uint; typedef unsigned char uchar; //--------------Delay--------------------void Delay(uin…

单片机开发—呼吸灯的三种实现方法

目录 一、前言1、什么是呼吸灯2、如何实现呼吸灯 二、利用for循环实现呼吸灯三、利用定时器实现呼吸灯1.利用定时器中断实现2.利用定时器输出PWM波实现 四、总结 一、前言 提示&#xff1a;本文使用的芯片并非STM32系列&#xff0c;利用定时器实现呼吸灯是从寄存器层面讲解的&…

【STM32】8.简单呼吸灯的制作教程,附代码、效果视频

目的&#xff1a; 实现呼吸灯。逐渐亮&#xff0c;逐渐灭。 一&#xff0e;呼吸灯原理分析 外设工作靠电流&#xff0c;因此要改变电流大小&#xff0c;所以要动电阻或电压。 电阻&#xff1a;阻值固定&#xff0c;不可更改。 电压&#xff1a;怎么改变电压大小呢&#xff1…

自适应滤波C语言实现

这里写目录标题 原理代码结果 原理 自适应滤波是近年以来发展起来的一种最佳滤波方法。它是在维纳滤波,Kalman滤波等线性滤波基础上发展起来的一种最佳滤波方法。由于它具有更强的适应性和更优的滤波性能。从而在工程实际中,尤其在信息处理技术中得到了广泛的应用。自适应滤波…

c语言实现图像滤波处理

#include<stdio.h> #include<malloc.h> #include<stdlib.h> #include <math.h> /* 位图头结构 */ #pragma pack(1) typedef struct tagBITMAPFILEHEADER {unsigned char bfType[2];//文件格式unsigned long bfSize;//文件大小unsigned short bfReserve…

卡尔曼滤波一阶矩阵C语言实现

①估计时刻k的状态&#xff1a; ②误差相关矩阵P&#xff0c;度量估计值的精确程度 ③卡尔曼增益 ④更新误差相关矩阵 ⑤更新状态变量 ⑥最后输出 C语言代码&#xff1a; float X_pre,P_pre,X_kalman_last,P_kalman_last,X_kalman2500,P_kalman2; //赋初值 float H,Q…

卡尔曼滤波C语言实现

卡尔曼5条基本公式&#xff0c;参考https://wenku.baidu.com/view/8523cb6eaf1ffc4ffe47ac24.html #include "stdio.h" #include "stdlib.h" #include "math.h"#define kal_Q 0.001 /*过程噪声协方差,Q增大&#xff0c;动态响应变快&#xff0…

C语言实现简单卡尔曼滤波

https://www.bilibili.com/video/BV1ez4y1X7eR DR.CAN讲的真的很好 卡尔曼滤波的步骤 步骤说明Step 1计算卡尔曼增益Step 2更新本次迭代的估计值Step 3更新本次迭代的估计误差 具体请看上面DR.CAN的视频 代码 参数说明x_mea测量值x_est估计值e_mea固有的测量误差&#xf…

C语言实现双边滤波

参考博文 看不懂公式的先去看参考博文 一个3*3窗口的代码 /**双边法滤波 3*3窗口 *参数 data[][COL] 图像数据 *参数 row 图像数据行数 *参数 varD 空域核d的标准差 *参数 varR 值域核r的标准差 *需要引用头文件math.h *typedef unsi…

数字图像处理,读懂频域处理的“傅里叶变换”

转载自&#xff1a;https://blog.csdn.net/ebowtang/article/details/39004979 以下部分文字资料整合于网络&#xff0c;本文仅供自己学习用&#xff01; 这是一幅很绝的一维傅里叶变换动态图 一&#xff0c;读懂傅里叶变换 一个信号能表示成傅里叶级数的形式是有条件的&…

傅立叶变换和拉普拉斯变换

欧拉公式 证明过程如下 首先是泰勒展开 参考cosX和sinX的泰勒展开可以证明这个问题。 还有下面这个号称宇宙最美公式 “自然底数e&#xff0c;自然数1和0&#xff0c;虚数i还有圆周率pi&#xff0c;它是这么简洁&#xff0c;这么美丽啊&#xff01;” 傅立叶级数 傅立叶在…

Little’s Law 利特尔法则

1 A simple definition: Little’s Law states that the long-term average number of customers in a stable system L L L is equal to the long-term average effective arrival rate, λ \lambda λ, multiplied by the average time a customer spends in the system, …