首页 > 编程学习 > STM32F103的FSMC模块驱动LCD屏幕

STM32F103的FSMC模块驱动LCD屏幕

发布时间:2022/11/24 23:14:26

     以下内容为对正点原子的STM32F103STM32F103STM32F103精英板的FSMCFSMCFSMC模块驱动LCDLCDLCD屏幕例程的学习。做一个记录来加强对模块的认知。
     FSMCFSMCFSMC的全称是FlexiblestaticmemorycontrollerFlexible\quad static\quad memory\quad controllerFlexiblestaticmemorycontroller,翻译成中文就是灵活的静态存储器控制器,这样来看的话FSMCFSMCFSMC模块无法支持DRAMDRAMDRAM,因为DRAMDRAMDRAM属于动态存储器,它需要刷新电路不断的刷新来维持存储的数据,而SRAMSRAMSRAM不需要刷新电路,只需要芯片有供电就可以维持存储的数据。但是我看STM32H750STM32H750STM32H750FMC,FlexiblememorycontrollerFMC,Flexible\quad memory\quad controllerFMC,Flexiblememorycontroller,注意这里没有了S,StaticS,StaticS,Static,模块加入了对SDRAMSDRAMSDRAM(同步DRAMDRAMDRAM)的支持。如图1所示,同时STM32H750STM32H750STM32H750FMCFMCFMC模块也删除了对PC_CARDPC\_CARDPC_CARD的支持,可能是因为这玩意已经过时了吧,STM32F103STM32F103STM32F103FSMCFSMCFSMC模块是支持PC_CARDPC\_CARDPC_CARD的。FSMCFSMCFSMC支持的存储器类型如下所示:

  • SRAMSRAMSRAM
  • PSRAMPSRAMPSRAM
  • NORFLASHNOR\quad FLASHNORFLASH
  • NANDFLASHNAND\quad FLASHNANDFLASH

     NORFLASHNOR\quad FLASHNORFLASHNANDFLASHNAND\quad FLASHNANDFLASH在断电之后存储的内容不会丢失。SRAMSRAMSRAMPSRAMPSRAMPSRAM在断电之后存储的内容会丢失。NORFLASHNOR\quad FLASHNORFLASHNANDFLASHNAND\quad FLASHNANDFLASH一般在写入之前首先要对要写入的位置所在的存储块进行擦除的操作,也就是将所有的比特存储位变为二进制值1,SRAMSRAMSRAMPSRAMPSRAMPSRAM不需要。由于NANDFLASHNAND\quad FLASHNANDFLASH的特性决定,NANDFLASHNAND\quad FLASHNANDFLASH不支持XIP,ExcecuteInPlaceXIP,Excecute\quad In\quad PlaceXIP,ExcecuteInPlace,功能,NORFLASHNOR\quad FLASHNORFLASH支持XIPXIPXIP功能。

图1.

     我这里测试的时候用的是如图2所示的3.53.53.5英寸的屏幕,这里我们要知道这里的屏幕不单单只是一块玻璃而已,而是一整个模组。一个基本的模组包含了液晶面板和显示驱动芯片(我这里的屏幕模组的显示驱动芯片的型号是NT35310NT35310NT35310),如图3所示。MCUMCUMCU,比如STM32F103STM32F103STM32F103,通过FSMCFSMCFSMC模块将数据通过8080接口(当然显示驱动芯片一般支持多种接口类型,可以根据需要进行选择)发送给显示驱动芯片之后,显示驱动芯片一般再通过RGBRGBRGB接口将接收到的数据显示到屏幕上。但是为什么STM32F103STM32F103STM32F103FSMCFSMCFSMC模块可以用来驱动LCDLCDLCD屏幕?如果你去看STM32F103STM32F103STM32F103FSMCFSMCFSMC模块用户手册的话,它也没有提到它可以驱动LCDLCDLCD屏幕,也没有8080接口相关的关键字。

