本实验的源码工程在开发板光盘资料的:i.MX6UL终结者光盘资料\04_裸机例程源码\14_lcd 目录下。我们在Ubuntu系统下使用命令“mkdir 14_lcd”建立“14_lcd”文件夹,如图1所示:
然后使用“cd 14_lcd”命令进入到14_lcd文件夹。
然后使用命令“cp -r …/13_printf/* ./”将上一章试验中的所有内容拷贝到刚刚新建的“14_lcd”里面,如图 2所示:
然后在drivers目录下建立lcd文件夹,用来保存lcd的驱动文件,然后在“drivers/lcd”目录中新建lcd.h、lcd.c、lcdapi.c、lcdapi.h、font.h五个文件,lcd.h和lcd.c是LCD的驱动文件,lcdapi.h和lcdapi.c应用层接口实现画点,画线,画圆,显示字符串等一些列函数。我们打开lcd.h文件,输入下面的代码:
1 #ifndef _BSP_LCD_H
2 #define _BSP_LCD_H
3
4 #include "imx6ul.h"
5
6
7 /* 颜色 */
8 #define LCD_BLUE 0x000000FF
9 #define LCD_GREEN 0x0000FF00
10 #define LCD_RED 0x00FF0000
11 #define LCD_CYAN 0x0000FFFF
12 #define LCD_MAGENTA 0x00FF00FF
13 #define LCD_YELLOW 0x00FFFF00
14 #define LCD_LIGHTBLUE 0x008080FF
15 #define LCD_LIGHTGREEN 0x0080FF80
16 #define LCD_LIGHTRED 0x00FF8080
17 #define LCD_LIGHTCYAN 0x0080FFFF
18 #define LCD_LIGHTMAGENTA 0x00FF80FF
19 #define LCD_LIGHTYELLOW 0x00FFFF80
20 #define LCD_DARKBLUE 0x00000080
21 #define LCD_DARKGREEN 0x00008000
22 #define LCD_DARKRED 0x00800000
23 #define LCD_DARKCYAN 0x00008080
24 #define LCD_DARKMAGENTA 0x00800080
25 #define LCD_DARKYELLOW 0x00808000
26 #define LCD_WHITE 0x00FFFFFF
27 #define LCD_LIGHTGRAY 0x00D3D3D3
28 #define LCD_GRAY 0x00808080
29 #define LCD_DARKGRAY 0x00404040
30 #define LCD_BLACK 0x00000000
31 #define LCD_BROWN 0x00A52A2A
32 #define LCD_ORANGE 0x00FFA500
33 #define LCD_TRANSPARENT 0x00000000
34
35 /* LCD显存地址 */
36 #define LCD_FRAMEBUF_ADDR (0x89000000)
37
38 /* LCD控制参数结构体 */
39 struct tftlcd_typedef{
40 unsigned short height; /* LCD屏幕高度 */
41 unsigned short width; /* LCD屏幕宽度 */
42 unsigned char pixsize; /* LCD每个像素所占字节大小 */
43 unsigned short vspw;
44 unsigned short vbpd;
45 unsigned short vfpd;
46 unsigned short hspw;
47 unsigned short hbpd;
48 unsigned short hfpd;
49 unsigned int framebuffer; /* LCD显存首地址 */
50 unsigned int forecolor; /* 前景色 */
51 unsigned int backcolor; /* 背景色 */
52 };
53
54 extern struct tftlcd_typedef tftlcd_dev;
55
56
57 /* 函数声明 */
58 void lcd_init(void);
59 void lcdgpio_init(void);
60 void lcdclk_init(unsigned char loopDiv, unsigned char prediv, unsigned char div);
61 void lcd_reset(void);
62 void lcd_noreset(void);
63 void lcd_enable(void);
64 void video_pllinit(unsigned char loopdivi, unsigned char postdivi);
65
66 inline void lcd_drawpoint(unsigned short x,unsigned short y,unsigned int color);
67 inline unsigned int lcd_readpoint(unsigned short x,unsigned short y);
68 void lcd_clear(unsigned int color);
69 void lcd_fill(unsigned short x0, unsigned short y0, unsigned short x1, unsigned short y1, unsigned int color);
70 #endif
我们在lcd.h里面定义了些宏表示不同的颜色。第36行的LCD_FRAMEBUF_ADDR是显存的地址,我们将显存的地址放在了内存的0X89000000地址处,前面我们介绍了,本例程用到的屏幕分辨率是1024x600,所以占用的内存是1024x600x4,大约是2.4MB大小。I.MX6 ULL终结者开发板配置的内存有256MB和512MB两种。内存的地址范围范别是:0x80000000-0x90000000和0x80000000-0xA0000000,所以我们设置显存的地址是0x89000000,不管是256MB还是512MB的内存都在有效范围内,所以都可以使用。第39行的tftlcd_typedef结构体,里面保存的是LCD的一些配置参数。第58行到第69行是一些函数的声明。
然后我们打开lcd.c文件,在里面输入下面的代码:
1 #include "lcd.h"
2 #include "gpio.h"
3 #include "delay.h"
4 #include "stdio.h"
5
6 /* 液晶屏参数结构体 */
7 struct tftlcd_typedef tftlcd_dev;
8
9 /*
10 * @description : 始化LCD
11 * @param : 无
12 * @return : 无
13 */
14 void lcd_init(void)
15 {
16 lcdgpio_init(); /* 初始化IO */
17 lcdclk_init(32, 3, 5); /* 初始化LCD时钟 */
18
19 lcd_reset(); /* 复位LCD */
20 delayms(10); /* 延时10ms */
21 lcd_noreset(); /* 结束复位 */
22
23 /* TFTLCD参数结构体初始化 */
24 tftlcd_dev.height = 600;
25 tftlcd_dev.width = 1024;
26 tftlcd_dev.pixsize = 4; /* ARGB8888模式,每个像素4字节 */
27 tftlcd_dev.vspw = 3;
28 tftlcd_dev.vbpd = 20;
29 tftlcd_dev.vfpd = 12;
30 tftlcd_dev.hspw = 20;
31 tftlcd_dev.hbpd = 140;
32 tftlcd_dev.hfpd = 160;
33 tftlcd_dev.framebuffer = LCD_FRAMEBUF_ADDR;
34 tftlcd_dev.backcolor = LCD_WHITE;/* 背景色为白色 */
35 tftlcd_dev.forecolor = LCD_BLACK;/* 前景色为黑色 */
36
37 /* 初始化ELCDIF的CTRL寄存器
38 * bit [31] 0 : 停止复位
39 * bit [19] 1 : 旁路计数器模式
40 * bit [17] 1 : LCD工作在dotclk模式
41 * bit [15:14] 00 : 输入数据不交换
42 * bit [13:12] 00 : CSC不交换
43 * bit [11:10] 11 : 24位总线宽度
44 * bit [9:8] 11 : 24位数据宽度,也就是RGB888
45 * bit [5] 1 : elcdif工作在主模式
46 * bit [1] 0 : 所有的24位均有效
47 */
48 LCDIF->CTRL |= (1 << 19) | (1 << 17) | (0 << 14) | (0 << 12) |
49 (3 << 10) | (3 << 8) | (1 << 5) | (0 << 1);
50 /*
51 * 初始化ELCDIF的寄存器CTRL1
52 * bit [19:16] : 0X7 ARGB模式下,传输24位数据,A通道不用传输
53 */
54 LCDIF->CTRL1 = 0X7 << 16;
55
56 /*
57 * 初始化ELCDIF的寄存器TRANSFER_COUNT寄存器
58 * bit [31:16] : 高度
59 * bit [15:0] : 宽度
60 */
61 LCDIF->TRANSFER_COUNT = (tftlcd_dev.height << 16)
62 | (tftlcd_dev.width << 0);
63
64 /*
65 * 初始化ELCDIF的VDCTRL0寄存器
66 * bit [29] 0 : VSYNC输出
67 * bit [28] 1 : 使能ENABLE输出
68 * bit [27] 0 : VSYNC低电平有效
69 * bit [26] 0 : HSYNC低电平有效
70 * bit [25] 0 : DOTCLK上升沿有效
71 * bit [24] 1 : ENABLE信号高电平有效
72 * bit [21] 1 : DOTCLK模式下设置为1
73 * bit [20] 1 : DOTCLK模式下设置为1
74 * bit [17:0] : vsw参数
75 */
76 LCDIF->VDCTRL0 = 0; //先清零
77 LCDIF->VDCTRL0 = (0 << 29) | (1 << 28) | (0 << 27) |
78 (0 << 26) | (0 << 25) | (1 << 24) |
79 (1 << 21) | (1 << 20) | (tftlcd_dev.vspw << 0);
80 /*
81 * 初始化ELCDIF的VDCTRL1寄存器
82 * 设置VSYNC总周期
83 */
84 LCDIF->VDCTRL1 = tftlcd_dev.height + tftlcd_dev.vspw
85 + tftlcd_dev.vfpd + tftlcd_dev.vbpd;
86
87 /*
88 * 初始化ELCDIF的VDCTRL2寄存器
89 * 设置HSYNC周期
90 * bit[31:18] :hsw
91 * bit[17:0] : HSYNC总周期
92 */
93 LCDIF->VDCTRL2 = (tftlcd_dev.hspw << 18)
94 | (tftlcd_dev.width
95 + tftlcd_dev.hspw
96 + tftlcd_dev.hfpd
97 + tftlcd_dev.hbpd);
98
99 /*
100 * 初始化ELCDIF的VDCTRL3寄存器
101 * 设置HSYNC周期
102 * bit[27:16] :水平等待时钟数
103 * bit[15:0] : 垂直等待时钟数
104 */
105 LCDIF->VDCTRL3 = ((tftlcd_dev.hbpd + tftlcd_dev.hspw) << 16) | (tftlcd_dev.vbpd + tftlcd_dev.vspw);
106
107 /*
108 * 初始化ELCDIF的VDCTRL4寄存器
109 * 设置HSYNC周期
110 * bit[18] 1 : 当使用VSHYNC、HSYNC、DOTCLK的话此为置1
111 * bit[17:0] : 宽度
112 */
113
114 LCDIF->VDCTRL4 = (1<<18) | (tftlcd_dev.width);
115
116 /*
117 * 初始化ELCDIF的CUR_BUF和NEXT_BUF寄存器
118 * 设置当前显存地址和下一帧的显存地址
119 */
120 LCDIF->CUR_BUF = (unsigned int)tftlcd_dev.framebuffer;
121 LCDIF->NEXT_BUF = (unsigned int)tftlcd_dev.framebuffer;
122
123
124 lcd_enable(); /* 使能LCD */
125 delayms(10);
126 lcd_clear(LCD_WHITE); /* 清屏 */
127
128 }
129
130 /*
131 * IO引脚: LCD_DATA00 -> LCD_B0
132 * LCD_DATA01 -> LCD_B1
133 * LCD_DATA02 -> LCD_B2
134 * LCD_DATA03 -> LCD_B3
135 * LCD_DATA04 -> LCD_B4
136 * LCD_DATA05 -> LCD_B5
137 * LCD_DATA06 -> LCD_B6
138 * LCD_DATA07 -> LCD_B7
139 *
140 * LCD_DATA08 -> LCD_G0
141 * LCD_DATA09 -> LCD_G1
142 * LCD_DATA010 -> LCD_G2
143 * LCD_DATA011 -> LCD_G3
144 * LCD_DATA012 -> LCD_G4
145 * LCD_DATA012 -> LCD_G4
146 * LCD_DATA013 -> LCD_G5
147 * LCD_DATA014 -> LCD_G6
148 * LCD_DATA015 -> LCD_G7
149 *
150 * LCD_DATA016 -> LCD_R0
151 * LCD_DATA017 -> LCD_R1
152 * LCD_DATA018 -> LCD_R2
153 * LCD_DATA019 -> LCD_R3
154 * LCD_DATA020 -> LCD_R4
155 * LCD_DATA021 -> LCD_R5
156 * LCD_DATA022 -> LCD_R6
157 * LCD_DATA023 -> LCD_R7
158 *
159 * LCD_CLK -> LCD_CLK
160 * LCD_VSYNC -> LCD_VSYNC
161 * LCD_HSYNC -> LCD_HSYNC
162 * LCD_DE -> LCD_DE
163 * LCD_BL -> GPIO1_IO08
164 */
165
166 /*
167 * @description : LCD GPIO初始化
168 * @param : 无
169 * @return : 无
170 */
171 void lcdgpio_init(void)
172 {
173 gpio_pin_config_t gpio_config;
174
175
176 /* 1、IO初始化复用功能 */
177 IOMUXC_SetPinMux(IOMUXC_LCD_DATA00_LCDIF_DATA00,0);
178 IOMUXC_SetPinMux(IOMUXC_LCD_DATA01_LCDIF_DATA01,0);
179 IOMUXC_SetPinMux(IOMUXC_LCD_DATA02_LCDIF_DATA02,0);
180 IOMUXC_SetPinMux(IOMUXC_LCD_DATA03_LCDIF_DATA03,0);
181 IOMUXC_SetPinMux(IOMUXC_LCD_DATA04_LCDIF_DATA04,0);
182 IOMUXC_SetPinMux(IOMUXC_LCD_DATA05_LCDIF_DATA05,0);
183 IOMUXC_SetPinMux(IOMUXC_LCD_DATA06_LCDIF_DATA06,0);
184 IOMUXC_SetPinMux(IOMUXC_LCD_DATA07_LCDIF_DATA07,0);
185
186 IOMUXC_SetPinMux(IOMUXC_LCD_DATA08_LCDIF_DATA08,0);
187 IOMUXC_SetPinMux(IOMUXC_LCD_DATA09_LCDIF_DATA09,0);
188 IOMUXC_SetPinMux(IOMUXC_LCD_DATA10_LCDIF_DATA10,0);
189 IOMUXC_SetPinMux(IOMUXC_LCD_DATA11_LCDIF_DATA11,0);
190 IOMUXC_SetPinMux(IOMUXC_LCD_DATA12_LCDIF_DATA12,0);
191 IOMUXC_SetPinMux(IOMUXC_LCD_DATA13_LCDIF_DATA13,0);
192 IOMUXC_SetPinMux(IOMUXC_LCD_DATA14_LCDIF_DATA14,0);
193 IOMUXC_SetPinMux(IOMUXC_LCD_DATA15_LCDIF_DATA15,0);
194
195 IOMUXC_SetPinMux(IOMUXC_LCD_DATA16_LCDIF_DATA16,0);
196
197 IOMUXC_SetPinMux(IOMUXC_LCD_DATA17_LCDIF_DATA17,0);
198 IOMUXC_SetPinMux(IOMUXC_LCD_DATA18_LCDIF_DATA18,0);
199 IOMUXC_SetPinMux(IOMUXC_LCD_DATA19_LCDIF_DATA19,0);
200 IOMUXC_SetPinMux(IOMUXC_LCD_DATA20_LCDIF_DATA20,0);
201 IOMUXC_SetPinMux(IOMUXC_LCD_DATA21_LCDIF_DATA21,0);
202 IOMUXC_SetPinMux(IOMUXC_LCD_DATA22_LCDIF_DATA22,0);
203 IOMUXC_SetPinMux(IOMUXC_LCD_DATA23_LCDIF_DATA23,0);
204
205 IOMUXC_SetPinMux(IOMUXC_LCD_CLK_LCDIF_CLK,0);
206 IOMUXC_SetPinMux(IOMUXC_LCD_ENABLE_LCDIF_ENABLE,0);
207 IOMUXC_SetPinMux(IOMUXC_LCD_HSYNC_LCDIF_HSYNC,0);
208 IOMUXC_SetPinMux(IOMUXC_LCD_VSYNC_LCDIF_VSYNC,0);
209
210 IOMUXC_SetPinMux(IOMUXC_GPIO1_IO08_GPIO1_IO08,0); /* 背光BL引脚 */
211
212 /* 2、配置LCD IO属性
213 *bit 16:0 HYS关闭
214 *bit [15:14]: 0 默认22K上拉
215 *bit [13]: 0 pull功能
216 *bit [12]: 0 pull/keeper使能
217 *bit [11]: 0 关闭开路输出
218 *bit [7:6]: 10 速度100Mhz
219 *bit [5:3]: 111 驱动能力为R0/7
220 *bit [0]: 1 高转换率
221 */
222 IOMUXC_SetPinConfig(IOMUXC_LCD_DATA00_LCDIF_DATA00,0xB9);
223 IOMUXC_SetPinConfig(IOMUXC_LCD_DATA01_LCDIF_DATA01,0xB9);
224 IOMUXC_SetPinConfig(IOMUXC_LCD_DATA02_LCDIF_DATA02,0xB9);
225 IOMUXC_SetPinConfig(IOMUXC_LCD_DATA03_LCDIF_DATA03,0xB9);
226 IOMUXC_SetPinConfig(IOMUXC_LCD_DATA04_LCDIF_DATA04,0xB9);
227 IOMUXC_SetPinConfig(IOMUXC_LCD_DATA05_LCDIF_DATA05,0xB9);
228 IOMUXC_SetPinConfig(IOMUXC_LCD_DATA06_LCDIF_DATA06,0xB9);
229 IOMUXC_SetPinConfig(IOMUXC_LCD_DATA07_LCDIF_DATA07,0xB9);
230 IOMUXC_SetPinConfig(IOMUXC_LCD_DATA08_LCDIF_DATA08,0xB9);
231 IOMUXC_SetPinConfig(IOMUXC_LCD_DATA09_LCDIF_DATA09,0xB9);
232 IOMUXC_SetPinConfig(IOMUXC_LCD_DATA10_LCDIF_DATA10,0xB9);
233 IOMUXC_SetPinConfig(IOMUXC_LCD_DATA11_LCDIF_DATA11,0xB9);
234 IOMUXC_SetPinConfig(IOMUXC_LCD_DATA12_LCDIF_DATA12,0xB9);
235 IOMUXC_SetPinConfig(IOMUXC_LCD_DATA13_LCDIF_DATA13,0xB9);
236 IOMUXC_SetPinConfig(IOMUXC_LCD_DATA14_LCDIF_DATA14,0xB9);
237 IOMUXC_SetPinConfig(IOMUXC_LCD_DATA15_LCDIF_DATA15,0xB9);
238 IOMUXC_SetPinConfig(IOMUXC_LCD_DATA16_LCDIF_DATA16,0xB9);
239 IOMUXC_SetPinConfig(IOMUXC_LCD_DATA17_LCDIF_DATA17,0xB9);
240 IOMUXC_SetPinConfig(IOMUXC_LCD_DATA18_LCDIF_DATA18,0xB9);
241 IOMUXC_SetPinConfig(IOMUXC_LCD_DATA19_LCDIF_DATA19,0xB9);
242 IOMUXC_SetPinConfig(IOMUXC_LCD_DATA20_LCDIF_DATA20,0xB9);
243 IOMUXC_SetPinConfig(IOMUXC_LCD_DATA21_LCDIF_DATA21,0xB9);
244 IOMUXC_SetPinConfig(IOMUXC_LCD_DATA22_LCDIF_DATA22,0xB9);
245 IOMUXC_SetPinConfig(IOMUXC_LCD_DATA23_LCDIF_DATA23,0xB9);
246
247 IOMUXC_SetPinConfig(IOMUXC_LCD_CLK_LCDIF_CLK,0xB9);
248 IOMUXC_SetPinConfig(IOMUXC_LCD_ENABLE_LCDIF_ENABLE,0xB9);
249 IOMUXC_SetPinConfig(IOMUXC_LCD_HSYNC_LCDIF_HSYNC,0xB9);
250 IOMUXC_SetPinConfig(IOMUXC_LCD_VSYNC_LCDIF_VSYNC,0xB9);
251
252 IOMUXC_SetPinConfig(IOMUXC_GPIO1_IO08_GPIO1_IO08,0xB9); /* 背光BL引脚 */
253
254 /* GPIO初始化 */
255 gpio_config.direction = kGPIO_DigitalOutput;/* 输出 */
256 gpio_config.outputLogic = 1; /* 默认关闭背光 */
257 gpio_init(GPIO1, 8, &gpio_config); /* 背光默认打开 */
258 gpio_pinwrite(GPIO1, 8, 1); /* 打开背光 */
259 }
260
261 /*
262 * @description : LCD时钟初始化, LCD时钟计算公式如下:
263 * LCD CLK = 24 * loopDiv / prediv / div
264 * @param - loopDiv : loopDivider值
265 * @param - loopDiv : lcdifprediv值
266 * @param - div : lcdifdiv值
267 * @return : 无
268 */
269 void lcdclk_init(unsigned char loopDiv, unsigned char prediv, unsigned char div)
270 {
271 /* 先初始化video pll
272 * VIDEO PLL = OSC24M *
273 (loopDivider + (denominator / numerator))
274 / postDivider
275 *不使用小数分频器,因此denominator和numerator设置为0
276 */
277 CCM_ANALOG->PLL_VIDEO_NUM = 0; /* 不使用小数分频器 */
278 CCM_ANALOG->PLL_VIDEO_DENOM = 0;
279
280 /*
281 * PLL_VIDEO寄存器设置
282 * bit[13]: 1 使能VIDEO PLL时钟
283 * bit[20:19] 2 设置postDivider为1分频
284 * bit[6:0] : 32 设置loopDivider寄存器
285 */
286 CCM_ANALOG->PLL_VIDEO = (2 << 19)
287 | (1 << 13)
288 | (loopDiv << 0);
289
290 /*
291 * MISC2寄存器设置
292 * bit[31:30]: 0 VIDEO的post-div设置,
293 时钟源来源于postDivider,1分频
294 */
295 CCM_ANALOG->MISC2 &= ~(3 << 30);
296 CCM_ANALOG->MISC2 = 0 << 30;
297
298 /* LCD时钟源来源于PLL5,也就是VIDEO PLL */
299 CCM->CSCDR2 &= ~(7 << 15);
300 CCM->CSCDR2 |= (2 << 15);/* 设置LCDIF_PRE_CLK使用PLL5 */
301
302 /* 设置LCDIF_PRE分频 */
303 CCM->CSCDR2 &= ~(7 << 12);
304 CCM->CSCDR2 |= (prediv - 1) << 12;/* 设置分频 */
305
306 /* 设置LCDIF分频 */
307 CCM->CBCMR &= ~(7 << 23);
308 CCM->CBCMR |= (div - 1) << 23;
309
310 /* 设置LCD时钟源为LCDIF_PRE时钟 */
311 CCM->CSCDR2 &= ~(7 << 9); /* 清除原来的设置 */
312 CCM->CSCDR2 |= (0 << 9); /* LCDIF_PRE时钟源选择LCDIF_PRE时钟 */
313 }
314
315 /*
316 * @description : 复位ELCDIF接口
317 * @param : 无
318 * @return : 无
319 */
320 void lcd_reset(void)
321 {
322 LCDIF->CTRL = 1<<31; /* 强制复位 */
323 }
324
325 /*
326 * @description : 结束复位ELCDIF接口
327 * @param : 无
328 * @return : 无
329 */
330 void lcd_noreset(void)
331 {
332 LCDIF->CTRL = 0<<31; /* 取消强制复位 */
333 }
334
335 /*
336 * @description : 使能ELCDIF接口
337 * @param : 无
338 * @return : 无
339 */
340 void lcd_enable(void)
341 {
342 LCDIF->CTRL |= 1<<0; /* 使能ELCDIF */
343 }
344
345 /*
346 * @description : 画点函数
347 * @param - x : x轴坐标
348 * @param - y : y轴坐标
349 * @param - color : 颜色值
350 * @return : 无
351 */
352 inline void lcd_drawpoint(unsigned short x,unsigned short y,unsigned int color)
353 {
354 *(unsigned int*)((unsigned int)tftlcd_dev.framebuffer +
355 tftlcd_dev.pixsize * (tftlcd_dev.width * y+x))=color;
356 }
357
358
359 /*
360 * @description : 读取指定点的颜色值
361 * @param - x : x轴坐标
362 * @param - y : y轴坐标
363 * @return : 读取到的指定点的颜色值
364 */
365 inline unsigned int lcd_readpoint(unsigned short x,unsigned short y)
366 {
367 return *(unsigned int*)((unsigned int)tftlcd_dev.framebuffer +
368 tftlcd_dev.pixsize * (tftlcd_dev.width * y + x));
369 }
370
371 /*
372 * @description : 清屏
373 * @param - color : 颜色值
374 * @return : 读取到的指定点的颜色值
375 */
376 void lcd_clear(unsigned int color)
377 {
378 unsigned int num;
379 unsigned int i = 0;
380
381 unsigned int *startaddr=(unsigned int*)tftlcd_dev.framebuffer; //指向帧缓存首地址
382 num=(unsigned int)tftlcd_dev.width * tftlcd_dev.height; //缓冲区总长度
383 for(i = 0; i < num; i++)
384 {
385 startaddr[i] = color;
386 }
387 }
388
389 /*
390 * @description : 以指定的颜色填充一块矩形
391 * @param - x0 : 矩形起始点坐标X轴
392 * @param - y0 : 矩形起始点坐标Y轴
393 * @param - x1 : 矩形终止点坐标X轴
394 * @param - y1 : 矩形终止点坐标Y轴
395 * @param - color : 要填充的颜色
396 * @return : 读取到的指定点的颜色值
397 */
398 void lcd_fill(unsigned short x0, unsigned short y0,
399 unsigned short x1, unsigned short y1,
400 unsigned int color)
401 {
402 unsigned short x, y;
403
404 if(x0 < 0) x0 = 0;
405 if(y0 < 0) y0 = 0;
406 if(x1 >= tftlcd_dev.width) x1 = tftlcd_dev.width - 1;
407 if(y1 >= tftlcd_dev.height) y1 = tftlcd_dev.height - 1;
408
409 for(y = y0; y <= y1; y++)
410 {
411 for(x = x0; x <= x1; x++)
412 lcd_drawpoint(x, y, color);
413 }
414 }
lcd_init函数是屏幕初始化函数,先是调用lcdgpio_init函数,初始化LCD对应的IO,然后分别调用时钟初始化函数,复位函数,然后按照我们前面介绍的步骤初始化eLCDIF各种参数配置寄存器,最后使能eLCDIF。
lcdgpio_init函数主要完成LCD相关IO引脚的初始化
lcdclk_init函数负责完成LCD时钟初始化,包括选择eLCDIF的时钟源,分频值。
lcd_reset函数是复位eLCDIF。
lcd_noreset函数是结束复位eLCDID。
lcd_enable函数使能eLCDIF。
lcd_drawpoint函数是画点函数。
lcd_readpoint函数是读点函数。
lcd_clear函数是清屏幕函数。
lcd_fill函数是填充屏幕区域函数。
然后打开lcdapi.h文件,在里面输入下面的代码:
1 #ifndef BSP_LCDAPI_H
2 #define BSP_LCDAPI_H
3
4 #include "imx6ul.h"
5 #include "lcd.h"
6
7 /* 函数声明 */
8 void lcd_drawline(unsigned short x1,
9 unsigned short y1,
10 unsigned short x2,
11 unsigned short y2);
12
13 void lcd_draw_rectangle(unsigned short x1,
14 unsigned short y1,
15 unsigned short x2,
16 unsigned short y2);
17
18 void lcd_draw_Circle(unsigned short x0,
19 unsigned short y0,
20 unsigned char r);
21
22 void lcd_showchar(unsigned short x,
23 unsigned short y,
24 unsigned char num,
25 unsigned char size,
26 unsigned char mode);
27
28 unsigned int lcd_pow(unsigned char m, unsigned char n);
29 void lcd_shownum(unsigned short x,
30 unsigned short y,
31 unsigned int num,
32 unsigned char len,
33 unsigned char size);
34
35 void lcd_showxnum(unsigned short x,
36 unsigned short y,
37 unsigned int num,
38 unsigned char len,
39 unsigned char size,
40 unsigned char mode);
41
42 void lcd_show_string(unsigned short x, unsigned short y,
43
44 unsigned short width, unsigned short height,
45 unsigned char size, char *p);
46
47 #endif
我们在lcdapi.h里面声明了一些函数。然后打开lcdapi.c文件,熟悉如下的代码:
1 #include "lcdapi.h"
2 #include "font.h"
3
4 /*
5 * @description : 画线函数
6 * @param - x1 : 线起始点坐标X轴
7 * @param - y1 : 线起始点坐标Y轴
8 * @param - x2 : 线终止点坐标X轴
9 * @param - y2 : 线终止点坐标Y轴
10 * @return : 无
11 */
12 void lcd_drawline(unsigned short x1,
13 unsigned short y1,
14 unsigned short x2,
15 unsigned short y2)
16 {
17 u16 t;
18 int xerr = 0, yerr = 0, delta_x, delta_y, distance;
19 int incx, incy, uRow, uCol;
20 delta_x = x2 - x1; /* 计算坐标增量 */
21 delta_y = y2 - y1;
22 uRow = x1;
23 uCol = y1;
24 if(delta_x > 0) /* 设置单步方向 */
25 incx = 1;
26 else if(delta_x==0) /* 垂直线 */
27 incx = 0;
28 else
29 {
30 incx = -1;
31 delta_x = -delta_x;
32 }
33 if(delta_y>0)
34 incy=1;
35 else if(delta_y == 0) /* 水平线 */
36 incy=0;
37 else
38 {
39 incy = -1;
40 delta_y = -delta_y;
41 }
42 if( delta_x > delta_y) /*选取基本增量坐标轴 */
43 distance = delta_x;
44 else
45 distance = delta_y;
46 for(t = 0; t <= distance+1; t++ )/* 画线输出 */
47 {
48
49 lcd_drawpoint(uRow, uCol, tftlcd_dev.forecolor);/* 画点 */
50 xerr += delta_x ;
51 yerr += delta_y ;
52 if(xerr > distance)
53 {
54 xerr -= distance;
55 uRow += incx;
56 }
57 if(yerr > distance)
58 {
59 yerr -= distance;
60 uCol += incy;
61 }
62 }
63 }
64
65 /*
66 * @description : 画矩形函数
67 * @param - x1 : 矩形坐上角坐标X轴
68 * @param - y1 : 矩形坐上角坐标Y轴
69 * @param - x2 : 矩形右下角坐标X轴
70 * @param - y2 : 矩形右下角坐标Y轴
71 * @return : 无
72 */
73 void lcd_draw_rectangle(unsigned short x1,
74 unsigned short y1,
75 unsigned short x2,
76 unsigned short y2)
77 {
78 lcd_drawline(x1, y1, x2, y1);
79 lcd_drawline(x1, y1, x1, y2);
80 lcd_drawline(x1, y2, x2, y2);
81 lcd_drawline(x2, y1, x2, y2);
82 }
83
84 /*
85 * @description : 在指定位置画一个指定大小的圆
86 * @param - x0 : 圆心坐标X轴
87 * @param - y0 : 圆心坐标Y轴
88 * @param - y2 : 圆形半径
89 * @return : 无
90 */
91 void lcd_draw_Circle(unsigned short x0,unsigned short y0,unsigned char r)
92 {
93 int mx = x0, my = y0;
94 int x = 0, y = r;
95
96 int d = 1 - r;
97 while(y > x) /* y>x即第一象限的第1区八分圆 */
98 {
99 lcd_drawpoint(x + mx, y + my, tftlcd_dev.forecolor);
100 lcd_drawpoint(y + mx, x + my, tftlcd_dev.forecolor);
101 lcd_drawpoint(-x + mx, y + my, tftlcd_dev.forecolor);
102 lcd_drawpoint(-y + mx, x + my, tftlcd_dev.forecolor);
103
104 lcd_drawpoint(-x + mx, -y + my, tftlcd_dev.forecolor);
105 lcd_drawpoint(-y + mx, -x + my, tftlcd_dev.forecolor);
106 lcd_drawpoint(x + mx, -y + my, tftlcd_dev.forecolor);
107 lcd_drawpoint(y + mx, -x + my, tftlcd_dev.forecolor);
108 if( d < 0)
109 {
110 d = d + 2 * x + 3;
111 }
112 else
113 {
114 d= d + 2 * (x - y) + 5;
115 y--;
116 }
117 x++;
118 }
119 }
120
121 /*
122 * @description : 在指定位置显示一个字符
123 * @param - x : 起始坐标X轴
124 * @param - y : 起始坐标Y轴
125 * @param - num : 显示字符
126 * @param - size: 字体大小, 可选12/16/24/32
127 * @param - mode: 叠加方式(1)还是非叠加方式(0)
128 * @return : 无
129 */
130 void lcd_showchar(unsigned short x,
131 unsigned short y,
132 unsigned char num,
133 unsigned char size,
134 unsigned char mode)
135 {
136 unsigned char temp, t1, t;
137 unsigned short y0 = y;
138 unsigned char csize = (size / 8+ ((size % 8) ? 1 : 0))
139 * (size / 2);/* 得到字体一个字符对应点阵集
所占的字节数*/
140 num = num - ' '; /*得到偏移后的值 */
141 for(t = 0; t < csize; t++)
142 {
143 if(size == 12)
144 temp = asc2_1206[num][t]; /* 调用1206字体 */
145 else if(size == 16)
146 temp = asc2_1608[num][t];/* 调用1608字体 */
147 else if(size == 24)
148 temp = asc2_2412[num][t];/* 调用2412字体 */
149 else if(size == 32)
150 temp = asc2_3216[num][t];/* 调用3216字体 */
151 else return; /* 没有的字库 */
152 for(t1 = 0; t1 < 8; t1++)
153 {
154 if(temp & 0x80)lcd_drawpoint(x, y, tftlcd_dev.forecolor);
155 else if(mode==0)lcd_drawpoint(x, y, tftlcd_dev.backcolor);
156 temp <<= 1;
157 y++;
158 if(y >= tftlcd_dev.height) return;/* 超区域了 */
159 if((y - y0) == size)
160 {
161 y = y0;
162 x++;
163 if(x >= tftlcd_dev.width) return;/* 超区域了 */
164 break;
165 }
166 }
167 }
168 }
169
170 /*
171 * @description : 计算m的n次方
172 * @param - m : 要计算的值
173 * @param - n : n次方
174 * @return : m^n次方.
175 */
176 unsigned int lcd_pow(unsigned char m,unsigned char n)
177 {
178 unsigned int result = 1;
179 while(n--) result *= m;
180 return result;
181 }
182
183 /*
184 * @description : 显示指定的数字,高位为0的话不显示
185 * @param - x : 起始坐标点X轴。
186 * @param - y : 起始坐标点Y轴。
187 * @param - num : 数值(0~999999999)。
188 * @param - len : 数字位数。
189 * @param - size: 字体大小
190 * @return : 无
191 */
192 void lcd_shownum(unsigned short x,
193 unsigned short y,
194 unsigned int num,
195 unsigned char len,
196 unsigned char size)
197 {
198 unsigned char t, temp;
199 unsigned char enshow = 0;
200 for(t = 0; t < len; t++)
201 {
202 temp = (num / lcd_pow(10, len - t - 1)) % 10;
203 if(enshow == 0 && t < (len - 1))
204 {
205 if(temp == 0)
206 {
207 lcd_showchar(x + (size / 2) * t,
208 y, ' ',
209 size, 0);
210 continue;
211 }else enshow = 1;
212 }
213 lcd_showchar(x + (size / 2) * t,
214 y, temp + '0', size, 0);
215 }
216 }
217
218 /*
219 * @description : 显示指定的数字,高位为0,还是显示
220 * @param - x : 起始坐标点X轴。
221 * @param - y : 起始坐标点Y轴。
222 * @param - num : 数值(0~999999999)。
223 * @param - len : 数字位数。
224 * @param - size : 字体大小
225 * @param - mode : [7]:0,不填充;1,填充0.
226 * [6:1]:保留
227 * [0]:0,非叠加显示;1,叠加显示.
228 * @return : 无
229 */
230 void lcd_showxnum(unsigned short x,
231 unsigned short y,
232 unsigned int num,
233 unsigned char len,
234 unsigned char size,
235 unsigned char mode)
236 {
237 unsigned char t, temp;
238 unsigned char enshow = 0;
239 for(t = 0; t < len; t++)
240 {
241 temp = (num / lcd_pow(10, len - t- 1)) % 10;
242 if(enshow == 0 && t < (len - 1))
243 {
244 if(temp == 0)
245 {
246 if(mode & 0X80)
247 lcd_showchar(x + (size / 2) * t,
248 y, '0', size,
249 mode & 0X01);
250 else
251 lcd_showchar(x + (size / 2) * t,
252 y , ' ', size,
253 mode & 0X01);
254 continue;
255 }else enshow=1;
256
257 }
258 lcd_showchar( x + (size / 2) * t,
259 y, temp + '0' ,
260 size , mode & 0X01);
261 }
262 }
263
264 /*
265 * @description : 显示一串字符串
266 * @param - x : 起始坐标点X轴。
267 * @param - y : 起始坐标点Y轴。
268 * @param - width : 字符串显示区域长度
269 * @param - height : 字符串显示区域高度
270 * @param - size : 字体大小
271 * @param - p : 要显示的字符串首地址
272 * @return : 无
273 */
274 void lcd_show_string(unsigned short x,
275 unsigned short y,
276 unsigned short width,
277 unsigned short height,
278 unsigned char size,char *p)
279 {
280 unsigned char x0 = x;
281 width += x;
282 height += y;
283 while((*p <= '~') &&(*p >= ' '))/* 判断是不是非法字符! */
284 {
285 if(x >= width) {
x = x0; y += size;}
286 if(y >= height) break; /* 退出 */
287 lcd_showchar(x, y, *p , size, 0);
288 x += size / 2;
289 p++;
290 }
291 }
lcdapi.c里面的函数主要实现画线、画矩形、画圆、显示数字,显示字符,字符串等功能。
接下来我们来了解一下LCD上显示字符的原理。要在LCD上显示字符,需要有字符对应的点阵数据,ASCII常用的字符集一共有95个,分别是:
!"#$%&'()*+,-0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{
|}~.
如果要显示一个字符,我们首先要得到这个字符对应的点阵数据,我们给大家推荐一款很好用的字符提取软件:PCtoLCD2002 完美版。该软件可以提取各种字符,取模方式有好几种设置方式。该软件还支持图形模式(用户可以自己定义图片大小,然后画图,根据所画的图形生成点阵数据)。
我们使用该软件把ASCII字符集按12*6
大小、16*8
大小、24*12
大小、32*16
大小取模出来(对应汉字大小为12*12
、16*16
、24*24
、32*32
)得到的对应数组保存在了font.h里面。每个12*6的字符占12个字节,16*8
的占16个字节,24*12
的占36个字节,32*16
的占64字节。Font.h里面的字符集点阵数组asc2_1206、 asc2_1608、 asc2_2412、asc2_3216对应这四种大小的字符集。
最后打开main.c,在里面输入下面的代码:
1 #include "clk.h"
2 #include "delay.h"
3 #include "led.h"
4 #include "beep.h"
5 #include "key.h"
6 #include "int.h"
7 #include "uart.h"
8 #include "stdio.h"
9 #include "lcd.h"
10 #include "lcdapi.h"
11
12
13 /* 背景颜色索引 */
14 unsigned int backcolor[10] = {
15 LCD_BLUE,
16 LCD_GREEN,
17 LCD_RED,
18 LCD_CYAN,
19 LCD_YELLOW,
20 LCD_LIGHTBLUE,
21 LCD_DARKBLUE,
22 LCD_WHITE,
23 LCD_BLACK,
24 LCD_ORANGE
25
26 };
27
28
29 /*
30 * @description : main函数
31 * @param : 无
32 * @return : 无
33 */
34 int main(void)
35 {
36 unsigned char index = 0;
37 unsigned char state = OFF;
38
39 int_init(); /* 初始化中断(一定要最先调用!) */
40 imx6u_clkinit(); /* 初始化系统时钟 */
41 delay_init(); /* 初始化延时 */
42 clk_enable(); /* 使能所有的时钟 */
43 led_init(); /* 初始化led */
44 beep_init(); /* 初始化beep */
45 uart_init(); /* 初始化串口,波特率115200 */
46 lcd_init(); /* 初始化LCD */
47
48 tftlcd_dev.forecolor = LCD_RED;
49 lcd_show_string(10,10,400,32,
50 32,(char*)"i.MX6ULL LCD TEST"); /* 显示字符串 */
51 lcd_draw_rectangle(10, 52, 1014, 290); /* 绘制矩形框 */
52 lcd_drawline(10, 52,1014, 290); /* 绘制线条 */
53 lcd_drawline(10, 290,1014, 52); /* 绘制线条 */
54 lcd_draw_Circle(512, 171, 119); /* 绘制圆形 */
55
56 while(1)
57 {
58 index++;
59 if(index == 10)
60 index = 0;
61 lcd_fill(0, 300, 1023, 599,
62 backcolor[index]);
63 lcd_show_string(800,10,240,32,
64 32,(char*)"INDEX="); /*显示字符串*/
65 lcd_shownum(896,10, index, 2, 32); /* 显示数字,叠加显示 */
66
67 state = !state;
68 led_switch(LED0,state);
69 delayms(1000); /* 延时一秒*/
70 }
71 return 0;
72 }
第46行调用lcd_init函数初始化LCD。
第48行设置前景色为红色(显示的字符颜色)。
第49行在LCD上显示一串字符信息。
第51行到第54行在LCD上绘制各种图形
然后进入while(1)循环,每隔1秒钟调用lcd_fill填充函数,填充lcd指定区域的背景色,并且调用lcd_show_string显示字符串,lcd_shownum显示数字。