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

STM32 TouchGFX经验分享(五)

[复制链接]
STMCU小助手 发布时间:2023-3-5 12:09
Graph控件是用于实现图表以及需要绘制图像的控件,该控件在TouchGFX Designer中没有体现,但是可以在工程中自行添加,TouchGFX官方给的Demo中有针对于该部分的Demo,这些Demo可以给大家些帮助,下面针对于该控件给大家详细的介绍下。$ z9 u( O& t2 w" [# A
CanvasWidget; g3 j- K; Y6 o, E2 Y$ d
Canvas Widgets和Canvas Widget Renderer是TouchGFX的功能强大且用途广泛的附加组件,它使用相对较少的内存就可以提供流畅,抗锯齿的几何形状图形,同时保持了高性能。但是,渲染几何形状必须被视为相当昂贵的操作,并且如果不小心使用,很容易使微控制器资源紧张。
9 Y9 U1 `! F8 g* C3 ]* z' F% i* s" b; [7 m6 k) p
Canvas Widget Renderer(以下称为CWR)是一种通用的图形API,可为图元提供优化的图形,并自动消除大多数多余的图形。TouchGFX使用CWR绘制复杂的几何形状。几何形状由“画布小部件”定义。TouchGFX随附了许多受支持的Canvas Widget,但就像普通的Widget一样,您可以制作自己的自定义Canvas Widget来满足您的需求。当Canvas Widget定义要由CWR绘制的图形的几何形状时,图形内每个像素的实际颜色由关联的Painter类定义。同样,TouchGFX附带了许多Painter,但是您可以根据自己的需要制作自己的自定义Painter。, [) J# F  z2 w" ~) Q

' r: @3 S- A+ F% F' N& y% eTouchGFX中的其他小部件的大小会自动设置。例如,位图小部件将自动获取所包含位图的宽度和高度。因此,足以setXY()在位图小部件上使用以将位图放置在显示器上。" V. t) ]% H& `. Y: z3 w
画布小部件没有默认大小,该默认大小可以自动确定并进行初始设置。必须不仅要注意位置,而且还要正确调整窗口小部件的大小,否则“画布”窗口小部件的宽度和高度将为零,并且在显示器上不会绘制任何内容。. u. g5 c% i. ~
" d4 @- g* w3 `
因此,不要setXY()使用setPosition()来放置和调整画布小部件的大小,而要使用。另请参阅下面的“自定义画布小部件”,以获取有关如何创建和使用自定义画布小部件的示例。% @+ p* V% ^* T0 y6 `2 \

. Y! K( ^# e9 f3 T设置画布小部件的位置和大小后,便可以在其中绘制几何形状。坐标系将在小部件(而不是显示)的左上角具有(0,0),X轴向右延伸,Y轴向下延伸。
7 t: c; Q: l& O# j
! N5 {' D! l% lCanvas窗口小部件在TouchGFX Designer中也受支持,它使用法简单并具有自动内存分配功能。
: j6 h1 }  L- }4 T
7 n! N( Z8 s. Q' H! [* ^

$ @& F, M7 s% F1 r5 U/ W# T9 VCanvasWidget内存分配
0 e, b: O1 C- S$ ITouchGFX Designer中的内存分配( e, \" t0 u- k" l  Q  b1 W8 W
将小部件添加到屏幕的画布时,会自动生成一个内存缓冲区。缓冲区的大小基于具有以下公式的屏幕宽度(Width × 3)× 5。但是,这并非总是适用于所有情况的理想缓冲区大小。因此,可以覆盖缓冲区大小,如下图所示。
0 Y0 \) {# n4 e0 d7 F" y$ U) N8 {
20200628131939213.png 4 m' u, L; [- A+ A3 u6 [+ ~

: K' O: K' t2 r用户代码中的内存分配
) w# \+ c  e  ^
内存可被分配和设置在target/main.cpp与simulator/main.cpp或可以设置和每画面分配。
) a7 r0 f4 m' n
  1. static const uint16_t CANVAS_BUFFER_SIZE = 3600;
    8 p) f/ q! T" |# @; }$ o% e
  2. static uint8_t canvasBuffer[CANVAS_BUFFER_SIZE]8 _# }' V  [4 `0 l5 g4 \# v5 \$ T9 @
复制代码

! L# r5 O; v0 L0 p- p定义内存缓冲区大小的静态const,可以在main.cpp或的开头定义实际的内存缓冲区ScreenView.hpp
9 Z/ z/ M" l* }2 k( n& m; U然后,在任一main()的方法main.cpp或setupScreen()方法ScreenView.cpp中的以下行建立缓冲区可以被添加。" p% L, \( _2 O1 m/ U! i2 C0 I
  1. CanvasWidgetRenderer :: setupBuffer (canvasBuffer ,CANVAS_BUFFER_SIZE );
    ; L# |2 Q, n0 x5 _* F# w0 v
复制代码
# E" b3 U- b6 p8 ?, M
所需的CWR内存量取决于应用程序中要绘制的形状的最大大小。但是,您可以保留少于最大形状所需的内存。为了处理这种情况,CWR将形状的图纸分成较小的帧缓冲区部分,从而导致稍长的渲染时间,因为在这种情况下,有时不得不多次渲染形状。在模拟器模式下运行时,可以更仔细地调查和微调内存消耗。只需将以下函数调用添加到main.cpp中:
. i! t% m. v; T2 ^
  1. CanvasWidgetRenderer :: setWriteMemoryUsageReport (true );: e/ A, r) Y: I5 N( G  E5 _. r0 x3 d
复制代码
  Z2 F5 V" T% ]9 A0 Q
现在,每当绘制操作完成时,CWR都会报告(在控制台中打印)需要多少内存。对于canvas_widget_example,它可能是“ CWR需要3604字节”(对于第一次绘制操作),然后是“ CWR需要7932字节(缺少4328个字节)”(对于第二次绘制操作)。即使CWR似乎没有足够的内存(在这种情况下,缺少4328字节),应用程序也可以正常运行。这是因为CWR检测到可用内存太少,无法在一次运行中完成复杂的绘制操作。取而代之的是,它将绘制操作分为两个单独的绘制操作,形状将被绘制得很好,但需要更多时间进行渲染。& V7 E* K8 X. }4 M% ^4 A. c
& z0 z, E7 V& P6 h
因此,设置正确的内存缓冲区大小需要在内存和性能(渲染时间)之间进行权衡。好的起始值通常约为3000,但是使用上述技术,通常可以确定更好的值。如果形状太复杂而分配的内存缓冲区太小,则将不会绘制形状的一部分(某些垂直像素线将被跳过),并且有可能根本没有绘制。无论如何,渲染时间都会增加很多。7 j% T0 ^  c5 l  z0 N
; c6 U# V' p; Z
这意味着,如果您希望您的应用程序以最快的速度渲染CWR图形,则需要分配请求的内存量。但是,如果可以使用较慢的渲染计时器,则可以减少内存缓冲区。) G7 E" _$ E  b

