i.MX6ULL终结者LCD屏幕显示例程程序设计

本实验的源码工程在开发板光盘资料的:i.MX6UL终结者光盘资料\04_裸机例程源码\14_lcd 目录下。我们在Ubuntu系统下使用命令“mkdir 14_lcd”建立“14_lcd”文件夹,如图1所示:
在这里插入图片描述

图 1

然后使用“cd 14_lcd”命令进入到14_lcd文件夹。

然后使用命令“cp -r …/13_printf/* ./”将上一章试验中的所有内容拷贝到刚刚新建的“14_lcd”里面,如图 2所示:
在这里插入图片描述

图 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*1216*1624*2432*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显示数字。在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_46635880/article/details/109154681