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

STM32 TouchGFX经验分享(五)

[复制链接]
STMCU小助手 发布时间:2023-3-5 12:09
Graph控件是用于实现图表以及需要绘制图像的控件,该控件在TouchGFX Designer中没有体现,但是可以在工程中自行添加,TouchGFX官方给的Demo中有针对于该部分的Demo,这些Demo可以给大家些帮助,下面针对于该控件给大家详细的介绍下。  v- \6 x# q2 T; s6 B( u; S
CanvasWidget
1 g0 A6 a: j! h  `& R) NCanvas Widgets和Canvas Widget Renderer是TouchGFX的功能强大且用途广泛的附加组件,它使用相对较少的内存就可以提供流畅,抗锯齿的几何形状图形,同时保持了高性能。但是,渲染几何形状必须被视为相当昂贵的操作,并且如果不小心使用,很容易使微控制器资源紧张。9 V. s4 n! `4 f' R- e
3 G/ X/ h7 [1 k( B
Canvas Widget Renderer(以下称为CWR)是一种通用的图形API,可为图元提供优化的图形,并自动消除大多数多余的图形。TouchGFX使用CWR绘制复杂的几何形状。几何形状由“画布小部件”定义。TouchGFX随附了许多受支持的Canvas Widget,但就像普通的Widget一样,您可以制作自己的自定义Canvas Widget来满足您的需求。当Canvas Widget定义要由CWR绘制的图形的几何形状时,图形内每个像素的实际颜色由关联的Painter类定义。同样,TouchGFX附带了许多Painter,但是您可以根据自己的需要制作自己的自定义Painter。
; |0 g! S# ^# u9 ~2 Y, Z8 ]

) b+ {, B5 }9 N+ F0 ~TouchGFX中的其他小部件的大小会自动设置。例如,位图小部件将自动获取所包含位图的宽度和高度。因此,足以setXY()在位图小部件上使用以将位图放置在显示器上。
# T8 X8 L' s" k8 B画布小部件没有默认大小,该默认大小可以自动确定并进行初始设置。必须不仅要注意位置,而且还要正确调整窗口小部件的大小,否则“画布”窗口小部件的宽度和高度将为零,并且在显示器上不会绘制任何内容。
- g+ N4 }3 a5 @3 |, U2 n7 [8 A) v1 `  a! c
因此,不要setXY()使用setPosition()来放置和调整画布小部件的大小,而要使用。另请参阅下面的“自定义画布小部件”,以获取有关如何创建和使用自定义画布小部件的示例。% i) W6 @+ h8 H4 ~& |" |

. G! t/ n/ P$ t) Z" a( }设置画布小部件的位置和大小后,便可以在其中绘制几何形状。坐标系将在小部件(而不是显示)的左上角具有(0,0),X轴向右延伸,Y轴向下延伸。
, c' I6 `/ d% i% Y1 W& `0 d7 w& T) k- x% n- D+ [" {. `
Canvas窗口小部件在TouchGFX Designer中也受支持,它使用法简单并具有自动内存分配功能。
) J' A, A; A: T) X0 o
: `5 Y7 I+ g1 G% m' L$ a3 F1 R# D

* t, u  t0 g  ICanvasWidget内存分配
$ {3 U. o) w2 ^/ a, }TouchGFX Designer中的内存分配
% Q& E1 o, Y" h5 y将小部件添加到屏幕的画布时,会自动生成一个内存缓冲区。缓冲区的大小基于具有以下公式的屏幕宽度(Width × 3)× 5。但是,这并非总是适用于所有情况的理想缓冲区大小。因此,可以覆盖缓冲区大小,如下图所示。! l6 ^' Z% @& D! ?+ I* m0 L# Q+ ?! v( L

0 i/ G& t; j1 V( ~" a6 G3 C( z
20200628131939213.png
9 H! L% @% r+ G! s- y3 O3 b" o: ?3 {' y2 @7 e% p
用户代码中的内存分配

# ]$ ~3 X# c2 X( e$ y: B, Y9 _内存可被分配和设置在target/main.cpp与simulator/main.cpp或可以设置和每画面分配。5 A1 E5 J# Z* L; ]
  1. static const uint16_t CANVAS_BUFFER_SIZE = 3600;5 z; ~/ Y. Y$ j$ N1 {
  2. static uint8_t canvasBuffer[CANVAS_BUFFER_SIZE]
    0 p; {6 b& A% r' ?) q
复制代码
; l  N$ p1 ?1 J3 I
定义内存缓冲区大小的静态const,可以在main.cpp或的开头定义实际的内存缓冲区ScreenView.hpp
7 d0 l, M' I) {7 ~& t) @% s  n0 A然后,在任一main()的方法main.cpp或setupScreen()方法ScreenView.cpp中的以下行建立缓冲区可以被添加。  V$ @' o- }) Z6 n& d
  1. CanvasWidgetRenderer :: setupBuffer (canvasBuffer ,CANVAS_BUFFER_SIZE );
    5 s* J; w) f) Z4 D) M
复制代码

4 I! h, b1 H2 A' w! Y: \所需的CWR内存量取决于应用程序中要绘制的形状的最大大小。但是,您可以保留少于最大形状所需的内存。为了处理这种情况,CWR将形状的图纸分成较小的帧缓冲区部分,从而导致稍长的渲染时间,因为在这种情况下,有时不得不多次渲染形状。在模拟器模式下运行时,可以更仔细地调查和微调内存消耗。只需将以下函数调用添加到main.cpp中:3 F0 p! X  a( h+ p
  1. CanvasWidgetRenderer :: setWriteMemoryUsageReport (true );
    / M: T8 D- G7 G: g/ T; e
复制代码
" v, i3 }2 z/ n  ^' x
现在,每当绘制操作完成时,CWR都会报告(在控制台中打印)需要多少内存。对于canvas_widget_example,它可能是“ CWR需要3604字节”(对于第一次绘制操作),然后是“ CWR需要7932字节(缺少4328个字节)”(对于第二次绘制操作)。即使CWR似乎没有足够的内存(在这种情况下,缺少4328字节),应用程序也可以正常运行。这是因为CWR检测到可用内存太少,无法在一次运行中完成复杂的绘制操作。取而代之的是,它将绘制操作分为两个单独的绘制操作,形状将被绘制得很好,但需要更多时间进行渲染。
4 m  Z* Q- @% c2 E# I. _
5 g% o9 O4 I* x, P
因此,设置正确的内存缓冲区大小需要在内存和性能(渲染时间)之间进行权衡。好的起始值通常约为3000,但是使用上述技术,通常可以确定更好的值。如果形状太复杂而分配的内存缓冲区太小,则将不会绘制形状的一部分(某些垂直像素线将被跳过),并且有可能根本没有绘制。无论如何,渲染时间都会增加很多。
0 I% w$ v& d4 p0 a7 [& \( q+ }! U8 s
7 @9 \5 L# q" a/ \. a' v
这意味着,如果您希望您的应用程序以最快的速度渲染CWR图形,则需要分配请求的内存量。但是,如果可以使用较慢的渲染计时器,则可以减少内存缓冲区。( S% x% Z* X" M8 S' P- h. o  P

, O( O  E  h/ R1 |  h. H

% A. ~) u/ `+ V) y3 QCWR坐标系9 M' }; y2 R4 D- ^" y) d
TouchGFX中的坐标系通常用于寻址像素,以在显示器上定位位图。位图,文本和其他图形元素都放置在坐标系中,其中(0,0)是左上像素,x轴向右延伸,y轴向下延伸。在CWR中,仅能够使用整数寻址像素是不够的,尽管在特殊情况下这可能就足够了,但通常这还远远不够。为了说明这一点,请考虑一个线宽为1的圆,它必须恰好适合5 x 5像素的框。该圆的中心必须位于(2.5,2.5),半径必须为2,因此中心坐标需要分数。同样,如果圆应适合于6 x 6像素的框,则中心必须位于(3,3),半径必须为2.5,因此此处半径必须为分数。
8 N2 E( U1 f' Y  r; |3 X# y" p/ O- H: B5 I
这种寻址图形坐标的新方法意味着(0,0)的像素中心具有CWR坐标(0.5,0.5)。因此,包含在屏幕左上角像素的框具有以下轮廓:(0,0)->(1,0)->(1,1)->(0,1)->(0 ,0)。
* e2 r! P6 n; F) L. _+ ^. R/ i' E& L  e/ d* u  H
20200628132621835.png
) |4 ?! j4 s0 T# K' V# S  E' a

