你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

基于STM32的TouchGFX 图形应用在亮屏时的低功耗实现

[复制链接]
STMCU小助手 发布时间:2023-6-18 11:07
1. 图形应用和低功耗
3 y4 k' {5 A' D/ z8 t7 O/ G随着智能设备的普及,越来越多的设备会用到图形界面,而在 STM32 MCU 上使用TouchGFX,使得图形设计变得非常简单。其中智能手表、智能手环等产品对功耗要求比较高,这就需要在图形应用同时,结合芯片的低功耗模式来优化能耗。6 d6 {" }% W5 t- W. w! Z5 u
在图形应用中使用低功耗,一般分为两种场景,一种是在关闭屏幕时,MCU 进入 STOP 模式,能极大的降低 MCU 功耗;另一种是在屏幕亮着的状态,MCU 进入 SLEEP 模式,也能降低MCU 功耗。而在 STM32L4+芯片上,LPSLEEP 模式相比 SLEEP 模式功耗更低,本文将在STM32L4R9 芯片上,结合 TouchGFX 和 LPSLEEP 来介绍如何在亮屏状态下实现 MCU 低功耗。9 N- h* @( Z9 }5 A& R& u
2. 低功耗实现原理介绍1 V- h" e; O( V# h( J
在使用 TouchGFX 做图形应用时,会使用到 FreeRTOS 操作系统,使用 SysTick 定时产生 1ms的中断来作为系统 tick。在所有应用任务空闲时,系统会调度 Idle 任务。此时我们能让 MCU 进入低功耗,从而达到降低 MCU 功耗的目的。0 n% t: W' h. y! F4 u) E+ D% n

  `0 S% c6 {# a
2.1. Tickless Idle
- s5 I% ^! n' U* F' C+ RFreeRTOS 提供了 Tickless Idle 配置,在 FreeRTOSConfig.h 文件中,通过配置configUSE_TICKLESS_IDLE 为不同的值来配置 FreeRTOS 是否在 Idle 任务里进入低功耗,默认为 0 表示不进入低功耗。本文中将配置为 2,自定义 Idle 任务中低功耗实现。  A" c4 s+ b* G

' {2 e: r5 Q; H; A# Z7 z
MXTS%_0U37[GA{N7AIGY[DQ.png ; ^7 {* R' ~% L  Z# `

7 ~( H8 Q! V* h* CFreeRTOS 中进入低功耗具体是由 vPortSuppressTicksAndSleep 函数实现。配置configUSE_TICKLESS_IDLE 为 2 时,还需要重写 vPortSetupTimerInterrupt 函数。 / i, K# [. T* g/ f" {4 l) e

5 k% R6 R" d1 A" N4 l2 U: n7 j9 j2.2. TouchGFX 渲染与进 LPSLEEP 限制6 U  n3 E0 j; O! Y$ V* d5 m
根据参考手册描述,在进入 LPSLEEP 时要先切换到 LPRUN 模式,系统时钟要降低到2MHz,因此在进入、退出 LPSLEEP 时会有时钟的切换过程。6 H6 [) I4 E6 @2 x$ G
在屏幕亮着的状态时,用户可能随时触摸屏幕,或者系统信息可能更新,因此屏幕显示的内容可能随时变化,如果此时 MCU 进入 LPSLEEP 状态,会导致系统响应不及时。另外,在屏幕内容更新时,L4R9 通过 DSI 向屏幕更新显示数据,此过程由硬件完成,在此过程中,软件也有可能会进入 LPSLEEP,这样系统时钟发生变化,会导致 DSI 更新显示数据异常,产生屏幕显示花屏的问题。! Y7 W3 F0 Q2 `' l
为了避免以上问题,在进入 LPSLEEP 前会检查当前状态,如果用户未操作屏幕,: y1 ^, s6 C% w% w6 D
TouchGFX 已经完成渲染,DSI 刷新过程也完成,则允许系统进入 LPSLEEP 状态。这样既保证了系统正常工作,又达到了降低功耗的目的。
* h8 h+ M& n# ~7 \5 j0 X6 t- f  `, o2 ^. B) q
3. 实现过程" }( z, D* ~7 B3 i  s/ |$ C
重写 vPortSetupTimerInterrupt 函数,主要是设置与休眠时间补偿相关的变量,初始化 systick 寄存器:5 j$ y" m( u8 Z: A$ T' C
5 A5 H: d& {% ^: a. ]/ I& y2 T
/ m( E* T  z& B6 @
  1. void vPortSetupTimerInterrupt( void )
    2 y" R, F* i# Z; a8 j, H
  2. {
    * o+ j  K/ v" e* r" u8 X
  3. /* Calculate the constants required to configure the tick interrupt. */7 M! J4 D8 k0 I( w* ?# K; D
  4. #if( configUSE_TICKLESS_IDLE == 2 )
    ( a8 q) X: {+ S. J" S  T
  5. {& V& H" s9 Y1 ^* c
  6. ulTimerCountsForOneTick = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ );
      w" |( ~3 b, V& O5 `4 p2 c$ h
  7. xMaximumPossibleSuppressedTicks = portMAX_24_BIT_NUMBER / ulTimerCountsForOneTick;4 Y* e) w* ^- ^; A% ?
  8. ulStoppedTimerCompensation = portMISSED_COUNTS_FACTOR / ( configCPU_CLOCK_HZ / 1 C5 D5 ^% @0 l- B1 j2 d$ |# _
  9. configSYSTICK_CLOCK_HZ );8 l$ C- P% e. ~
  10. }7 {% X8 W: N5 h% _, X% j1 [
  11. #endif /* configUSE_TICKLESS_IDLE */
    5 ]' P9 ?5 `" t; q2 S; S, D* m0 {6 V
  12. /* Stop and clear the SysTick. */
    , v, L/ F) P% T9 j% T5 I0 |
  13. portNVIC_SYSTICK_CTRL_REG = 0UL;
    3 L# c( J5 N" g
  14. portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL;
    / W' Z$ _+ c: X9 g
  15. /* Configure SysTick to interrupt at the requested rate. */5 n1 ?2 u" W2 m1 p( h
  16. portNVIC_SYSTICK_LOAD_REG = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ) - 1UL;
    3 ?! `. l7 w% E( m, P  K/ q
  17. portNVIC_SYSTICK_CTRL_REG = ( portNVIC_SYSTICK_CLK_BIT | portNVIC_SYSTICK_INT_BIT |
    4 M. G, H% l% f
  18. portNVIC_SYSTICK_ENABLE_BIT );& _$ D+ j+ @9 t! C5 r( F
  19. }
复制代码

4 u5 s7 [, q5 b. a; Q: a3 q( r  u4 Q+ ^- M( _" u
重写 vPortSuppressTicksAndSleep 函数。在进入 LPSLEEP 前,会停止 systick,在休眠完成后,需要补偿系统 tick 值,示例中使用 LPTIM 计时来补偿系统 tick 值。在进入 LPSLEEP 前先进行状态检查,只要有以下情况,都不进入低功耗模式:
0 l  ~1 b2 N# T% i, Xa) 触摸事件产生:nosleep_flg7 }* P  o6 a" @: `( A
b) DMA2D busy 状态:isDMAbusy_a()
2 m* O6 U/ y" U$ Y% E, L: ?8 Lc) DSI refreshing 状态:displayRefreshing3 T* P) n' I' M2 A6 s2 N& _7 L
d) 渲染完成,请求 DSI 刷新:refreshRequested
' N  [2 y( R4 c" Z; X$ S0 ~
  1. void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime )' D% d& F# R" y& k
  2. {( j) r% \6 i; O6 ], R- l1 y( ?+ E
  3. uint32_t xMaximumPossibleSuppressedTicks,ulReloadValue, ulCompleteTickPeriods,ulCount;
    ) T) a9 `' K& I7 ?3 g
  4. xMaximumPossibleSuppressedTicks=portMAX_16_BIT_NUMBER * configTICK_RATE_HZ / lptim_CLOCK_HZ;' u2 |0 K3 r1 D5 T% w
  5. if(nosleep_flg || isDMAbusy_a() || displayRefreshing || refreshRequested) {
    % Y6 G, x* l$ d, e( d# a6 M; z
  6. if(!isDMAbusy_a() && !displayRefreshing && !refreshRequested) {: r1 }3 a8 k5 h! O" A5 Y  j
  7. if(osKernelSysTick() > nosleep_flg_t0 + nosleep_time)# W/ U. `$ n& I" y
  8. nosleep_flg = 0;
    / Y2 b( }3 g6 j
  9. }; @# F: g& X. m; H
  10. return;) r9 P+ l+ q1 |# S. A% P( A6 U
  11. }
    ; h5 ?- h/ ~& s# Q5 y
  12. if( xExpectedIdleTime > xMaximumPossibleSuppressedTicks ) {& S9 ^. Z6 g4 M: H1 o, S6 L5 T
  13. xExpectedIdleTime = xMaximumPossibleSuppressedTicks;
    6 O3 p/ A( Y9 a3 P* p  P
  14. }
    # [) ], y  y* a: W; Z
  15. portNVIC_SYSTICK_CTRL_REG &= ~portNVIC_SYSTICK_ENABLE_BIT;4 }) y" f4 H( b0 u! ~8 y* c
  16. HAL_SuspendTick();, {& h3 F3 V; G) H! v
  17. ulReloadValue = (lptim_CLOCK_HZ * (xExpectedIdleTime - 1UL)/configTICK_RATE_HZ);
    8 t3 x- j8 O1 \- H
  18. __DSB();
    5 R& @: H+ v( x/ c8 @4 W) h) u
  19. __ISB();7 `# F# P0 k% m; r
  20. LPTIM_ReloadMatch_flag=pdFALSE;
    0 m4 a, G& x- S8 G0 n6 B
  21. if( eTaskConfirmSleepModeStatus() == eAbortSleep ) {
    % j5 L; v% C7 i, M7 Z
  22. portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT;; j5 i/ t9 E6 H4 {4 {4 {; k% p
  23. portNVIC_SYSTICK_LOAD_REG = configCPU_CLOCK_HZ/configTICK_RATE_HZ - 1UL;8 i% W+ S5 @, w
  24. HAL_ResumeTick();/ Y# k3 p/ o0 h6 Y! x$ Z6 h& U
  25. } else {" c- B; J( L; U8 H& E7 d! j
  26. MX_LPTIM1_Init(LPTIM_PRESCALER_DIV2, ulReloadValue);
    + w: K4 t7 b9 a9 y
  27. if( xExpectedIdleTime > 0 ) {& h+ K$ X* m7 G
  28. __DSB();
    ! D  a# x" w- U2 d; }+ i
  29. enter_lpsleep();; \% ?/ R& v8 o
  30. __ISB();
    ) L: Q0 t# Z7 F6 c
  31. }
    8 S2 A" a# H/ U$ t" A, J3 }
  32. ulCount = HAL_LPTIM_ReadCounter(&Lptim1);$ I1 n8 c* u  E7 W2 M2 y
  33. HAL_LPTIM_TimeOut_Stop_IT(&Lptim1);
    + x5 \$ ?0 Z) S  ]
  34. HAL_ResumeTick();
    0 l- O& f; g0 s- J
  35. if( LPTIM_ReloadMatch_flag != pdFALSE ) {0 ]5 Y. l* M5 t) I# j$ o4 @/ i2 w- q
  36. ulCompleteTickPeriods = xExpectedIdleTime - 1UL;3 M' F; O% M. `# @
  37. } else {
    , E2 |6 W2 O! w5 A1 C# a/ C
  38. ulCompleteTickPeriods = (ulCount * configTICK_RATE_HZ)/ lptim_CLOCK_HZ;
    7 B5 ~- e! o5 o7 X3 t1 Q( v
  39. }' E$ I/ x" q0 |# V: _
  40. portENTER_CRITICAL();
    2 f# U, O: E! _8 K8 ?
  41. uwTick += ulCompleteTickPeriods;6 Q+ t- q7 }8 a4 b
  42. vTaskStepTick( ulCompleteTickPeriods );
    , n# {1 H: v8 G. n+ z
  43. portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT;
    6 E! O$ q, w) K9 s! I
  44. portNVIC_SYSTICK_LOAD_REG = configCPU_CLOCK_HZ/configTICK_RATE_HZ - 1UL;) N7 Z# |  w" D% N% }( p
  45. portEXIT_CRITICAL();0 H# i3 g# _' M: {# s4 t- }2 z
  46. }" o% I8 X* n# U+ h
  47. }
