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

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

[复制链接]
STMCU小助手 发布时间:2023-6-18 11:07
1. 图形应用和低功耗
( K7 r3 V. k( \' C$ A随着智能设备的普及,越来越多的设备会用到图形界面,而在 STM32 MCU 上使用TouchGFX,使得图形设计变得非常简单。其中智能手表、智能手环等产品对功耗要求比较高,这就需要在图形应用同时,结合芯片的低功耗模式来优化能耗。& C9 w3 d7 _/ A$ w' _- |
在图形应用中使用低功耗,一般分为两种场景,一种是在关闭屏幕时,MCU 进入 STOP 模式,能极大的降低 MCU 功耗;另一种是在屏幕亮着的状态,MCU 进入 SLEEP 模式,也能降低MCU 功耗。而在 STM32L4+芯片上,LPSLEEP 模式相比 SLEEP 模式功耗更低,本文将在STM32L4R9 芯片上,结合 TouchGFX 和 LPSLEEP 来介绍如何在亮屏状态下实现 MCU 低功耗。3 S1 m, c0 b0 ~
2. 低功耗实现原理介绍
4 E& z2 q; ?* F& S7 q+ v* E在使用 TouchGFX 做图形应用时,会使用到 FreeRTOS 操作系统,使用 SysTick 定时产生 1ms的中断来作为系统 tick。在所有应用任务空闲时,系统会调度 Idle 任务。此时我们能让 MCU 进入低功耗,从而达到降低 MCU 功耗的目的。! Q$ i- `% c8 E. v# X
, W: v7 b4 h3 V# j& b; ^; Q! B
2.1. Tickless Idle
4 O% w. o9 H9 Y: SFreeRTOS 提供了 Tickless Idle 配置,在 FreeRTOSConfig.h 文件中,通过配置configUSE_TICKLESS_IDLE 为不同的值来配置 FreeRTOS 是否在 Idle 任务里进入低功耗,默认为 0 表示不进入低功耗。本文中将配置为 2,自定义 Idle 任务中低功耗实现。! d& r  e9 h0 D4 b/ [6 }' l! i1 |
; h' j. O6 u/ ]6 K/ p4 z( Z
MXTS%_0U37[GA{N7AIGY[DQ.png 0 W1 e" {/ p& S+ ~6 |# K
: Q- |* ?9 X# m. U+ k" N  M
FreeRTOS 中进入低功耗具体是由 vPortSuppressTicksAndSleep 函数实现。配置configUSE_TICKLESS_IDLE 为 2 时,还需要重写 vPortSetupTimerInterrupt 函数。
1 h1 c3 u3 S2 w2 `, E
" u5 J2 r' o! T3 F2 Z4 r5 T$ X  C" I2.2. TouchGFX 渲染与进 LPSLEEP 限制
: ?# X' i! n/ Z( m5 x根据参考手册描述,在进入 LPSLEEP 时要先切换到 LPRUN 模式,系统时钟要降低到2MHz,因此在进入、退出 LPSLEEP 时会有时钟的切换过程。4 G/ |" P6 Y# B5 O5 X# S  ~- \
在屏幕亮着的状态时,用户可能随时触摸屏幕,或者系统信息可能更新,因此屏幕显示的内容可能随时变化,如果此时 MCU 进入 LPSLEEP 状态,会导致系统响应不及时。另外,在屏幕内容更新时,L4R9 通过 DSI 向屏幕更新显示数据,此过程由硬件完成,在此过程中,软件也有可能会进入 LPSLEEP,这样系统时钟发生变化,会导致 DSI 更新显示数据异常,产生屏幕显示花屏的问题。
  j1 k+ v- Q0 G为了避免以上问题,在进入 LPSLEEP 前会检查当前状态,如果用户未操作屏幕,( ^# ?* i! F2 K& T* g
TouchGFX 已经完成渲染,DSI 刷新过程也完成,则允许系统进入 LPSLEEP 状态。这样既保证了系统正常工作,又达到了降低功耗的目的。
/ W3 ^( b2 F5 j& m4 b  |4 s2 B. K6 B7 ]0 |& B
3. 实现过程
7 g8 g7 F; J6 [2 L2 T重写 vPortSetupTimerInterrupt 函数,主要是设置与休眠时间补偿相关的变量,初始化 systick 寄存器:
4 y6 U0 B# @6 {  B1 C7 Y
0 s; \4 j$ X+ u5 {

2 \1 z3 [: Q: V1 y# C
  1. void vPortSetupTimerInterrupt( void )! X7 x$ i7 f' }! Q/ y
  2. {5 `/ ?. x( L& F' a
  3. /* Calculate the constants required to configure the tick interrupt. */
    ) G9 F% \9 j0 Q7 \  a5 w
  4. #if( configUSE_TICKLESS_IDLE == 2 )
    / k/ j$ \9 Y5 |, x0 |: A/ k: X
  5. {
    ' k9 w  n+ n' v+ G4 ~& ]' M
  6. ulTimerCountsForOneTick = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ );
    + |, R9 `) c; S1 d- b
  7. xMaximumPossibleSuppressedTicks = portMAX_24_BIT_NUMBER / ulTimerCountsForOneTick;6 N4 w# z6 s0 x$ \' [, T0 W4 K) \* R5 x3 n
  8. ulStoppedTimerCompensation = portMISSED_COUNTS_FACTOR / ( configCPU_CLOCK_HZ /
    . B! f4 K) ]& R6 ^; L! ^
  9. configSYSTICK_CLOCK_HZ );& P) Q, F5 F- Y5 v( v9 h' \
  10. }. j: u. R- m: j5 A
  11. #endif /* configUSE_TICKLESS_IDLE */
    : P& z( }; c0 `8 f* d
  12. /* Stop and clear the SysTick. */7 _; x1 D/ w- H  n1 j$ J; Y4 p
  13. portNVIC_SYSTICK_CTRL_REG = 0UL;
    1 d8 X( K: \$ ~: I/ b4 b. x
  14. portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL;4 i5 L5 ~1 r7 N
  15. /* Configure SysTick to interrupt at the requested rate. */
    5 g7 ~" _1 Z' x) x# H' k( p
  16. portNVIC_SYSTICK_LOAD_REG = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ) - 1UL;$ E; ]+ ?* ^# L
  17. portNVIC_SYSTICK_CTRL_REG = ( portNVIC_SYSTICK_CLK_BIT | portNVIC_SYSTICK_INT_BIT | 6 m: E6 F' S3 V8 T0 j! b3 @
  18. portNVIC_SYSTICK_ENABLE_BIT );
    4 z/ _4 Y) v/ u% p; {
  19. }
复制代码

7 j' E4 m. M' {+ G9 D9 k# g% q: n- G+ X
重写 vPortSuppressTicksAndSleep 函数。在进入 LPSLEEP 前,会停止 systick,在休眠完成后,需要补偿系统 tick 值,示例中使用 LPTIM 计时来补偿系统 tick 值。在进入 LPSLEEP 前先进行状态检查,只要有以下情况,都不进入低功耗模式:
% m; {, l: `% T/ u$ K  Ha) 触摸事件产生:nosleep_flg
, C8 |- x7 W6 a# B+ t' }2 rb) DMA2D busy 状态:isDMAbusy_a()9 N1 p/ m' Y1 d6 K: b9 L
c) DSI refreshing 状态:displayRefreshing5 A7 Z; ]$ o# k9 [" z
d) 渲染完成,请求 DSI 刷新:refreshRequested
! G% j& J# S: t4 Q: {( ]- W$ M
  1. void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ). P2 K# p, U5 `+ i: p) d
  2. {
    " F9 X4 k, C# p: k! |  t. H3 ?
  3. uint32_t xMaximumPossibleSuppressedTicks,ulReloadValue, ulCompleteTickPeriods,ulCount;
    9 y9 p5 H6 y9 A9 A
  4. xMaximumPossibleSuppressedTicks=portMAX_16_BIT_NUMBER * configTICK_RATE_HZ / lptim_CLOCK_HZ;
    $ U/ o* r9 h! ]" D
  5. if(nosleep_flg || isDMAbusy_a() || displayRefreshing || refreshRequested) {6 V  T+ A, j. B" @+ D8 y
  6. if(!isDMAbusy_a() && !displayRefreshing && !refreshRequested) {
    6 \7 t, {. a0 n& W$ d
  7. if(osKernelSysTick() > nosleep_flg_t0 + nosleep_time)
    / n: N) j8 P: P: F
  8. nosleep_flg = 0;3 b/ a3 M. l/ @5 R3 G7 H
  9. }
    . ~9 X2 G, q& B% K6 M
  10. return;
    ! b& t$ {, y, |2 z4 R% O2 W7 I
  11. }
      P. y/ Q8 Z4 Z; O( t  ~9 X
  12. if( xExpectedIdleTime > xMaximumPossibleSuppressedTicks ) {
    5 S9 z* k8 U( G  R9 {, z
  13. xExpectedIdleTime = xMaximumPossibleSuppressedTicks;- k3 N8 k: P9 y9 u0 g' X9 w! ?. g. w
  14. }1 i0 D5 p, p! N- J! o
  15. portNVIC_SYSTICK_CTRL_REG &= ~portNVIC_SYSTICK_ENABLE_BIT;
    8 U  p6 S8 ^7 [7 k$ A  z4 c% y
  16. HAL_SuspendTick();
    9 B% [: S, T& K/ W
  17. ulReloadValue = (lptim_CLOCK_HZ * (xExpectedIdleTime - 1UL)/configTICK_RATE_HZ);8 O" ]( n. ]/ h: A: E- n
  18. __DSB();
    2 b7 S* A! V. r8 m. J; U. q
  19. __ISB();) I; T1 [5 L3 S, {& c4 e
  20. LPTIM_ReloadMatch_flag=pdFALSE;
    1 ?& [! H/ S3 u  P, W3 s/ G3 e% N5 Z
  21. if( eTaskConfirmSleepModeStatus() == eAbortSleep ) {: k4 _. B9 a; }. f9 y% v
  22. portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT;! S. e' \9 v. P
  23. portNVIC_SYSTICK_LOAD_REG = configCPU_CLOCK_HZ/configTICK_RATE_HZ - 1UL;$ @: y) ]2 C0 V' T, c
  24. HAL_ResumeTick();
    6 R- R$ M$ m# ?/ d& E  G5 x. C
  25. } else {7 H' A' ^* o# j
  26. MX_LPTIM1_Init(LPTIM_PRESCALER_DIV2, ulReloadValue);
    0 N$ [. n  @/ d% ?( N' a1 f* d
  27. if( xExpectedIdleTime > 0 ) {
    ! J3 }8 i$ Q2 p( x  i
  28. __DSB();
    $ g1 \3 F8 d/ J0 F* q+ S4 Q
  29. enter_lpsleep();
    0 Y4 b- D0 `7 a; v5 i. y; U
  30. __ISB();/ @( `, ?7 x8 f8 `
  31. }1 a' d" \% B( @  r
  32. ulCount = HAL_LPTIM_ReadCounter(&Lptim1);2 B, E. h! n8 T- Y, C
  33. HAL_LPTIM_TimeOut_Stop_IT(&Lptim1);1 N+ B, X5 X- L0 D  l9 g& M; c) E) X
  34. HAL_ResumeTick();! R$ G9 W& E0 v1 ^: d8 t( ?# @" @
  35. if( LPTIM_ReloadMatch_flag != pdFALSE ) {/ E% `) Y7 e8 F8 A. V9 H! v6 U
  36. ulCompleteTickPeriods = xExpectedIdleTime - 1UL;
      @0 F( C' R& f: R
  37. } else {5 w& W9 I0 ~1 H4 ^4 z% y
  38. ulCompleteTickPeriods = (ulCount * configTICK_RATE_HZ)/ lptim_CLOCK_HZ;. }9 g6 y+ `) Z
  39. }" F0 O+ [  ^1 u( q3 c' \& @0 k
  40. portENTER_CRITICAL();
    4 \: Z+ Y3 p6 B# a4 n
  41. uwTick += ulCompleteTickPeriods;" k/ P: a5 g. v, _4 S5 F  b  ^
  42. vTaskStepTick( ulCompleteTickPeriods );$ L/ D/ H$ i  ?5 {
  43. portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT;
    % @2 v7 e3 G$ A  P: Q# h' D! P
  44. portNVIC_SYSTICK_LOAD_REG = configCPU_CLOCK_HZ/configTICK_RATE_HZ - 1UL;# Q' _2 y6 K* f% `
  45. portEXIT_CRITICAL();
    6 p* l& _4 v8 q2 l) ]
  46. }
    * I, F% r) _, Y! S/ F
  47. }
复制代码

) T8 W1 [! E3 aTouchGFX 渲染过程包括两部分,CPU 渲染和 DMA2D 渲染。在 CPU 渲染过程中,TouchGFX任务处于执行状态,系统不会主动调度 Idle 任务。而在 DMA2D 渲染过 TouchGFX 会让出CPU,此时可能会切换到 Idle 任务执行,因此需要对 DMA2D 是否正在渲染进行判断。对DMA2D 是否在渲染的判断如下:
! N$ P( b! j( V5 B5 R2 y$ W
  1. extern "C" uint8_t isDMAbusy_a()
    * F6 e* A. A  V$ a$ B
  2. {
    1 i' i) c# ~6 R1 n- x. N
  3. return ((TouchGFXHAL*)(touchgfx::HAL::getInstance()))->isDMAbusy();( f  H) C: `; h; V4 i, p, u
  4. }
    ) h9 A3 Z& M0 T; w4 Z; w+ S" X
  5. uint8_t TouchGFXHAL::isDMAbusy()* G8 K, J+ i+ A2 b8 b( I( R
  6. {
    + Y" x1 G+ k% v/ _, w3 O, B) z; P* F
  7. bool dma_qe = dma.isDmaQueueEmpty();
    ) h4 H$ E  F# l
  8. bool dma_run = dma.isDMARunning();
    8 N# P- ~( V  j+ b  Z+ ~7 Y
  9. return (uint8_t)(!dma_qe || dma_run);
    3 n5 {, P& l! n$ W
  10. }
复制代码

; d+ Q$ F7 H5 v! n% ~* l' F/ s' q& U2 L
完整版请查看:附件
; E' Z: e5 {0 {4 k3 ? 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管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版