一 . 简介& J- Q4 H r3 b
本次教程使用的是1.54寸240*240像素的tft屏幕,其接口协议为SPI协议。在使用的过程中仅需要四根数据即可驱动点亮屏幕。然后硬件使用的是STM32F103C8T6核心板,用的是SPI2。一般购买屏幕的话它们会提供对应的例程,直接拿过来修改即可。0 g5 s2 D* D5 B7 y
, ?2 z; i3 q& _$ y& d二. 屏幕的使用: @# v# @6 J, w
第一步肯定是引脚初始化,接口是SPI接口,所以也需要对SPI进行初始化(软件模拟SPI时序的话就不需要,硬件SPI需要),如果需要用到DMA进行加速的话,也需要对DMA进行初始化(不用DMA显示一副图片的速度比使用DMA的要慢上几倍,如果不显示图片的话可以不进行初始化)。* q& G3 Q A0 P
4 S j$ L9 Q I- R J1.屏幕引脚介绍:
: ]# M! v! P) K2 uDC: 写数据后命令控制,1为数据
0 o$ h! t, _5 D' p V9 uCS:片选,低电平有效
4 L/ Y$ n# p" v9 X oRES: 控制lCD重启4 S2 {" o: D/ y( T4 x
SDA:SPI的MOSI线
% c* Z8 \3 A" l. d/ ^7 QSCK:SPI的时钟线
4 n. i$ v; _; X, m( wBLK:控制LCD是否显示,不进行控制的话直接接高电平即可(这里直接接的高电平)
- p9 ]& J* K$ c2.引脚初始化
: a* g* ]' J( X' r% k, j这里除了SDA和SCK是SPI信号线需要进行复用之外,其他的都可以当做普通的IO进行初始化即可。查看手册可以知道SPI2的时钟映射到了GPIOB-Pin13,MOSI映射到了GPIOB-Pin15(一下子找不到了 ̄□ ̄||)。对其引脚进行复用初始化即可。
8 Y6 M8 ?- A: u3.SPI初始化 c7 h' s) y6 `5 \ ? Y1 l
这里就直接上代码了
' z1 j3 o, l" x, t: h, z- SPI_InitTypeDef SPI_InitStructure;# I( U, P1 n+ J S
- RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2,ENABLE); //初始化SPI2端口时钟
+ N- ?. X5 [; r1 \( z; [: ^ - SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;- X! w; y9 x0 [1 ~3 B- @+ q2 |4 h; o. u
- SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;% I* D9 m7 z- ?5 ]% t. D3 |1 f" b
- SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;; f# ^! G5 x% C ~
- SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
5 r# O& i9 X: g5 @ - SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;; o8 O5 J/ u" R# D7 @! b
- SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;. ^' Q% U, o! J; o7 d: m; V
- SPI_InitStructure.SPI_Direction = SPI_Direction_1Line_Tx;
. Z- D: V `% R' n* F6 L - SPI_InitStructure.SPI_CRCPolynomial = 7;
0 \. ~! {6 U2 i% }6 n2 B U - SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;
& e! b, ^! ?0 @2 y1 Y - SPI_Init(SPI2,&SPI_InitStructure);" Q: N( W7 _6 {3 h" w+ {
- SPI_Cmd(SPI2,ENABLE);
复制代码
* H9 ~) }5 Y3 Z2 i4.DMA初始化
M0 A3 n* y% \. q( g需要查看SPI2的Tx是使用的DMA1的那个通道,这个也需要查看手册。如下图,映射到的时候通道5,根据这个就可以进行初始化了。1 u( L* U) f' L# r
* J- q+ _1 @0 c
/ j/ ^+ \9 {4 y7 r- o! Q3 [' ]
% A3 X. \, U% E3 |6 l- DMA_InitTypeDef DMA_InitStructure;
! h3 _4 H- X' Z& {. j - 6 q6 _8 T$ q& C' T, K
- RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
, ]& k* A+ r) [. q$ ?
4 ^* C9 @1 E2 t) G9 g2 y- DMA_DeInit(DMA1_Channel5); : p. E. Y. V W; v
- DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32)&SPI2->DR;8 C" e- H3 x2 P9 C2 E
- DMA_InitStructure.DMA_MemoryBaseAddr = (uint32)buffer; //发送数据的内存地址" ^6 z: i' n5 L, p2 m8 E
- DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; //方向为内存到外设' }% a6 o1 S/ ~
- DMA_InitStructure.DMA_BufferSize = 480; //数据大小
- c9 s$ h* ~+ m- _# A( y - DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //每发送一个外设地址不变 r2 |) [+ e- c2 f5 P- S* R9 I9 m
- DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //每发送一个内存地址加一# D* N) ?0 c, G0 v9 l
- DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;) L$ I" T6 z. H7 w% S
- DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;6 [ o. I( d0 o, D- d4 {, |
- DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;7 P4 ]' c3 N9 ?5 j, b7 m
- DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;
* M; [" }4 _4 r) N& U - DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;! [% d6 h0 ^4 S8 g$ t
- DMA_Init(DMA1_Channel5,&DMA_InitStructure);
复制代码 # N) i( K) s) ^: S( N {1 u
5.然后就是对LCD屏幕的初始化了,也就是配置对应的寄存器。这个可以拿卖家提供的直接拿来用就可以。
4 ^6 S+ V- B" E7 B这里我们就完成了所有的初始化工作了。
# |3 Y ]& h' G3 W+ s1 }0 k
2 U% J U( I2 z! E1. 设置数据地址范围
" d4 x E( m' f每次发送显示数据的时候,需要设置显示范围,也就是行列的起始值。设置好范围,然后就发送对应大小的数据即可。其显示发送为从左往右,从上到下。
7 ?) F( L: f% i! C
& {# i" P; f( x9 w b( ~2 g: b5 T
5 ?* ]2 z8 N: l
- 9 ?8 l* b: q8 |. b K' T" Z. u
- //设置数据显示地址范围2 p% C$ |$ f3 M
- void LCD_Address_Set(uint16 x1,uint16 y1,uint16 x2,uint16 y2)
* X- L8 [1 z3 j9 q: M - {
' R! x8 T" s7 y% Q5 e; }" p! d; W - LCD_Write_Cmd(0x2a);//列地址设置+ G& E; P7 a; @9 ^$ C" ]/ ~
- LCD_WriteDisplayData16(x1);# ~4 g4 i/ k/ b) T1 _
- LCD_WriteDisplayData16(x2);, g: e" [; V, L8 R8 _( Y
- LCD_Write_Cmd(0x2b);//行地址设置4 ^2 y* w# t: l" d
- LCD_WriteDisplayData16(y1);
1 Y1 f: I2 d' W* ` - LCD_WriteDisplayData16(y2);5 D$ l$ J; `: b
- LCD_Write_Cmd(0x2c);//储存器写5 \% l) |4 L$ i9 S% T) ]
- }
复制代码 3 p' i! N. m! S9 h9 T# L
2. 点亮LCD屏幕
7 l2 l+ m" l/ `6 ~6 r, R8 f1 V- //LCD全亮$ n8 X; v4 Q; g$ V) |) a% A
- void LCDFullOn(uint16 Color)* s8 {0 R8 T9 ]. J; b
- {* L5 @5 L" X1 q/ T( e. E' w
- uint16 i,j; % ?# j- ~( t2 O& K" @
- LCD_Address_Set(0,0,LCD_Width-1,LCD_Height-1);! {% M! V( E+ K
- for(i=0;i<LCD_Width;i++)1 i, L% P6 q* E; V& k
- { , a4 F+ f+ S, S
- for (j=0;j<LCD_Height;j++)
6 g: j& D) q. M! _ - {2 g1 \3 I" J# p$ L' q& e
- LCD_WriteDisplayData16(Color); v* _) t R& ~; h7 l1 E V
- } 8 I! T2 h' F. s/ A- W% O
- }" t$ q. J8 ^( W+ W( M( s
- }
复制代码 ) X4 t! l0 s. h& l. Z2 K
3. 使用DMA搬运数据
9 g9 \! Q' {7 t! {1.对设置DMA搬运数据的大小并且使能
; T6 X' O& `% |7 L- //spi2 DMA使能
4 D' b4 C* d( [* g - void SPI2DMA_Enable(void). _$ h* i& C7 e# @
- {
* i- n( o# \! b ~2 J - DMA_Cmd(DMA1_Channel5, DISABLE ); 0 B) G) g6 W" A
- DMA_SetCurrDataCounter(DMA1_Channel5,480); //设置搬运数据的大小
1 ?3 f; Y! z( b; D0 N4 r - DMA_Cmd(DMA1_Channel5, ENABLE); 6 }( i P5 q" V M. O/ C7 j
- }
复制代码 ) w+ W% e2 [! F- b- G" n- Q9 W
2.开启DMA请求,且等待发送完成,清除标志位4 O- V5 k/ q# Q0 V P# m9 Y
- for(j = 0 ; j< 240 ; j++)- x8 U j. x/ ]. @
- {1 p' u- K7 Z$ z2 o: f" e" E, N
- SPI_I2S_DMACmd(SPI2,SPI_I2S_DMAReq_Tx,ENABLE); 2 C4 B" \2 E5 d6 n- q B3 Z$ [8 ?3 m
- SPI2DMA_Enable();$ N1 E% c! b) U8 R) h3 r
- while((DMA_GetFlagStatus(DMA1_FLAG_TC5)==RESET)); // 等待dma发送完成0 p ?2 F$ U4 X8 R! v8 x9 ~3 W
- DMA_ClearFlag(DMA1_FLAG_TC5);- k0 _8 b# ?0 k1 P V
- }
复制代码
- r$ c5 H' V# ^通过测试,可以明显看到使用DMA进行加速的显示效果要快很多。
9 [+ Y" ?$ Z$ a
E [# L6 X7 w: o0 d: y1 S# d1 `. h1 J8 R% n% i+ p1 b# U5 N# [! z
+ {' G$ }) ^6 ~
|