图2.
图3.

     图4是驱动芯片NT35310NT35310NT35310的用户手册里面的8080接口的时序图,左边是写的,右边是读的。8080接口主要的信号线如下:

  • CSXCSXCSX:片选信号,低电平的时候选中显示驱动芯片。
  • DCXDCXDCX:低电平的时候告诉显示驱动芯片数据线上的是命令,高电平的时候告诉显示驱动芯片数据线上的是命令参数或数据。
  • WRXWRXWRX:写使能信号,低电平的时候告诉显示驱动芯片马上要写数据了。
  • RDXRDXRDX:读使能信号,低电平的时候告诉显示驱动芯片马上要读数据了。
  • D[23:0]D[23:0]D[23:0]:数据线,数据或命令的传输线。

     图5是STM32F103STM32F103STM32F103FSMCFSMCFSMC模块的模式AAA的读和写的时序,图5中的地址信号线A[25:0]A[25:0]A[25:0]和高低字节选择信号NBL[1:0]NBL[1:0]NBL[1:0]这里我们可以不看,在驱动LCDLCDLCD屏幕的时候用不到。图5中NExNExNEx是片选信号,NOENOENOE是读使能信号,NWENWENWE是写使能信号,D[15:0]D[15:0]D[15:0]是数据线。对比一下图4和图5我们就可以发现NT35310NT35310NT35310的8080接口的读写时序和STM32F103STM32F103STM32F103FSMCFSMCFSMC模块的模式AAA的读和写的时序基本差不多,这就是我们可以用STM32F103STM32F103STM32F103FSMCFSMCFSMC模块的模式AAA来和NT35310NT35310NT35310的8080接口进行通信来驱动LCDLCDLCD屏幕的原因。由于在驱动LCDLCDLCD屏幕的时候需要有一根信号线来告诉显示驱动芯片现在发送的是命令还是数据或参数,这里在实际测试的时候采用的方法是用FSMCFSMCFSMC模块的一根地址线来告诉显示驱动芯片现在发送的是命令还是数据或参数,这里选择的是地址信号线A10A10A10,地址信号线从0开始排序,因此在初始化FSMCFSMCFSMC模块的地址的GPIOGPIOGPIO引脚的时候也只初始化了一个引脚,但是因为测试的时候采用的是16bitbitbit数据通信,因此期望在地址信号线A10A10A10GPIOGPIOGPIO引脚上输出高低电平的时候需要在地址的第11位上取值1或0,而不是在第10位上,具体原因请见STM32F103STM32F103STM32F103FSMCFSMCFSMC模块的文档说明,如图6所示。

  • 命令的地址为:((u16∗)(0x6C000000))((u16 *)(0x6C000000))((u16)(0x6C000000))
  • 数据或参数的地址为: ((u16∗)(0x6C000800))((u16 *)(0x6C000800))((u16)(0x6C000800))
图4.
图5.
图6.

     目前也有一些专门的模块来驱动LCDLCDLCD屏幕,比如STM32H750STM32H750STM32H750LTDCLTDCLTDC模块,如图6所示。这种模块驱动LCDLCDLCD屏幕的时候,LCDLCDLCD屏幕模组应该就不需要显示驱动芯片了。

图7.

     说到这里我也有一个疑问显示驱动芯片NT35310NT35310NT35310是支持多种接口的,如图7所示,那我们在选择某一种接口的时候是否需要进行某种软件或硬件配置,对于这一点我还不是很明确。图8似乎是说明了有IM0IM0IM0IM1IM1IM1IM2IM2IM2三个引脚用来选择使用哪一种接口,不知是否正确。还有就是图8里面的serialinterfaceserial\quad interfaceserialinterface不知道是不是就是图7里面说的SerialPeripheralInterface,SPISerial\quad Peripheral \quad Interface,SPISerialPeripheralInterface,SPI接口,还有就是它这里的描述的SPISPISPI接口和我们常见的四个引脚(CSCSCS:片选,CLKCLKCLK:时钟,MOSIMOSIMOSIMISOMISOMISO)的SPISPISPI接口好像又有一点差异。有知道的朋友可以帮忙明确一下。

