一 . 简介' B% J& T2 r& E2 u6 ^- B0 ^. a
本次教程使用的是1.54寸240*240像素的tft屏幕,其接口协议为SPI协议。在使用的过程中仅需要四根数据即可驱动点亮屏幕。然后硬件使用的是STM32F103C8T6核心板,用的是SPI2。一般购买屏幕的话它们会提供对应的例程,直接拿过来修改即可。( v1 v5 }. W" L8 u
0 o) V1 V. {& `二. 屏幕的使用
9 I3 m J0 H9 \8 s; X$ ~第一步肯定是引脚初始化,接口是SPI接口,所以也需要对SPI进行初始化(软件模拟SPI时序的话就不需要,硬件SPI需要),如果需要用到DMA进行加速的话,也需要对DMA进行初始化(不用DMA显示一副图片的速度比使用DMA的要慢上几倍,如果不显示图片的话可以不进行初始化)。7 J; j% K7 D* \ ?
- e4 F$ ^) q9 S# E
1.屏幕引脚介绍:
" d1 E' r z2 }/ w% uDC: 写数据后命令控制,1为数据
- N6 H! q. m5 E# tCS:片选,低电平有效6 w, B+ S, X) X, w5 {8 z
RES: 控制lCD重启
7 i3 S" F; r# P' J) z0 n. w7 eSDA:SPI的MOSI线
+ C. A3 Z( j; u) {, X2 ZSCK:SPI的时钟线
! ?* w: L* H) uBLK:控制LCD是否显示,不进行控制的话直接接高电平即可(这里直接接的高电平)4 v: r5 E+ b5 k8 X' T* V! m4 T
2.引脚初始化
- T2 i7 j# f& T# ]这里除了SDA和SCK是SPI信号线需要进行复用之外,其他的都可以当做普通的IO进行初始化即可。查看手册可以知道SPI2的时钟映射到了GPIOB-Pin13,MOSI映射到了GPIOB-Pin15(一下子找不到了 ̄□ ̄||)。对其引脚进行复用初始化即可。; T" N; G4 F& ?# F( D& N
3.SPI初始化5 n4 c6 ]: F7 b- x% N0 |
这里就直接上代码了- e' o- B; a. O: D( C& Y. Z7 t
- SPI_InitTypeDef SPI_InitStructure;! @8 R, X+ }. L1 r7 R/ r( j" n
- RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2,ENABLE); //初始化SPI2端口时钟& b% H4 r- K$ A' s3 i
- SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
- H7 _* e- I- W3 _6 f/ B/ g; q5 h - SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;+ z4 R* a7 V) E) C+ F! o8 @
- SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;, V$ @# r- W& u* w( ]8 q
- SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
6 s7 V* V) B- [& F" v - SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;+ r$ X% r, \$ h0 n- X$ W0 k0 p9 @$ D- R
- SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
8 I: R5 E% b/ I3 R - SPI_InitStructure.SPI_Direction = SPI_Direction_1Line_Tx;
7 i r; Z+ z1 G8 Q5 H - SPI_InitStructure.SPI_CRCPolynomial = 7;
r9 ?9 P( E. G- @% g - SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;
" W6 y* V5 n, f" d - SPI_Init(SPI2,&SPI_InitStructure);& O' C) E4 i9 p" b- _6 [
- SPI_Cmd(SPI2,ENABLE);
复制代码 , f: s0 c0 G7 `( x/ ^! M
4.DMA初始化
; ^2 k. v/ w3 r3 J需要查看SPI2的Tx是使用的DMA1的那个通道,这个也需要查看手册。如下图,映射到的时候通道5,根据这个就可以进行初始化了。
9 j) y; L! F+ ?5 |6 m% ?9 W4 a" S6 F: }- X3 P' {$ @) I, d* ?* D
1 Z/ C% \8 o3 s6 u
3 P2 Y, ^0 I' d& Z' D8 }: _8 @
- DMA_InitTypeDef DMA_InitStructure;
% x0 \; @; z, f, w8 D
* S6 d9 `. c2 |3 {# {: R- RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);/ J- |/ N2 A$ m7 \
: [+ j, Z n; c- DMA_DeInit(DMA1_Channel5); 0 y& ?8 q& ], E# [
- DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32)&SPI2->DR;5 M7 i' F b" \9 T! M
- DMA_InitStructure.DMA_MemoryBaseAddr = (uint32)buffer; //发送数据的内存地址4 B8 P9 |' r- G, T. N- u5 f
- DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; //方向为内存到外设
% A5 [5 ~- m) [- l! ? - DMA_InitStructure.DMA_BufferSize = 480; //数据大小
) L% L! L5 \! ]2 a - DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //每发送一个外设地址不变8 ` s$ l Q& r* O& N
- DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //每发送一个内存地址加一
7 e& k3 x7 v) a$ q - DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
+ z; p+ f% ?: @ c$ R - DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;5 |2 ?6 R; ^9 k6 p9 K0 x' N1 o9 Z
- DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
4 S+ M( a+ p+ G9 }) ?9 K. h, L - DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;: ^. Y" o1 ?; h8 A1 ^) z, `* }+ I. q
- DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
) }* }* k0 c" X6 b - DMA_Init(DMA1_Channel5,&DMA_InitStructure);
复制代码
$ J8 N8 v6 w+ \3 [3 s' s) Q5.然后就是对LCD屏幕的初始化了,也就是配置对应的寄存器。这个可以拿卖家提供的直接拿来用就可以。
- A) F- ? b8 s. K) ~- m这里我们就完成了所有的初始化工作了。
8 A1 A j& A; H! r) V) V% |# ~4 ~$ J# Q
1. 设置数据地址范围
% _: E+ s1 z2 J每次发送显示数据的时候,需要设置显示范围,也就是行列的起始值。设置好范围,然后就发送对应大小的数据即可。其显示发送为从左往右,从上到下。
/ W" K; y0 R/ T% g4 C
' t; r0 n7 m) Z) ?: H, u
5 P+ }0 I r7 f& F6 E% F
7 N( G) h0 v/ L7 I; P1 A$ x- //设置数据显示地址范围0 u* z6 b$ I7 ^7 }: m; c8 S
- void LCD_Address_Set(uint16 x1,uint16 y1,uint16 x2,uint16 y2)
' [ b* y1 L5 U7 X X - {
! A3 ]7 R- r; L& n7 N - LCD_Write_Cmd(0x2a);//列地址设置
$ h' L4 e* P, | - LCD_WriteDisplayData16(x1);6 C0 n. C; u" O$ W8 a
- LCD_WriteDisplayData16(x2);
6 U2 D* Q! O: z - LCD_Write_Cmd(0x2b);//行地址设置
! B3 a% Q8 C; J! c/ x% X1 ^ - LCD_WriteDisplayData16(y1);7 X. r% n0 i2 x* ^. r3 x# {
- LCD_WriteDisplayData16(y2);' J2 H5 L0 _' c0 y: }
- LCD_Write_Cmd(0x2c);//储存器写
. g' l3 A( C# D+ l - }
复制代码 - f. d/ }$ w, P0 S, l! M
2. 点亮LCD屏幕. D: c3 K) W$ S4 |3 X- ?( ?
- //LCD全亮
+ {, j$ {2 a, ~, m$ u# B) V - void LCDFullOn(uint16 Color)
5 o7 W) e8 z7 W. b& Q8 [ - {. z- i( j3 _) ?4 F C. i
- uint16 i,j;
- S% J! j5 [7 a - LCD_Address_Set(0,0,LCD_Width-1,LCD_Height-1);
2 k" ^: W, t& i. {, c% L! T' E* o$ c - for(i=0;i<LCD_Width;i++)1 q# y* j# t m
- {
* V) n5 B5 Q/ b9 _ - for (j=0;j<LCD_Height;j++)
: H3 ~ n3 o/ H _ - {1 H/ ?0 J0 K: i; ?+ x# F4 o
- LCD_WriteDisplayData16(Color);1 e5 ~" ^8 }" q, S
- } 2 C0 h0 I' n9 Q% |8 V0 w9 B6 \
- }) N% X( H6 U3 t; ~+ k% R
- }
复制代码 2 s [. F8 z6 y, D9 }0 w, f
3. 使用DMA搬运数据
3 x! u+ }! O7 W; @1.对设置DMA搬运数据的大小并且使能
9 j6 w: W( L: b8 W% {+ D& q- //spi2 DMA使能 - n/ C8 g- b' b' p
- void SPI2DMA_Enable(void). E/ z3 K' H6 T( M4 x! A0 |6 c
- {
: t+ H( E; K' Q3 u. u - DMA_Cmd(DMA1_Channel5, DISABLE );
9 b5 @2 x: O% z+ Y - DMA_SetCurrDataCounter(DMA1_Channel5,480); //设置搬运数据的大小- O! c* \& }9 i. _: }# g+ Q
- DMA_Cmd(DMA1_Channel5, ENABLE);
2 F* l6 ^4 a+ ~ - }
复制代码 ) t: q5 \! |9 x, g! ~* y
2.开启DMA请求,且等待发送完成,清除标志位7 m6 D6 ^6 O/ s$ ]- \1 ~
- for(j = 0 ; j< 240 ; j++)
' q9 }2 h4 c- i8 R" y% W! B) r. g - {
7 i, y! w' |. G: @; o5 ~% r, U - SPI_I2S_DMACmd(SPI2,SPI_I2S_DMAReq_Tx,ENABLE);
8 k9 A1 J6 [) B- B$ k* e - SPI2DMA_Enable();
7 {6 |4 x3 U I" ?# w8 R/ w - while((DMA_GetFlagStatus(DMA1_FLAG_TC5)==RESET)); // 等待dma发送完成
& o1 m6 K v/ h/ K; Y/ h - DMA_ClearFlag(DMA1_FLAG_TC5);; }. @9 z. B/ h3 q
- }
复制代码
) w0 h+ e: e& e: M通过测试,可以明显看到使用DMA进行加速的显示效果要快很多。
$ A" U( p5 t/ r2 n: }
; r) J5 `0 W7 F1 i. l9 Z1 t
: g* s. c+ @9 x t! c% d( Y1 G$ b G7 W) D3 C
|