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