本帖最后由 鱼香Rose 于 2022-10-14 15:03 编辑
一、屏幕介绍:
目前市场上的LCD种类繁多,驱动方式也多种多样,一般都是液晶面板,液晶控制器,触摸控制器三种器件进行组合的。
市场上一般的LCD驱动如下:
不带液晶驱动器的LCD一般都采用RGB接口,MCU内部集成LTDC等液晶控制器(使用芯片内部的SRAM或外扩的SDRAM当显存)。
带液晶驱动器的LCD一般都内置显存,采用8080/SPI接口,MCU使用FSMC或者SPI接口去驱动。
由于8080接口的时序与FSMC的时序高度一致,所以我们可以FSMC来模拟8080接口驱动。
LCD常见的驱动芯片ST7789用8080接口进行驱动的话,只要满足它的驱动时序就可以驱动LCD了,如果使用普通的IO进行时序模拟的话,实现起来比较繁琐。而常见的方式是采用FSMC进行模拟,而FSMC和8080信号线和时序看起来基本一致,只是FSMC多了许多地址线而已。
从FSMC时序图来看,在一个写入周期内,除了地址信号,其它信号都是一致的,而地址信号A[25:0]在整个写入周期内都在生效,但是8080接口没有地址信号只有D/CX(数据/指令切换)信号,而D/CX信号只需要一个IO,那我们就可以将A[25:0]中的一个IO当作D/CX信号来用。
那么D/CX信号怎么实现呢? D/CX信号高电平表示图像数据,低电平表示指令数据。
对于8080接口来说它一个周期需要D[15:0]+D/CX共17位数据就可以进行数据解析了,
而对于FSMC接口来说它一个周期需要发送A[25:0]+D[15:0]共42位数据。
数据宽度是16位的话就要注意FSMC_A地址信号和内部存储地址的对应关系,FSMC_A0对应内存地址的bit1,依次类推,FSMC_A24对应内存地址的bit25
二、外设存储器的位宽分为8Bit和16Bit驱动
APM32内部存储单位为一个字节,也就是一个地址存储一个字节大小的数据,当外部存储器的位宽为8位时,写入一个数据需要一个字节即地址加1,当外部存储器的位宽为16位时,写入一个数据需要两个字节即地址加2,如下图所示:
外设存储器位宽8位:写入0x11 和 0x22
外设存储器位宽16位:写入0x1122 和 0x3344
由于内存地址映射都是偶地址开始,当外设存储器的位宽为16位时,每次写数据时地址加2,相当于所有的地址为2的倍数,即写16位数据时地址位右移1位。
FSMC将使用内部的HADDR[25:1]地址来作为对外部存储器的寻址地址FSMC_A[24:0]。
当FSMC模拟8080并口时序驱动LCD外设时,以A6为例,作为LCD_RS信号控制线,A6输出0写命令,A6输出1写数据。当外设是8位位宽时,FSMC以HADDR[25:0] (映射FSMC_A[25:0])对外寻址,只要HADDR[25:0]的Bit6为0写命令,为1写数据。当外设是16位位宽时,FSMC以HADDR[25:1](映射FSMC_A[24:0])对外寻址,相当于整个地址右移一位,原来的Bit6输出实则是FSMC_A5输出,要让A6输出需要设置Bit7输出。如下所示:
外设为8位:Bit6->0100 0000->0x40
写命令->A6输出0 地址0x6000 0000用HADDR[25:0]表示,FSMC_A6为0
写数据->A6输出1 地址(0x6000 0000|0x40)用HADDR[25:0]表示,FSMC_A6为1
外设为16位:Bit6->0100 0000->0x40 Bit7->1000 0000->0x80
写命令->A6输出0 地址0x6000 0000用HADDR[25:1]表示即右移1位写入FSMC_A[24:0],FSMC_A6仍为0
写数据->A6输出1 地址(0x6000 0000|0x40)用HADDR[25:1]表示即右移1位写入FSMC_A[24:0],FSMC_A6变为0, FSMC_A5变为1,要使FSMC_A6输出1,地址需或上Bit7即地址(0x6000 0000|0x80)
三:核心代码
EMMC的配置,除了GPIO的设置外,关键的就是NORSRAM的配置,常规选择BANK1,数据宽度与外设一致都是8bit,使能突发写,读写时序选择默认即可。
lcd_t.bank = EMMC_BANK1_NORSRAM_1;
lcd_t.memoryDataWidth = EMMC_MEMORY_DATA_WIDTH_8BIT;
lcd_t.writeOperation = EMMC_WRITE_OPERATION_ENABLE;
lcd_t.dataAddressMux = EMMC_DATA_ADDRESS_MUX_DISABLE;
lcd_t.writeBurst = EMMC_WRITE_BURST_ENABLE;
lcd_t.burstAcceesMode = EMMC_BURST_ACCESS_MODE_ENABLE;
lcd_t.readWriteTimingStruct=&readWriteTimingStruct;
lcd_t.writeTimingStruct=&writeTimingStruct;
EMMC_ConfigNORSRAM(&lcd_t);
EMMC_EnableNORSRAM(EMMC_BANK1_NORSRAM_1);
RS信号线(数据&命令选择线)选择A16,对应LCD_DATA_ADDR 为0x60010000。
#define LCD_CMD_ADDR (0x60000000)
#define LCD_DATA_ADDR (0x60010000)
#define WriteCom(x) (*((uint8_t*)LCD_CMD_ADDR) = x)
#define WriteData(x) (*((uint8_t*)LCD_DATA_ADDR) = x)
附件是驱动代码 ,希望对大家有帮助 O(∩_∩)O~
|