基础知识简介$ T. D, s9 C4 V. `9 F9 B0 B: F
RGBLCD
' }& @& z3 ]$ Z9 I) w1.RGBLCD的信号2 @" s. V8 h, r' {+ J
3 Y/ K2 o7 d* R" N6 H2 m3 {( k
( U0 t- I# {* A: x% n5 {% ~! y一般的 RGB 屏有 24 根颜色数据线(RGB 各站 8 根,即RGB888 格式),这样可以表示最多 1600W 色,DE、VS、HS 和 DCLK,用于控制数据传输。
; a1 |, d# d5 e' [: C! s) G
/ w3 b5 V( T, Q2.RGBLCD驱动模式
0 c, Y/ ?+ m4 m其驱动模式主要分为两种) S+ y! j/ _, b2 _7 U+ I" E% G
. \6 l$ F+ f8 u+ O, T DE模式& C9 e! u$ ?; q; a2 x/ o! P
DE 模式使用 DE 信号来确定有效数据,DE 为高/低时,数据有效3 _% p0 O; E0 E# L
HV模式
5 W0 k! Y: l( d; C2 B3 Q$ w; q HV模式需要行同步和列同步,来表示扫描的行和列- v4 J2 M' w y' ~, | v. ]3 h7 k
' T' T3 N2 b3 K4 _3 I+ \
LTDC
% n$ ]* S9 W; I; R) B6 \STM32F767xx 系列芯片都带有 TFT LCD 控制器,即 LTDC,通过这个 LTDC,STM32F767可以直接外接 RGBLCD 屏,实现液晶驱动。, z. \0 o& w. }$ S
% r( f8 a3 K! M( O
1.信号线: _" u3 N( L# e4 H
STM32F767 核心板板载的 LCD 接口引出为了,节省 IO,并提高图片显示速度,使用 RGB565 颜色格式,这样只需要 16 个 IO 口,当使用 RGB565 格式的时候,LCD 面板的数据线,必须连接到 LTDC 数据线的 MSB
4 k- A9 e6 Y! ~/ d$ W( x. _# u+ d' G3 `+ o; r) K
* X# ~- x* }& g
: F5 L2 o8 c9 c6 d* v0 \" B
2.图像处理单元7 I+ Q" c+ H) g L4 y1 K
先从 AHB 接口获取显存中的图像数据,然后经过层 FIFO(有 2 个,对应 2 个层)缓存,每个层 FIFO 具有 64*32 位存储深度,然后经过像素格式转换器(PFC),把从层的所选输入像素格式转换为 ARGB8888 格式,再通过混合单元,把两层数据合并,混合得到单层要显示的数据,最后经过抖动单元处理(可选)后,输出给 LCD 显示。
0 v! _( H, `$ e6 ]1 r9 n7 x 混合单元首先将第一层与背景层进行混合,随后,第二层与第一层和第二层的混合颜色结果再次混合,完成混合后,送给 LCD显示。
9 l0 \, B: ^' B/ o- |( \3 w
! |- z; l9 A+ D7 v E
9 A8 I# W: a* }2 j+ H1 o& H
' [' T( n7 l2 b% A' T5 }2 s: V3.AHB 接口! X- t# g2 |3 |- C* f& [8 Y" H7 T
由于 LTDC 驱动 RGBLCD 的时候,需要有很多内存来做显存,比如一个 800480 的屏幕,按一般的 16 位 RGB565 模式,一个像素需要 2 个字节的内存,总共需要:800480*2=768K 字节内存,STM32 内部是没有这么多内存的,所以必须借助外部 SDRAM,而 SDRAM 是挂在AHB 总线上的,LTDC 的 AHB 接口,就是用来将显存数据,从 SDRAM 存储器传输到 FIFO 里面。
* ~! _5 `& K+ }2 R* `2 ]8 F
p# x: O' _& g, s7 n. x5 z4.LTDC相关寄存器及使用方法
- V0 y' J" L) T' f4 |( `LTDC 的各种配置寄存器以及状态寄存器,用于控制整个 LTDC 的工作参数,
7 Z" Q+ T: }* ~ Z% {: U主要有:各信号的有效电平、垂直/水平同步时间参数、像素格式、数据使能等等。
* x3 o; _5 z# J7 A7 d' o' Z如果 RGBLCD 使用的是 DE 模式,需要设置如下参数,然后 LTDC会根据这些设置,自动控制 DE 信号。$ x0 V$ y/ t* A; W' H4 K
2 S' S! o% V- _# c/ K/ K2 nLTCD全局控制寄存器 LTCD_GCR+ A, u) e% ~$ t* F
+ x$ W# n9 O: d# @
( J" D, G3 N3 o6 w3 O* o. Y
' W1 ?+ G* u: p3 F3 T% J& m
3 X3 ?. k+ [* d% N0 x6 `2 u8 t
9 z' _% j( D0 L3 K" _8 u: dLTCD同步大小寄存器 LTDC_SSCR0 ~7 l2 w& o& C
该寄存器用于设置垂直同步高度(VSH)和水平同步宽度(HSW)
1 T( N" d# I, s5 [# X% l9 z5 ^" ^3 X0 z; h
+ F* q- x. Z& @. L; k) I% B# K9 n
* ^2 ^# F! I1 X
* i2 v& _0 r1 Y2 E
% u: W) h2 ^2 _% @
2 G* O5 d* @$ m7 `: \4 o% qLTDC有效宽度配置寄存器 LTDC_AWCR3 j7 Z% _1 A0 x
这里所说的有效高度和有效宽度,是指 LCD 面板的宽度和高度,构成分辨率 V. }" }; Y& `' p9 X# s Q
0 |0 A) p! U) r1 H4 `$ |: Q
8 B4 r$ M7 u# s$ D5 ^6 @- S+ g6 E+ {4 ] Z8 N3 u
( n* Z& g; a4 [$ b2 Z7 j
- s" @; ?) F5 ?# \# n# u; k
. h) O; l: h7 S# HLTDC 总宽度配置寄存器 LTDC_TWCR
: Q0 ^ I6 Y5 f; x$ a! c8 ^
% k( g/ X3 c: }1 r- r4 H, U+ S& J/ K6 K6 O3 G5 k* [4 M
( h9 `( D: `( s- ], p5 O; F$ T" r9 P, d
0 A" f& D" w3 n7 A- g; f ' x' A6 ^& {* O: }, t
LTDC 背景色配置寄存器 LTDC_BCCR
/ |; n/ p& J4 X" @. d" w$ w! F7 [该寄存器定义背景层的颜色(RGB888),通过低 24 位配置,我们一般设置为全 0 即可。
' _1 p5 ^& V* ]; d: E6 ~& U: ~8 k* T9 D9 t
; U) [# \1 ?5 Y& s; x
3 _4 K9 w- E f( s( g
# `4 t) S& f* o$ d; ~3 G) m# }# @
LTDC当前位置状态寄存器 LTDC_CPSR
: w3 U$ F( X4 @/ @7 [, ?* v# V3 J5 v
( O1 ~, l) b! V2 M
( {8 G2 Z9 j# E$ sCXPOS[31~16]:返回当前X坐标位置. A% y$ c: q7 t+ M# Q: x' J3 a
CYPOS[15~0]:返回当前Y坐标位置
' \5 ~9 K5 v/ H( m8 K) w4 f+ c8 k9 t( U) ]3 T
LTDC当前显示状态寄存器 LTDC_CDSR
7 u8 x5 a* @8 Z/ x; `此寄存器返回由 HSYNC、VSYNC 和水平/垂直 DE 信号控制的当前显示阶段的状态
( e* z5 M( R6 w, S, j( v. O5 r9 c0 u' @" Y* t( z
: H1 I+ z8 U. P/ r8 }! r; N
' ^7 c+ H ?+ z& P$ ]! U位 31:24 保留,必须保持复位值( r, A8 g- ^. |8 y5 i2 [1 ?/ B4 P
位 3 HSYNCS:水平同步显示状态 (Horizontal Synchronization display Status)
+ v1 x# }3 Z9 ~! c( h0:低电平有效
- }3 a7 s% {+ q. A. L6 ^8 e. M1:高电平有效& z: Z0 d3 e- T* @ `# |4 X j
位 2 VSYNCS:垂直同步显示状态 (Vertical Synchronization display Status)
( Z3 g; d6 j0 h0:低电平有效2 |1 a4 I; F e; D+ I
1:高电平有效! f- P W5 V, ~7 ~$ m
位 1 HDES:水平数据使能显示状态 (Horizontal Data Enable display Status) p# [7 B' z: ^) } E
0:低电平有效$ a3 j; W9 f# i; j
1:高电平有效
2 s# W( x" k& \% i8 t) M! F位 0 VDES:垂直数据使能显示状态 (Vertical Data Enable display Status)
6 ?6 X7 V* k& E& A0:低电平有效& ]9 q/ w; m9 X* d
1:高电平有效
0 m) }2 b% J8 ?% N7 f& z4 e. M- d/ P8 A! K$ ]+ ?% ~7 O9 g3 W
LTDC 的层颜色帧缓冲区地址寄存器 LTDC_LxCFBAR(x=1/2)
3 o q% e7 M* H该寄存器用来定义一层显存的起始地址。STM32F767 的 LTDC 支持 2 个层,所以总共有两个寄存器,分别设置层 1 和层 2 的显存起始地址。
; L$ J Z" Q! u$ B5 d" |! s, T$ F1 i) i1 G
% A8 J" i& }' L& R' x6 v. U9 P+ C; \
7 g# r$ S! j* S' u) Z( W& ]; T
LTDC 的层像素格式配置寄存器 LTDC_LxPFCR(x=1/2)8 n( F$ P/ B1 }2 f6 G
该寄存器只有最低 3 位有效,用于设置层颜色的像素格式,一般使用 RGB565 格式,即该寄存器设置为:010 即可。
! ~" K8 g- T0 h3 O5 J# m+ c' e; E/ g! }; z
3 |4 K# {7 g; ]( z- [% d; q0 {9 p
& e; w$ k$ j. g7 E# P& X
6 `1 |% V# J8 j$ y, }" [
LTDC 的层恒定 Alpha 配置寄存器 LTDC_LxCACR(x=1/2)0 B6 F, A1 G" v( X3 b+ b. s+ \
该寄存器低 8 位(CONSTA)有效,这些位配置混合时使用的恒定 Alpha。恒定 Alpha 由硬件实现 255 分频。$ ^9 |/ j0 L& j5 g, r" ]
. D; P9 u; o3 _+ \8 w: c
) H& X* Z, j4 Z: |8 p7 |7 p0 K9 i$ G
LTDC 的层默认颜色配置寄存器 LTDC_LxDCCR(x=1/2)
. ?) }; t5 P" ~1 {3 o该寄存器定义采用 ARGB8888 格式的层的默认颜色。默认颜色在定义的层窗口外使用或在层禁止时使用。一般情况下,用不到,所以该寄存器一般设置为 0 即可。( G1 T& l. ~/ I2 C7 B
7 y2 r& Y3 i' g4 L; a3 ]8 E- W/ M% W: t$ D/ p5 J" K
3 A. T4 ^; V) a) U" o; I/ JLTDC 层混合系数配置寄存器:LTDC_LxBFCR(x=1/2)! s, [% g7 p" w- N @! g
该寄存器用于定义混合系数:BF1 和 BF2。BF1=100 的时候,使用恒定的 Alpha 混合系数(由LTDC_LxCACR寄存器设置恒定Alpha值),BF1=110的时候,使用像素Alpha恒定Alpha。像素 Alpha 即 ARGB 格式像素的 A 值(Alpha 值),仅限 ARGB 颜色格式时使用。在 RGB565格式下,我们设置 BF1=100 即可。BF2 同 BF1 类似,BF2=101 的时候,使用恒定的 Alpha 混合系数,BF2=111 的时候,使用像素 Alpha恒定 Alpha。在 RGB565 格式下,我们设置 BF2=101即可。
5 X5 B% m$ k( q4 j: L通用的混合公式为:BC=BF1C+BF2Cs
6 A& U& a( d1 @4 W# y/ m/ U; Y5 E/ M其中:BC=混合后的颜色;BF1=混合系数 1;C=当前层颜色,即我们写入层显存的颜色值;BF2=混合系数 2;Cs=底层混合后的颜色,对于层 1 来说,Cs=背景层的颜色,对于层 2 来说,Cs=背景层和层 1 混合后的颜色。
0 _! |7 j( X$ v; J. O( m; ~3 { h1 l
5 t0 V. | Q0 ?. y) N2 b* r' g
. d- A/ O; ?% _% d3 I6 vLTDC 的层窗口水平位置配置寄存器 LTDC_LxWHPCR(x=1/2)
2 f; C4 ]# d; n8 x A* q该寄存器定义第 1 层或第 2 层窗口的水平位置,第一个和最后一个像素( d( v3 S2 ~: g' Y
3 o1 R% _1 A. v( r4 `: G- H( U2 S& U+ R9 e z: k( a
, \) L6 q+ W( i, v1 P8 [: ^* Y% {9 R, UWHSTPOS:窗口水平起始位置,定义层窗口的一行的第一个可见像素,
$ N) n: J. G; ?& u; cWHSPPOS:窗口水平停止位置,定义层窗口的一行的最后一个可见像素
( s2 T% R* |' H* V( w/ h. V7 {7 t9 b& i1 \8 Q. i+ m. R* b
LTDC 的层窗口垂直位置配置寄存器 LTDC_LxWVPCR(x=1/2)" ^1 q3 L. J3 p- Q, n% a* v5 {
该寄存器定义第 1 层或第 2 层窗口的垂直位置(第一行或最后一行)
3 S) j, c# j/ P% N! {
# c! {! T0 X5 S- `- V- h9 [ L( j7 [2 ?1 [/ a9 Q9 z
0 Y9 \5 `3 A! l3 w; m
WVSTPOS:窗口垂直起始位置,定义层窗口的第一个可见行。
r2 a6 ~5 O; O1 r5 ^WVSPPOS:窗口垂直停止位置,定义层窗口的最后一个可见行。& J3 h/ k% J" z) `9 z+ d- D
! D# g% {5 ~' `0 R- b/ M
LTDC 的层颜色帧缓冲区长度寄存器 LTDC_LxCFBLR(x=1/2)
: w! R- A6 ^" m% _, M- \7 K该寄存器定义颜色帧缓冲区的行长和行间距。. b3 N1 ?2 Q: A/ E6 P
5 U7 J! F4 C1 {1 j% [% {
1 W! E2 m+ w9 @6 g. F
( p3 G. J0 F8 C% c- t* y' j# h( ICFBLL:这些位定义一行像素的长度(以字节为单位)+3。. F! ?6 Z; G( Z& X; @
行长的计算方法为:有效宽度 * 每像素的字节数 + 3。5 N/ E; c5 N- u1 z- G' C
比如,LCD 面板的分辨率为 800480,有效宽度为 800,采用 RGB565 格式,那么 CFBLL 需要设置为:8002+3=1603。
1 m5 Y: J# l: f& OCFBP:这些位定义从像素某行的起始处到下一行的起始处的增量(以字节为单位)。3 y2 F. R* O J. B, f9 e9 c' k2 M
这个设置,其实同样是一行像素的长度,对于 800480 的 LCD 面板,RGB565 格式,设置 CFBP 为:8002=1600 即可。1 \) [, Q6 \: x7 k" K. T8 V6 @
$ l( n, k A2 U7 P$ w# x4 i
LTDC 的层颜色帧缓冲区行数寄存器 LTDC_LxCFBLNR(x=1/2)# F& w& b+ X" Q0 `' s4 b: g4 p
该寄存器定义颜色帧缓冲区中的行数。8 @7 _& |! J- i% Z, U# t. z
比如,LCD 面板的分辨率为 800*480,那么帧缓冲区的行数为 480 行,则设置 CFBLNBR=480 即可
" o" ^) E% M: U1 M L5 f9 `: F) z: F5 j1 I4 M m. N% u. L
! _8 W( J' I$ ?# M1 n+ o. [/ W1 c' ]2 ?: @9 ~2 q6 G
5.时钟域
# U/ }6 ?2 z9 i- W( v' G8 z* [. tLTDC 有三个时钟域:AHB 时钟域(HCLK)、APB2 时钟域(PCLK2)和像素时钟域(LCD_CLK),
) N1 [9 g- P) b6 iAHB 时钟域用于驱动 AHB 接口,读取存储器的数据到 FIFO 里面;% O* ?2 f Z8 j5 J
APB2 时钟域用于配置寄存器;. J( `/ O: \( q8 l4 q1 d# r
LCD_CLK像素时钟域则用于生成 LCD 接口信号,LCD_CLK 的输出应按照 LCD 面板要求进行配置。' h5 F+ G! r) j( c$ m0 Z2 X/ g' i
9 B1 \9 d- T) u8 i# ^! @' B1 N
DMA2D
( t, V$ ~) b& W4 g; U为了提高STM32F767的图像处理能力,ST公司设计了一个专用于图像处理的专业 DMA,通过 DMA2D 对图像进行填充和搬运,可以完全不用CPU 干预,从而提高效率,减轻 CPU 负担。
- p, A' W3 E# S- z( e1 Z+ Q/ j, Y+ X, t
) }7 W& G, w c: r# _/ G) V- [它可以执行下列操作:
& V: e8 n. m, u! G用特定颜色填充目标图像的一部分或全部(可用于快速单色填充)
, w% y4 l) [ t" A; `; l* @将源图像的一部分(或全部)复制到目标图像的一部分(或全部)中(可用于快速图像填充)9 m, b( x+ x7 t
通过像素格式转换将源图像的一部分(或全部)复制到目标图像的一部分(或全部)中
# D& K/ w8 J3 d4 M! A! }$ G将像素格式不同的两个源图像部分和/ 或全部混合,再将结果复制到颜色格式不同的部分或整个目标图像中- w( J! U3 u' D6 g$ z
5 e$ c( h( a" e6 o
! b1 G1 M2 P7 s. ]5 W$ R" S
DMA2D 有四种工作模式,通过 DMA2D_CR 寄存器的 MODE[1:0]位选择工作模式:
0 \" C0 O9 @4 M, R; j, z6 H1, 寄存器到存储器' Z! I9 k7 I* F9 l& `9 \2 n
2, 存储器到存储器
6 ]7 v [+ T- m& p% J& H& @3, 存储器到存储器并执行 PFC
, \( {, U! U; m% G4, 存储器到存储器并执行 PFC 和混合" u: T. t% J0 k) i% C T% K
: q& z5 g: T+ z, G- M0 D1 I% N$ P/ Y7 S6 o/ }4 v/ S' X! ?+ l
寄存器到储存器
1 u# [1 E8 D& z, e6 V0 F寄存器到存储器模式用于以预定义颜色填充用户自定义区域,也就是可以实现快速的单色5 k# K4 q* h1 i2 y
填充显示,比如清屏操作。
& K) \4 @/ O$ i, u0 \在该模式下:颜色格式在 DMA2D_OPFCCR 中设置,DMA2D 不从任何源获取数据,它只
( K( G% p7 R) F% X2 d4 e7 K是将 DMA2D_OCOLR 寄存器中定义的颜色写入通过 DMA2D_OMA 寻址以及 DMA2D_NLR 和
- t5 r0 h! j* [+ r: p- ^DMA2D_OOR 定义的区域7 x D% s0 g C* G7 d
9 ^) ]* o9 b# f& x# C4 ?9 F7 O# g! b- X
储存器到储存器
- s- y: b1 ~2 Q! H$ z% J0 E该模式下,DMA2D 不执行任何图形数据转换。前景层输入 FIFO 充当缓冲区,数据从- @& P+ l4 x+ p b5 [5 K- y
DMA2D_FGMAR 中定义的源存储单元传输到 DMA2D_OMAR 寻址的目标存储单元,可用于快7 V& N/ w8 x; G7 L. G2 S% ?
速图像填充。DMA2D_FGPFCCR 寄存器的 CM[3:0]位中编程的颜色模式决定输入和输出的每像. a2 j; k' P$ z! G. l J6 T
素位数。对于要传输的区域大小,源区域大小由 DMA2D_NLR 和 DMA2D_FGOR 寄存器定义,9 L# w0 D+ S' ]# u3 u* G# b9 M
目标区域大小则由 DMA2D_NLR 和 DMA2D_OOR 寄存器定义。
- Y( x4 {2 V N+ `. z
- q3 d( G7 E0 F
! ~9 v+ F. m- X/ y0 U- e! n/ r以上两个工作模式,LTDC 在层帧缓存里面的开窗关系都一样的,经过如下三个寄存器的配置,就可以确定窗口的显示位置和大小。
& C/ ^) ^, d% @0 ?(1). 窗口显示区域的显存首地址由 DMA2D_OMAR 寄存器指定,5 ]' G ~+ f. q# k9 v
(2).窗口宽度和高度由DMA2D_NRL 寄存器的 PL 和 NL 指定0 g8 m- o" t8 d( w/ _, n6 h
(3).行偏移(确定下一行的起始地址)由 DMA2D_OOR寄存器指定,0 }! `+ ^8 u! v' v X1 R2 L
' P8 I3 j( {. i4 R$ C% e8 |/ N. O0 z6 d# v8 G
6 \" D, `* `' l1 M
: J, |. Y3 O3 ]$ J( x在寄存器到存储器模式下,在开窗完成后,DMA2D 可以将 DMA2D_OCOLR 指定的颜色,5 H% n7 @7 z9 S" w) }7 U+ K
自动填充到开窗区域,完成单色填充。* W; ]5 }" K1 S' J- b5 q: N3 N7 p
8 A4 Z. X. o+ S" n
6 v8 T, n- [+ o6 n3 O
在存储器到存储器模式下,需要完成两个开窗:前景层和显示层,完成配置后,图像数据从前景层拷贝到显示层(仅限窗口范围内),从而显示到 LCD 上面。显示层的开窗如上图所示,前景层也和上图类似,只是 DMA2D_OMAR 寄存器变成了DMA2D_FGMAR,DMA2D_OOR寄存器变成了DMA2D_FGOR,DMA2D_NRL则两个层共用,然后就可以完成对前景层的开窗,确定好两个窗口后,DMA2D 就将前景层窗口内的数据,拷贝到显示层窗口,完成快速图像填充。
) x3 H9 Y R, P7 v) s8 F* k+ F" I/ B6 _, z v2 O: h: d, z
+ n6 a8 y8 I0 w- W* \6 F
DMA2D相关寄存器
+ |+ X7 \/ B1 U1 a- z PDMA2D控制寄存器 DMA2D_CR3 m" n9 X" N3 `$ s( t$ e
该寄存器,主要是MODE 和 START 这两个设置
5 l% @, {) _# k: w4 `. h
. _! w/ y0 o+ F/ q* Z& K1 ?8 V1 Y. L: C, N" T0 J: H' Z) C
( f4 E6 g r! X. P; ]
MODE:表示 DMA2D 的工作模式,
! J9 |# ?% v% `0 S4 s00:存储器到存储器模式;01:存储器到存储器模式并执行 PFC;. |3 w; Q9 Z6 v
10:存储器到存储器并执行混合;11,寄存器到存储器模式;( P3 i: o0 ^4 M0 y$ h
START:该位控制 DMA2D 的启动,在配置完成后,设置该位为 1,启动 DMA2D 传输。
3 a* m& }" Q9 M; }3 [4 d. D( h7 U* G. @8 I
DMA2D输出 PFC 控制寄存器 DMA2D_OPFCCR
n/ z/ q" o& b0 H# {该寄存器用于设置寄存器到存储器模式下的颜色格式
* s, w1 d. ` m1 I
' Q! O1 A; ^ y8 z) P
# j/ k+ }1 Q1 L" ?' |0 ?) X, [% I
只有最低 3 位有效(CM[2:0]),表示的颜色格式有:# S/ g. f6 G! a c+ A' ]) D
000,ARGB8888;001:RGB888;010:RGB565;
" l$ U4 B! i0 H: |( \$ P8 o011:ARGB1555;100:ARGB1444。
, i( M, g7 i' v* ~+ N6 v6 G: I8 Q我们一般使用的是 RGB565 格式,所以设置 CM[2:0]=010 即可。
( z4 ^3 x* E$ c( s( r
" t! A% {7 ~8 |# g6 y3 uDMA2D前景层 PFC 控制寄存器:DMA2D_FGPFCCR, I! R7 p% b8 Q% h5 I
# \2 A8 l! e$ E" z, a' w/ ~; Z( k) H \" m* N: @! y
! w- k" D8 i1 k: d4 G
该寄存器,低 4 位:CM[3:0],用于设置存储器到存储器模式下的颜色格式,这四个位表示的颜色格式为:
- Q1 [' n; Q" v& g! G3 [, x0000:ARGB8888;0001:RGB888;0010:RGB565;$ |+ _( [- D7 k2 Z- e6 o" S$ r' U
0011:ARGB1555;0100:ARGB4444;0101:L8;* [' p( c$ u" h! w2 \: y; G
0110:AL44;0111:AL88;1000:L4;1001:A8;1010:A4;7 Q: t+ z& E2 {' v" }
我们一般使用 RGB565 格式,所以设置 CM[3:0]=0010 即可。
5 T: Y8 o) {9 t, u2 T; `- G2 Q8 \4 R1 _$ r
DMA2D输出偏移寄存器 DMA2D_OOR
; t7 ^ f# ?, P' g y& x: L+ I. l3 H" A
+ T+ y: X) X$ r: r- r/ ^, {该寄存器仅最低 14 位有效(LO[13:0]),用于设置输出行偏移,作用于显示层,以像素为
3 y+ h2 a- z% v3 H( l, P单位表示。此值用于生成地址。行偏移将添加到各行末尾,用于确定下一行的起始地址。
: b& z2 [" Z3 i' g1 @ J同样的,还有前景层偏移寄存器:DMA2D_FGOR,该寄存器同 DMA2D_OOR 一样,也是: A3 I' _. i- K/ a* q* B1 a$ v
低 14 位有效,用于控制前景层的行偏移,也是用于生成地址,添加到各行末尾,从而确定下一6 B( P G( i5 ?9 p
行的起始地址。
4 q7 h8 q. ?6 P6 g" L& ]2 R
9 } q5 v8 V2 u Z- R& I# n1 C3 yDMA2D输出存储器地址寄存器 DMA2D_OMAR
9 ]5 n$ K) t9 G4 u+ I) ] b1 w) S1 I6 H" C4 s
8 D6 d' a! f* \& p- r3 `1 M3 J
! b% g. b, g5 p, L% `! c该寄存器设置由 MA[31:0]设置输出存储器地址,也就是输出 FIFO 所存储的数据地址,该地址需要根据开窗的起始坐标来进行设置。以 800480 的 LCD 屏为例,行长度为 800 像素,假定帧缓存数组为:ltdc_framebuf,我们设置窗口的起始地址为:sx(<800),sy(<480),颜色格式为 RGB565,每个像素 2 个字节,那么 MA 的设置值应该为:: e: @' v9 p9 H* A) f1 ?1 p
*MA[31:0]= framebuf+2(800sy+sx)**
# N- T Y$ p8 `% {; R! f2 ~同样的,还有前景层偏移寄存器:DMA2D_ FGMAR,该寄存器同 DMA2D_OMAR 一样,4 Y7 Q; j& @: P3 E6 _
不过是用于控制前景层的存储器地址,计算方法同 DMA2D_OMAR。( k& b; _7 z% C! K; L0 T8 J8 W
4 J8 i: o+ M: e) R r, PDMA2D行数寄存器 DMA2D_NLR) \2 Z2 |1 _2 \% M
* i, |* C5 ~9 b& w1 Y" l7 I
, k' l e/ Q7 [, X该寄存器用于控制每行的像素和行数,该寄存器的设置对前景层和显示层均有效,通过该寄存器的配置,就可以设置开窗的大小。其中:: R0 b m7 R. w/ H
NL[15: 0]:设置待传输区域的行数,用于确定窗口的高度。! O2 j4 C" C* L2 o% J
PL[13: 0]:设置待传输区域的每行像素数,用于确定窗口的宽度
+ u+ Z+ c! r% C# a; C8 @% J, v$ [6 z7 b) s1 v* @- j$ E6 |
DMA2D输出颜色寄存器 DMA2D_OCOLR
) c6 ^- I* x+ x! Q& x" F
: V) z; M. S, ^; n6 o! V) @9 t5 u0 y0 s4 q1 {
该寄存器用于配置在寄存器到存储器模式下,填充时所用的颜色值,该寄存器是一个 32位寄存器,可以支持 ARGB8888 格式,也可以支持 RGB565 格式。我们一般使用 RGB565 格式,比如要填充红色,那么直接设置 DMA2D_OCOLR=0XF800 就可以了。& T2 C1 N, r1 s9 j' y3 s
$ b" ?2 I. s' I- dDMA2D中断状态寄存器 DMA2D_ISR# D7 [4 {# ]- | w
8 y. }: `- `7 m r
$ P- j L- s. f
: Z' d% w# K; C7 @该寄存器表示了 DMA2D 的各种状态标识
% S9 a3 N% R6 I8 K: MTCIF 位,表示 DMA2D 的传输完成中断标志。当 DMA2D 传输操作完成(仅限数据传输)时此位置 1,表示可以开始下一次 DMA2D 传输了
" @) D9 C5 O& b" D. wDMA2D 中断标志清零寄存器:DMA2D_IFCR,用于清除 DMA2D_ISR 寄存器对应位的标志。通过向该寄存器的第 1 位(CTCIF)写 1,可以用于清除 DMA2D_ISR 寄存器的 TCIF 位标志。
9 G* s! [' F' W3 }3 [# }& p* ^2 v( v7 Z0 B: S2 r1 E
) B1 c( c) M, ?1 ZLTDC具体使用步骤
- x- h- M# R0 E3 K5 E1.使能 LTDC 时钟,并配置 LTDC 相关的 IO 及其时钟使能。! N' G# [4 P5 `- j8 `
使用 LTDC,当然首先得开启其时钟。
5 W" W9 `; F; x6 [: Z! ^然后需要把 LCD_R/G/B 数据线、LCD_HSYNC和 LCD_VSYNC 等相关 IO 口,全部配置为复用输出,并使能各 IO 组的时钟。) b5 T. K( Z, E2 Q/ t
1 O8 X# R: l% y* n- //LTDC底层IO初始化和时钟使能& V2 Q7 {$ r6 ?) H# _
- //此函数会被HAL_LTDC_Init()调用' @. ?% J+ ?( L
- //hltdc:LTDC句柄2 C& C+ z4 P6 F, \
- void HAL_LTDC_MspInit(LTDC_HandleTypeDef* hltdc)9 c( y; {7 v. n8 Q
- {7 }* P) K. Z+ f4 V( T% |# @8 p9 C
- GPIO_InitTypeDef GPIO_Initure;; M9 A% G: o" M8 \2 u9 b
- & ~+ @% u4 ?7 @
- __HAL_RCC_LTDC_CLK_ENABLE(); //使能LTDC时钟
5 H' G; e! p& z7 W - __HAL_RCC_DMA2D_CLK_ENABLE(); //使能DMA2D时钟7 ?* E& I% N4 w' w, p
- __HAL_RCC_GPIOB_CLK_ENABLE(); //使能GPIOB时钟
8 q% ?& t4 j4 I! C/ ]6 l, S/ Z - __HAL_RCC_GPIOF_CLK_ENABLE(); //使能GPIOF时钟
. s. o. a7 J# I" U3 s/ Q, A" K; | - __HAL_RCC_GPIOG_CLK_ENABLE(); //使能GPIOG时钟( @: H3 p! h/ C% j
- __HAL_RCC_GPIOH_CLK_ENABLE(); //使能GPIOH时钟5 B- h! y9 m0 h1 a6 j+ E4 U
- __HAL_RCC_GPIOI_CLK_ENABLE(); //使能GPIOI时钟/ S C# j# k! H
- 6 B w3 F& a6 N* u' V- |. E
- //初始化PB5,背光引脚
- z" o, J0 y% S - GPIO_Initure.Pin=GPIO_PIN_5; //PB5推挽输出,控制背光
1 w5 `# m4 O& H5 B - GPIO_Initure.Mode=GPIO_MODE_OUTPUT_PP; //推挽输出
$ }7 Q0 i: ]- \/ t" G - GPIO_Initure.Pull=GPIO_PULLUP; //上拉
. k* J1 J0 ]: c7 a2 ] - GPIO_Initure.Speed=GPIO_SPEED_HIGH; //高速
, F+ ~: O; o1 @( W2 t - HAL_GPIO_Init(GPIOB,&GPIO_Initure);
! D. f* o# }2 R! p* Y - 0 f7 Q5 }( E. {3 l0 z+ \
- //初始化PF10, B9 o- M* h3 x, j
- GPIO_Initure.Pin=GPIO_PIN_10; 8 T# Q1 B/ X6 I+ ~" ?
- GPIO_Initure.Mode=GPIO_MODE_AF_PP; //复用. K% t" e I2 @) j6 j
- GPIO_Initure.Pull=GPIO_NOPULL;
9 j# ?- e! k4 [ - GPIO_Initure.Speed=GPIO_SPEED_HIGH; //高速
- }( \ S3 {. C L6 H" B% x - GPIO_Initure.Alternate=GPIO_AF14_LTDC; //复用为LTDC3 Q5 z1 u! x$ F' A0 e" |. q
- HAL_GPIO_Init(GPIOF,&GPIO_Initure);2 ~7 j2 T8 K3 V7 o& D; U9 A
- $ n D2 m7 \( h" ~+ \8 |: e
- //初始化PG6,7,11" Z7 v @$ e& B( o+ U$ l
- GPIO_Initure.Pin=GPIO_PIN_6|GPIO_PIN_7|GPIO_PIN_11;2 \6 V' N% Z% P; w3 R; @1 N6 e" _' f. M
- HAL_GPIO_Init(GPIOG,&GPIO_Initure);
) m1 j6 j* b" K$ r! K
2 M* O0 m$ H, y: K: L9 _- //初始化PH9,10,11,12,13,14,15: W9 Q6 G( ^& c3 ]) C2 B
- GPIO_Initure.Pin=GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11|\. Y2 R' Z" V. L& T5 b7 c/ {
- GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15;
k* z$ F; u5 y: g - HAL_GPIO_Init(GPIOH,&GPIO_Initure);- X& d+ _- J: v6 j6 l5 h* \9 n1 R0 x
& }8 D" U% Z0 U# X- //初始化PI0,1,2,4,5,6,7,9,102 r% h1 T7 i7 Q! W/ Q& v& S( c* U
- GPIO_Initure.Pin=GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_4|GPIO_PIN_5|\
% o4 |0 A; H0 e6 C) | - GPIO_PIN_6|GPIO_PIN_7|GPIO_PIN_9|GPIO_PIN_10;5 q9 p# A7 Y( k3 v
- HAL_GPIO_Init(GPIOI,&GPIO_Initure);
3 m# G4 r; M% J) M! A! m - }
复制代码
! X) c7 f' A3 h% T' x3 N8 Z2.设置LCD_CLK时钟1 S" Z4 D* R9 ?! [; o" W/ s
配置 LCD 的像素时钟,根据 LCD 的面板参数进行设置,LCD_CLK 由 PLLSAI进行配置,配置使用到的 HAL 库函数为:
7 d4 E8 }) a$ W7 {/ [( J: h2 B9 g& \
- //pllsain:SAI时钟倍频系数N,取值范围:50~432.
8 T, i% j3 i1 y3 D - //pllsair:SAI时钟的分频系数R,取值范围:2~79 i$ V! V, N! d6 g
- //pllsaidivr:LCD时钟分频系数,取值范围:0~3,对应分频2~16
' R) U' n V: p& E - //假设:外部晶振为25M,pllm=25的时候,Fin=1Mhz.
4 u X5 _* |- `. K# d [% [ - //例如:要得到20M的LTDC时钟,则可以设置:pllsain=400,pllsair=5,pllsaidivr=13 d& ~8 A, v/ B$ u' X; v
- //Fdclk=1*396/3/2*2^1=396/12=33Mhz
9 |+ _8 C7 M+ |* V6 a - RCC_PeriphCLKInitTypeDef PeriphClkIniture;
# ~; P. z. Z' ]' W
9 e& B; I/ F$ a/ z- //LTDC输出像素时钟,需要根据自己所使用的LCD数据手册来配置!
. Q2 G/ a" h- ?: s - PeriphClkIniture.PeriphClockSelection=RCC_PERIPHCLK_LTDC; //LTDC时钟
: q1 j/ n( M3 b. w) [1 { - PeriphClkIniture.PLLSAI.PLLSAIN=pllsain;
+ I4 [$ ?' q2 ?* N, l - PeriphClkIniture.PLLSAI.PLLSAIR=pllsair;" G+ n: v" _7 c3 \: D
- PeriphClkIniture.PLLSAIDivR=pllsaidivr;
; N" K5 G/ Y' ^" u, ] - HAL_StatusTypeDef HAL_RCCEx_PeriphCLKConfig(RCC_PeriphCLKInitTypeDef *PeriphClkInit)
复制代码 # |9 N, o0 @0 c! z
3.设置RGBLCD相关参数,并使能LTDC
# Y6 x* N2 h" S& g% |- typedef struct
4 g( q, {5 Z4 T4 ] i - {
; r1 `8 a/ Y3 F' [& Z - uint32_t HSPolarity; //水平同步极性' \0 u6 |! s! Y
- uint32_t VSPolarity; //垂直同步极性
2 G2 Y; i! t* i - uint32_t DEPolarity; //数据使能极性1 j! }9 |1 k; I( Q! f; t
- uint32_t PCPolarity; //像素时钟极性& a6 \. @0 F' E4 x2 `" P, @
- uint32_t HorizontalSync; //水平同步宽度 8 X' g- t; s+ K8 N! |
- uint32_t VerticalSync; //垂直同步高度
( E+ q D4 P1 _* o! n& f - uint32_t AccumulatedHBP; //水平同步后沿宽度; H) s) u0 ]+ h. m
- uint32_t AccumulatedVBP; //垂直同步后沿高度
}; e9 `+ t9 X8 d8 ]3 ] - uint32_t AccumulatedActiveW; //累加有效宽度 0 q3 Q8 M! R/ H; F; n
- uint32_t AccumulatedActiveH; //累加有效高度7 I9 w* d3 W* v
- uint32_t TotalWidth; //总宽度
2 r2 N6 D8 h6 O* R1 H - uint32_t TotalHeigh; //总高度; f7 X: ^/ }2 n* C
- LTDC_ColorTypeDef Backcolor; //屏幕背景层颜色
1 g$ }/ u( q3 Y7 y; q) ? - } LTDC_InitTypeDef;. L. h* M! l0 f2 {6 l" C! r
- N3 ~) h% p& H
- typedef struct
" J* E+ T, I. [5 e& }2 ] - {$ O9 A( x5 N; t, l4 n I9 C
- LTDC_TypeDef *Instance; /*Instance变量是LTDC_TypeDef结构体指针类型*/
) s& r4 l7 A4 t/ _ - LTDC_InitTypeDef Init; /*用来初始化LTDC的结构体变量*/
. p7 O/ P, k1 K5 J T: c2 x - LTDC_LayerCfgTypeDef LayerCfg[MAX_LAYER]; /*保存LTDC层配置参数*/2 K5 h6 }# H# E. ^' X, ?. q6 ~' n
- HAL_LockTypeDef Lock; /*锁状态*/
! |+ z7 b! f2 Z0 n# V0 A - __IO HAL_LTDC_StateTypeDef State; /*LTDC的状态*/
3 C; p. A; k0 `3 S - __IO uint32_t ErrorCode; /*错误处理*// T" E4 q. [# y1 O P9 `
- } LTDC_HandleTypeDef;, T, f- i0 B$ v' P$ o( p
- 5 _) u; v9 }) J* Z7 Q7 \( w. j
- LTDC_HandleTypeDef LTDC_Handler; //LTDC句柄5 u. o- ~3 a ?0 I
- / p3 X4 Q: W/ {# F
- LTDC_Handler.Instance=LTDC;
+ [- R( w1 _+ H4 v% d - LTDC_Handler.Init.HSPolarity=LTDC_HSPOLARITY_AL; //水平同步极性
# m2 h/ X* ]! i b* { - LTDC_Handler.Init.VSPolarity=LTDC_VSPOLARITY_AL; //垂直同步极性# e( h" y2 m: a' K
- LTDC_Handler.Init.DEPolarity=LTDC_DEPOLARITY_AL; //数据使能极性- m" p) h2 k& i$ P( Y% D5 R
- LTDC_Handler.Init.PCPolarity=LTDC_PCPOLARITY_IPC; //像素时钟极性. }1 u5 Y0 Z( n, ^5 H. c
- LTDC_Handler.Init.HorizontalSync=10-1; //水平同步宽度, |4 u2 q g& X! P$ B7 I8 e
- LTDC_Handler.Init.VerticalSync=2-1; //垂直同步宽度
; D# E9 j6 x/ ^7 J! c - LTDC_Handler.Init.AccumulatedHBP=10+20-1; //水平同步后沿宽度
7 D3 R/ E9 @/ m" Q4 u1 U - LTDC_Handler.Init.AccumulatedVBP=2+2-1; //垂直同步后沿高度% B9 _3 x1 V% H4 q# `7 _* B
- LTDC_Handler.Init.AccumulatedActiveW=10+20+480-1; //有效宽度
3 N2 ~: g. V0 Q( `3 l( N) Z - LTDC_Handler.Init.AccumulatedActiveH=2+2+272-1; //有效高度/ O2 r( m! A* x" n8 T6 V6 M# h
- LTDC_Handler.Init.TotalWidth=10+20+480+10-1; //总宽度
. f0 w: [ s5 V: N2 \3 ~0 S - LTDC_Handler.Init.TotalHeigh=2+2+272+4-1; //总高度
4 }. u, k% u6 n$ ?6 s [/ o - LTDC_Handler.Init.Backcolor.Red=0; //屏幕背景层红色部分
! H- g# `8 d0 u) @1 y6 E - LTDC_Handler.Init.Backcolor.Green=0; //屏幕背景层绿色部分- ^' N! l# }+ H( O+ o5 j
- LTDC_Handler.Init.Backcolor.Blue=0; //屏幕背景色蓝色部分
$ C5 T- E; n$ Y: B3 f - HAL_LTDC_Init(<DC_Handler); //设置 RGBLCD 的相关参数,并使能 LTDC,会调用HAL_LTDC_MspInit
复制代码 . D7 g- a: v& ^! S% g- G0 ^
4.设置 LTDC层参数0 ~7 ^( z' _( C% X2 r; d
- //layerx:层值,0/1.
3 @+ l6 j& u* @. t7 w9 C& f+ A2 ? - //bufaddr:层颜色帧缓存起始地址
: X6 p/ q* j* }1 i i+ s p1 k - //pixformat:颜色格式.0,ARGB8888;1,RGB888;2,RGB565;3,ARGB1555;4,ARGB4444;5,L8;6;AL44;7;AL88. D+ W& A% {* x
- //alpha:层颜色Alpha值,0,全透明;255,不透明* \1 p$ b6 f9 R k# J& p0 c K
- //alpha0:默认颜色Alpha值,0,全透明;255,不透明5 ^) I* L7 n6 ~" Q' P8 u
- //bfac1:混合系数1,4(100),恒定的Alpha;6(101),像素Alpha*恒定Alpha
$ i, |" x3 E- [- l2 H" O - //bfac2:混合系数2,5(101),恒定的Alpha;7(111),像素Alpha*恒定Alpha) S+ e2 O& S. ?( ~
- //bkcolor:层默认颜色,32位,低24位有效,RGB888格式
) D* P+ ]: | c, d% M; m, w - void LTDC_Layer_Parameter_Config(u8 layerx,u32 bufaddr,u8 pixformat,u8 alpha,u8 alpha0,u8 bfac1,u8 bfac2,u32 bkcolor)
9 ]; L5 P( V7 |8 h6 u - {" m% w2 V& j: e- s
- LTDC_LayerCfgTypeDef pLayerCfg;. v! i' a m* @
, }6 A- I2 Q( C; ~/ W1 H- pLayerCfg.WindowX0=0; //窗口起始X坐标8 O9 v3 u' H$ A! W9 s8 ^1 G/ f
- pLayerCfg.WindowY0=0; //窗口起始Y坐标# N3 C6 `! A$ T) ?% O9 V
- pLayerCfg.WindowX1=lcdltdc.pwidth; //窗口终止X坐标: \+ _5 q2 [ Y, F$ ?( P) N$ [
- pLayerCfg.WindowY1=lcdltdc.pheight; //窗口终止Y坐标) N/ r9 p p' @7 o3 h' Z( w
- pLayerCfg.PixelFormat=pixformat; //像素格式/ E5 d. {' R' t' D4 V
- pLayerCfg.Alpha=alpha; //Alpha值设置,0~255,255为完全不透明
/ a8 `* I6 r/ Q( x2 y4 z2 m' R' A - pLayerCfg.Alpha0=alpha0; //默认Alpha值
& H w3 d3 W4 n: ` [ - pLayerCfg.BlendingFactor1=(u32)bfac1<<8; //设置层混合系数: B, g9 c+ y* y! f2 V8 N1 i2 d
- pLayerCfg.BlendingFactor2=(u32)bfac2<<8; //设置层混合系数
' D2 l$ R/ R1 \* } - pLayerCfg.FBStartAdress=bufaddr; //设置层颜色帧缓存起始地址# k7 v: {4 B! F1 G7 d) S' g- Z
- pLayerCfg.ImageWidth=lcdltdc.pwidth; //设置颜色帧缓冲区的宽度 , e, H7 v- A( ` Q! t% ^8 Q
- pLayerCfg.ImageHeight=lcdltdc.pheight; //设置颜色帧缓冲区的高度
. X4 | V' g, n3 g$ G0 L: | - pLayerCfg.Backcolor.Red=(u8)(bkcolor&0X00FF0000)>>16; //背景颜色红色部分
- Y3 W9 o" f2 J7 c; p4 r, J! Q# S; { - pLayerCfg.Backcolor.Green=(u8)(bkcolor&0X0000FF00)>>8; //背景颜色绿色部分, C* l: n& g# r7 {
- pLayerCfg.Backcolor.Blue=(u8)bkcolor&0X000000FF; //背景颜色蓝色部分- ]- v/ R* R9 @2 v% Z
- HAL_LTDC_ConfigLayer(<DC_Handler,&pLayerCfg,layerx); //设置所选中的层
" a4 \) S2 A$ K8 B5 l; T - }
# Q: `( ^# e0 r: M - 1 ~" g7 Q) ~. I0 @ t; A
- //在init时调用- Z4 g( X. ]) F, K j' k) K+ [7 q
- LTDC_Layer_Parameter_Config(0(u32)ltdc_framebuf[0],LCD_PIXFORMAT,255,0,6,7,0X000000); //层参数配置
复制代码 1 j' N+ ^; S7 S, X, v
5.设置LTDC层窗口9 B; W1 w( V/ l3 t) z _
- //layerx:层值,0/1.
& x7 X) F: K1 I0 d - //sx,sy:起始坐标 E' G* r* p" \( _
- //width,height:宽度和高度. [4 d' R" Y' _
- void LTDC_Layer_Window_Config(u8 layerx,u16 sx,u16 sy,u16 width,u16 height)5 q4 D* p: H# }& Q4 k8 L2 }6 q4 E7 [9 P
- {
3 e, D/ d! J$ Y5 m4 @% w - HAL_LTDC_SetWindowPosition(<DC_Handler,sx,sy,layerx); //设置窗口的位置7 ~; |- T% o* D. R7 `* r# Z& e
- HAL_LTDC_SetWindowSize(<DC_Handler,width,height,layerx); //设置窗口大小 $ T, M; y. D7 h7 S
- }$ d* |7 }# y$ n* F3 M6 o
- ! V H; s! c3 p: D, \& M7 W
- //在init时调用
l+ I+ \: T `/ ^' O - LTDC_Layer_Window_Config(0,0,0,lcdltdc.pwidth,lcdltdc.pheight); //层窗口配置,以LCD面板坐标系为基准
复制代码
+ a" d5 e1 T( v! D, n: O+ U完成了 LTDC 的配置,可以控制 RGBLCD 显示了。4 N7 ^6 i9 ]1 l- @& z" X
9 g7 c9 l/ g; ]- F& i3 @. H: ~
使用DMA2D完成颜色填充
5 d2 ?2 l- M& }/ O使用官方提供的 HAL 库 DMA2D 相关库函数进行颜色填充效率极为低下,大量时间浪费在函数的入栈出栈以及过程处理,所以在项目开发中一般都不会使用 DMA2D 库函数进行颜色填充
]( K8 |& n. K! V& Y% I. m0 ]
( B! t8 ~/ Y* Q: t. \, d: T1)使能 DMA2D 时钟,并先停止 DMA2D。
: w& t0 h9 f( K; O/ i1 `要使用 DMA2D,先得开启其时钟。然后 DMA2D 在配置其相关参数的时候,需要先停止DMA2D 传输。 使能 DMA2D 时钟和停止 DMA2D 方法为:* y* C3 X2 }3 [ }2 j
- __HAL_RCC_DMA2D_CLK_ENABLE(); //使能 DM2D 时钟
) R: C) z* x" |# y9 y: B - DMA2D->CR&=~DMA2D_CR_START; //停止 DMA2D
复制代码
/ I& Q/ S( [7 {2)设置 DMA2D 工作模式。( C8 [5 a$ v, w: J5 k
通过 DMA2D_CR 寄存器,配置 DMA2D 的工作模式。我们用了寄存器到存储器模式和存储器到存储器这两个模式。0 g$ {7 X( S. @/ q2 b
寄存器到存储器模式设置:
2 _1 Q& F+ r8 j* I: |% q5 |- DMA2D->CR=DMA2D_R2M; //寄存器到存储器模式
复制代码 ' D! |( X( v. `
存储器到存储器模式设置:6 C8 f+ D. ~: t# k1 @9 s1 `. L# ]7 `
- DMA2D->CR= DMA2D_M2M; //存储器到存储器模式
复制代码
! n- v5 b: S5 t* {, T% C3)设置 DMA2D 的相关参数。
% x$ N& i, J/ D6 B1 p# w这一步,我们需要设置:颜色格式、输出窗口、输出存储器地址、前景层地址(仅存储器到存储器模式需要设置)、颜色寄存器(仅寄存器到存储器模式需要设置)等,由:DMA2D_OPFCCR、DMA2D_FGPFCCR、DMA2D_OOR、DMA2D_FGOR 、DMA2D_OMAR、DMA2D_FGMAR 和 DMA2D_NLR 等寄存器进行配置。具体配置过程请参考实验源码。
7 v+ I4 z+ G. d6 O P1 m( z# _% ^" m/ s
4)启动 DMA2D 传输。
/ m8 K6 D$ _+ o1 E/ z通过 DMA2D_CR 寄存器配置开启 DMA2D 传输,实现图像数据的拷贝填充,方法为:* y& E/ ^ E/ o
3 b. U5 r5 w; t/ e* YDMA2D->CR|=DMA2D_CR_START; //启动 DMA2D* g0 W/ g( o* A# n) D
1/ ^/ U8 O) T3 S7 d" \
5)等待 DMA2D 传输完成,清除相关标识。
% \4 a- g3 {, ~2 l最后,在传输过程中,不要再次设置 DMA2D,否则会打乱显示,所以一般在启动 DMA2D后,需要等待 DMA2D 传输完成(判断 DMA2D_ISR),在传输完成后,清除传输完成标识(设置 DMA2D_IFCR),以便启动下一次 DMA2D 传输。
$ L. y! L9 z) u4 O) p, E
3 b6 [- P9 O- t( C- while((DMA2D->ISR&DMA2D_FLAG_TC)==0) ; //等待传输完成' \- L e" R* {+ }! R# Z
- DMA2D->IFCR|=DMA2D_FLAG_TC; //清除传输完成标志
复制代码 5 [! H- C3 P' x3 Z
通过以上几个步骤,我们就完成了 DMA2D 填充
, R" F4 u- Q$ v1 l: R e& ?* ]" `2 @
使用示例
6 u3 o( ^8 l; W' k- //LCD LTDC重要参数集/ P" Y- F9 Y7 }
- typedef struct
" I+ _" M3 A3 @5 U4 D - {; p0 S* g8 ^/ W
- u32 pwidth; //LCD面板的宽度,固定参数,不随显示方向改变,如果为0,说明没有任何RGB屏接入6 E+ B: g4 N; z# g8 B. ]& Y0 R
- u32 pheight; //LCD面板的高度,固定参数,不随显示方向改变2 h4 \) e4 Y7 O4 Z1 ^
- u16 hsw; //水平同步宽度 Horizontal synchronization width8 d6 q: P* M, u `
- u16 vsw; //垂直同步宽度 Vertical synchronization width
, U9 d" Z* D) x, j2 X - u16 hbp; //水平后廊6 x& o6 Y( N m4 ?% K- q
- u16 vbp; //垂直后廊' r! O, S) W2 h ?0 t
- u16 hfp; //水平前廊
6 [/ n& T( W7 X - u16 vfp; //垂直前廊- Y" h8 c) l; P6 z, @9 l
- u8 activelayer; //当前层编号:0/1
; s% V0 L+ [6 z+ G# `; k6 Q - u8 dir; //0,竖屏;1,横屏;, N) u6 S- D8 c# ^8 z# n
- u16 width; //LCD宽度
* S o; r# ?& A+ F1 K! q1 ] - u16 height; //LCD高度
& ~) _# d4 @2 l3 F+ G) G - u32 pixsize; //每个像素所占字节数
! s% b! D# L4 Q P- e2 r7 W$ Z6 h - }_ltdc_dev;
! N2 f# O9 T) P4 N
2 @# x' B* N T- _ltdc_dev lcdltdc; //管理LCD LTDC的重要参数0 Y; q8 j+ T$ N/ O' ?7 f1 r
- i2 S# T. ]6 D3 H* P- //LTDC填充矩形,DMA2D填充
/ s X' b$ P* p - //(sx,sy),(ex,ey):填充矩形对角坐标,区域大小为:(ex-sx+1)*(ey-sy+1) # b t; D3 L Z, {) M; Z/ S
- //注意:sx,ex,不能大于lcddev.width-1;sy,ey,不能大于lcddev.height-1!!!- t, N7 p' R) l; x5 P
- //color:要填充的颜色" z4 s" [6 O+ H
- void LTDC_Fill(u16 sx,u16 sy,u16 ex,u16 ey,u32 color). N; x/ L, _. [5 P& p
- {
8 \9 U4 H. d2 f - u32 psx,psy,pex,pey; //以LCD面板为基准的坐标系,不随横竖屏变化而变化2 P% @- Q# L6 n# w' R
- u32 timeout=0; . @7 u& g6 B/ R# ^
- u16 offline;- z j- n+ _2 A! P
- u32 addr;
( O% h# V, u7 O4 O: [' y H8 |- X7 J - //坐标系转换! q# T: T c3 n) s7 ?
- if(lcdltdc.dir) //横屏; ~0 Z" e: F& d) x* l* A2 p- v
- {
8 Y0 F5 t3 u# u. a6 i( |- Q - psx=sx;psy=sy;
. A1 V2 R$ I2 Z, { - pex=ex;pey=ey;
. e V7 E: v' v N* ?! H3 C - }else //竖屏
, J& R' [1 j- ?1 [5 ~ - {( t* Y- @# ~1 f6 W& z
- psx=sy;psy=lcdltdc.pheight-ex-1;# `9 U" A$ J+ e' T3 G' f
- pex=ey;pey=lcdltdc.pheight-sx-1;
3 y) {: f9 z% }! l# K2 W - }
\7 E8 l u9 a" V% F$ X - offline=lcdltdc.pwidth-(pex-psx+1);
8 y2 U9 i0 X% |6 U- k9 c4 [. Y - addr=((u32)ltdc_framebuf[lcdltdc.activelayer]+lcdltdc.pixsize*(lcdltdc.pwidth*psy+psx));
, {; I9 ]. m* P3 ^ ~' s! M - RCC->AHB1ENR|=1<<23; //使能DM2D时钟& z4 M1 y f; V" o* T9 G5 |
- DMA2D->CR=3<<16; //寄存器到存储器模式2 i4 W! T, F5 Q3 H2 h8 T
- DMA2D->OPFCCR=LCD_PIXFORMAT; //设置颜色格式
" d5 |% @& r3 E6 m/ J5 u: u - DMA2D->OOR=offline; //设置行偏移
; y7 l. S% [3 t9 Z! t. S& Z4 v7 d - DMA2D->CR&=~(1<<0); //先停止DMA2D
0 } r3 j; K5 K+ k9 k& N - DMA2D->OMAR=addr; //输出存储器地址
# | d% ?" y4 P7 P7 z# n- c - DMA2D->NLR=(pey-psy+1)|((pex-psx+1)<<16); //设定行数寄存器
z$ ]- E$ i4 ` - DMA2D->OCOLR=color; //设定输出颜色寄存器
8 i; i% z, N( p3 r - DMA2D->CR|=1<<0; //启动DMA2D& r/ y# y# B8 @& I0 r; q
- while((DMA2D->ISR&(1<<1))==0) //等待传输完成
& i* z( o& _5 U* Z# R - {
+ _& m' q' \' [ s5 E - timeout++;
, y) m& C8 f: O7 A; w - if(timeout>0X1FFFFF)break; //超时退出0 O# S- `& ^. M: V' a
- }
3 n2 j+ @# E4 K! A" Y - DMA2D->IFCR|=1<<1; //清除传输完成标志* g& M' Y" ] H# p. u/ Z/ U9 o% m
- }& G0 R; t. ?8 E) A5 d* n4 K6 g! y
) l& B. C7 x! u+ W- //LCD清屏
. N$ h6 x9 t& q - //color:颜色值 r" \9 x/ i1 z4 }# s
- void LTDC_Clear(u32 color)
8 `$ ~, i$ R5 {3 r - {2 n- t" f1 w5 w0 j, o* G/ c2 K6 s: b
- LTDC_Fill(0,0,lcdltdc.width-1,lcdltdc.height-1,color);, V( ~3 |/ `( H6 K5 \4 u
- }
; r: K. ~6 K' \- m- P - 4 T# x0 t% @( e- P ^
- 5 P3 N4 U4 X( _/ S* U4 p( {4 ~! u
复制代码
. O) Z% K- y- L7 B4 B4 S" T
4 X! |+ U# k/ M |