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

STM32 TouchGFX经验分享(五)

[复制链接]
STMCU小助手 发布时间:2023-3-5 12:09
Graph控件是用于实现图表以及需要绘制图像的控件,该控件在TouchGFX Designer中没有体现,但是可以在工程中自行添加,TouchGFX官方给的Demo中有针对于该部分的Demo,这些Demo可以给大家些帮助,下面针对于该控件给大家详细的介绍下。
4 b7 C, r: S" M
CanvasWidget
5 o5 E: e+ I& X6 D* |0 ^2 K% {- LCanvas Widgets和Canvas Widget Renderer是TouchGFX的功能强大且用途广泛的附加组件,它使用相对较少的内存就可以提供流畅,抗锯齿的几何形状图形,同时保持了高性能。但是,渲染几何形状必须被视为相当昂贵的操作,并且如果不小心使用,很容易使微控制器资源紧张。0 a; @5 D, o! f
% ^0 s3 q' P5 @6 b4 y. P# |
Canvas Widget Renderer(以下称为CWR)是一种通用的图形API,可为图元提供优化的图形,并自动消除大多数多余的图形。TouchGFX使用CWR绘制复杂的几何形状。几何形状由“画布小部件”定义。TouchGFX随附了许多受支持的Canvas Widget,但就像普通的Widget一样,您可以制作自己的自定义Canvas Widget来满足您的需求。当Canvas Widget定义要由CWR绘制的图形的几何形状时,图形内每个像素的实际颜色由关联的Painter类定义。同样,TouchGFX附带了许多Painter,但是您可以根据自己的需要制作自己的自定义Painter。2 J3 A8 u9 u6 c" h9 j
. w5 X0 V/ K& c
TouchGFX中的其他小部件的大小会自动设置。例如,位图小部件将自动获取所包含位图的宽度和高度。因此,足以setXY()在位图小部件上使用以将位图放置在显示器上。# B2 w1 ]8 v# {9 k/ Y1 r# ~
画布小部件没有默认大小,该默认大小可以自动确定并进行初始设置。必须不仅要注意位置,而且还要正确调整窗口小部件的大小,否则“画布”窗口小部件的宽度和高度将为零,并且在显示器上不会绘制任何内容。6 R! k7 y; Y6 X8 |/ U; x6 ^
, m! y# a$ y2 F; S9 O2 x
因此,不要setXY()使用setPosition()来放置和调整画布小部件的大小,而要使用。另请参阅下面的“自定义画布小部件”,以获取有关如何创建和使用自定义画布小部件的示例。
2 b" T! v9 ~4 W  g; J4 Y$ C, P+ @
设置画布小部件的位置和大小后,便可以在其中绘制几何形状。坐标系将在小部件(而不是显示)的左上角具有(0,0),X轴向右延伸,Y轴向下延伸。
0 n3 ~6 e4 z1 h' \6 H" b6 k9 I- z0 i. M+ Q
Canvas窗口小部件在TouchGFX Designer中也受支持,它使用法简单并具有自动内存分配功能。# `7 Z0 [  H. E  E/ }
: e4 W: B* V! i8 o. R5 t& L

* b$ K1 \3 \8 C+ o. a- M" ^. sCanvasWidget内存分配) B4 v! \* r8 J& l
TouchGFX Designer中的内存分配& y6 m, H: E% v1 a% o
将小部件添加到屏幕的画布时,会自动生成一个内存缓冲区。缓冲区的大小基于具有以下公式的屏幕宽度(Width × 3)× 5。但是,这并非总是适用于所有情况的理想缓冲区大小。因此,可以覆盖缓冲区大小,如下图所示。- w* I# P( |$ D2 R! o
4 L& y% }0 t4 J; k
20200628131939213.png
) x9 c/ x! Z, @* O9 f. ^6 z! W# h$ ?3 J
用户代码中的内存分配
8 t# c  _2 n# _* R) G
内存可被分配和设置在target/main.cpp与simulator/main.cpp或可以设置和每画面分配。0 h' _7 W* U0 K! ~) _
  1. static const uint16_t CANVAS_BUFFER_SIZE = 3600;
    , q7 r* P7 W* f
  2. static uint8_t canvasBuffer[CANVAS_BUFFER_SIZE]6 T5 W8 k) F  q* S9 d5 Y/ r, _
