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的占空比,即可实现呼吸灯效果。
想要完整代码的也可以移步呼吸灯伽马曲线代码下载。

C语言实现呼吸灯(HAL库)相关推荐

  1. (35)SystemVerilog语言编写呼吸灯

    2.05 SystemVerilog语言编写呼吸灯 2.5.1 本节目录 1)章节目录: 2)FPGA简介: 3)SystemVerilog简介: 4)SystemVerilog语言编写呼吸灯: 5) ...

  2. (5)verilog语言编写呼吸灯

    2.5 verilog语言编写呼吸灯 2.5.1 本节目录 1)本节目录: 2)FPGA简介: 3)verilog简介: 4)verilog语言编写呼吸灯: 5)本节结束. 2.5.2 FPGA简介 ...

  3. HAL库与Cubemx\rt-thread Nano系列教程-01-新建HAL工程及移植RT-Nano到Alios Developer Kit

    Part1教程计划 1第一阶段 基于Alios DevelopeKit开发板(主控STM32L496VGT6); HAL库与cubemx开发,RT_THread作为操作系统使用,仅仅是作为操作系统,目 ...

  4. 单片机很好玩 ,制作呼吸灯(转载)

    上一节为了解决单片机 IO 电流驱动能力不足,导致 LED 小灯亮度不高的问题,借助了三极管.单片机的 IO 通过三极管开关 LED 小灯的电流,最后的确明显让小灯的亮度更高了. 使用电路模拟软件 还 ...

  5. 【STM32】HAL库 STM32CubeMX教程七---PWM输出(呼吸灯)

    前言: 本系列教程将 对应外设原理,HAL库与STM32CubeMX结合在一起讲解,使您可以更快速的学会各个模块的使用 所用工具: 1.芯片: STM32F407ZET6/ STM32F103ZET6 ...

  6. 【嵌入式基础】用C语言编程、寄存器实现LED流水灯程序;stm32CubeMX+Keil使用HAL库点亮流水灯

    本文主要介绍STM32F103系列芯片的地址映射和寄存器映射原理,GPIO端口的初始化设置步骤.利用C语言编程和寄存器点亮流水灯以及stm32CubeMX+Keil使用HAL库点灯 目录 一.STM3 ...

  7. STM32F103C8T6基础开发教程(HAL库)—点亮第一颗LED灯

    STM32F103C8T6基础开发教程目录 STM32F103C8T6基础开发教程(HAL库)-开发环境配置 STM32F103C8T6基础开发教程(HAL库)-Keil添加注释的快捷键 STM32F ...

  8. STM32Cubemx的安装及用寄存器HAL库完成LED流水灯程序

    目录 一.STM32CubeMx安装 (一)简介 (二)下载地址 1.官方下载地址 2.网盘下载地址 二.STM32CubeMX安装过程 三.HAL库安装 四.HAL库实现LED流水灯 (一)新建项目 ...

  9. STM32F103C8T6基础开发教程(HAL库)—LED灯1S周期闪烁

    STM32F103C8T6基础开发教程目录 STM32F103C8T6基础开发教程(HAL库)-开发环境配置 STM32F103C8T6基础开发教程(HAL库)-Keil添加注释的快捷键 STM32F ...

  10. 【嵌入式04】用寄存器HAL库完成LED流水灯程序

    目录 一.原理学习 1.寄存器映射原理 2.GPIO端口的初始化设置步骤 二.LED流水灯 1.程序设计思路 2.寄存器方式编程实现 3.HAL库编程实现 4.软件仿真 三.总结 四.参考链接 一.原 ...

最新文章

  1. 关于IOS中的self关键字
  2. 分布式文件系统:HDFS
  3. c语言里怎么解释程序,C语言程序详细解释一下各步骤意思
  4. MATLAB的基础-虽然基础,但全是细节,掌握了这些细节,才是MATLAB高手
  5. Get Started Part 2
  6. linux应用之----多线程
  7. 一致性哈希算法 应用
  8. Eclipse 插件开发 向导
  9. mysql 视图 动态sql_sql-server – 使用动态Sql创建视图
  10. STM32工作笔记0057---外部中断实验
  11. 使用Word2010直接编辑、发布博客→博客园cnblogs
  12. linux板级设备的,linux板级设备的初始化过程是怎样的?
  13. [译]利用贝叶斯推理做硬件故障率的准实时预测
  14. 基于SSM【爱校图书馆管理系统】附源码+论文
  15. python人名独特性统计_荐第六章:组合数据类型练习[人名独特性统计]学习思考...
  16. VC编程实现 excel插入一行单元格Insert
  17. 用友U9 SOA引领企业IT架构全面升级
  18. 互联网创业公司失败的7大特征
  19. 三防产品外观设计要点
  20. 前台、中台、后台,业务中台、技术中台、数据中台、算法中台分别是什么?

热门文章

  1. Axure中SVG矢量图标的使用方法
  2. 小程序 富文本解析方式
  3. 故障树手册(Fault Tree handbook)(2)
  4. (187)Verilog HDL:32位线性反馈移位寄存器
  5. WebView 指南
  6. Carrot2 in action 初步印象
  7. google地图静态api使用助手(html源码)
  8. Microsoft Office 2016 简体中文 Vol 版镜像下载
  9. 我为什么放弃用了近10年的金山
  10. 软件项目投标流程及注意事项