图8.
图9.

     好了下面来看代码,正点原子的代码是支持多种显示存储芯片的,为了代码的简洁,我把它改成只是基于NT35310NT35310NT35310的测试代码,其它不先关的我全部删除了,因为原理差不多。代码最开始肯定是FSMCFSMCFSMC模块和LCDLCDLCD屏幕模组的初始化,FSMCFSMCFSMC模块初始化了16根数据线,一根地址线来告诉显示驱动芯片发送的是命令还是数据或参数。读使能信号线,写使能信号线,片选信号线。由于液晶本身是不发光的,所以需要有一个背光灯提供光源,因此还需要一个GPIOGPIOGPIO口来控制光源的开和关。下面来看一下FSMCFSMCFSMC模块的参数配置:

  1. 这里地址和数据线不复用,用的是扩展模式A,因此存储器类型选择SRAMSRAMSRAM,扩展模式也要使能,读写时序不同。
  2. 因为LCDLCDLCD屏幕模组配置的是16比特数据宽度,因此外部存储器宽度参数配置为16比特。
  3. 因为这里和LCDLCDLCD屏幕模组采用的是异步通信所以同步功能关闭,WAITWAITWAIT信号也没有用到
  4. 因为要向LCDLCDLCD屏幕模组发送数据,因此写功能要开启。
	readWriteTiming.FSMC_AddressSetupTime = 15;	 readWriteTiming.FSMC_AddressHoldTime = 0;	 readWriteTiming.FSMC_DataSetupTime = 11;		 readWriteTiming.FSMC_BusTurnAroundDuration = 0;readWriteTiming.FSMC_CLKDivision = 0;readWriteTiming.FSMC_DataLatency = 0;readWriteTiming.FSMC_AccessMode = FSMC_AccessMode_A;	 writeTiming.FSMC_AddressSetupTime =1;	 writeTiming.FSMC_AddressHoldTime =0;	 writeTiming.FSMC_DataSetupTime =1;		writeTiming.FSMC_BusTurnAroundDuration = 0;writeTiming.FSMC_CLKDivision = 0;writeTiming.FSMC_DataLatency =0;writeTiming.FSMC_AccessMode = FSMC_AccessMode_A;	 FSMC_NORSRAMInitStructure.FSMC_Bank = FSMC_Bank1_NORSRAM4; FSMC_NORSRAMInitStructure.FSMC_DataAddressMux = FSMC_DataAddressMux_Disable; FSMC_NORSRAMInitStructure.FSMC_MemoryType =FSMC_MemoryType_SRAM; FSMC_NORSRAMInitStructure.FSMC_MemoryDataWidth = FSMC_MemoryDataWidth_16b;  FSMC_NORSRAMInitStructure.FSMC_BurstAccessMode =FSMC_BurstAccessMode_Disable; FSMC_NORSRAMInitStructure.FSMC_WaitSignalPolarity = FSMC_WaitSignalPolarity_Low;FSMC_NORSRAMInitStructure.FSMC_AsynchronousWait=FSMC_AsynchronousWait_Disable; FSMC_NORSRAMInitStructure.FSMC_WrapMode = FSMC_WrapMode_Disable;   FSMC_NORSRAMInitStructure.FSMC_WaitSignalActive = FSMC_WaitSignalActive_BeforeWaitState;  FSMC_NORSRAMInitStructure.FSMC_WriteOperation = FSMC_WriteOperation_Enable;	 FSMC_NORSRAMInitStructure.FSMC_WaitSignal = FSMC_WaitSignal_Disable;   FSMC_NORSRAMInitStructure.FSMC_ExtendedMode = FSMC_ExtendedMode_Enable;  FSMC_NORSRAMInitStructure.FSMC_WriteBurst = FSMC_WriteBurst_Disable; FSMC_NORSRAMInitStructure.FSMC_ReadWriteTimingStruct = &readWriteTiming;  FSMC_NORSRAMInitStructure.FSMC_WriteTimingStruct = &writeTiming; 

     至于FSMCFSMCFSMC模块的读写时序参数的配置,我们这里只需要关注FSMC_AddressSetupTimeFSMC\_AddressSetupTimeFSMC_AddressSetupTimeFSMC_DataSetupTimeFSMC\_DataSetupTimeFSMC_DataSetupTime这连个参数就可以了,其它的配置为0就可以了。对于这两个参数分别可以简单的认为是读写使能信号的高电平持续时间和读写使能信号的低电平持续时间,如图10和图11中红圈中标示的位置所示。这里的HCLKHCLKHCLK为72MHZMHZMHZ,周期大概为14nsnsnsFSMCFSMCFSMC模块配置为不对HCLKHCLKHCLK进行分频。这里的时序要求只规定了最小值,只要配置的值超过这个最小值应该就可以,写时序的高低电平最小值是19nsnsns因此我们配置为2个HCLKHCLKHCLK周期就可以了,因此FSMC_AddressSetupTimeFSMC\_AddressSetupTimeFSMC_AddressSetupTimeFSMC_DataSetupTimeFSMC\_DataSetupTimeFSMC_DataSetupTime都配置的为1。对于读时序,读使能的低电平最低要求时间是150nsnsns,因此读时序的FSMC_DataSetupTimeFSMC\_DataSetupTimeFSMC_DataSetupTime参数配置为11,12×14=16812\times14=16812×14=168。对于读时序,读使能的高电平最低要求时间是250nsnsns,因此读时序的FSMC_AddressSetupTimeFSMC\_AddressSetupTimeFSMC_AddressSetupTime参数应该配置为17,18×14=25218\times14=25218×14=252。但是FSMC_AddressSetupTimeFSMC\_AddressSetupTimeFSMC_AddressSetupTime最高能配置到15,因此这里配置为15。我们前面也说过了FSMC_AddressSetupTimeFSMC\_AddressSetupTimeFSMC_AddressSetupTimeFSMC_DataSetupTimeFSMC\_DataSetupTimeFSMC_DataSetupTime这两个参数只是大概的对应于读写使能信号的高电平持续时间和读写使能信号的低电平持续时间,因此实际的读时序的FSMC_AddressSetupTimeFSMC\_AddressSetupTimeFSMC_AddressSetupTime时间还远没有到250nsnsns,所以这里也不用纠结。

