历史上的今天
返回首页

历史上的今天

今天是:2024年09月29日(星期日)

正在发生

2021年09月29日 | STM8S(105K4)使用笔记——通过TIM1输出PWM做呼吸灯

2021-09-29 来源:eefocus

STM8S105K4相关

已知的可以作为TIM1 PWM的输出通道为PC1、PC2、PC3、PC4。

已知可选的TIM1时钟为fmaster。


若使用的STM8S的芯片不为105K4,请查阅芯片相对应的文档,确认TIM1的PWM的输出通道,同时设置相应的选项字节。


呼吸灯功能需求设置

呼吸灯有这么两个最常见的功能需求:


LED灯一次灭到亮的耗时T(s),即周期/2

LED灯的刷新率P(Hz)

由这两个参数可得:


LED灯一次灭到亮需要刷新的次数N,N = T × P

例如:


LED灯一次灭到亮需求为1.5s

LED灯的刷新率为60Hz(约每0.017s刷新一次)

LED灯一次灭到亮需要刷新的次数为90次

至此,LED功能需求约定完成。


TIM1的PWM设置

由ST所提供的官方库中,有这么一个函数。


/**

  * @brief  Initializes the TIM1 Time Base Unit according to the specified parameters.

  * @param  TIM1_Prescaler specifies the Prescaler value.

  * @param  TIM1_CounterMode specifies the counter mode  from @ref TIM1_CounterMode_TypeDef .

  * @param  TIM1_Period specifies the Period value.

  * @param  TIM1_RepetitionCounter specifies the Repetition counter value

  * @retval None

  */

void TIM1_TimeBaseInit(uint16_t TIM1_Prescaler,

                       TIM1_CounterMode_TypeDef TIM1_CounterMode,

                       uint16_t TIM1_Period,

                       uint8_t TIM1_RepetitionCounter)

{

  /* Check parameters */

  assert_param(IS_TIM1_COUNTER_MODE_OK(TIM1_CounterMode));

  

  /* Set the Autoreload value */

  TIM1->ARRH = (uint8_t)(TIM1_Period >> 8);

  TIM1->ARRL = (uint8_t)(TIM1_Period);

  

  /* Set the Prescaler value */

  TIM1->PSCRH = (uint8_t)(TIM1_Prescaler >> 8);

  TIM1->PSCRL = (uint8_t)(TIM1_Prescaler);

  

  /* Select the Counter Mode */

  TIM1->CR1 = (uint8_t)((uint8_t)(TIM1->CR1 & (uint8_t)(~(TIM1_CR1_CMS | TIM1_CR1_DIR)))

                        | (uint8_t)(TIM1_CounterMode));

  

  /* Set the Repetition Counter value */

  TIM1->RCR = TIM1_RepetitionCounter;

}


相信很多人与我一样,无法理解使用PWM,需要如何配置TIM1_Prescaler,TIM1_Period这两个值。

下面将对这两个值的配置做出解释:


TIM1_Prescaler:该参数的设置会使TIM1的工作频率为f = fmaster / (TIM1_Prescaler + 1)Hz,即每1/f s做一次计数。

TIM1_Period:该参数的设置会使TIM1计数TIM1_Period次进行一次更新。即每(1 / f)× TIM1_Period s进行一次更新。

同时,在PWM模式下,TIM1_Period也作为PWM占空比的宽度。当你在占空比设置时,若你设置的占空比值为TIM1_Period / 2,那么你的灯泡将会以50%的亮度被点亮。

例如:


HSI的频率fmaster = 16MHz

LED灯一次灭到亮需求为1.5s

LED灯的刷新率为60Hz(约每0.017s刷新一次)

LED灯一次灭到亮需要刷新的次数为90次

TIM1_Period设置为90

由于LED灯的刷新率为60Hz,则需TIM1的更新频率(16000000/ (TIM1_Prescaler + 1) × 90) > 60Hz。

即需TIM1_Period + 1 < 2962.962 ≈ 2963。

此时,只需要每0.017s使通道PWM输出的占空比值步进1,就能让LED灯以60Hz的刷新率在1.5s内由灭到亮(亮到灭)。


/* 关键代码片段 */

  TIM1_TimeBaseInit(2963, TIM1_COUNTERMODE_UP, 90, 0);


/* 关键代码片段 */

void main(void)

{

  static uint32_t startTick = 0;


  while (1)

  {

    static uint16_t pwm = 90;

    static FlagStatus pwmtozero = RESET;

    if (SYS_GetTick() - startTick > 17)

    {

if (pwmtozero == RESET)

      {

        pwm -= 1;

      }

      else

      {

        pwm += 1;

      }

      TIM1_SetCompare3(pwm);

      if (pwm == 0)

      {

        pwmtozero = SET;

      }

      else if ((pwm == 90))

      {

        pwmtozero = RESET;

      }

      startTick = SYS_GetTick();

    }

    }

}