复制代码

$ V0 k  T) \! B9 _0 ~. o( K6 xTouchGFX 渲染过程包括两部分,CPU 渲染和 DMA2D 渲染。在 CPU 渲染过程中,TouchGFX任务处于执行状态,系统不会主动调度 Idle 任务。而在 DMA2D 渲染过 TouchGFX 会让出CPU,此时可能会切换到 Idle 任务执行,因此需要对 DMA2D 是否正在渲染进行判断。对DMA2D 是否在渲染的判断如下:
( k4 ~3 l  }1 A5 n$ v5 v
  1. extern "C" uint8_t isDMAbusy_a()% @. F6 L- |- T6 d; K! l6 b0 C
  2. {; C$ [! j4 X  P6 ?9 \
  3. return ((TouchGFXHAL*)(touchgfx::HAL::getInstance()))->isDMAbusy();' W- S* y! d, Q
  4. }$ N. P2 T  S6 L
  5. uint8_t TouchGFXHAL::isDMAbusy()
    * a2 j! }/ R, {* r2 V
  6. {
    $ i- v( U" m7 a' _# K& C# `0 |3 U! P
  7. bool dma_qe = dma.isDmaQueueEmpty();. e8 W: t, Y: x  s
  8. bool dma_run = dma.isDMARunning();. L0 v" s0 z9 V5 i3 V6 v9 Z7 ?
  9. return (uint8_t)(!dma_qe || dma_run);
    0 C/ e6 L; p: @; a
  10. }
复制代码
, |9 L: j$ Z2 d5 B" Y3 A

4 L1 K2 P# T0 I& m- {' a: F 完整版请查看:附件! W; b4 E/ e3 q( c
TouchGFX图形应用在亮屏时的低功耗实现.pdf (255.25 KB, 下载次数: 4)
收藏 评论2 发布时间:2023-6-18 11:07

举报

2个回答
lospring 回答时间:2023-8-10 10:15:58

低功耗确实是非常需要考虑的一个点

shenxiaolin_mai 回答时间:2023-8-10 10:16:03
感谢分享!

所属标签

相似技术帖

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版