复制代码
$ m/ Y/ [& R/ a( L; }! T0 B
定义内存缓冲区大小的静态const,可以在main.cpp或的开头定义实际的内存缓冲区ScreenView.hpp
0 U3 N3 C; k7 `然后,在任一main()的方法main.cpp或setupScreen()方法ScreenView.cpp中的以下行建立缓冲区可以被添加。
) C* e0 K3 l- G0 @! G
  1. CanvasWidgetRenderer :: setupBuffer (canvasBuffer ,CANVAS_BUFFER_SIZE );
    6 ?. u$ n; B7 }4 r; |5 G
复制代码

3 A! B) f- Q. b5 E所需的CWR内存量取决于应用程序中要绘制的形状的最大大小。但是,您可以保留少于最大形状所需的内存。为了处理这种情况,CWR将形状的图纸分成较小的帧缓冲区部分,从而导致稍长的渲染时间,因为在这种情况下,有时不得不多次渲染形状。在模拟器模式下运行时,可以更仔细地调查和微调内存消耗。只需将以下函数调用添加到main.cpp中:( @$ Q) }! e. m2 Q2 v, t! l. F
  1. CanvasWidgetRenderer :: setWriteMemoryUsageReport (true );8 X' H$ z5 l+ h' B
复制代码
& q$ C4 c$ T& O) k6 V: z
现在,每当绘制操作完成时,CWR都会报告(在控制台中打印)需要多少内存。对于canvas_widget_example,它可能是“ CWR需要3604字节”(对于第一次绘制操作),然后是“ CWR需要7932字节(缺少4328个字节)”(对于第二次绘制操作)。即使CWR似乎没有足够的内存(在这种情况下,缺少4328字节),应用程序也可以正常运行。这是因为CWR检测到可用内存太少,无法在一次运行中完成复杂的绘制操作。取而代之的是,它将绘制操作分为两个单独的绘制操作,形状将被绘制得很好,但需要更多时间进行渲染。
2 W3 ]) y' T9 r

( J' C& Z# i$ Y) c* Z3 j因此,设置正确的内存缓冲区大小需要在内存和性能(渲染时间)之间进行权衡。好的起始值通常约为3000,但是使用上述技术,通常可以确定更好的值。如果形状太复杂而分配的内存缓冲区太小,则将不会绘制形状的一部分(某些垂直像素线将被跳过),并且有可能根本没有绘制。无论如何,渲染时间都会增加很多。
, v2 t, I, |& [* ?0 M, W

! W: N, r7 M  [这意味着,如果您希望您的应用程序以最快的速度渲染CWR图形,则需要分配请求的内存量。但是,如果可以使用较慢的渲染计时器,则可以减少内存缓冲区。, j# }2 d- Y& C& m( m& K; t: z

  z  o) ?. y( ?9 a9 a  A- [
: B0 {1 j( Y* y! M2 m2 p
CWR坐标系. z3 F9 k- S; B1 E# e
TouchGFX中的坐标系通常用于寻址像素,以在显示器上定位位图。位图,文本和其他图形元素都放置在坐标系中,其中(0,0)是左上像素,x轴向右延伸,y轴向下延伸。在CWR中,仅能够使用整数寻址像素是不够的,尽管在特殊情况下这可能就足够了,但通常这还远远不够。为了说明这一点,请考虑一个线宽为1的圆,它必须恰好适合5 x 5像素的框。该圆的中心必须位于(2.5,2.5),半径必须为2,因此中心坐标需要分数。同样,如果圆应适合于6 x 6像素的框,则中心必须位于(3,3),半径必须为2.5,因此此处半径必须为分数。* B1 G8 ^1 G% H) Z/ N" E

/ G( |" e2 J  j# G0 Q4 o这种寻址图形坐标的新方法意味着(0,0)的像素中心具有CWR坐标(0.5,0.5)。因此,包含在屏幕左上角像素的框具有以下轮廓:(0,0)->(1,0)->(1,1)->(0,1)->(0 ,0)。
, l; @% @: z4 H  q) I* @
  y9 F* w7 a1 T* k- `; G
20200628132621835.png & T1 t1 e5 X5 l+ R( F5 C

, R" P% D: D3 q& |: i尽管起初看起来似乎很混乱,但很快就变得很自然。当位图的坐标系统处理像素时,画布小部件的相同坐标处理像素之前和上方的间隙。
% d0 O& A7 Z* {8 }$ V7 d% L5 x1 d9 ]# }7 n

8 c1 H* L) ]( U( \4 ~  I" b. L点、线、面
0 j4 {, O! N7 \3 q当创建完画图的画板后就可在其中创建图像, TouchGFX的图像不支持线性的曲率变化,换句话说就是只能通过点去链接线,但是如何能做到曲率的线呢,这时我们可以通过贝塞尔算法,或者其他的曲率算法推算出每一个点的坐标,或者是相隔固定点的坐标,之后在界面上显示出来。' \* a9 L+ \$ s5 N
官方提供了两个demo 分别是TouchGFX Demo1 和TouchGFX Demo2,两个demo中都有graph绘制方法的体现,但体现的方法不同,对用CanvasWidget继承程度也不尽相同。) T  x% G: q% v. O  T8 r& @% d
这里我们讲解个内容比较多的TouchGFX Demo1,我会在下一节提出代码。: k9 A( s. e, _6 q: ~# A

2 s8 [; N  q& r- m1 ~
" ^! Y; U* o% p' B( h& \% ?
Demo

4 |( ]# I4 g. v3 C首先先看一下该部分代码的调用的方法。
4 H. Q  N  w" s9 J4 I
  1.     primaryGraph.setXY(graphXOffset, graphYOffset);6 {& }2 j9 v" `* g; `
  2.     primaryGraph.setup(graphWidth, graphHeight, Color::getColorFrom24BitRGB(0x24, 0x73, 0xAC), graphBackground.getColor());, P/ `" z6 A' e1 ~/ z
  3.     primaryGraph.setDotShape(0, 30, 5);//设置折线上点的参数
    ( B; L5 o" W. O+ N( |7 C; Q
  4.     primaryGraph.setDotBackgroundShape(0, 30, 7);//设置折线上点的背景参数. r% t% {7 o5 r7 A6 U9 P
  5.     graphArea.add(primaryGraph);& v5 t6 Y! C' z* t# u
复制代码

, f1 U: b# s6 {7 m) y8 x6 I0 ?在此可以看出graph作为一个控件调用方法是一致的,都是先设置x,y的位置,也需要设置画布的宽高,以及转向点或者其他的设置。
  S: H1 C5 p% @6 Z# j这里唯一的不一样的地方是setup()函数,这个函数中除了宽高外还有颜色,颜色的含义则是画笔的颜色,想要作画除了画布同样还需要画笔。至于化成什么样子则是我们掌握的,下面看下setup函数。
0 x5 @" {" B0 l" f* P3 E
  1. void Graph::setup(int newWidth, int newHeight, colortype lineColor, colortype backgroundColor)
    - ]+ X) r2 u& l" S5 p" U
  2. {& V2 \! P- W1 D+ a( j& q- u) H
  3.     setWidth(newWidth);
    " g6 Z- y7 P$ N% ]% U
  4.     setHeight(newHeight);
    7 T% x/ p# C6 S: o
  5. ' A: L% M* S8 P% M9 b
  6.     graphLinePainter.setColor(lineColor);
    ) a3 D$ ~" g" D& `. g  A
  7.     graphAreaPainter.setColor(lineColor, 255);# j/ l" z# m5 v  D
  8.     graphDotsPainter.setColor(lineColor);4 i! P8 H1 a4 a4 C# `0 T
  9.     graphDotsBackgroundPainter.setColor(backgroundColor);' C/ N" z7 ?7 |9 z

  10. / ^/ [6 d" m" w. L4 a- }, j
  11.     graphLine.setLinkedGraph(graphArea);& i4 b0 \5 J! C. e
  12.     graphLine.setLinkedGraph(graphDots);
    $ Y3 Q7 u& F7 @2 n6 y/ K) i& B
  13.     graphLine.setLinkedGraph(graphDotsBackground);
    * k6 `# T5 [$ X0 r; R( q) H
  14. 3 C( X' H  @1 e: ~; \5 F
  15.     graphLine.setPosition(0, 0, getWidth(), getHeight());) \+ x/ q1 S; I' v4 f: g8 D
  16.     graphLine.setPainter(graphLinePainter);  J3 N. B1 C+ X9 s: y
  17.     graphLine.setBuffer(graphBuffer, NUMBER_OF_POINTS);
    / Z/ Y5 F/ H) E! E
  18.     graphLine.setLineWidth(1);
    + b9 _9 S6 l( `, ~. e
  19.     graphLine.setRange(-2, 216, 400, 0);
    ' |5 y* U" H% ]

  20. & n+ F; a6 [+ a% ]5 Y
  21.     graphArea.setPainter(graphAreaPainter);" |& C5 j! E7 M7 Y- N
  22.     graphArea.setLineWidth(0);
    + U" V0 V& w: D9 p
  23. : \* P5 @& ^/ v9 F8 X4 y2 T& q
  24.     graphDots.setPainter(graphDotsPainter);
    ; r6 k2 V8 K' \9 M. s' W
  25.     graphDots.setLineWidth(7);
    1 b9 d; w$ ^( \' X# _
  26.     graphDots.setDotShape(0, 90);3 G4 c" ~8 o' u$ U' b* a: t2 y1 ?' O

  27. ' e. C4 }% z( K9 w, `% B+ r
  28.     graphDotsBackground.setPainter(graphDotsBackgroundPainter);9 z% {# E# e& d, V/ b; f
  29.     graphDotsBackground.setLineWidth(9);; A& n- i% C, N, _3 G4 C# w8 w
  30.     graphDots.setDotShape(0, 30);
    & T2 C# X6 N+ ~
  31. 2 y; Q' y6 t7 t7 w2 e: y# V
  32.     add(graphArea);
    9 k" g; J$ Q% O4 u4 f$ N6 v. p
  33.     add(graphLine);
    0 [. Z4 v, `% d: i
  34.     add(graphDotsBackground);4 K6 ?4 Z+ s2 P+ S/ I9 b
  35.     add(graphDots);
    3 a/ r+ }3 w9 u  I$ c2 Z  p9 a
  36. }
    " F) [, k# U) W# a' V+ H' T
复制代码
# s% A1 N: Z4 `- W" L
可以看出,整体采用的画笔以及将其画布放置在一起的过程。
" u( M! L& o6 E1 G$ Z! C. i2 L% A$ e除此之外还有些细节的这里不将展示。需要可以参考官方的代码,我将具体的部分放到这里供大家快速定位。
3 _, Y3 a& q. K3 e
' a4 J. h. s; ?1 m6 P0 D9 |9 W! c; }4 i
20200628134829920.png
* t9 C5 v3 b1 ?8 G/ w8 ]+ t$ P

! W1 e; k8 M( B7 hGraphView.cpp中放的是使用方法,文件夹graph_widget文件夹中放的是控件的源码,大家可以根据需要自己研究下该控件。
: m  k7 a- R! G1 q$ K) u# [4 l4 z, E7 s' a5 |0 c2 k% ?2 o

" v4 S$ x; N2 k- J7 w注意
' s+ L& \! m- M9 G本人在开发该部分控件时,会弹出buffer空间不足的提示框,原因则是在新建的工程中未添加画布空间的buffer,所以才会报错,具体请参照本章内容。# b4 L" `- w3 O2 N. K7 {
————————————————
5 S  i/ B# G0 j+ l7 G4 V版权声明:空闲的程序员
8 h( v# N* \* p8 `2 g, G0 W( ]& ~9 q5 y* Y' B' T. {
4 _( o% c) L0 d
收藏 评论0 发布时间:2023-3-5 12:09

举报

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