前言
- f- F- ]& i' q/ y在一些图形界面应用中,系统架构会时常考虑使用 FSMC 接口来驱动 8080 接口的 LCD 屏。在 MCU 渲染完成,将framebuffer 发送到 LCD 时,有可能会遇到存儲大小端的问题。STM32 MCU 都采用 ARM Cortex 内核,内存使用小端格式。而 intel 8080 接口的 LCD 在传输 RGB 数据时,使用的是大端格式。MCU 在传输 RGB 数据时,字节序有可能不匹配。
0 Z- T# K, _+ n% o( D' I$ |* K0 ?; h在图形界面应用中,像素格式一般会使用 RGB888 或 RGB565。而在使用 FSMC 接口驱动 8bit 位宽的 LCD 时,很可能会使用 RGB565 格式。本文中会介绍两种方法来处理 8bit 8080 接口 RGB565 格式图像数据字节序问题。1 r' \9 y) f1 n
6 }8 g) A+ y5 V" [
& ^1 w% ^) x7 m+ o' G" M
图像数据字节序
5 Y# n% g5 w1 h8bit 8080 LCD 在接收 RGB565 数据时,会将第一个字节解析为{R4-R0,G5-G3}, 第二个字节解析为{G2-G0,B4-B0},并按这个顺序接收所有数据,如下图:; ~' b. o; o3 o4 [
; t9 @3 D+ i& z- v, K3 I! ~; p* e3 Y, E4 c3 s0 b
& `/ I9 L' |# i0 _5 d9 y" v0 H+ c
0 X. h6 s# b: f
. N# k% J. x6 v6 ]% D/ U, l2 B而在 MCU 内存中,数据按小端格式存放,RGB565 数据存放的字节序如下:7 N7 O; S8 y, H4 ]. m0 t5 F5 ~
4 M3 x# q! m7 g/ x& Z
, W# c6 S* S& ~7 F& d1 H8 a( @
( K( D) f) m5 I7 r/ p( }5 K( a# U! o6 G9 |8 U: @' C) s5 G0 L
/ ^7 ?0 V& |1 y; \8 R3 H4 \% _: q
7 [: O( M3 }+ r. f3 h
8 _* v1 G9 `( r! q$ j
如果不做调整直接将 RGB 数据发给 LCD,MCU 会先发送 Byte 0,再发送 Byte 1,这样 LCD 显示的内容就会错乱。对比上面两张图可以看出,只需将内存中 Byte 0 与 Byte 1 字节位置对调(Byte swap),就能满足 LCD 接口的字节序要求。可以直接使用 CPU 进行 Byte swap,但这会消耗过多 CPU 算力,同时也会占用更多内存。这里我们将使用硬件进行 Byte swap.0 I: ^ m% b) X
1 {2 W. U7 E3 `5 h! A6 e- u$ `* ] h2 s( O. I, I
DMA2D 进行 Byte swap 3 \$ [" w+ \7 j3 h/ t9 c
DMA2D 是 ST 为图形应用专门设计并优化的 2D 加速引擎,拥有丰富的功能。其字节序重排功能包含了 Red blueswap 以及 Byte swap 特性。Red blue swap 特性在 L4 和 L4+系列 MCU 都支持,而 Byte swap 仅在 L4+系列有支持。在 L4+系列上,通过配置 DMA2D_OPFCCR 寄存器的 SB 位,即可使能 Byte swap 功能,在 DMA2D 的 outputFIFO 中完成字节序调整,如下图:4 }& o7 j0 j' I# b3 o- w" m# `
9 O' e1 d& T9 u7 p
6 }: N- U$ j' p8 u* |
( D. t! e# C2 Y# e. ~
' W M! r, v9 I3 R" d7 N- Z
4 V4 ^9 Z" Q9 O- S& \( R因此在图形界面应用中,需要 Byte swap 时,可以考虑用 DMA2D 来传输 RGB 数据给 LCD。, B2 I4 J3 F l$ A/ r) R+ e4 ~
1 p \9 b3 i) _ b6 ^& r
9 i9 @" z4 G2 GGPDMA 进行 Byte swap $ z7 v/ k3 T5 b
在新推出的 U5 系列芯片上,集成了 GPDMA 模块。这是新的通用 DMA 模块,能在传输数据的同时,还有丰富的数据处理能力。在初始化 GPDMA 时,通过配置源和目的数据位宽为 DMA_SRC_DATAWIDTH_WORD,在数据处理中配置 DataExchange 为 DMA_EXCHANGE_DEST_BYTE,如下面代码,也能实现 Byte swap 功能。 ^; X/ i& f) K* M: D
- DMA_HandleTypeDef hgpdma_channel0;
2 {7 f2 {# z$ Z0 t7 P1 R9 U - DMA_DataHandlingConfTypeDef DataHandlingConfig;
8 c) O8 y1 G! Q! A I$ \' U - static void MX_DMA_Init(void) 7 K2 Q, @& D6 R3 v1 H% O5 d
- {
, F& h. b% @5 l, r- X4 h - /* DMA controller clock enable */* {5 {% [8 m) j! e1 g
- __HAL_RCC_GPDMA1_CLK_ENABLE();4 u. E8 C9 |( F7 V: l
- hgpdma_channel0.Instance = GPDMA1_Channel0;" G$ U6 v8 W9 ?+ p
- hgpdma_channel0.Init.Request = DMA_REQUEST_SW;# O. o5 O) }& X+ R$ {5 a
- hgpdma_channel0.Init.BlkHWRequest = DMA_BREQ_SINGLE_BURST;
# c0 T5 `2 V4 H6 r - hgpdma_channel0.Init.Direction = DMA_MEMORY_TO_MEMORY;* o: h% Z' v. p* I+ I2 Y5 V
- hgpdma_channel0.Init.SrcInc = DMA_SINC_INCREMENTED;
) b) S+ V3 Q; _. s! r/ Z - hgpdma_channel0.Init.DestInc = DMA_DINC_FIXED;. g. U/ C/ g! t2 [1 D! d3 T7 M
- hgpdma_channel0.Init.SrcDataWidth = DMA_SRC_DATAWIDTH_WORD;
" I1 S! h6 t. b - hgpdma_channel0.Init.DestDataWidth = DMA_DEST_DATAWIDTH_WORD;
- q) V4 o: ^! z% s! P# w$ S; a - hgpdma_channel0.Init.SrcBurstLength = 1;
9 u1 _7 K& C' t4 @' G1 [ - hgpdma_channel0.Init.DestBurstLength = 1;
~7 }, P# ~3 P% b - hgpdma_channel0.Init.Priority = DMA_LOW_PRIORITY_LOW_WEIGHT;
' t% N9 ?' Q( }* ` - hgpdma_channel0.Init.TransferEventMode = DMA_TCEM_BLOCK_TRANSFER;; g+ X4 w; r% u
- hgpdma_channel0.Init.TransferAllocatedPort = DMA_SRC_ALLOCATED_PORT0 | DMA_DEST_ALLOCATED_PORT1;; A" H0 n1 J: x. Q; @) D" G: Y
- hgpdma_channel0.Mode = DMA_NORMAL;
+ c D$ G( q% q6 \0 ? - if (HAL_DMA_Init(&hgpdma_channel0) != HAL_OK)# A8 Z" n7 t' H% }6 x
- {
# L+ Q0 E- U6 i- N: t( \9 E/ L - Error_Handler( );1 \7 O- U% J$ O# n. B9 H; i% Z- j
- }
4 M* L; C/ T& G$ x; v" t$ g% N: D -
1 C5 Z' l& j' h7 T - /* Set data handling block configuration */+ H# K4 {: J0 n' P. q+ R
- //swap bytes in Half-word! H) n. [/ P$ d9 G
- DataHandlingConfig.DataExchange = DMA_EXCHANGE_DEST_BYTE;
$ q: Z' e! j# J" ^( K - DataHandlingConfig.DataAlignment = DMA_DATA_RIGHTALIGN_ZEROPADDED;
& j! ~/ K7 v: L( C0 s' q2 L( q - /* Data handling configuration */
* j- ?2 a, u8 u- i1 }3 H - if (HAL_DMAEx_ConfigDataHandling(&hgpdma_channel0, &DataHandlingConfig) != HAL_OK)$ d9 h* @: [: a$ z. l& B( Y% o$ K. B
- {# {6 H$ ^' N8 L- i1 P, o, T" M. S
- Error_Handler( );2 E8 f$ W" R! g( Q: j
- }
* u1 t. v+ U, {8 t6 i -
" d! B# n) Y* [ w7 ?" r0 A0 T - /* DMA interrupt init */$ e, B4 p3 q2 g( V' N
- HAL_NVIC_SetPriority(GPDMA1_Channel0_IRQn, 3, 0);
$ w3 g8 _3 k% }2 ?9 n* C4 ^ - HAL_NVIC_EnableIRQ(GPDMA1_Channel0_IRQn);' E" Q; b; A2 N# B
- HAL_DMA_RegisterCallback(&hgpdma_channel0, HAL_DMA_XFER_CPLT_CB_ID, DMA_Xfer_Cmplt);4 @9 b; {" n) e( l
- }
复制代码
7 A. z: I9 f% v" _ G7 \/ M2 _, h; x# Q( f: b
这样,在图形应用中,既能使用 DMA2D 加速渲染过程,也能使用 GPDMA 的数据处理能力。通过 GPDMA 直接向FSMC 接口输出 Byte swap 后的 RGB565 格式的图形数据给 LCD。$ E+ G3 P7 u, H& x8 P
: g+ B' W* c0 z7 ]
& F1 l+ n1 }) y1 L
小结 : D, \! T7 y, W, u) b$ g1 s4 D
在驱动 8 位 8080 接口 LCD 时,需要注意图像数据字节序问题。使用 RGB565 格式时,可以使用 DMA2D 或 GPDMA来传输图像数据,同时对图像数据做 Byte swap 调整,满足 LCD 的字节序要求。- D# [3 j0 L$ Z j. K
7 p/ R5 v4 N* o4 a; H, P4 k3 Z. N# x5 H
0 ~( W0 s1 l* p- G9 E. A
. `( G4 L& C. M3 m2 ?! u完整版请查看:附件4 H7 q3 h o- B$ K
% h% x, F4 s5 x1 M
* _) l! E r- c& U; c; L9 F$ Z
|