日志档案

发表于 2008-4-30 23:34:11

2

标签: STM32  SPI  ARM  CORTEXM3  LCD驱动  

STM32的SPI应用之LCD

STM32SPI应用之LCD

源代码:点击下载

介绍文档:点击下载

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

1、  首先来开硬件电路,个人觉得是编写程序的第2步;第1步当然是先看STM32的手册了,SPI的详细介绍见STM32的中文使用手册。

 

点击看大图

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

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

点击看大图

可以看出,SPI14根引脚MISOMOSISCKCLK)、CSNCS)分别对应的引脚为GPIO的:PA6PA7PA5PA4

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

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

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

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

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

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

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

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

       2、这里必须关心的是SPI口对应的4根线,这里只用到了两根MOSIPA7)、CLKPA5),片选CSN用的是通用IOPA8代替,至于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 ,所以只对PA5PA7进行了初始化*/

  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;//关于这个参数的描述可以见GPIOH文件

  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的时序很重要,或者说只要是串口,对时序都有一些特定的要求。对于STM32SPI口来说,时序是可以配置。

先来看看STM32 SPI口时序图:

点击看大图

       可以看出,SPI口的时序主要控制在两个位:CPHACPOL,这个不用我多罗嗦,大家都知道两个位组合一共有4种组合,也就是说SPI4种时序,那么这里选择哪一种呢?这就要取决与所用LCD的驱动IC了。这里采用的驱动IC ,下面来看看LCD驱动ICSPI口的时序:

点击看大图

      

       我想看到这里,稍微有点眼神的人都能看出来这个时序与STM32 的哪个对应了。这里应该设置STM32CPHA=1CPOL=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位数据-----我肯定,呵呵!STM32SPI口还可以为从模式、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这个的意思是第一个位为最高位。因为是串行传输,所以移位肯定有先后的。从时序图上也能看出来高位先行。

       3SPI配置完后就要写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还要出去玩,早点休息!

 

 

 

                            20084302330   

                                   YYYtech于成都

 

 

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

 

 

 

 

 

 

 

 

系统分类: 单片机   |   用户分类: 无分类   |   来源: 原创   |   【推荐给朋友】   |   【添加到收藏夹】

    阅读(362)    回复(1)  

投一票您将和博主都有获奖机会!