一 . 简介
* m& Y; E. c& ]9 F3 A W& i& q本次教程使用的是1.54寸240*240像素的tft屏幕,其接口协议为SPI协议。在使用的过程中仅需要四根数据即可驱动点亮屏幕。然后硬件使用的是STM32F103C8T6核心板,用的是SPI2。一般购买屏幕的话它们会提供对应的例程,直接拿过来修改即可。& D3 E: y+ P3 ^& z/ r) _
2 [8 E7 O3 j9 ?) v3 Y) `% l n7 i) U
二. 屏幕的使用
6 u. C ^9 o4 a0 |0 x第一步肯定是引脚初始化,接口是SPI接口,所以也需要对SPI进行初始化(软件模拟SPI时序的话就不需要,硬件SPI需要),如果需要用到DMA进行加速的话,也需要对DMA进行初始化(不用DMA显示一副图片的速度比使用DMA的要慢上几倍,如果不显示图片的话可以不进行初始化)。' h( l: c# j1 E5 ]8 v" p j
, w9 K( o5 K$ w ?/ Z0 E* v0 d
1.屏幕引脚介绍:2 {7 R: L7 Q. K! u0 c) E
DC: 写数据后命令控制,1为数据
/ V7 x' e. v% a& ACS:片选,低电平有效
4 y2 P H1 U' i% T3 m) ZRES: 控制lCD重启1 T9 ~; {' G% u7 Q+ v
SDA:SPI的MOSI线$ H9 F, |; m# f+ w
SCK:SPI的时钟线0 }5 d( G# `+ b/ p% s0 ]
BLK:控制LCD是否显示,不进行控制的话直接接高电平即可(这里直接接的高电平)
% {0 a! A. U' A" j2.引脚初始化
) h$ n, V# D8 n6 x% u+ I5 x7 [$ _这里除了SDA和SCK是SPI信号线需要进行复用之外,其他的都可以当做普通的IO进行初始化即可。查看手册可以知道SPI2的时钟映射到了GPIOB-Pin13,MOSI映射到了GPIOB-Pin15(一下子找不到了 ̄□ ̄||)。对其引脚进行复用初始化即可。
% J; Z- r/ V$ w1 y( g3.SPI初始化
% S4 q! R4 ]: G0 U: y5 F这里就直接上代码了
* c4 D% r1 e/ J7 ?, f- SPI_InitTypeDef SPI_InitStructure;
% \5 w! U+ e7 Y$ T& ~1 ~- U - RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2,ENABLE); //初始化SPI2端口时钟
; E; m m' P& l, T e3 i, h - SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;& j8 ?4 z5 H" e1 k
- SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
4 D2 y; w& b9 I5 L8 ^- O8 L - SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;* Q* L/ m' W4 y& k" s+ j
- SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
% S5 h: b, w, f: _! u - SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
% I! Z4 e. g" r/ G. O3 G5 F q - SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;; f& F$ T9 X7 f
- SPI_InitStructure.SPI_Direction = SPI_Direction_1Line_Tx;
I3 P# A0 S* D - SPI_InitStructure.SPI_CRCPolynomial = 7;- @. w( G& s" F! x. d
- SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;
9 r; L7 B% I9 X7 F - SPI_Init(SPI2,&SPI_InitStructure);. h8 z; [) r) E6 A7 W
- SPI_Cmd(SPI2,ENABLE);
复制代码 6 }# r: j/ k2 T! a! W: ]
4.DMA初始化
& B5 `, G) J8 H7 L( h4 q8 ~ t' n需要查看SPI2的Tx是使用的DMA1的那个通道,这个也需要查看手册。如下图,映射到的时候通道5,根据这个就可以进行初始化了。
3 ^& G4 ]+ b3 e! T- w' e. p6 a) L: L; [* O, A. F# e
* {1 d# R% |4 |# v/ D* ?5 f( O7 G
U: y. v; z/ Q3 X4 o
- DMA_InitTypeDef DMA_InitStructure;
( p* `4 R/ d$ t, J& [! a - 0 p3 {' v( B: Q1 G L3 ]
- RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
- ^* p* G7 ]* x - " V! e, @: a1 Z; G0 ~- B/ N
- DMA_DeInit(DMA1_Channel5); 3 u- f* l3 s4 r2 r( x) T
- DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32)&SPI2->DR;) T! f- j( ^, K: V
- DMA_InitStructure.DMA_MemoryBaseAddr = (uint32)buffer; //发送数据的内存地址
3 s, P4 f; v( e* a* p - DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; //方向为内存到外设! T! [$ C) ~/ p! U. i4 l9 O* Q
- DMA_InitStructure.DMA_BufferSize = 480; //数据大小
4 ~3 y6 _& w) S' o& w% D1 V - DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //每发送一个外设地址不变, A9 p$ e x& K( N/ D* ^: u
- DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //每发送一个内存地址加一
6 _# e: {8 P( C( a - DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;: I8 }. F' M0 l( R q+ b* S
- DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
A2 e. j! d9 z& g v7 P7 G - DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;" j$ A: q* v' b( L- Q9 K1 q
- DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;+ [# P% g1 @# ]; o" L' [" A
- DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;- b5 l1 W) i3 b& C+ G
- DMA_Init(DMA1_Channel5,&DMA_InitStructure);
复制代码
5 l- s; G1 K4 p$ ~% P% `+ m5.然后就是对LCD屏幕的初始化了,也就是配置对应的寄存器。这个可以拿卖家提供的直接拿来用就可以。8 i! g; p+ }) p/ I' C/ s6 x
这里我们就完成了所有的初始化工作了。" Q" z& l7 r3 a, E- Q( ]; B2 v
; K; [0 k5 W) r% o6 r9 m
1. 设置数据地址范围+ X8 B$ _+ [& B; t
每次发送显示数据的时候,需要设置显示范围,也就是行列的起始值。设置好范围,然后就发送对应大小的数据即可。其显示发送为从左往右,从上到下。5 H) l, p; {: k p2 G
6 J* N# c2 \% S
# f5 p& @% I, `$ O ]& S
- + @9 t/ x1 n/ Q% O. F
- //设置数据显示地址范围
: N9 L& B* w$ [1 p& Y4 W - void LCD_Address_Set(uint16 x1,uint16 y1,uint16 x2,uint16 y2)
/ a% L* K9 {3 P& B. J - {
: T8 a' L0 E; n( D- j: g5 h3 ] - LCD_Write_Cmd(0x2a);//列地址设置 N; t2 y# r2 P J9 G) ?
- LCD_WriteDisplayData16(x1);9 f' L; T" e% i& f x8 V) i
- LCD_WriteDisplayData16(x2);7 o& ^+ u4 m8 y8 m' W5 L) t
- LCD_Write_Cmd(0x2b);//行地址设置
( J* o, p- N1 D+ e; I1 J - LCD_WriteDisplayData16(y1);
$ d' Y1 s0 O- f* b( Y; f% R: B - LCD_WriteDisplayData16(y2);
9 t( V0 `/ d. M4 t+ y! U/ d+ S, g - LCD_Write_Cmd(0x2c);//储存器写% n* K* g) _1 Y! u5 I* B- N
- }
复制代码
$ Z3 E; |2 ~# K. x9 k2 v, U2. 点亮LCD屏幕* ^2 N2 d- _% k m, L
- //LCD全亮
0 R- b7 c% o& X. P# f - void LCDFullOn(uint16 Color)
' J9 g9 o a7 `5 r0 ]- I - {
) i, N2 x# S9 ?, }7 _2 ] - uint16 i,j;
$ A! D5 w7 c6 u5 p- d/ a - LCD_Address_Set(0,0,LCD_Width-1,LCD_Height-1);
! ?& b# T; i, T' i2 i; w0 f - for(i=0;i<LCD_Width;i++)
1 v/ x# x q, W; _* o/ u' [& f k - {
( z- e5 ~: D. F - for (j=0;j<LCD_Height;j++)# V, e2 P5 ^: R$ ^& f2 ?
- {
0 e/ ~% e, O% S& J( A* c - LCD_WriteDisplayData16(Color);7 T# o7 O, }- W2 y4 D6 u' X% V' u! z* E$ U
- } 8 \4 K0 g; n H; `0 S4 U
- }
8 t3 V& E2 b( l. Q - }
复制代码
$ q+ t5 l! g& p3. 使用DMA搬运数据/ [6 l g% A6 [
1.对设置DMA搬运数据的大小并且使能! U& y7 i1 w- ]/ B3 |
- //spi2 DMA使能
8 V3 h# j) }8 `. I - void SPI2DMA_Enable(void)1 z! i* U0 {" F$ F
- {# `: y( j2 p, {0 P$ U
- DMA_Cmd(DMA1_Channel5, DISABLE ); % p5 ?, q. ^0 Z" ^5 J
- DMA_SetCurrDataCounter(DMA1_Channel5,480); //设置搬运数据的大小
: p. C6 K/ m% ~( ^$ m, d - DMA_Cmd(DMA1_Channel5, ENABLE); 2 Y$ Z H3 I2 P1 ~2 ]; g
- }
复制代码
7 g5 |; o: O, S' u1 H2.开启DMA请求,且等待发送完成,清除标志位
5 {: A# ~; Q8 g+ k- for(j = 0 ; j< 240 ; j++)
& A" Z; D4 O5 Y) ~7 c Z - {! i T5 q! Q. y/ W/ |
- SPI_I2S_DMACmd(SPI2,SPI_I2S_DMAReq_Tx,ENABLE);
& @$ L6 g; \: {& j - SPI2DMA_Enable();
% M( }# L [, N* \. r" x! {9 [ - while((DMA_GetFlagStatus(DMA1_FLAG_TC5)==RESET)); // 等待dma发送完成. [8 r6 |5 Z( i
- DMA_ClearFlag(DMA1_FLAG_TC5);
. S5 b& T+ B S: U* G - }
复制代码 1 U3 A, u8 X( q
通过测试,可以明显看到使用DMA进行加速的显示效果要快很多。. S1 J( [' g: W$ X7 v- ~
2 O R4 H* X2 b$ C- E& r! Z
" E- V* d2 C2 h6 f) }! C+ o$ Q
2 Y9 p" P5 m/ a |