注:SYS_GetTick()将返回一个uwTick变量,该变量每1ms步进一次,实现方式可以参考这篇博客:STM8S(105K4)使用笔记——TIM4的基础配置


至此,TIM1通过PWM输出实现LED呼吸灯基本实现。


Gamma

由上面的参数与代码逻辑可知,PWM的占空比变化是线性,因此,LED灯的亮度变化也是线性的。但由于人眼对亮度认知的问题,上面代码跑出来的呼吸灯,在视觉认知上的亮度变化并不是线性。如下方图片所示:

为此,需引入Gamma系数对占空比进行变换,让LED灯亮度完成视觉认知上线性变换。

根据不同颜色的灯,有如下Gamma系数:

由于stm8105k4的性能限制,开根对性能要求太大,pwm值变换引入的Gamma值一律为2。


本篇博客不对Gamma值进行探讨,仅引入解决方案。


引入Gamma系数

回到原本的例子:


HSI的频率fmaster = 16MHz

LED灯一次灭到亮需求为1.5s

LED灯的刷新率为60Hz(约每0.017s刷新一次)

LED灯一次灭到亮需要刷新的次数为90次

TIM1_Period设置为90^2 = 8100


这里TIM1_Period的设置为刷新次数N^Gamma。若你所选用的芯片有较高的计算性能,可以选用合适的Gamma值,由于stm8s105k4的计算性能并不高,因此简单地使用2作为Gamma值。


TIM1_Prescaler至少需小于等于32

/* TIM1初始化配置 */

/**

  * @brief TIM1 Initialization Function

  * @param None

  * @retval None

  */

static void _TIM1_Init(void)

{

/* De-Init TIM1 peripheral*/

  TIM1_DeInit();


  /* Time Base configuration */

  /*

  TIM1_Period = 32

  TIM1_Prescaler = 8100

  TIM1_CounterMode = TIM1_COUNTERMODE_UP

  TIM1_RepetitionCounter = 0

  */


  TIM1_TimeBaseInit(32, TIM1_COUNTERMODE_UP, 8100, 0);

  

  /*TIM1_Pulse = CCR3_Val*/

  TIM1_OC3Init(TIM1_OCMODE_PWM1, TIM1_OUTPUTSTATE_ENABLE, TIM1_OUTPUTNSTATE_DISABLE,

                0, TIM1_OCPOLARITY_LOW, TIM1_OCNPOLARITY_HIGH, TIM1_OCIDLESTATE_SET,

                TIM1_OCNIDLESTATE_SET);

               

  /* TIM1 counter enable */

  TIM1_Cmd(ENABLE);


  /* TIM1 Main Output Enable */

  TIM1_CtrlPWMOutputs(ENABLE);

}


/* main中算法与代码 */

void main(void)

{

  static uint32_t startTick = 0;


  while (1)

  {

    static uint8_t number = 90;

    static uint16_t pwm = 0;

    static FlagStatus pwmtozero = RESET;

    

    if (SYS_GetTick() - startTick > 17)

    {

      if (pwmtozero == RESET)

      {

        number -= 1;

      }

      else

      {

        number += 1;

      }

      pwm = number * number;


      TIM1_SetCompare3(pwm);

      if (number == 0)

      {

        pwmtozero = SET;

      }

      else if ((number == 90))

      {

        pwmtozero = RESET;

      }


      startTick = SYS_GetTick();

    }

  }

}


至此,符合人眼视觉认知的亮度线性变化完成。


注:官方库TIM1_OCxInitx()中的变量TIM1_Pulse可理解为初始占空比值,即LED灯初始亮度。


写在最后

我非常感谢我的同事Mak对我的帮助。最初在实验PWM功能时,查阅过很多博客,但大多都是在写如何初始化TIM1,并没有说如何实现一个LED呼吸灯功能,所以在做了诸多实际试验后,决定把我的理解和成果写在了这篇文章中,希望对大家有帮助。


还有一点!如果在此前对TIM1的时钟门控进行过关闭,请记得打开!!!不然你会调代码调到怀疑人生


推荐阅读

史海拾趣

Econais公司的发展小趣事

自成立以来,Econais一直致力于技术创新。XXXX年,公司成功推出了一款具有里程碑意义的单芯片IEEE802.11b/g/n Wi-Fi系统级封装(SiP)模块——EC19D01。这款模块在业界引起了广泛关注,因为它不仅体积小、易于集成,而且待机功耗极低。这一突破性的创新使得Econais在物联网领域树立了技术领先的地位。

德索五金(dosinconn)公司的发展小趣事
DCX-CHOL Enterprises公司的发展小趣事