图10.
图11.

     FSMCFSMCFSMC模块初始化完成之后就是LCDLCDLCD模块初始化了,这里的代码是厂家提供的,但是我看NT35310NT35310NT35310的用户手册上没有,不知道出处在哪里。这里有一个地方要注意的是虽然这里的屏幕模组已经在硬件上改为16比特接口,但是这里不代表一个像素点就是用16比特表示的,至于一个像素点的格式是有命令配置的。如图10和图11所示,可以配置为16比特或18bit。初始化代码里面配置的是16比特。对于16根数据线的8080接口,如果像素比特是16比特的话,一个像素可以一次发完,但是如果像素比特是18比特的话,两个像素一共要3次发送才能完成。但是我看正点原子的程序里面对于16根数据线的8080接口,如果像素比特是16比特的话,读两个像素一共要3次读取才能完成,这我也是很迷惑。

图12.
图13.
int main(void)
{	 u8 color_index=0;u8 id1=0;u8 id2=0;  u8 id3=0;u8 id4=0; 	  delay_init();	    	  uart_init(115200);	 	FEMC_LCD_Init();		  	LCD_WR_REG(0xD4);      id1=LCD_RD_DATA();id2=LCD_RD_DATA();id3=LCD_RD_DATA();id4=LCD_RD_DATA();printf("id1=%x,id2=%x,id3=%x,id4=%x.\r\n",id1,id2,id3,id4);           while(1){switch(color_index){case 0:LCD_Clear(WHITE);point_color=BLACK;break;case 1:LCD_Clear(BLACK);point_color=BLUE;break;case 2:LCD_Clear(BLUE);point_color=RED;break;case 3:LCD_Clear(RED);point_color=MAGENTA;break;case 4:LCD_Clear(MAGENTA);point_color=GREEN;break;case 5:LCD_Clear(GREEN);point_color=CYAN;break;case 6:LCD_Clear(CYAN);point_color=YELLOW;break; case 7:LCD_Clear(YELLOW);point_color=BRRED;break;case 8:LCD_Clear(BRRED);point_color=GRAY;break;case 9:LCD_Clear(GRAY);point_color=LGRAY;break;case 10:LCD_Clear(LGRAY);point_color=BROWN;break;case 11:LCD_Clear(BROWN);point_color=WHITE;break;}LCD_ShowString(30,40,"This is FEMC test for LCD."); LCD_ShowString(30,70,"LCD Display driver IC is NT35310.");LCD_ShowString(30,90,"2022.11.22");                                                  color_index++;if(color_index==12)color_index=0;delay_ms(1000);}
}

     好了我们最后来看一下主函数,流程比较简单,FSMCFSMCFSMC模块和LCDLCDLCD模块初始化完成之后就去读一下显示驱动芯片的IDIDID,然后循环不断地首先将屏幕刷新为一种颜色,然后用另一种颜色显示3行字符串。这里比较重要的是如何去构建字符串的字模,所谓的字模就是在一个点阵上要保证那些点为1,那些点为0,才能显示出特定的字符。这里使用的软件是PCtoLCD2002PCtoLCD2002PCtoLCD2002,这里我测试的时候采用是是12×1212\times1212×12的点阵,但是似乎对于英文字符自动的变为12×612\times612×6的点阵了。这里我们在配置的时候选择阴码(亮点为1,即对应的点阵的该点的比特位位1,暗点为0,即对应的点阵的该点的比特位位0),选择C51C51C51格式。取模方式表示最后在生成点阵的16进制数组的时候遍历点阵的顺序,点阵的一个点表示一个比特位,这里的逐列式的遍历顺序是从上到下,从左到右。顺向表示每遍历8个比特位,第一个遍历的比特位为这个构成的字节数据的最高位。

