STM32的SPI应用之LCD

STM32的SPI应用之LCD
STM32的SPI应用之LCD

STM32的SPI应用之LCD

开始本来先玩串口,虽然程序已经跑通,但是由于网上类似文章很多,就先来个SPI 玩玩,与上次GPIO一样,技术含量仍然不高,仅是业余学习玩玩。

1、首先来开硬件电路,个人觉得是编写程序的第2步;第1步当然是先看STM32的

手册了,SPI的详细介绍见STM32的中文使用手册。

这个电路的正确性是以我把它点亮为依据的,不是我设计的,我也不会,但是我驱动它关心的是那几个信号引脚怎么接的。这里采用的是SPI口,加上几根控制线。关于SPI口的介绍不大清楚的朋友可以查下相关资料,一般具有SPI接口的处理器的手册都有比较详细的介绍。

结合STM32的手册对引脚的描述:

可以看出,SPI1的4根引脚MISO、MOSI、SCK(CLK)、CSN(CS)分别对应的引脚为GPIO的:PA6、PA7、PA5、PA4。

所以LCD的控制线与处理器的GPIO具体对应如下:

LCD_RST--------------PC7

LCD_RS------------------PC8

LCD_CLK-----------------------PA5

LCD_SDO-----------------------PA7

LCD_CS----------------------PA8

LCD_PWR----------------PC1

其实也就这六根信号线就能驱动LCD了。

2、这里必须关心的是SPI口对应的4根线,这里只用到了两根MOSI(PA7)、CLK(PA5),片选CSN用的是通用IO口PA8代替,至于MISO这里可以不用使用。

所以在初始化SPI口是有如下程序:

void SetupSPI(void)

{

SPI_InitTypeDef SPI_InitStructure;

GPIO_InitTypeDef GPIO_InitStructure;

/*允许SPI1 和GPIOA时钟,这两个外设都是挂在APB2总线上的*/

RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1 | RCC_APB2Periph_GPIOA, ENABLE);

/*配置SPI1 引脚,由于这里只用到了SCK,和MOSI ,所以只对PA5和PA7进行了初始化*/

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_7;

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//关于这个参数的描述可以见GPIO的H文件GPIO_Init(GPIOA, &GPIO_InitStructure);

/* 配置PA.4作为推挽输出,因为这里用来作为SPI口的片选,既选中LCD操作*/

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;

GPIO_Init(GPIOA, &GPIO_InitStructure);

/* 初始化片选为高,不选种LCD */

GPIO_ResetBits(GPIOA, GPIO_Pin_8);//PA8;

/* SPI1 配置,关于这个怎么配置见STM32的手册,为什么这样配置见下*/

SPI_InitStructure.SPI_Direction = SPI_Direction_1Line_Tx;

SPI_InitStructure.SPI_Mode = SPI_Mode_Master;

SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;

SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;

SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;

SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;

SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4;

SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;

SPI_Init(SPI1, &SPI_InitStructure);

SPI_Cmd(SPI1, ENABLE);

}

关于SPI的配置,这里还要多说点。因为SPI的时序很重要,或者说只要是串口,对时序都有一些特定的要求。对于STM32的SPI口来说,时序是可以配置。

先来看看STM32 的SPI口时序图:

可以看出,SPI口的时序主要控制在两个位:CPHA和CPOL,这个不用我多罗嗦,大家都知道两个位组合一共有4种组合,也就是说SPI有4种时序,那么这里选择哪一种呢?

这就要取决与所用LCD的驱动IC了。这里采用的驱动IC为,下面来看看LCD驱动IC的SPI口的时序:

我想看到这里,稍微有点眼神的人都能看出来这个时序与STM32 的哪个对应了。这里应该设置STM32的CPHA=1和CPOL=1。它的意义为:SPI空闲保持高电平,在时钟的第2个边沿采样。其他具体信息可以去看使用手册,里面有相当详细的介绍。

那么在固件函数库里需要设置对应的初始化参数为:

SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;

SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;

从英文意思上也能看出两个参数的意义,我就是这么看出来的,使用手册太长,而且是电子版的,看起来不太适应(本人喜欢看书,所以现在在等待STM32的书出版,该书应该很快会出来了,应该是英蓓特组织出的,MDK那个书已经出来了,周末去书店逛了下,看见还可以,不过没有买,原因有2:第1我现在做市场没有太多的时间去研究MDK,第2本人经济拮据------留着钱过5.1)。

SPI口配置还有几个参数,其实从英文意义上都不难理解(申明本人英文很菜,我能看懂的我相信大家都绝对没有问题)。

SPI_InitStructure.SPI_Direction = SPI_Direction_1Line_Tx;

配置SPI为只输出,这个SPI口的输入输出方向好象有4种,这也是从H文件看到的:#define SPI_Direction_2Lines_FullDuplex ((u16)0x0000)

#define SPI_Direction_2Lines_RxOnly ((u16)0x0400)

#define SPI_Direction_1Line_Rx ((u16)0x8000)

#define SPI_Direction_1Line_Tx ((u16)0xC000)

分别代表的意义从字面上也好理解,要是对应使用手册来看,我相信效果更好。

SPI_InitStructure.SPI_Mode = SPI_Mode_Master;

SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;

这个是配置为主模式和8位数据-----我肯定,呵呵!STM32的SPI口还可以为从模式、16位数据方式。

SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;

SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4;

SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;

这几个位就更加容易理解了,SPI_NSS_Soft;软件设置片选,这个介绍起来有点罗嗦,可以看使用手册(偷个小懒),SPI_BaudRatePrescaler_4这个是拨特率设置,可以设置为如下参数:

#define SPI_BaudRatePrescaler_2 ((u16)0x0000)

#define SPI_BaudRatePrescaler_4 ((u16)0x0008)

#define SPI_BaudRatePrescaler_8 ((u16)0x0010)

#define SPI_BaudRatePrescaler_16 ((u16)0x0018)

#define SPI_BaudRatePrescaler_32 ((u16)0x0020)

#define SPI_BaudRatePrescaler_64 ((u16)0x0028)

#define SPI_BaudRatePrescaler_128 ((u16)0x0030)

#define SPI_BaudRatePrescaler_256 ((u16)0x0038)

这里设置为多少其实都应该可以的。

SPI_FirstBit_MSB这个的意思是第一个位为最高位。因为是串行传输,所以移位肯定有先后的。从时序图上也能看出来高位先行。

3、SPI配置完后就要写LCD驱动了,这个驱动也是拿现成的修改的,凭本人的技术水准很难把LCD手册看明白,也很难自己一个字母一个字母把驱动写出来。既然有高手写好了,就不妨拿来借鉴!这个也是省事的办法,要是哪位有兴趣研究驱动,可以自己尝试写下,写好了也象我这样写过简单的过程再公开,那就功德无量了!不过就我了解,现在做芯片的厂商都会把驱动写好的,就象ST一样,把芯片的固件库写的很完善,方便了我这样的技术半吊子水准的人。

首先定义下LCD的控制引脚,这样操作起来方便:

//LCD_RST

#define LCD_RST_SET GPIO_SetBits(GPIOC, GPIO_Pin_7)//PC7

#define LCD_RST_RESET GPIO_ResetBits(GPIOC, GPIO_Pin_7)//PC7

//LCD_RS

#define LCD_RS_SET GPIO_SetBits(GPIOC, GPIO_Pin_8)//PC8

#define LCD_RS_RESET GPIO_ResetBits(GPIOC, GPIO_Pin_8)//PC8

//LCD_CS

#define LCD_CS_SET GPIO_SetBits(GPIOA, GPIO_Pin_8)//PA8

#define LCD_CS_RESET GPIO_ResetBits(GPIOA, GPIO_Pin_8)//PA8

// LCD_PWR

#define LCD_PWR_SET GPIO_SetBits(GPIOC, GPIO_Pin_1)//PC1

#define LCD_PWR_RESET GPIO_ResetBits(GPIOC, GPIO_Pin_1)//PC1

上面定义完全是为了程序操作方便。也是我见到的比较好的编程习惯。

由于LCD驱动比较多,为了不浪费篇幅,这里只写两个比较重要的函数:

/*******************************************************************************

//函数名:void Lcdwritecom(int8u com)

//功能:lcd写指令

//输入:com指令

//输出:无

********************************************************************************/ void Lcdwritecom(INT8U com)

{

LCD_CS_RESET; //片选拉低,选中LCD

LCD_RS_RESET; //设置为写命令

SPI_SendData(SPI1, com);//SPI写数据

while(SPI_GetFlagStatus(SPI1, SPI_FLAG_BSY) != RESET);//等待发送完成

LCD_CS_SET; //片选拉高,释放LCD

}

/******************************************************************************* //函数名:void Lcdwritedata(int8u dat)

//功能:lcd写数据

//输入:dat数据

//输出:无

********************************************************************************/ void Lcdwritedata(INT8U dat)

{

LCD_CS_RESET; //片选拉低,选中LCD

LCD_RS_SET; //设置为写数据

SPI_SendData(SPI1, dat); //SPI写数据

while(SPI_GetFlagStatus(SPI1, SPI_FLAG_BSY) != RESET); //等待发送完成

LCD_CS_SET; //片选拉高,释放LCD

}

差不多到最后了,秀下主函数:

int main (void)

{

INT8U num_test[]="123456789";

INT8U char_test[]="abcdef";

INT8U china_test[]="无线测试";

//SetupClock();

//LED_Init();

SetupSPI();

InitLcd();

TurnOnDisp();

Delay(0xfffff);

ClearScreen();

while(1){

Rectangle(10,0,120,0); //画线测试

Rectangle(10,7,120,7);

Printn(1,10,255,1,3); //显示整数测试

Print6(2,10,num_test,1); //显示数字测试

Print8(3,10,char_test,1); //显示字母测试

Print(5,10,china_test,1); //显示汉字测试

}

}

注意这里把//SetupClock();函数屏蔽了,在源文件中有该函数,也就是配置系统时钟,不配置采用默认时钟。函数里配置为72MHz。关于RCC的一些应用我还没有时间研究,有时间了在好好看看,专门弄几个例子出来表现下。

就写到这里,明天5.1还要出去玩,早点休息!

2008年4月30日23:30

YYYtech于成都

另外这里采用的是STM32硬件SPI,用软件模拟SPI也是可以实现的,有兴趣的可以试下,反正我试了是成功的。准确的说,我是先测试软件SPI成功才用的硬件SPI。

相关主题
相关文档
最新文档