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

STM32 TouchGFX经验分享(五)

[复制链接]
STMCU小助手 发布时间:2023-3-5 12:09
Graph控件是用于实现图表以及需要绘制图像的控件,该控件在TouchGFX Designer中没有体现,但是可以在工程中自行添加,TouchGFX官方给的Demo中有针对于该部分的Demo,这些Demo可以给大家些帮助,下面针对于该控件给大家详细的介绍下。
% u* q; m4 n3 |# X! R4 T" \
CanvasWidget/ q' O$ Y: o2 X2 c/ g( n
Canvas Widgets和Canvas Widget Renderer是TouchGFX的功能强大且用途广泛的附加组件,它使用相对较少的内存就可以提供流畅,抗锯齿的几何形状图形,同时保持了高性能。但是,渲染几何形状必须被视为相当昂贵的操作,并且如果不小心使用,很容易使微控制器资源紧张。
( `) ~$ H- K" N/ x3 L. l4 I, n
( r4 Z8 R* s8 v2 v: H- k! f' ECanvas Widget Renderer(以下称为CWR)是一种通用的图形API,可为图元提供优化的图形,并自动消除大多数多余的图形。TouchGFX使用CWR绘制复杂的几何形状。几何形状由“画布小部件”定义。TouchGFX随附了许多受支持的Canvas Widget,但就像普通的Widget一样,您可以制作自己的自定义Canvas Widget来满足您的需求。当Canvas Widget定义要由CWR绘制的图形的几何形状时,图形内每个像素的实际颜色由关联的Painter类定义。同样,TouchGFX附带了许多Painter,但是您可以根据自己的需要制作自己的自定义Painter。
! l( F+ |& t1 A/ G
# ]- [5 D/ N$ p0 p1 G8 {( Z% N5 O
TouchGFX中的其他小部件的大小会自动设置。例如,位图小部件将自动获取所包含位图的宽度和高度。因此,足以setXY()在位图小部件上使用以将位图放置在显示器上。0 W$ X) Q9 v& Q( s: G. A9 T
画布小部件没有默认大小,该默认大小可以自动确定并进行初始设置。必须不仅要注意位置,而且还要正确调整窗口小部件的大小,否则“画布”窗口小部件的宽度和高度将为零,并且在显示器上不会绘制任何内容。$ M0 ^% o' R8 n/ Z2 x
7 w/ H" `# n8 g' `9 n
因此,不要setXY()使用setPosition()来放置和调整画布小部件的大小,而要使用。另请参阅下面的“自定义画布小部件”,以获取有关如何创建和使用自定义画布小部件的示例。
6 l' o; }2 Z/ u
5 S+ P5 a1 Z7 ]# }% q设置画布小部件的位置和大小后,便可以在其中绘制几何形状。坐标系将在小部件(而不是显示)的左上角具有(0,0),X轴向右延伸,Y轴向下延伸。
7 _6 R7 o! q  F2 g$ B/ ]9 a! S& r% n/ O
Canvas窗口小部件在TouchGFX Designer中也受支持,它使用法简单并具有自动内存分配功能。* e# Z& J# u/ g6 Q1 f/ v! h

5 Q, t& F5 v/ m7 V" ^, _8 |5 E

