一、本文摘要
本文主要基于STM32F407ZET6的FSMC控制器,实现了LCD的驱动芯片的读写通信测试,实现了对SSD1963的读写控制,后面将介绍其中的驱动代码和调试过程,着重结合实际调试波形分析读写时序。
二、硬件及原理介绍
如图所示,stm32与ssd1963通过FSMC总线连接,其中值得注意的是FSMC_A18地址线当作SSD1963的D/C#(数据/指令线),通过该信号告知SSD1963当前数据总线上的数据为parameter还是command。
1.SSD1963介绍(8080时序)
虽然该芯片具备6800和8080两种通信方式,但是由于至调试了8080时序,这里仅对8080时序做介绍。
(1)接口
结合图表信息,8080时序主要使用表中红色框标注的控制及数据总线,先不写TE信号,自己没用到(尾注为#的说明该信号为低电位有效),包括片选信号线CS#,数据/指令信号线D/C#,读信号线RD#,写信号线WR#,和数据总线D[23:0]。这里说一下数据总线,虽然是24位总线,但实际MCU与SSD1963通信为8位并行总线。这里贴出手册的原文。 通过上面两段话,我们可以知道,***当读写SSD1963寄存器时,数据总线仅低八位有效,但当读写显示数据时,将根据按下表数据格式将颜色数据打包至数据总线上。***我采用565数据格式,则可以一次将显示数据通过D[15:0]将数据写入或读出。综上,在硬件连接时,使用16位数据总线。
(2)时序
下面三张图介绍了芯片通过8080时序读写数据的相关参数。主要包括tACC,数据访问时间,不少于32ns,片选信号时间tCS,不少于2ns,地址建立时间tAS,不少于1ns。片选信号高电位时间,即读写切换时间,tPWCSH,根据情况不同,时间长短不同,如连续写入,不少于13ns,使用典型值1.5*芯片主时钟周期(100M-10ns),不少于15ns,先写后读和连续读出,切换时间不少于80ns,使用典型值90ns。这段话,主要指导FSMC时序参数设置。
2.STM32F407ZET6 FSMC控制器介绍
先上框图。该模块,使用AHB总线配置寄存器,系统使用HCLK-168Mhz。根据***SSD1963硬件接口特征***,匹配FSMC总线,选用存储bank1,模式A访问。即片选信号线选用FSMC_NE1。读写信号线分别为FSMC_NOE和FSMC_NWE,均为低电位有效。选用FSMC_A18地址线当作指令/数据线。
(1)地址映射
STM32为32位地址线,根据片选信号,确定使用地址空间为bank1:0x60000000-0x6fffffff; 其中高四位固定为6,HADDR[27:26]又将bank1划分为4个小分区,这里使用第一分区。剩下26位为地址线根据具体地址线连接和数据线格式确定实际地址。 注:FSMC地址线(HADDR)按字节寻址,内部存储器按字寻址。 因此,当数据线选用8位,即按八位数据寻址时,地址线组合出来的地址也是STM32内存访问地址。如本帖提到的采用A18地址线作为D/C#线,如下图所示,可得数据读写地址为0x60040000,指令读写地址为0x60000000; 但当数据线选用16位,按16位数据寻址时,根据,表186所示,HADDR[25:1]右移1位发送至地址线上。则内部访问地址应将A18左移一位,即改变第19位。则数据访问地址为0x60080000,指令访问地址为0x60000000。
(2)时序
这个也是困扰我很久的,如何将FSMC的控制时序与SSD1963 8080时序对应。
三、贴代码
GPIO、时钟、控制器时序参数设置
void LCDHostConfig(void)
{
RCC->AHB1ENR |= (3<<3)|(1<<1);
GPIOD->MODER &= ~(((uint32_t)3<<0)|((uint32_t)3<<1*2)|((uint32_t)3<<4*2)|((uint32_t)3<<5*2)|((uint32_t)3<<7*2)|((uint32_t)3<<8*2)|((uint32_t)3<<9*2)|((uint32_t)3<<10*2)|((uint32_t)3<<13*2)|((uint32_t)3<<14*2)|((uint32_t)3<<15*2));
GPIOD->MODER |= ((uint32_t)2<<0)|((uint32_t)2<<1*2)|((uint32_t)2<<4*2)|((uint32_t)2<<5*2)|((uint32_t)2<<7*2)|((uint32_t)2<<8*2)|((uint32_t)2<<9*2)|((uint32_t)2<<10*2)|((uint32_t)2<<13*2)|((uint32_t)2<<14*2)|((uint32_t)2<<15*2);
GPIOD->OTYPER &= ~((1<<0)|(1<<1)|(1<<4)|(1<<5)|(1<<7)|(1<<8)|(1<<9)|(1<<10)|(1<<13)|(1<<14)|((uint32_t)1<<15));
GPIOD->OSPEEDR &= ~((3<<0)|(3<<1*2)|(3<<4*2)|(3<<5*2)|(3<<7*2)|(3<<8*2)|(3<<9*2)|(3<<10*2)|(3<<13*2)|((uint32_t)3<<14*2)|((uint32_t)3<<15*2));
GPIOD->OSPEEDR |= ((uint32_t)2<<0)|((uint32_t)2<<1*2)|((uint32_t)2<<4*2)|((uint32_t)2<<5*2)|((uint32_t)2<<7*2)|((uint32_t)2<<8*2)|((uint32_t)2<<9*2)|((uint32_t)2<<10*2)|((uint32_t)2<<13*2)|((uint32_t)2<<14*2)|((uint32_t)2<<15*2);
GPIOD->PUPDR &= ~(((uint32_t)3<<0)|((uint32_t)3<<1*2)|((uint32_t)3<<4*2)|((uint32_t)3<<5*2)|((uint32_t)3<<7*2)|((uint32_t)3<<8*2)|((uint32_t)3<<9*2)|((uint32_t)3<<10*2)|((uint32_t)3<<13*2)|((uint32_t)3<<14*2)|((uint32_t)3<<15*2));
GPIOD->AFR[0] &= ~(((uint32_t)15<<0)|((uint32_t)15<<1*4)|((uint32_t)15<<4*4)|((uint32_t)15<<5*4)|((uint32_t)15<<7*4));
GPIOD->AFR[1] &= ~(((uint32_t)15<<(8-8)*4)|((uint32_t)15<<(9-8)*4)|((uint32_t)15<<(10-8)*4)|((uint32_t)15<<(13-8)*4)|((uint32_t)15<<(14-8)*4)|((uint32_t)15<<(15-8)*4));
GPIOD->AFR[0] |= ((uint32_t)12<<0)|((uint32_t)12<<1*4)|((uint32_t)12<<4*4)|((uint32_t)12<<5*4)|((uint32_t)12<<7*4);
GPIOD->AFR[1] |= ((uint32_t)12<<(8-8)*4)|((uint32_t)12<<(9-8)*4)|((uint32_t)12<<(10-8)*4)|((uint32_t)12<<(13-8)*4)|((uint32_t)12<<(14-8)*4)|((uint32_t)12<<(15-8)*4);
GPIOE->MODER &= ~((uint32_t)0x3FFFF<<14);
GPIOE->MODER |= (uint32_t)0x2AAAA<<14;
GPIOE->OTYPER &= ~(0x1F<<7);
GPIOE->OSPEEDR &= ~((uint32_t)0x3FFFF<<14);
GPIOE->OSPEEDR |= (uint32_t)0x2AAAA<<14;
GPIOE->PUPDR &= ~((uint32_t)0x3FFFF<<14);
GPIOE->AFR[0] &= ~((uint32_t)15<<28);
GPIOE->AFR[0] |= ((uint32_t)12<<28);
GPIOE->AFR[1] = 0xCCCCCCCC;
GPIOB->MODER &= ~(3<<2);
GPIOB->MODER |= (1<<2);
GPIOB->OTYPER &= ~(1<1);
GPIOB->OSPEEDR &= ~(3<<2);
GPIOB->OSPEEDR |= (1<<2);
GPIOB->PUPDR &= ~(3<<2);
GPIOB->PUPDR |= (1<<2);
SSD1963RST(HIGH);
RCC->AHB3ENR |= (1<<0);
FSMC_Bank1->BTCR[0] = 0;
FSMC_Bank1->BTCR[0] |= (1<<12) | (0<<4) |(1<<0);
FSMC_Bank1->BTCR[1] = 0;
FSMC_Bank1->BTCR[1] |= (8<<8) | (15<<16) |(1<<0);
}
SSD1963初始化代码
uint8_t LCDHardwareReset(void)
{
uint8_t err[5];
LCDHostConfig();
SSD1963RST(LOW);
DELAYMS(10);
SSD1963RST(HIGH);
DELAYMS(10);
CMD_REG = SET_PLL_MN;
DAT_REG = 0x1D;
DAT_REG = 0x02;
DAT_REG = 0x54;
CMD_REG = SET_PLL;
DAT_REG = 0x01;
DELAYUS(120);
CMD_REG = SET_PLL;
DAT_REG = 0x03;
CMD_REG = SOFT_RESET;
DELAYMS(5);
while(err[0] != 0x04)
{
err[1] = 10;
CMD_REG = GET_PLL_STATUS;
err[0] = DAT_REG;
}
CMD_REG = GET_PLL_MN;
err[0] = DAT_REG;
err[1] = DAT_REG;
err[2] = DAT_REG;
CMD_REG = READ_DDB;
err[0] = DAT_REG;
err[1] = DAT_REG;
err[2] = DAT_REG;
err[3] = DAT_REG;
err[4] = DAT_REG;
if((err[0]==0x01)&&(err[1]==0x57)&&(err[2]==0x61)&&(err[3]==01)&&(err[4]==0xff)) return 0;
else return 1;
}
至此,完成了SSD1963的初始化和通信时序调试,下一步详细的驱动程序再做笔记。
四、调试问题
如上面代码所示,将指针强制转换为32位数据访问,当写数据时,一条写操作代码,通过示波器捕捉cs信号,如下图所示,发现触发两次写数据操作。改为*(uint16_t*)就解决了这个问题。
先贴点图,后面补充完整。首次写长文,望多多鼓励,
|