资讯专栏INFORMATION COLUMN

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

104828720 / 2422人阅读

摘要:关于的块不打算展开说,这里针对呼吸灯的详细说明。描述定时器底层回调初始化参数无返回无描述定时器中断处理函数参数无返回无呼吸灯注意由于产生中断过快,反转实现不了电平反转上面代码是中断处理函数,对呼吸灯的引脚电平反转。

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。
为了方便我这里使用一元二次曲线,有条件的可以使用sin函数,但是效果或许看起来会差不多。但是sin函数计算量会比一元二次方程大的多,所以衡量之下,选择通过一元二次曲线来实现。

通过这个曲线,你可能感觉到和线性折线差不多的效果。所以还需要降低由暗变亮时的亮度变化频率,即可让人眼看上去平滑。

有需要的可以移步呼吸灯曲线代码下载。或者有能力的可以自行参考上面代码,实现该曲线

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/118972.html

相关文章

  • 使用STM32CubeMX初始化STM32F031F6Px系列

    摘要:利用可视化界面来进行的相关配置,所以时钟滴答定时器串口等就不用根据数据手册去操作标准库甚至是寄存器了,可以通过软件直接生成。 使用STM32CubeMX初始化ST...

    Zhuxy 评论0 收藏0
  • STM32学习笔记 第二章 STM32资源介绍

    摘要:总线挂载的外设有等。外设地址映射片上外设区分为三条总线,根据外设速度的不同,不同总线挂载着不同的外设,挂载低速外设,和挂载高速外设。 第二章 STM32资源介绍 2...

    G9YH 评论0 收藏0
  • 通过css3动画和opacity透明度实现呼吸效果

    摘要:呼吸灯也可以通过加层罩,通过设置层罩透明度来实现亮度动画 呼吸灯 /*也可以通过加层罩,通过设置层罩透明度来实现亮度动画*/ body{ background-color: black; } div{ margin: 0 auto; margin-top: 200px; width: 300p...

    Cristalven 评论0 收藏0
  • 第一课:超级hello Arduino.使用多种知识串联一个入门小项目,很适合初学第一课哟.

    摘要:开关旋钮电位器的实验视频已经购买开发版的同学开始上课来一场紧张刺激的之旅吧前言开发工具的下载安装使用都很简单我这里就不赘述了附上官方的说明跟着步骤来十分钟搞定相关连接下载官方为什么成为开发函数 ...

    philadelphia 评论0 收藏0
  • 基于STM32的实验室点大师

    摘要:初始化引脚和基础设置利用宏定义定义引脚高或者低全部引脚为高原理图是低平点亮高电平灭利用宏定义设置时间关闭灯流水灯延时时间为的值先关闭全部灯循环流水灯 /* US...

    KunMinX 评论0 收藏0

发表评论

0条评论

104828720

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<