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