图14.
图15.

     图15中字符"A""A""A"的点阵字模放大之后如图16所示,按照逐列式的比特位读取顺序,从左到右,首先读取第一列的点阵数据,白色的方格表示比特位0,绿色的方格表示比特位1,。从上到下读取每一列的数据,这里需要注意的是如果每一列的点的个数不是8的倍速的话,在最后要补上0。因此这里的12×1212\times1212×12的点阵实际是12×612\times612×6的点阵的每一列的最后都要补上四个0,因此该点阵的第一列读取到的两个字节的点阵数据为0x00,0x400x00,0x400x00,0x40,因此整个点阵的数据字节数为6×2=86\times2=86×2=8。有了点阵数据之后在LCDLCDLCD屏幕上显示对应的字符就很简单了,只是点阵中的点的显示顺序要和构成字模的16进制数据的顺序一样,比如我们之前在构建字模16进制数据的时候的顺序为从上到下,从左到右的每一列构建的。那么我们在显示的时候每读取连个字节的数据就是相当于一列的点阵数据,比特位1表示亮点,比特位0表示暗点,高位优先。

图16.

我的工程在这里。


本文链接:https://www.ngui.cc/article/show-701447.html
Copyright © 2010-2022 ngui.cc 版权所有 |关于我们| 联系方式| 豫B2-20100000