今天学习了正点原子STM32F407的TFT LCD显示实验,教程代码为了兼容多款液晶,做了检测和判断等,代码量较大。经过我的简化,专用于NT35510。
学习过程中发现了一个关于读写通讯时序的问题,记录如下:
教程代码:
readWriteTiming.FSMC_AddressSetupTime = 0XF; //地址建立时间(ADDSET)为16个HCLK 1/168M=6ns*16=96ns
readWriteTiming.FSMC_AddressHoldTime = 0x00; //地址保持时间(ADDHLD)模式A未用到
readWriteTiming.FSMC_DataSetupTime = 60; //数据保存时间为60个HCLK =6*60=360ns
readWriteTiming.FSMC_BusTurnAroundDuration = 0x00;
readWriteTiming.FSMC_CLKDivision = 0x00;
readWriteTiming.FSMC_DataLatency = 0x00;
readWriteTiming.FSMC_AccessMode = FSMC_AccessMode_A; //模式A
writeTiming.FSMC_AddressSetupTime =9; //地址建立时间(ADDSET)为9个HCLK =54ns
writeTiming.FSMC_AddressHoldTime = 0x00; //地址保持时间(A
writeTiming.FSMC_DataSetupTime = 8; //数据保存时间为6ns*9个HCLK=54ns
writeTiming.FSMC_BusTurnAroundDuration = 0x00;
writeTiming.FSMC_CLKDivision = 0x00;
writeTiming.FSMC_DataLatency = 0x00;
writeTiming.FSMC_AccessMode = FSMC_AccessMode_A; //模式A
//教程代码在下方进行液晶控制器id判断后,又重设了模式a的写时序:
//重新配置写时序控制寄存器的时序
FSMC_Bank1E->BWTR[6]&=~(0XF<<0);//地址建立时间(ADDSET)清零
FSMC_Bank1E->BWTR[6]&=~(0XF<<8);//数据保存时间清零
FSMC_Bank1E->BWTR[6]|=3<<0; //地址建立时间(ADDSET)为3个HCLK =18ns
FSMC_Bank1E->BWTR[6]|=2<<8; //数据保存时间(DATAST)为6ns*3个HCLK=18ns
上述代码第一部分配置了模式A的读时序,第二部分配置了写时序,分别操作FSMC模块的寄存器BTR和BWTR。
以写时序为例分析(读时序的道理相同),在阅读NT35510数据手册以及STM32F4参考手册后可以发现,地址建立时间ADDSET,是NWE拉低之前的高电平时间,这个时间对于NT35510来说是没有意义的,允许范围内设为任意值都可以,经实测该值确实无影响。
而数据保持时间DATAST就是WRX的拉低时间,这个时间结束后的上升沿,液晶控制器会把端口上的数据读走。
(图片取自我以前的其他博客,故有水印)
根据下表,WRX拉低时间应大于15ns,整个写入周期应大于33ns。
同理,读液晶ID时,则有RDX拉低时间大于45ns等要求。
/* 扩展模式A读时序配置, 适应nt35510 */
readWriteTiming.FSMC_AddressSetupTime = 0x00; //AddressSetupTime,n*hclk
readWriteTiming.FSMC_AddressHoldTime = 0x00; //模式A不使用AddressHoldTime
readWriteTiming.FSMC_DataSetupTime = 0x08;//DataSetupTime,n*hclk
readWriteTiming.FSMC_BusTurnAroundDuration = 0x00;
readWriteTiming.FSMC_CLKDivision = 0x00;
readWriteTiming.FSMC_DataLatency = 0x00;
readWriteTiming.FSMC_AccessMode = FSMC_AccessMode_A; //模式A
/* 扩展模式A写时序配置, 适应nt35510 */
writeTiming.FSMC_AddressSetupTime = 0x00; //n*hclk
writeTiming.FSMC_AddressHoldTime = 0x00; //
writeTiming.FSMC_DataSetupTime = 0x02; //(n+1)*hclk
writeTiming.FSMC_BusTurnAroundDuration = 0x00;
writeTiming.FSMC_CLKDivision = 0x00;
writeTiming.FSMC_DataLatency = 0x00;
writeTiming.FSMC_AccessMode = FSMC_AccessMode_A; //模式A
问题就在这里出现,经过测试,就算我把写时序中的DATAST配置成0,一样可以正常运行,可能是因为主机进行改变IO口电平的操作本身就具有一定延迟,足以让液晶控制器准备好读走端口数据,或者其他原因。
而读时序中的DATAST却不可以太小,否则程序就会不稳定,或者直接出错,原因应该是液晶控制器需要足够的时间在端口上准备好数据,以让主机读取。
此外,NT35510要求的每个读周期或者写周期所要求的最小时间(例如写周期33ns),以及RDX或者WRX要求的上升沿后高电平延时(如上表所示,例如写操作要求最少15ns),我不清楚FSMC模块在硬件上是怎样实现的,配置程序中没有涉及到这一部分参数,又或者这些要求可以不满足?有待研究
故我认为,配置读写时序的重点在于WRX和RDX上的低电平时间。
暂做记录,希望高手指正,欢迎交流讨论。
补充关于液晶触屏控制器gt9147的一些说明:
正点原子出品的一款4.3寸液晶屏,使用nt35510控制器和gt9147触摸控制器,对于使用该公司stm32教程的人来说是非常熟悉的。其教程只给出了轮询扫描获取触摸坐标的方式,且初始化配置代码有些混乱,我略作整理,给出中断方式获取坐标的初始化配置方式,亲测可用但仅供参考:
//初始化GT9147触摸屏
void GT9147_Init(void)
{
u8 temp;
GPIO_InitTypeDef GPIO_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
EXTI_InitTypeDef EXTI_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB|RCC_AHB1Periph_GPIOC|RCC_AHB1Periph_GPIOF, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);//使能SYSCFG时钟
/* 模拟IIC SCL PB0;电容屏中断引脚 PB1 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1 ;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//输出
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化
PBout(1)=1; //使初始化时INT=1,设备地址设定为0x14
/* 电容屏复位引脚 PC13 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
GPIO_Init(GPIOC, &GPIO_InitStructure);//初始化
/* 模拟IIC SDA PF11 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
GPIO_Init(GPIOF, &GPIO_InitStructure);//初始化
/* 设置设备地址为0x14,对应特定的读写命令 */
GT_RST=0; //复位
delay_ms(1); //大于100us
GT_RST=1; //释放复位
delay_ms(10); //大于5ms
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;//PB1设置为浮空输入
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化
delay_ms(100); //必须大于50ms
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOB, &GPIO_InitStructure);//重新配置为上拉
/* 配置中断线来源,使能中断线 */
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOB, EXTI_PinSource1);//PB1 连接到中断线1
EXTI_InitStructure.EXTI_Line = EXTI_Line1;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;//中断模式
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; //下降沿触发
EXTI_InitStructure.EXTI_LineCmd = ENABLE;//中断线使能
EXTI_Init(&EXTI_InitStructure);//配置
/* 配置中断优先级 */
NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn;//外部中断1
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x03;//抢占优先级3
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02;//子优先级2
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//使能外部中断通道
NVIC_Init(&NVIC_InitStructure);//配置
temp=0X02;
GT9147_WR_Reg(GT_CTRL_REG,&temp,1);//软复位GT9147
GT9147_RD_Reg(GT_CFGS_REG,&temp,1);//读取GT_CFGS_REG寄存器
if(temp<0X60)GT9147_Send_Cfg(1);//原版本比较低,更新并保存配置
delay_ms(10);
temp=0X00;
GT9147_WR_Reg(GT_CTRL_REG,&temp,1);//结束复位
}
在中断服务函数中读取坐标即可。