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