STM32的SPI应用之LCD
介绍文档:<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />
开始本来先玩串口,虽然程序已经跑通,但是由于网上类似文章很多,就先来个SPI玩玩,与上次GPIO一样,技术含量仍然不高,仅是业余学习玩玩。
1、 首先来开硬件电路,个人觉得是编写程序的第2步;第1步当然是先看STM32的手册了,SPI的详细介绍见STM32的中文使用手册。
<?xml:namespace prefix = v ns = "urn:schemas-microsoft-com:vml" />
这个电路的正确性是以我把它点亮为依据的,不是我设计的,我也不会,但是我驱动它关心的是那几个信号引脚怎么接的。这里采用的是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还要出去玩,早点休息!
<?xml:namespace prefix = st1 ns = "urn:schemas-microsoft-com:office:smarttags" />2008年4月30日23:30
YYYtech于成都
另外这里采用的是STM32硬件SPI,用软件模拟SPI也是可以实现的,有兴趣的可以试下,反正我试了是成功的。准确的说,我是先测试软件SPI成功才用的硬件SPI。
用户239228 2010-1-31 17:11
用户947559 2009-7-21 17:02
用户1321576 2008-7-16 16:45