! x  i& X5 V2 ^$ `3 ]" \3 WCanvasWidget内存分配4 l* s' Y3 W! S5 z# c8 E* k! R3 G7 w
TouchGFX Designer中的内存分配
% m2 X9 L  S' x5 r! {将小部件添加到屏幕的画布时,会自动生成一个内存缓冲区。缓冲区的大小基于具有以下公式的屏幕宽度(Width × 3)× 5。但是,这并非总是适用于所有情况的理想缓冲区大小。因此,可以覆盖缓冲区大小,如下图所示。
7 E5 p( L1 e7 g  }8 X+ z/ r" R0 {; {7 ]  @- h) J' ?$ W; M, E
20200628131939213.png
+ n9 Y' b# M9 B+ @/ r
, y, I/ h1 E0 h' W9 X* i) ]用户代码中的内存分配
$ E7 q& L/ E' w# w
内存可被分配和设置在target/main.cpp与simulator/main.cpp或可以设置和每画面分配。
3 ^( ]4 ^$ I2 C$ G9 ?1 f, y
  1. static const uint16_t CANVAS_BUFFER_SIZE = 3600;; G. q/ g5 k$ W( D, p$ d
  2. static uint8_t canvasBuffer[CANVAS_BUFFER_SIZE]: P/ w' F+ R, R) o) c& A# q2 v$ ]2 P
复制代码

) q+ T2 Z" C7 X  J定义内存缓冲区大小的静态const,可以在main.cpp或的开头定义实际的内存缓冲区ScreenView.hpp
1 L4 w! L$ I' r, m; u7 b7 Q* M( e然后,在任一main()的方法main.cpp或setupScreen()方法ScreenView.cpp中的以下行建立缓冲区可以被添加。: Q( |8 y* \% y. z( F; {
  1. CanvasWidgetRenderer :: setupBuffer (canvasBuffer ,CANVAS_BUFFER_SIZE );
    4 K. E+ O2 S8 S+ E2 `
复制代码

4 G7 Y! y, d- i) i所需的CWR内存量取决于应用程序中要绘制的形状的最大大小。但是,您可以保留少于最大形状所需的内存。为了处理这种情况,CWR将形状的图纸分成较小的帧缓冲区部分,从而导致稍长的渲染时间,因为在这种情况下,有时不得不多次渲染形状。在模拟器模式下运行时,可以更仔细地调查和微调内存消耗。只需将以下函数调用添加到main.cpp中:
& h7 O3 D1 E  w
  1. CanvasWidgetRenderer :: setWriteMemoryUsageReport (true );* }0 Q9 C, v5 T7 ?4 p  \4 a' K
复制代码
3 v# |6 ^2 {- L6 k9 c+ ]
现在,每当绘制操作完成时,CWR都会报告(在控制台中打印)需要多少内存。对于canvas_widget_example,它可能是“ CWR需要3604字节”(对于第一次绘制操作),然后是“ CWR需要7932字节(缺少4328个字节)”(对于第二次绘制操作)。即使CWR似乎没有足够的内存(在这种情况下,缺少4328字节),应用程序也可以正常运行。这是因为CWR检测到可用内存太少,无法在一次运行中完成复杂的绘制操作。取而代之的是,它将绘制操作分为两个单独的绘制操作,形状将被绘制得很好,但需要更多时间进行渲染。
  H0 |2 C" m6 ~

  r" d6 |% l1 q3 ^0 |  F因此,设置正确的内存缓冲区大小需要在内存和性能(渲染时间)之间进行权衡。好的起始值通常约为3000,但是使用上述技术,通常可以确定更好的值。如果形状太复杂而分配的内存缓冲区太小,则将不会绘制形状的一部分(某些垂直像素线将被跳过),并且有可能根本没有绘制。无论如何,渲染时间都会增加很多。4 D9 ]) p5 O  N

