1. 前言$ s0 G. Z0 b3 ~9 I
客户在做 GUI 项目开发时,会碰到许多显示相关的问题。而屏幕花屏是比较严重的问题,如果产品出现花屏,会严重影响使用体验。正常的 UI 显示,包括画图和将帧缓冲传输到屏幕两个过程。如果画图过程出错,那帧缓冲的内容就不对了,传输到屏幕后显示也不对。而如果画图过程正确,而传输过程出错,虽然帧缓冲的内容是对的,而屏幕接收到的数据出错了,那屏幕显示的内容自然也是错误的。因此在定位显示错乱、花屏等问题时,需要先判断是画图出错还是传输过程出错。本文会介绍一种方法,将帧缓冲内容导出并保存为图片,来判断帧缓冲内容是否正确。8 E: W% f' S* `
由于帧缓冲的内容为全部像素的 RGB 数据,在导出后需要借助其它工具将 RGB 数据转换为图片,方便在 PC 上直接查看。这里可以使用 python 的 pillow 包,将 RGB 转换为JPG 图片。而读取帧缓冲的过程可以借助 python 的 pyswd 包来实现。0 d1 M5 I3 u% F: Q+ y! T3 `' g
2. 环境安装
% c" k/ n' i: m. D, {4 [4 S# [ 本机环境:Windows10 64bit安装 python 3.9.0 (测试过 3.9.6 正常),检查系统环境变量,将 python 添加到 Path中。4 Q# i. e# r6 q+ z5 v/ Q+ R
+ v4 \1 h" m0 K w
图1. 设置环境变量
3 A. s+ m! z7 ]
& b: m4 t1 {1 G( I* n
; j. F( `; x5 G5 j3 T
6 J( C7 m: `# c: b6 d
2.1. 安装 libusb8 a' O% C0 w" b+ F" Q
解压 libusb-1.0.24.7z,将 VS2019/MS64/dll/libusb-1.0.dll 拷贝到 python 安装目录,与 python.exe 相同目录
3 Y! q0 {( J* l- L2.2. 安装 pyusb
$ y4 g5 C; M# j' y$ O# ?打开 powershell,输入 pip install pyusb-1.2.1-py3-none-any.whl
8 f. B% o1 {* q. t( a; j. l) c2.3. 安装 Pillow
% ^2 W# [0 G) B; w打开 powershell,输入 pip install Pillow-8.3.1-cp39-cp39-win_amd64.whl' E* e" h" g; H
2.4. 安装 pyswd
6 a! V' N' }# W# P* ~/ y解压 pyswd-1.0.0.zip,打开 powershell,进入 pyswd-1.0.0 目录,输入 python setup.py install
$ \( C7 D0 N b% L! g
; A7 E3 k) k# N* G8 G$ _5 l+ f* K3. 将帧缓冲保存为图片
: t$ p1 V* W( P3.1. 读取帧缓冲' n$ A$ w; z* f& q
通过 pyswd,可以创建 stlink 对象连接到目标板,用 read_mem 函数来读取目标板内存。
2 F& u" l" s8 l. |3.2. 保存图片$ p3 @& k8 h8 m7 k3 z8 O
从 Pillow 包导入 Image 类,创建图片对象,将 pyswd 读取的 framebuffer 数据填充到 image 对象中并保存实现代码如下,保存为 pyswd_rgb2jpg.py 文件。 0 \% {- _7 ?: P$ H; _
- #!/usr/bin/env python# W" [0 a1 u, u& N2 H
- # pyswd_rgb2jpg.py fb_addr width hight bpp output_name
2 c# _. Q# c) L) @+ {/ C - # pyswd_rgb2jpg.py 0x20000090 390 390 3 abc.jpg, a4 ~7 Z6 ]+ h$ \* Q
- import swd) w5 R' a" x# K8 Y1 |/ t" a
- from PIL import Image$ _- J& b; S4 t: U7 m
- #from io import BytesIO
8 @) m' `9 n) O: G, @$ v - import sys
. R* H5 d/ F4 R8 L' ^ - w = int(sys.argv[2]) #390
9 B9 @" z( s5 _. K: H4 Z, Q - h = int(sys.argv[3]) #390* t: b# `; F9 ? [5 C
- bpp = int(sys.argv[4])
* F& @; E4 R0 ~ - size = w * h * bpp #RGB565/RGB888
8 [( k( f. s5 N8 z - fb_addr = int(sys.argv[1], 16) #hex# m/ [% G# f, O6 X, Y- M
- dev = swd.Stlink(swd_frequency=1800000, logger=None)! S4 g- f; g& p9 D# J' L
- im_data = dev.read_mem(fb_addr, size)" V5 R+ @( q8 j$ ]( n0 a
- im_list = list(im_data)- I% z Q, E8 j! C
- ## test rgb565.dta file, RGB offset = 16
$ f6 c0 K& i7 M0 Q% A2 U - ## pyswd_rgb2jpg.py rgb565.dta 100 100 2 rgb565.jpg
! @! A4 s# d7 Y, Y' q - #with open(sys.argv[1], 'rb') as f:
. h% l4 J d4 Y2 Y - # f.seek(16). i3 C9 W6 ]" \/ w3 C
- # data = f.read()
% U+ E9 }) }( Z7 D2 A$ u$ t% O& w - # #print(data)6 A* X7 L4 o1 E8 h
- #im_list = list(data)' l; y8 Z; c/ f3 j4 i& {3 R
- im = Image.new("RGB", (w, h))& {9 X9 \ h! E5 b8 w) Z9 y" q" L
- if bpp == 3:+ K- t j1 [, Q
- for j in range(0, h):& i( p3 ?+ R" p' z9 l. `6 P
- for i in range(0, w):
1 W+ g8 n6 j: ~1 m- R( j! h - im.putpixel((i, j), (im_list[(w*j + i)*bpp + 2], im_list[(w*j + i)*bpp + 1], im_list[(w*j + i)*bpp]))% g( b7 n1 j3 ~ c1 q
- elif bpp == 2:
3 {' j9 L% ?' o9 \/ C - for j in range(0, h):
& i# k9 t7 K4 f2 R1 m) {6 g' y - for i in range(0, w):9 j- q2 P2 g6 Q6 N6 k3 R1 ~
- r = im_list[(w*j + i)*bpp + 1] >> 35 j& R5 b' G3 H! a! t, C
- g = ((im_list[(w*j + i)*bpp + 1] & 0x07) << 3) | (im_list[(w*j + i)*bpp] >> 5)4 y% h. r2 X+ M# @8 C
- b = im_list[(w*j + i)*bpp] & 0x1f
. u/ N5 b5 Q) m" u - r = round((r * 255) / 31.0)6 j- q- }9 _: y+ p" H
- g = round((g * 255) / 63.0)- l6 l; y6 O' Y/ ^3 ]) }
- b = round((b * 255) / 31.0)
7 R: V# J Q0 N K+ N5 l; @ - im.putpixel((i, j), (r, g, b))
+ A4 ] c* c1 R& y0 k7 c - #print("%02x %02x %02x" %(r, g, b))* ?7 @. Z$ Q! I
- else:
8 Z/ `9 A8 I- n& d - print("bpp error")( X. f: X$ Q. T6 A
- im.save(sys.argv[5])
复制代码 5 E! d5 Q, O; s. }" }6 e! ^
! ?* @" b, v" [2 ^4 f; a, {
4. 测试及小结5 C3 W1 i# `. c0 z
在 L4R9-DK 板上进行了测试
0 W7 @* [! Q3 M8 n3 T4 P, q(1) 无 GFXMMU 时,传给脚本的参数,宽和高为实际 LCD 的宽和高将 GUI 固件烧录到开发板后,屏幕正常显示。在工程 map 文件中,找到 framebuffer 地址。打开 powershell,执行 pyswd_rgb2jpg.py:
4 P \ `! k/ w' F+ _% w6 R0 t
4 x' o; d/ G# z; s' U9 t H0 c
3 `0 D, W8 O% k, o; V/ K
* i) x; C$ w3 N5 c0 x+ I! K* {3 T
" `* z( ~4 J2 U; j(2)开启 GFXMMU 时,framebuffer 对应为虚拟地址,传给脚本的参数中,图像的宽度要用GFXMMU 像素宽度' n' }& M* t8 t/ t$ U
图4. L4R9-DK 开启 GFXMMU 测试
9 E* H J U9 }3 o0 j# O+ i" W, x! F3 P6 L$ k3 R$ y
2 Q3 ^0 t1 Z9 K: M4 {, v+ X
5 i* l& v$ V9 E7 R5 P) e4 u
4 W8 d) E z( D$ D/ b: O
: b+ X. ^1 t& h( o0 f/ G6 M
) `% y$ w0 v1 n4 I4 e完整版请查看:附件
! y. |9 B! t- j+ [3 L- M! a2 M4 j) W$ H7 U @* e8 j/ C" L
|