本帖最后由 huo_hu 于 2018-3-18 19:03 编辑
此内容由EEWORLD论坛网友huo_hu原创,如需转载或用于商业用途需征得作者同意并注明出处
这部分介绍fsmc驱动的lcd,我这个lcd的驱动芯片是ili9806,其他的ili9XX也都是可以的,硬件连接图
上面的是控制信号接线,另外FSMC的0~15接lcd的数据线0~15。
硬件上有几点说明一下,这个ili9XXX芯片本身是可以设置总线宽度的,有的厂家制作的时候把这个设置固定了,有的厂家做的屏有飞线选择,要对着说明看一下,如果用8线也是可以的数据分两次打出去。还有一个是屏的背光电路,其实就是个升压电路,可以配合pwm来调整背光亮度,屏上没有要自己做这部分。
然后cube里配置成使用fsmc总线lcd接口
这里面FSMC的控制管脚和数据管脚都是固定的,只有lcd register 的A18可以自己选择一下,用哪个都可以程序地址上有点差别
#define A16BIT 18
#define LCD_DATA_ADDR (0x60000000+(uint32_t)(1<<(A16BIT+1)))
#define LCD_REGS_ADDR (0x60000000)
液晶以这条信号线来区分是指令地址或者数据地址。我这里用的是16位数据宽度。
保存更新一下,cube已经帮你把fsmc配置好了,下一步就开始写液晶初始化程序了。这个初始化最开始的部分是对各个寄存器进行配置,因为各个厂家的寄存器值可能不一样,这部分只能找厂家要。类似的程序就是这样
#define A16BIT 18
#define LCD_DATA_ADDR (0x60000000+(uint32_t)(1<<(A16BIT+1)))
#define LCD_REGS_ADDR (0x60000000)
#define LCD_WR_REG(ind) { \
*(__IO uint16_t*)(LCD_REGS_ADDR)=ind; \
}
#define LCD_WR_DAT(dat) { \
*(__IO uint16_t *)(LCD_DATA_ADDR)=dat; \
}
#define LCD_RE_DAT (*(__IO uint16_t *)(LCD_DATA_ADDR))
#define write_command LCD_WR_REG
#define write_data LCD_WR_DAT
#define delay HAL_Delay
void ILI9806_800_480_Init(void) {
HAL_Delay(100);
// EXTC Command Set enable register
write_command(0xFF);
write_data(0xFF);
write_data(0x98);
write_data(0x06);
write_command(0xBA); // SPI Interface Setting
write_data(0x60);
write_command(0xBC); // GIP 1
write_data(0x01);
write_data(0x12);
。。。。。。。 这中间好长好长,我省略了
write_command(0x3A);
write_data(0x55); //55-16BIT,66-18BIT,77-24BIT
write_command(0x11);
delay(120);
write_command(0x29);
delay(25);
编译然后下载,(记得打开背光,不然什么也看不到),这个函数运行到最后部分就是0x29指令是开显示,这个时候如果看到屏幕上有好多花点,那恭喜你成功一大半了,至少硬件没问题了,那些花点是随机的内存数据。剩下的再改程序就行了。那些command的含义在手册里都有,指令很多但是绝大部分都用不到。
ILI9806G-Data Sheet 芯片手册.pdf
(6.54 MB, 下载次数: 5)
这个芯片最常用的一个指令是0x2a 0x2b 用来在屏幕上开一个窗口或者叫block,附带的数据就是窗口的位置和宽度和高度
void LCD_CreateBlock(unsigned int Y,unsigned int X,unsigned int Width,unsigned int Height) {
LCD_WR_REG(0x2a);
LCD_WR_DAT(X>>8);
LCD_WR_DAT(X);
LCD_WR_DAT((Height+X-1)>>8);//这里减1是因为数据从0开始算起
LCD_WR_DAT(Height+X-1);
LCD_WR_REG(0x2b);
LCD_WR_DAT(Y>>8);
LCD_WR_DAT(Y);
LCD_WR_DAT((Width+Y-1)>>8);
LCD_WR_DAT(Width+Y-1);
LCD_WR_REG(0x2c);
//之后连续写入数据
}
注意这个函数里面写指令(0x2A)之后连续写4个数据,这个数据是一个字节的长度,即便是16位的数据总线宽也只能一次写一个字节。
我这里偷换了一下宽度和高度的数据,屏就横过来显示了。这个函数的最后一个0x2c指令是开始向lcd写数据,连续把每个点的数据写入就行了,数据格式是由0x3A指令指定的,也就是每个点是2个字节16位,也就是rgb的565格式。0代表纯黑色点0xffff代表纯白色点。
下面这个函数用指定的颜色刷屏数据。
void LCD_Clear_Screen(unsigned int col) {
uint32_t i=0;
LCD_CreateBlock(0,0,LCD_SCREEN_WIDTH,LCD_SCREEN_HEIGHT);
for (i=0;i
LCD_WR_DAT(col);
}
这个能成功以后需要调整一下刷屏数据的方向,也就是上下左右组合的四种情况,我这里是左下到左上,再从左到右,这个需要根据你的屏摆放位置的方向来调整,如果不对可以通过指令0x36来设置,就是几个控制位改一下就行。
write_command(0x36);/*Memory Access Control*/
// 0 1 2 3 4 5 6 7
// GS SS MH BGR ML MV MX MY
write_data(0xc0);
这个刷屏方向和后面的图像数据顺序有关系,另外如果显示汉字或者字符的字模取值方向也有关系,全都统一就不用显示的时候改来改去的了。
好现在刷屏可以了,需要把位图数据导出到文件里。位图文件的格式有很多种,你可以移植一个ucgui之类的东西来获取位图数据,不过效果不理想,解算位图数据的开销很大,另外像调色板位图就没办法直接往液晶屏里直接刷。
我这里用了一个imgtolcd软件来生成位图数据
确保数据格式、宽度是对的,然后点保存就行了,这个软件不带缩放功能,网上下载的jpg可能要先处理一下。如果包含文件头数据的话会在文件最前面插入8个字节的宽度和高度数据。
显示图片的时候先开窗口,文件里的数据直接送到FSMC数据口就行了。
void LCD_SDBmp(u16 sx,u16 sy,u8 *bmpFileName) {
uint32_t rsize;//读出字节
uint32_t i;
uint16_t rsd[256];
retUSER = f_open(&USERFile,(const TCHAR *)bmpFileName , FA_READ);
if (retUSER) return;
LCD_CreateBlock(sx,sy,800,480);
while ((retUSER =f_read(&USERFile,rsd,512,&rsize))==0 && rsize==512) {
for (i=0;i<256;i++) DATABUS_OUTPUT(rsd
);
}
if (rsize!=0) {
//不整
}
f_close(&USERFile);
return;
}
下面这个是交替显示2个图的视频
VID_20180303_000141.mp4
(23.07 MB, 下载次数: 18)
这个视频的重点是刷完整屏数据的时间,总共是800*480*2=768000字节,通过文件系统读取文件比较慢,下一部分我们修改程序,不开缓冲,通过sdio接口的dma直接把数据写到液晶屏里。
补充一点,fsmc的时序没调整,以前用的默认值是最慢的。