2 ]4 w7 K7 X) q0 I* U2 j

- O9 V; q; B8 y: nCWR坐标系" f9 o" j' W" h
TouchGFX中的坐标系通常用于寻址像素,以在显示器上定位位图。位图,文本和其他图形元素都放置在坐标系中,其中(0,0)是左上像素,x轴向右延伸,y轴向下延伸。在CWR中,仅能够使用整数寻址像素是不够的,尽管在特殊情况下这可能就足够了,但通常这还远远不够。为了说明这一点,请考虑一个线宽为1的圆,它必须恰好适合5 x 5像素的框。该圆的中心必须位于(2.5,2.5),半径必须为2,因此中心坐标需要分数。同样,如果圆应适合于6 x 6像素的框,则中心必须位于(3,3),半径必须为2.5,因此此处半径必须为分数。4 S4 ]6 c4 d( g. I

( e: ~. k% ]* w0 U这种寻址图形坐标的新方法意味着(0,0)的像素中心具有CWR坐标(0.5,0.5)。因此,包含在屏幕左上角像素的框具有以下轮廓:(0,0)->(1,0)->(1,1)->(0,1)->(0 ,0)。
8 I5 _) y8 \! D
0 x# h( M0 a4 w! h
20200628132621835.png
& J- O( J0 N: s" O
2 |- z0 P7 D( p; k, k  x9 t+ C
尽管起初看起来似乎很混乱,但很快就变得很自然。当位图的坐标系统处理像素时,画布小部件的相同坐标处理像素之前和上方的间隙。
: O$ }, S# g7 ^! I) I
! b  p) a# V5 X$ M8 ]) q
8 [' R- m1 Y3 J9 f& K' g
点、线、面
( t9 i# G' a1 W3 s* X当创建完画图的画板后就可在其中创建图像, TouchGFX的图像不支持线性的曲率变化,换句话说就是只能通过点去链接线,但是如何能做到曲率的线呢,这时我们可以通过贝塞尔算法,或者其他的曲率算法推算出每一个点的坐标,或者是相隔固定点的坐标,之后在界面上显示出来。
* J2 R; p/ T3 u( B7 ~" l0 N1 Q8 ^官方提供了两个demo 分别是TouchGFX Demo1 和TouchGFX Demo2,两个demo中都有graph绘制方法的体现,但体现的方法不同,对用CanvasWidget继承程度也不尽相同。: W! Q1 Z9 c$ c8 s, u1 v! H, {
这里我们讲解个内容比较多的TouchGFX Demo1,我会在下一节提出代码。
6 ?1 O* S- ^. q+ y. v( H) i& @4 ~/ \# O$ l# \+ U

+ v8 k7 W% E0 j% x7 `Demo

9 K$ a  U( Q7 |/ K% C5 U% P首先先看一下该部分代码的调用的方法。
. M) X' x  j. m6 D
  1.     primaryGraph.setXY(graphXOffset, graphYOffset);4 ^# h) |/ E$ Y5 \2 ~7 a
  2.     primaryGraph.setup(graphWidth, graphHeight, Color::getColorFrom24BitRGB(0x24, 0x73, 0xAC), graphBackground.getColor());- v% a% y9 D! _
  3.     primaryGraph.setDotShape(0, 30, 5);//设置折线上点的参数
    ! }5 b. }. b& R1 {. p2 |
  4.     primaryGraph.setDotBackgroundShape(0, 30, 7);//设置折线上点的背景参数& S/ s6 w/ n+ M/ K/ h1 L
  5.     graphArea.add(primaryGraph);4 V7 O6 v; E. u6 b. T1 o6 O7 f
复制代码
( @% `' f3 U0 ?9 j
在此可以看出graph作为一个控件调用方法是一致的,都是先设置x,y的位置,也需要设置画布的宽高,以及转向点或者其他的设置。
8 x5 C7 H" E; S* {+ `5 X- f这里唯一的不一样的地方是setup()函数,这个函数中除了宽高外还有颜色,颜色的含义则是画笔的颜色,想要作画除了画布同样还需要画笔。至于化成什么样子则是我们掌握的,下面看下setup函数。
5 S% I; S  x7 |5 G1 A
  1. void Graph::setup(int newWidth, int newHeight, colortype lineColor, colortype backgroundColor)1 l1 d' K2 E  \  R
  2. {
    0 e4 v7 g& F# X
  3.     setWidth(newWidth);1 l% B' |1 {- z
  4.     setHeight(newHeight);8 J5 @* T  |" [
  5. 2 B" x5 V4 H. A. j; E
  6.     graphLinePainter.setColor(lineColor);
    $ |* O" A" R2 l- v1 D, l1 B* }! Q
  7.     graphAreaPainter.setColor(lineColor, 255);
    . x0 W( s6 `3 N& N: ?% i+ C$ O, B7 d
  8.     graphDotsPainter.setColor(lineColor);
    : ^( b% U/ }) t* H
  9.     graphDotsBackgroundPainter.setColor(backgroundColor);$ N/ N6 J' i  l$ q

  10. ) r% V' K  z- e0 s+ s
  11.     graphLine.setLinkedGraph(graphArea);
    " ~5 q1 W" R" r  I7 y! u2 R1 O
  12.     graphLine.setLinkedGraph(graphDots);0 C) q1 d' [9 F. n0 v/ B6 U
  13.     graphLine.setLinkedGraph(graphDotsBackground);6 M+ ]+ ^. ^, |; x. o8 v" b
  14. $ S! E5 ?+ }/ K2 M7 r, D" V
  15.     graphLine.setPosition(0, 0, getWidth(), getHeight());: |8 d# y, E( S, Q& ]# a% Z
  16.     graphLine.setPainter(graphLinePainter);3 j' A* b/ }0 f: }
  17.     graphLine.setBuffer(graphBuffer, NUMBER_OF_POINTS);) ^2 y! ~$ _3 E0 ]( W; i  j
  18.     graphLine.setLineWidth(1);3 Y# t9 z0 }( B8 A  x- c/ N
  19.     graphLine.setRange(-2, 216, 400, 0);
    8 r! d0 v9 J4 [% g
  20. ( ?+ N- e, W" ^0 n! Z
  21.     graphArea.setPainter(graphAreaPainter);
    4 C' D5 G- V$ @! X  {; ~( R
  22.     graphArea.setLineWidth(0);
    7 g/ v5 v% U& E+ D* X

  23. 0 j& F  E8 [7 s5 |$ s: u- D! d  @
  24.     graphDots.setPainter(graphDotsPainter);
    & a6 L+ F# [# V/ d$ k, D
  25.     graphDots.setLineWidth(7);
    ' D2 u8 T4 _9 r% t/ ~8 |( p& X
  26.     graphDots.setDotShape(0, 90);
    8 P/ |) R% ]$ }1 s: d$ a: ~
  27. % \8 q% I3 q$ X1 i+ i$ L
  28.     graphDotsBackground.setPainter(graphDotsBackgroundPainter);
    + Z: [7 |7 m- t' R8 N) Q6 K
  29.     graphDotsBackground.setLineWidth(9);  w, S  b% W) |( L& |
  30.     graphDots.setDotShape(0, 30);) q7 }6 P5 T' u$ B
  31. 7 j) e- R- r) ?' \: S7 M. |0 Z- V
  32.     add(graphArea);9 P! y& {3 x( l: q
  33.     add(graphLine);
    % J- \/ N2 x7 [6 }
  34.     add(graphDotsBackground);
    # j8 H  l+ w# c. N% p
  35.     add(graphDots);* N9 C; r7 a' ~% b
  36. }( {8 n0 E: g: I4 d# o5 c$ E
复制代码
: j) x. t; {& `& v. i
可以看出,整体采用的画笔以及将其画布放置在一起的过程。
1 Z2 {/ M! ?, N' g/ U; d除此之外还有些细节的这里不将展示。需要可以参考官方的代码,我将具体的部分放到这里供大家快速定位。
* E( C& @3 r5 g7 ]
# D! I  l! J% p/ Y/ o1 o
20200628134829920.png
% B  ~" y. R& d6 d5 f

4 V9 [# z' ?8 o+ M5 h$ b% M0 m( I( IGraphView.cpp中放的是使用方法,文件夹graph_widget文件夹中放的是控件的源码,大家可以根据需要自己研究下该控件。
9 q# @* Q) |$ m. H$ S- H
# c* I) b% G3 ?* `
; @( o/ c& P, j1 R8 K1 B, v6 H
注意
6 V2 \& I- ?8 l本人在开发该部分控件时,会弹出buffer空间不足的提示框,原因则是在新建的工程中未添加画布空间的buffer,所以才会报错,具体请参照本章内容。( ^! |- v6 B" Y4 w6 t
————————————————
+ b* S2 [7 |+ f" i. l版权声明:空闲的程序员) g% [( `( H. m9 `) S# x" V
* c+ u( j2 O8 m' X
6 Q' q3 _! [  V$ F
赞 收藏 评论0 发布时间:2023-3-5 12:09

举报

0个回答

所属标签

相似技术帖

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版