; W; q! Q; j7 k$ k' E& K+ u; |这意味着,如果您希望您的应用程序以最快的速度渲染CWR图形,则需要分配请求的内存量。但是,如果可以使用较慢的渲染计时器,则可以减少内存缓冲区。7 d0 ?( G" y' F9 u: R( A
" k3 }" g& I, c+ D3 F
& U$ R* ~' Z2 D2 O" r
CWR坐标系- d/ |6 W$ e; Y! H6 c
TouchGFX中的坐标系通常用于寻址像素,以在显示器上定位位图。位图,文本和其他图形元素都放置在坐标系中,其中(0,0)是左上像素,x轴向右延伸,y轴向下延伸。在CWR中,仅能够使用整数寻址像素是不够的,尽管在特殊情况下这可能就足够了,但通常这还远远不够。为了说明这一点,请考虑一个线宽为1的圆,它必须恰好适合5 x 5像素的框。该圆的中心必须位于(2.5,2.5),半径必须为2,因此中心坐标需要分数。同样,如果圆应适合于6 x 6像素的框,则中心必须位于(3,3),半径必须为2.5,因此此处半径必须为分数。1 R/ B7 ?- L2 x! E7 u( V. Q3 K

& Y5 G* S/ H% J/ [这种寻址图形坐标的新方法意味着(0,0)的像素中心具有CWR坐标(0.5,0.5)。因此,包含在屏幕左上角像素的框具有以下轮廓:(0,0)->(1,0)->(1,1)->(0,1)->(0 ,0)。4 e* }" n8 H8 c: I
6 r7 O* ]% ^8 V. j3 _
20200628132621835.png ) J9 P* `& S. @
6 J) {& s" N3 `! E
尽管起初看起来似乎很混乱,但很快就变得很自然。当位图的坐标系统处理像素时,画布小部件的相同坐标处理像素之前和上方的间隙。
# d0 q- V9 d! h! ], h' K3 F# v" D4 v/ J
. F( e7 C9 o; J/ }, U% ?+ V
点、线、面
: D- U  x. g- T8 v0 p当创建完画图的画板后就可在其中创建图像, TouchGFX的图像不支持线性的曲率变化,换句话说就是只能通过点去链接线,但是如何能做到曲率的线呢,这时我们可以通过贝塞尔算法,或者其他的曲率算法推算出每一个点的坐标,或者是相隔固定点的坐标,之后在界面上显示出来。$ Q- _, c+ \4 }( `5 `; g7 O
官方提供了两个demo 分别是TouchGFX Demo1 和TouchGFX Demo2,两个demo中都有graph绘制方法的体现,但体现的方法不同,对用CanvasWidget继承程度也不尽相同。
- b, N  n" }. ~, f' ^* f这里我们讲解个内容比较多的TouchGFX Demo1,我会在下一节提出代码。
, k" e  \! v+ _4 S+ a
* c! b% _! k4 A( u( t& w3 Y  {

9 ?! n' N, u, S1 ^: K- T& B& IDemo
7 T3 }' W, I/ R' |
首先先看一下该部分代码的调用的方法。
. E4 `! A  L8 w4 Y
  1.     primaryGraph.setXY(graphXOffset, graphYOffset);* ?: @) Y2 o# v1 j  S5 D' [
  2.     primaryGraph.setup(graphWidth, graphHeight, Color::getColorFrom24BitRGB(0x24, 0x73, 0xAC), graphBackground.getColor());+ }: j4 {1 G- F4 f
  3.     primaryGraph.setDotShape(0, 30, 5);//设置折线上点的参数
    ( @( G8 u4 ]8 Q) g- l: O% Y
  4.     primaryGraph.setDotBackgroundShape(0, 30, 7);//设置折线上点的背景参数* y9 u) h* {/ P8 e
  5.     graphArea.add(primaryGraph);' k! n; L+ u* ?8 v8 ~( f* Y  m
复制代码

- d5 e- I& p; l& K$ T在此可以看出graph作为一个控件调用方法是一致的,都是先设置x,y的位置,也需要设置画布的宽高,以及转向点或者其他的设置。
& ?- ~0 ?. M/ F8 X# L这里唯一的不一样的地方是setup()函数,这个函数中除了宽高外还有颜色,颜色的含义则是画笔的颜色,想要作画除了画布同样还需要画笔。至于化成什么样子则是我们掌握的,下面看下setup函数。% G; A$ K# ^2 i/ w: d; B# D1 L6 ?
  1. void Graph::setup(int newWidth, int newHeight, colortype lineColor, colortype backgroundColor)2 M: i- e9 z$ C" q
  2. {
    5 Y; B+ u0 R- M
  3.     setWidth(newWidth);% @: n/ t4 ~/ A5 t
  4.     setHeight(newHeight);
    8 T0 q$ [: o' K

  5. : l2 f9 w/ s# C7 \# b4 Y
  6.     graphLinePainter.setColor(lineColor);
    8 I4 ?$ X3 g- B% N
  7.     graphAreaPainter.setColor(lineColor, 255);" [) k9 A; ]0 o" D- C: s, E
  8.     graphDotsPainter.setColor(lineColor);
    8 X8 g$ \" B- p& V/ k; |8 r5 T
  9.     graphDotsBackgroundPainter.setColor(backgroundColor);
    " c- @, i4 A, p; G
  10. + H9 X- y! V0 x
  11.     graphLine.setLinkedGraph(graphArea);
    / C* |7 n9 L. l" A& f
  12.     graphLine.setLinkedGraph(graphDots);
    . ^3 s% x+ k/ z2 @9 Z6 p% b6 d
  13.     graphLine.setLinkedGraph(graphDotsBackground);
    , O" i6 u6 q" V
  14.   t( q& U% `% I( n/ K0 X- D
  15.     graphLine.setPosition(0, 0, getWidth(), getHeight());
    ! I2 u9 h' A: w: F  p% Q* X
  16.     graphLine.setPainter(graphLinePainter);
    4 `' p+ h' }; K7 @$ C
  17.     graphLine.setBuffer(graphBuffer, NUMBER_OF_POINTS);
    ( c7 I4 g- U5 ?5 t7 z) n
  18.     graphLine.setLineWidth(1);9 j- Z+ t' U* Q5 B% k1 s3 w' h
  19.     graphLine.setRange(-2, 216, 400, 0);
      A; ]0 \$ U- Z8 h4 c6 ?3 m9 y

  20. : ?7 ^/ I0 ^. V
  21.     graphArea.setPainter(graphAreaPainter);
    & ^& ~2 q( ^# \3 b( U
  22.     graphArea.setLineWidth(0);
    + {% b- Q/ D4 w# [# Q* r

  23. $ [: ]# [4 ]" |. `& ^
  24.     graphDots.setPainter(graphDotsPainter);
    : f3 o( Z6 y  k/ [9 F' i( R
  25.     graphDots.setLineWidth(7);" M' h" s8 d' r4 x
  26.     graphDots.setDotShape(0, 90);: w" T5 R3 e+ c7 a

  27. 9 O) w8 j) c  R( d2 [7 i9 N0 `/ U
  28.     graphDotsBackground.setPainter(graphDotsBackgroundPainter);$ i4 `4 x. A/ k/ z, x5 F9 Y% P
  29.     graphDotsBackground.setLineWidth(9);
    , Z6 ~- J2 e7 Q: l, w: Y- L' h
  30.     graphDots.setDotShape(0, 30);* E0 q- w  i0 T& C

  31. & ?& a; K  T# Z) |4 m5 S
  32.     add(graphArea);6 H) f) S8 U5 C3 B! h( T9 c7 ^
  33.     add(graphLine);
      @. T: ~- `0 U$ A0 _! a: E: ?
  34.     add(graphDotsBackground);
    ) b1 e/ L+ I: v, t; c
  35.     add(graphDots);& {" E  y2 w& ?- W" L
  36. }7 L/ d: F' P* \) M: y/ a$ {+ \
复制代码
; [" k4 x8 j; f0 F9 l) S
可以看出,整体采用的画笔以及将其画布放置在一起的过程。
7 B' S" b" X' Z除此之外还有些细节的这里不将展示。需要可以参考官方的代码,我将具体的部分放到这里供大家快速定位。
$ a% v1 U8 a6 B7 T% p2 l4 f# ~8 Q* {% k9 I1 A' @5 k+ ?
20200628134829920.png
; m/ J; ~) X! e% A, Z6 Q8 V$ x  ^
/ Z: O. h+ d& n& Y2 M- V
GraphView.cpp中放的是使用方法,文件夹graph_widget文件夹中放的是控件的源码,大家可以根据需要自己研究下该控件。
+ d! l5 C3 q9 L& R$ }8 W8 S9 c1 R/ b8 g% T5 [5 g9 L: z
7 O, s( H& U5 K; t3 T
注意- `3 R% `; N  B* E) g. e4 Q- @
本人在开发该部分控件时,会弹出buffer空间不足的提示框,原因则是在新建的工程中未添加画布空间的buffer,所以才会报错,具体请参照本章内容。
- q; }% \% H6 R+ G————————————————0 H8 _% \- S" g5 e
版权声明:空闲的程序员4 a# g& t3 S9 }
9 d% ~6 ?+ Z" k3 l, q& z

  Y4 p( B( ~! M" v  K5 X
收藏 评论0 发布时间:2023-3-5 12:09

举报

0个回答

所属标签

相似技术帖

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