前言
, E) r6 K0 o, @/ u% W! i在一些图形界面应用中,系统架构会时常考虑使用 FSMC 接口来驱动 8080 接口的 LCD 屏。在 MCU 渲染完成,将framebuffer 发送到 LCD 时,有可能会遇到存儲大小端的问题。STM32 MCU 都采用 ARM Cortex 内核,内存使用小端格式。而 intel 8080 接口的 LCD 在传输 RGB 数据时,使用的是大端格式。MCU 在传输 RGB 数据时,字节序有可能不匹配。
+ d4 T' Q- L, l0 e2 |. E在图形界面应用中,像素格式一般会使用 RGB888 或 RGB565。而在使用 FSMC 接口驱动 8bit 位宽的 LCD 时,很可能会使用 RGB565 格式。本文中会介绍两种方法来处理 8bit 8080 接口 RGB565 格式图像数据字节序问题。
4 e- t( @5 _( D c! w9 y
% z( P- E' D- k$ o9 E l! P7 m* r$ Z: [ g/ ^* J
图像数据字节序 2 a4 V! ]( y" B/ ~; W
8bit 8080 LCD 在接收 RGB565 数据时,会将第一个字节解析为{R4-R0,G5-G3}, 第二个字节解析为{G2-G0,B4-B0},并按这个顺序接收所有数据,如下图:3 e# {# D4 Z& K6 n
M- I) _7 \6 C T N
7 T0 @: I) }" W4 |
) b& h* c9 D! N2 R7 g8 J+ q6 \+ Q M/ }# V- o8 c/ k
5 o3 j0 T1 d% y. _* e
而在 MCU 内存中,数据按小端格式存放,RGB565 数据存放的字节序如下:
4 n; x" T s2 X6 l5 ?( J9 e% T- e; T1 M' r8 t
6 {5 {' y# k% T4 |. g, r
/ {, B5 R- C! c& n5 S9 q& {
( {& W5 ~" M9 l. B- M$ N/ ~: O) p9 ?8 v5 P4 m
# ]0 E! V. G* f! K0 Y7 o8 i) Y' b
# ?& Q+ h4 h, T如果不做调整直接将 RGB 数据发给 LCD,MCU 会先发送 Byte 0,再发送 Byte 1,这样 LCD 显示的内容就会错乱。对比上面两张图可以看出,只需将内存中 Byte 0 与 Byte 1 字节位置对调(Byte swap),就能满足 LCD 接口的字节序要求。可以直接使用 CPU 进行 Byte swap,但这会消耗过多 CPU 算力,同时也会占用更多内存。这里我们将使用硬件进行 Byte swap.
- d. d2 t; i8 l% L
2 S% }' ]# [% L/ m6 p+ i7 D% }" {( G8 e1 k" v8 o$ \
DMA2D 进行 Byte swap
3 _6 J" Q" g) ?0 }9 e7 O: u0 |DMA2D 是 ST 为图形应用专门设计并优化的 2D 加速引擎,拥有丰富的功能。其字节序重排功能包含了 Red blueswap 以及 Byte swap 特性。Red blue swap 特性在 L4 和 L4+系列 MCU 都支持,而 Byte swap 仅在 L4+系列有支持。在 L4+系列上,通过配置 DMA2D_OPFCCR 寄存器的 SB 位,即可使能 Byte swap 功能,在 DMA2D 的 outputFIFO 中完成字节序调整,如下图:. b" J: y+ S; l1 `
7 s* ~' {; P% C3 M
! l0 d; {2 R6 P
. X9 Y, \0 i! X3 o8 |9 j1 x' ^
x5 N0 R( F. Q' }1 o' w, v" j& u
& N% K' C9 H$ @. r因此在图形界面应用中,需要 Byte swap 时,可以考虑用 DMA2D 来传输 RGB 数据给 LCD。
7 C/ H% q" s7 Z$ a$ N4 N$ u/ o4 g d6 G4 s4 {9 v; k/ a L
' | q# c% `8 Z! Q* w$ b
GPDMA 进行 Byte swap
4 u- a4 y p4 w$ ?! Z4 D( V) }) Y' `在新推出的 U5 系列芯片上,集成了 GPDMA 模块。这是新的通用 DMA 模块,能在传输数据的同时,还有丰富的数据处理能力。在初始化 GPDMA 时,通过配置源和目的数据位宽为 DMA_SRC_DATAWIDTH_WORD,在数据处理中配置 DataExchange 为 DMA_EXCHANGE_DEST_BYTE,如下面代码,也能实现 Byte swap 功能。
$ D8 [2 v) X. F- DMA_HandleTypeDef hgpdma_channel0;
) g* a0 A. s/ E - DMA_DataHandlingConfTypeDef DataHandlingConfig;& O! `( i' ^6 F/ u0 C1 b7 c
- static void MX_DMA_Init(void)
1 ^0 t+ R% g! M3 u" v* }% W9 P# o - {
. b+ E) {% H0 r9 f - /* DMA controller clock enable */! z; @7 c' w% c
- __HAL_RCC_GPDMA1_CLK_ENABLE();
6 ]" M7 l# n; _1 ~ b - hgpdma_channel0.Instance = GPDMA1_Channel0; f0 A' u P1 L
- hgpdma_channel0.Init.Request = DMA_REQUEST_SW;* Z+ _) U# G4 J( r6 M5 A
- hgpdma_channel0.Init.BlkHWRequest = DMA_BREQ_SINGLE_BURST;
/ J. M5 ^8 h% [& b& O1 p [& y7 Q - hgpdma_channel0.Init.Direction = DMA_MEMORY_TO_MEMORY;1 J: f4 k2 U" }1 @2 r/ ]
- hgpdma_channel0.Init.SrcInc = DMA_SINC_INCREMENTED;/ ?7 a7 R! R* V4 p9 }
- hgpdma_channel0.Init.DestInc = DMA_DINC_FIXED;- c: V n, z- q$ i q
- hgpdma_channel0.Init.SrcDataWidth = DMA_SRC_DATAWIDTH_WORD;
0 b& r7 n6 w3 T - hgpdma_channel0.Init.DestDataWidth = DMA_DEST_DATAWIDTH_WORD;
* E6 \$ e/ i0 [% R+ v - hgpdma_channel0.Init.SrcBurstLength = 1;
3 o& r9 M j8 r - hgpdma_channel0.Init.DestBurstLength = 1;* M8 l" @1 E! X0 ~6 K
- hgpdma_channel0.Init.Priority = DMA_LOW_PRIORITY_LOW_WEIGHT;( @7 H5 X& i( u( B- i5 n
- hgpdma_channel0.Init.TransferEventMode = DMA_TCEM_BLOCK_TRANSFER;
1 P" m7 V+ ~; i- ?% _: V - hgpdma_channel0.Init.TransferAllocatedPort = DMA_SRC_ALLOCATED_PORT0 | DMA_DEST_ALLOCATED_PORT1;
3 m, E, m! h* K- O* W+ @ - hgpdma_channel0.Mode = DMA_NORMAL;0 i: L) [* J! c( ~
- if (HAL_DMA_Init(&hgpdma_channel0) != HAL_OK)
$ g; R7 k$ Y+ x+ e( c* [ - {
C2 g+ o1 W" l: K8 w+ w - Error_Handler( );
! |) }( `. a+ s- h8 [1 j* f; K) V% ? - }
5 \# V+ Z+ Q" z' p3 g; Z# ? - 8 W' ]0 x8 Z2 c, u9 E
- /* Set data handling block configuration *// c" `: }% O. e/ t) u2 _. S
- //swap bytes in Half-word1 q3 N, S6 s/ `9 ~0 N
- DataHandlingConfig.DataExchange = DMA_EXCHANGE_DEST_BYTE;
- a) z' [$ L0 o - DataHandlingConfig.DataAlignment = DMA_DATA_RIGHTALIGN_ZEROPADDED; m5 t# v% ?) l P5 Q* l" U. Y
- /* Data handling configuration */5 [6 ]4 K }5 Y7 \; U: a9 P
- if (HAL_DMAEx_ConfigDataHandling(&hgpdma_channel0, &DataHandlingConfig) != HAL_OK)' j! A" E' f% V( F3 }- u
- {) X& Z9 A2 d. o9 ]0 R2 _ f. g
- Error_Handler( );6 e( ^4 l2 J: `8 F+ F7 g
- }
: s0 @- {. X7 I. Y6 k' s -
3 E" C0 \" |- O& z - /* DMA interrupt init */- C+ C1 ^, t" _6 Q& m' ]5 G/ V
- HAL_NVIC_SetPriority(GPDMA1_Channel0_IRQn, 3, 0);
8 j: m, W: V6 {; \ - HAL_NVIC_EnableIRQ(GPDMA1_Channel0_IRQn);* k; H& `1 |* _3 h; C/ S
- HAL_DMA_RegisterCallback(&hgpdma_channel0, HAL_DMA_XFER_CPLT_CB_ID, DMA_Xfer_Cmplt);1 p0 A# t t8 T9 a
- }
复制代码
1 }; B; y: g: q* S
6 a- ^) \3 z7 C, ~# _1 N这样,在图形应用中,既能使用 DMA2D 加速渲染过程,也能使用 GPDMA 的数据处理能力。通过 GPDMA 直接向FSMC 接口输出 Byte swap 后的 RGB565 格式的图形数据给 LCD。
2 e: _! a6 l# l0 {/ E. w! d
R& \& G- ?' P5 R1 d+ H
5 }" G0 _; G- q0 Y+ ~小结 & O0 [% i3 e! P5 `* g2 P
在驱动 8 位 8080 接口 LCD 时,需要注意图像数据字节序问题。使用 RGB565 格式时,可以使用 DMA2D 或 GPDMA来传输图像数据,同时对图像数据做 Byte swap 调整,满足 LCD 的字节序要求。: D i+ A0 R2 R4 A- F, p; H! F3 M
5 a) }: O3 l9 V9 y$ O. g1 R. t
! J, h2 g( L2 |! E
; Z" v& ]3 p" u
. h& b3 e* f9 i% n完整版请查看:附件
1 v" b4 U& s, {, t9 G$ f! V1 ]
, P3 D7 u- L5 U, N* w" h
; O$ f9 t L$ z$ \' ?; A |