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