随着公司规模的扩大和市场竞争的加剧,DCX-CHOL Enterprises意识到品质管理的重要性。公司投入大量资源提升生产线自动化水平,引进先进的品质检测设备,并建立了严格的质量管理体系。这些措施有效地提高了产品的品质稳定性和可靠性,赢得了客户的信赖。同时,公司还注重员工培训和技能提升,培养了一支高素质、专业化的技术和管理团队。

思瑞浦微电子科技(3PEAK INCORPORATED)公司的发展小趣事

为了进一步拓展业务领域和提升竞争力,DCX-CHOL Enterprises开始积极探索跨界合作的可能性。公司与多家知名企业和研究机构建立了战略合作关系,共同开展技术研发和产品创新。通过跨界合作,DCX-CHOL Enterprises不仅获得了更多的技术支持和市场资源,还拓宽了视野和思路,为公司未来的发展注入了新的活力。同时,公司还积极参与行业协会和组织的活动,加强与其他企业的交流与合作,共同推动电子行业的发展和进步。

GTK UK Ltd公司的发展小趣事
望远镜在跟踪过程中可能受到机械振动的影响,影响跟踪精度。
高博(GBG)公司的发展小趣事

谷峰电子有限公司的故事始于1995年,当时公司在香港成立,标志着其半导体元器件研发与销售的起点。初期,面对激烈的市场竞争和技术挑战,谷峰团队凭借对半导体技术的深刻理解和对市场需求的敏锐洞察,逐步确立了以功率MOSFET为核心产品的战略方向。2000年,谷峰在深圳设立分公司,进一步扩大了其研发和销售网络,为公司的快速发展奠定了坚实基础。

问答坊 | AI 解惑

i2c总线问题——起始停止条件

大家好,现在学习实践I2C总线方面的知识。这个过程中遇到的问题还希望能够得到大家的指教,谢谢! 下面这段话摘自“I2C总线规范”, “在SCL线是高电平时SDA线从高电平向低电平切换,这个情况表示起始条件。 在SCL线是高电平时SDA线由低电平向 ...…

查看全部问答>

USB CCID

最近在做usb,ccid部分的东西,每次在读入ccid descriptor的内容后电脑就会自动重启,很费解,有哪位朋友做过类似的东西希望能指点一下! 谢谢!…

查看全部问答>

WINCE下驱动项目外包

现有两个WINCE下驱动项目外包: 1、S3C2416下驱动16C554多串口芯片的驱动程序 2、S3C2416的声音驱动(芯片的驱动代码有2442平台下的可以做参考) 有意者请加QQ嵌入式外包群:48348107 谢谢各位!…

查看全部问答>

这里挺好

想学点东西这里挺不错的…

查看全部问答>

VMware+RH9+skyeye1.2 仿真lcd出现segmentation fault

VMware+RH9+skyeye1.2 仿真lcd出现segmentation fault 配置文件内容: cpu: arm720t mach: ep7312 mem_bank: map=M, type=R,  addr=0x0, size=0xC0000 mem_bank: map=M, type=R,  addr=0x000C0000, size=0x00340000, fil ...…

查看全部问答>

串口传输的奇怪问题

Hi all 由于使用芯片的升级,公司使用的vxworks(5.4)的BSP包也随之升级,使用的是tornado2.02 现在目标板的bootrom烧进去,可以正常启动 可是不知道为什么,下载vxworks的映像文件的时候常常出错,提示 rpccore target server can\'t decode arg ...…

查看全部问答>

at+cipstart无法用域名的形式访问网络?

我使用sim3000的gprs模块,昨天已经可以用ip地址访问我在花生壳上注册的地址了.然后我尝试用域名的形式访问网络.先 at+cipshut,关闭连接 然后 配置dns服务器 at+cdnscfg=\"202.96.128.56\" 然后设置为域名访问方式at+cdnsorip=1 最后使用域名访 ...…

查看全部问答>

请问EVC能不能用USB口进行下载 在线调试

现在我用的是网口调试,经常出现下了一半就下不下去了 重起一次机器就好了 这样用就太不稳定了 想用USB的方法进行在线调试 但不知道怎么下手 麻烦各位大侠指点指点了 小女子先谢谢了哈…

查看全部问答>

使用51单片机与pc串口通信中数据不一致问题

我用51单片机与pc机进行串口通讯,上位机软件用的是出口调试助手3.0,下位机是我自己编的,主要目的是pc向单片机发个数据,单片机接收后向p2口输出,之后再把接收到的数据发给pc。但是我发现pc上发出与收到的数据总是不一致。例如,我用pc输出字符 ...…

查看全部问答>

看到许多朋友抱怨LPC17xx的资料难找,发一套全套示例代码

这是一套基于LPC17XX各个模块的示例代码,开发环境是基于KEIL MDK的,我现在用LPC1756做开发,也是初次接触。 这套资料还比较齐全,给了我不少帮助,希望对你们有用。…

查看全部问答>