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