7 H& K" v$ O1 |" V) U( a% o2 E) O尽管起初看起来似乎很混乱,但很快就变得很自然。当位图的坐标系统处理像素时,画布小部件的相同坐标处理像素之前和上方的间隙。8 h/ ?+ ~' _! o2 C, L, T

3 p2 e7 c8 V0 ?. E' _7 Y

$ }  k& F. u% s2 P, r/ [点、线、面. E- g8 T( ~5 {2 T
当创建完画图的画板后就可在其中创建图像, TouchGFX的图像不支持线性的曲率变化,换句话说就是只能通过点去链接线,但是如何能做到曲率的线呢,这时我们可以通过贝塞尔算法,或者其他的曲率算法推算出每一个点的坐标,或者是相隔固定点的坐标,之后在界面上显示出来。
+ D4 h) \# D$ \' R! F, I官方提供了两个demo 分别是TouchGFX Demo1 和TouchGFX Demo2,两个demo中都有graph绘制方法的体现,但体现的方法不同,对用CanvasWidget继承程度也不尽相同。7 N3 H( `6 @' ^1 e$ ^6 p& p
这里我们讲解个内容比较多的TouchGFX Demo1,我会在下一节提出代码。4 J5 E$ \4 A! Y2 G! W
3 U0 e, r: d) N1 b) E4 i% [! ^

3 J) B4 `8 r1 \: |9 f% d$ i" pDemo

" h( |) w7 y' _& [0 p3 d( K5 G: a首先先看一下该部分代码的调用的方法。' E1 |" V; K% p! c- a4 y" v. T
  1.     primaryGraph.setXY(graphXOffset, graphYOffset);
    , D( M) n5 Z3 t: d9 }, S
  2.     primaryGraph.setup(graphWidth, graphHeight, Color::getColorFrom24BitRGB(0x24, 0x73, 0xAC), graphBackground.getColor());
    ( _; i1 N- F& a+ p$ I  A+ i$ U
  3.     primaryGraph.setDotShape(0, 30, 5);//设置折线上点的参数
      u# F7 {3 t1 d# n3 o( s
  4.     primaryGraph.setDotBackgroundShape(0, 30, 7);//设置折线上点的背景参数
    " g* E0 [2 ?) i1 F+ c2 O
  5.     graphArea.add(primaryGraph);! |4 _8 W" z# S" t( P* P. B* g% K
复制代码

1 N6 P% \0 u/ ?6 A" n4 f在此可以看出graph作为一个控件调用方法是一致的,都是先设置x,y的位置,也需要设置画布的宽高,以及转向点或者其他的设置。* F! W) }# U7 G' L
这里唯一的不一样的地方是setup()函数,这个函数中除了宽高外还有颜色,颜色的含义则是画笔的颜色,想要作画除了画布同样还需要画笔。至于化成什么样子则是我们掌握的,下面看下setup函数。
, B5 L* W) o, i
  1. void Graph::setup(int newWidth, int newHeight, colortype lineColor, colortype backgroundColor)4 v4 _/ |9 U  I9 u% ^( j
  2. {( R4 O; M7 w4 O' o' {
  3.     setWidth(newWidth);) t% G3 J6 }) y8 X$ _/ C& Z
  4.     setHeight(newHeight);
    # V# G6 p* i8 }2 |

  5. 3 e- u! ?1 a5 W$ \* X6 o/ K
  6.     graphLinePainter.setColor(lineColor);7 Z5 J6 \/ \- p4 Y" W5 ]
  7.     graphAreaPainter.setColor(lineColor, 255);
    4 Z) ]$ J, T  N1 h1 d
  8.     graphDotsPainter.setColor(lineColor);# o8 }: M  c6 [$ c! q# N+ [
  9.     graphDotsBackgroundPainter.setColor(backgroundColor);
    8 x5 }1 G! _! O7 ]

  10. % p& n4 x4 o- F" l9 K6 F0 W
  11.     graphLine.setLinkedGraph(graphArea);2 d2 U; C+ `6 x9 O( h  B
  12.     graphLine.setLinkedGraph(graphDots);
    / B8 v) A: Y8 r& N5 `
  13.     graphLine.setLinkedGraph(graphDotsBackground);
    " x" O$ w, Q. u5 i3 i
  14.   c7 P. _1 D6 i9 O% S4 ]
  15.     graphLine.setPosition(0, 0, getWidth(), getHeight());
    # E# ~! X2 q; C
  16.     graphLine.setPainter(graphLinePainter);& x6 Y5 Z& i( J% H: ~" h
  17.     graphLine.setBuffer(graphBuffer, NUMBER_OF_POINTS);
      Y6 o1 V1 I- t
  18.     graphLine.setLineWidth(1);" O8 f  v" A3 G! I! @. l0 `" w
  19.     graphLine.setRange(-2, 216, 400, 0);
    : G8 D6 N, J7 e7 W7 q
  20.   Q& W4 R+ T* D. t% L: k
  21.     graphArea.setPainter(graphAreaPainter);
    $ E: o& A/ d6 I4 Z0 U
  22.     graphArea.setLineWidth(0);- v, G* D; @5 m. e8 w
  23. # K/ r( r4 Z5 R# E9 B
  24.     graphDots.setPainter(graphDotsPainter);
    & h7 y! M. I7 w. q
  25.     graphDots.setLineWidth(7);& F0 J. R7 }! w8 b! G. H
  26.     graphDots.setDotShape(0, 90);* H. h: f, v; K+ ]  K9 P, Q
  27. . ?/ f3 [8 i0 V. e- L
  28.     graphDotsBackground.setPainter(graphDotsBackgroundPainter);' t' T  T8 p/ m1 G. p
  29.     graphDotsBackground.setLineWidth(9);
    + {' \2 S2 |' p. P, p5 ^
  30.     graphDots.setDotShape(0, 30);8 n; Z/ r% |& ~$ ^# z# E! M
  31. ( q: ~+ g) i1 a: W
  32.     add(graphArea);- z1 X, I4 K1 i# [5 j
  33.     add(graphLine);
    ( X9 K9 _/ {. _% n3 Z
  34.     add(graphDotsBackground);2 w3 U- B/ n- F5 N$ K$ l
  35.     add(graphDots);( m  u7 a. r* b, t* M
  36. }
    ) u+ J# R, i1 ^1 r# Q, n
复制代码

' r% J  @1 Q$ n0 I, i可以看出,整体采用的画笔以及将其画布放置在一起的过程。
" r9 p! s9 e, g* K) \. N. x除此之外还有些细节的这里不将展示。需要可以参考官方的代码,我将具体的部分放到这里供大家快速定位。
1 P  i: w# S3 k" e# A% y" J  x# J& M. z7 m1 ~$ J7 L
20200628134829920.png 0 W/ ^& q; h) i
$ o) |, L4 K( j! z$ _
GraphView.cpp中放的是使用方法,文件夹graph_widget文件夹中放的是控件的源码,大家可以根据需要自己研究下该控件。
' h; o* M2 `- M9 }8 h, T7 }) Q. Y0 H- U/ z5 Z- ^$ i0 H

9 r% M" g! `) N$ u3 a9 i8 q) E. a注意& B' j  N8 O& Z& ?- x3 ~* d( s
本人在开发该部分控件时,会弹出buffer空间不足的提示框,原因则是在新建的工程中未添加画布空间的buffer,所以才会报错,具体请参照本章内容。8 j( J9 g* J) O: L5 A6 s/ {
————————————————
9 |, Y& ^* `+ R版权声明:空闲的程序员% k+ ^7 U- n0 G6 c6 \% E

( n. p0 k- M! ?5 Q* y: J& ~0 y) F) V& T3 c9 ]6 q
收藏 评论0 发布时间:2023-3-5 12:09

举报

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