当前位置: 首页 > article >正文

37. RGBLCD实验

一、RGBLCD显示原理简介

1、像素点
于一个“小灯“,不管是液晶屏,还是手机,平板,RGBLCD屏幕他都是有由一个个的彩色小灯构成的。彩色点阵屏每个像素点有三个小灯,红色、绿色和蓝色,也叫做RGB。RGB就是光的三原色。通过调整RGB三种颜色的比例,就可以实现姹紫千红的世界。

2、分辨率
要想显示文字,图片,视频等等就需要很多个像素点,分辨率说的就是像素点的个数,1080P、720P、2K、4K,8K。1080P=19201080,表示一行有1920个像素点,一列有1080个。显示器有尺寸!24村,27村、55寸。尺寸不变的情况下,分辨率越高,显示效果越精细。4K=38402160相当于4个1080P
正点原子的RGB屏幕有:4.3寸480272,800480,7寸的800480和1024600,10.1寸的1280800。
Iphone4屏幕尺寸是3.5寸,960
640分辨率,PPI=327.
3、像素格式
如何将RGB三种颜色进行量化,每种颜色用8bit表示,RGB就需要888共24bit。可以描述出2^24=中颜色16777216=1677万种颜色。现在流行10bit,HDR10,支持HDR效果的10bit面板,RGB10 10 10。
在RGB888的基础上在加上8bit的ALPHA通道,也就是透明通道,ARGB8888=32位。
4、LCD屏幕接口
RGB格式的屏幕,一般叫做RGB接口屏。
屏幕接口有:MIPI、LVDS、MCU、RGB接口。
正点原子屏幕ID:使用ID可以识别出不同的屏幕,在RGBLCD屏幕上对R7,G7,B7焊接上拉或下拉电阻实现不同的ID。,
正点原子的ALPHA地板RGB屏幕接口用了3个3157模拟开关。原因是防止LCD屏幕上的ID电阻影响到6ULL的启动。

5、LCD时间参数和LCD时序
水平:
HSYNC:水平同步信号,行同步信号,当出现HSYNC信号的时候表示新的一行开始显示
1、产生HSYNC信号,表示新的一行开始显示,HSYNC信号得维持一段时间,这个时间叫做HSPW。
2、HSYNC信号完成以后,需要一段时间延时,这段时间叫做HBP。
3、显示1024个像素点的数据,1024个clk。
3、一行像素显示完成以后,到HSYNC下一行信号的产生之间有个延时,叫做HFP。
因此真正显示一行所需的时间计时HSPW+HBP+WIDTH(屏幕水平像素点个数,比如1024)+HFP=20+140+1024+160=1344CLK.

垂直:
VSYNC:垂直同步信号,帧同步信号,当出现VSYNC信号的时候表示新的一帧开始显示。
1、VSYNC信号,持续一段时间,位VSPW。
2、VSYNC信号完成以后,需要一段时间,叫做VBP
3、VBP信号结束以后,就是要显示的行数,比如600行,
4、所有的行显示完成以后,一段VFP延时,
(VSPW+VBP+height(600)+VFP)* (HSPW+HBP+WIDTH(1024)+HFP)=

像素时钟:

6、显存
显存:显示存储空间,采用ARGB8888=32bit=4B。这4个字节的数据表示一个像素点的信息,必须得存起来。10246004=2.5MB。因此需要流出2.5MB的内存给LCD用,方法很简单,直接定义一个32位的数组,u32 lcdframe[1024*600];

二、6ULL LCDIF控制器接口原理

1、我们使用DOTCLK接口,也就是VSYNC、HSYNC、ENABLE(DE)和DOTCLK(PCLK)
2、LCDIF_CTRL寄存器,bit0必须置1,bit1设置数据格式24位全部有效,设置为0。Bit5设置LCDIF接口工作在主机模式下,要置1。Bit9:8设置输入像素格式位24bit,写3。Bit11:10,设置数据传输宽度位24bit,写3。Bit13:12设置数据交换,我们不需要交换设置位0。Bit15:14设置输入数据交换,不交换设置位0。Bit17置1,LCDIF工作在DOTCLK模式下。Bit19必须置1,因为工作在DOTCL模式。Bit31是复位功能必须置0.
3、LCDIF_CTRL1寄存器的bit19:16设置位0X7。24位的格式,
4、LCDIF_TRANSFER_COUNT寄存器的bit15:0是LCD一行的像素数,1024。Bit31:16是LCD一共有多少行,600行。
5、LCDIF_VDCTRL0寄存器,bit17:0为vspw参数。Bit20设置vsync信号的宽度单位 ,设置为1。Bit21设置为1。Bit24设置ENABLE信号极性,为0的时候是低电平有效,为1是高电平,我们设置为1。Bit25设置CLK信号极性,设置为0.。bit26设置HSYNC信号极性,设置0,低电平有效,bit27设置VSYNC信号极性,设置为0,低电平有效。Bit28设置1,开始ENABLE信号。Bit29设置为0,VSYNC输出。
6、LCDIF_VDCTRL1寄存器为两个VSYNC信号之间的长度,那就是VSPW+ VBPD+HEIGHT+VFP。
7、CDIF_VDCTRL2寄存器bit17:0是两个HSYNC信号之间的长度,那就是hspw+hbp+width+hfp。Bit31:18为hspw。
8、LCDIF_VDCTRL3寄存器,bit15:0是vbp+vspw。Bit27:16是hbp+hspw。
9、LCDIF_VDCTRL4寄存器,bit17:0是一行有多少个像素点,1024
10、LCDIF_CUR_BUF,LCD当前缓存,显存首地址。
11、LCDIF_NEXT_BUF,LCD下一帧数据首地址。
12、LCD IO初始化。

三、LCD像素时钟的设置。

LCD需要一个CLK信号,这个时钟信号是6ULL的CLK引脚发送给RGB LCD的。比如7寸1024600的屏幕需要51.2MHz的CLK。
LCDIF_CLK_ROOT就是6ULL的像素时钟。我们设置PLL5(video PLL)为LCD的时钟源,
PLL5 CLK=Fref
DIV_SELECT=,DIV_SELECT就是CCM_ANALOG_PLL_VIDEO的bit6:0,也就是DIV_SELEC位,可选范围27-54。设置PLL_VIDEO寄存器的bit20:19为2,表示1分频。设置CCM_ANALOG_MISC2寄存器的bit31:30为0,也就是VIDOE_DIV为0,1分频。
我们不是用小数分频器,因此CCM_ANALOG_PLL_VIDEO_NUM=0,在设置CCM_ALALOG_PLL_VIDEO_DENOM=0。
CCM_CSCDR2寄存器的bit17:15,设置LCDIF_PRE_CLK_SEL,选择LCDIF_CLK_ROOT的时钟源,设置为0x2。表示LCDIF时钟源为PLL5。Bit14:12为LCDIF_PRED位,设置前级分频,可以设置07,分别对应18分频。
CCM_CBCMR寄存器,bit25:23为LCDIF_PODF,设置第二级分频,可以设置为07,分别对应18分频。
继续设置CCM_CSCDR2寄存器的bit11:9为LCDIF_CLK_SEL,选择LCD CLK的最终时钟源,设置为0,LCDIF的最终时钟源来源于pre-muxed LCDIF clock

四、LCD驱动程序编写

1、如果使用的正点原子的开发板和RGB屏幕,那么在驱动LCD之前,要先读取屏幕ID。
//bsp_lcd.c

#include "bsp_lcd.h"
#include "bsp_gpio.h"
#include "bsp_delay.h"
#include "stdio.h"

/* 液晶屏参数结构体 */
struct tftlcd_typedef tftlcd_dev;

/*
 * @description : 始化LCD
 * @param       : 无
 * @return      : 无
 */
void lcd_init(void)
{
    lcdgpio_init();         /* 初始化IO            */
    lcdclk_init(32, 3, 5);  /* 初始化LCD时钟         */
    
    lcd_reset();            /* 复位LCD            */
    delayms(10);            /* 延时10ms           */
    lcd_noreset();          /* 结束复位             */

    /* TFTLCD参数结构体初始化 */
    tftlcd_dev.height = 600;    
    tftlcd_dev.width = 1024;
    tftlcd_dev.pixsize = 4;             /* ARGB8888模式,每个像素4字节 */
    tftlcd_dev.vspw = 3;
    tftlcd_dev.vbpd = 20;
    tftlcd_dev.vfpd = 12;
    tftlcd_dev.hspw = 20;
    tftlcd_dev.hbpd = 140;
    tftlcd_dev.hfpd = 160;
    tftlcd_dev.framebuffer = LCD_FRAMEBUF_ADDR; 
    tftlcd_dev.backcolor = LCD_WHITE;   /* 背景色为白色 */
    tftlcd_dev.forecolor = LCD_BLACK;   /* 前景色为黑色 */
     
    /* 初始化ELCDIF的CTRL寄存器
     * bit [31] 0 : 停止复位
     * bit [19] 1 : 旁路计数器模式
     * bit [17] 1 : LCD工作在dotclk模式
     * bit [15:14] 00 : 输入数据不交换
     * bit [13:12] 00 : CSC不交换
     * bit [11:10] 11 : 24位总线宽度
     * bit [9:8]   11 : 24位数据宽度,也就是RGB888
     * bit [5]     1  : elcdif工作在主模式
     * bit [1]     0  : 所有的24位均有效
     */
     LCDIF->CTRL |= (1 << 19) | (1 << 17) | (0 << 14) | (0 << 12) |
                    (3 << 10) | (3 << 8) | (1 << 5) | (0 << 1);
    /*
     * 初始化ELCDIF的寄存器CTRL1
     * bit [19:16]  : 0X7 ARGB模式下,传输24位数据,A通道不用传输
     */ 
     LCDIF->CTRL1 = 0X7 << 16; 

     /*
      * 初始化ELCDIF的寄存器TRANSFER_COUNT寄存器
      * bit [31:16]  : 高度
      * bit [15:0]   : 宽度
      */
    LCDIF->TRANSFER_COUNT  = (tftlcd_dev.height << 16) | (tftlcd_dev.width << 0);

    /*
     * 初始化ELCDIF的VDCTRL0寄存器
     * bit [29] 0 : VSYNC输出
     * bit [28] 1 : 使能ENABLE输出
     * bit [27] 0 : VSYNC低电平有效
     * bit [26] 0 : HSYNC低电平有效
     * bit [25] 0 : DOTCLK上升沿有效
     * bit [24] 1 : ENABLE信号高电平有效
     * bit [21] 1 : DOTCLK模式下设置为1
     * bit [20] 1 : DOTCLK模式下设置为1
     * bit [17:0] : vsw参数
     */
    LCDIF->VDCTRL0 = 0; //先清零
    LCDIF->VDCTRL0 = (0 << 29) | (1 << 28) | (0 << 27) |
                     (0 << 26) | (0 << 25) | (1 << 24) |
                     (1 << 21) | (1 << 20) | (tftlcd_dev.vspw << 0);
    /*
     * 初始化ELCDIF的VDCTRL1寄存器
     * 设置VSYNC总周期
     */  
    LCDIF->VDCTRL1 = tftlcd_dev.height + tftlcd_dev.vspw + tftlcd_dev.vfpd + tftlcd_dev.vbpd;  //VSYNC周期
     
     /*
      * 初始化ELCDIF的VDCTRL2寄存器
      * 设置HSYNC周期
      * bit[31:18] :hsw
      * bit[17:0]  : HSYNC总周期
      */ 
    LCDIF->VDCTRL2 = (tftlcd_dev.hspw << 18) | (tftlcd_dev.width + tftlcd_dev.hspw + tftlcd_dev.hfpd + tftlcd_dev.hbpd);

    /*
     * 初始化ELCDIF的VDCTRL3寄存器
     * 设置HSYNC周期
     * bit[27:16] :水平等待时钟数
     * bit[15:0]  : 垂直等待时钟数
     */ 
    LCDIF->VDCTRL3 = ((tftlcd_dev.hbpd + tftlcd_dev.hspw) << 16) | (tftlcd_dev.vbpd + tftlcd_dev.vspw);

    /*
     * 初始化ELCDIF的VDCTRL4寄存器
     * 设置HSYNC周期
     * bit[18] 1 : 当使用VSHYNC、HSYNC、DOTCLK的话此为置1
     * bit[17:0]  : 宽度
     */ 
    
    LCDIF->VDCTRL4 = (1<<18) | (tftlcd_dev.width);

    /*
     * 初始化ELCDIF的CUR_BUF和NEXT_BUF寄存器
     * 设置当前显存地址和下一帧的显存地址
     */
    LCDIF->CUR_BUF = (unsigned int)tftlcd_dev.framebuffer;
    LCDIF->NEXT_BUF = (unsigned int)tftlcd_dev.framebuffer;

    lcd_enable();           /* 使能LCD    */
    delayms(10);
    lcd_clear(LCD_WHITE);   /* 清屏       */
    
}

/*
 * IO引脚:    LCD_DATA00 -> LCD_B0
 *          LCD_DATA01 -> LCD_B1
 *          LCD_DATA02 -> LCD_B2
 *          LCD_DATA03 -> LCD_B3
 *          LCD_DATA04 -> LCD_B4
 *          LCD_DATA05 -> LCD_B5
 *          LCD_DATA06 -> LCD_B6
 *          LCD_DATA07 -> LCD_B7
 *
 *          LCD_DATA08 -> LCD_G0
 *          LCD_DATA09 -> LCD_G1
 *          LCD_DATA010 -> LCD_G2
 *          LCD_DATA011 -> LCD_G3
 *          LCD_DATA012 -> LCD_G4
 *          LCD_DATA012 -> LCD_G4
 *          LCD_DATA013 -> LCD_G5
 *          LCD_DATA014 -> LCD_G6
 *          LCD_DATA015 -> LCD_G7
 *
 *          LCD_DATA016 -> LCD_R0
 *          LCD_DATA017 -> LCD_R1
 *          LCD_DATA018 -> LCD_R2 
 *          LCD_DATA019 -> LCD_R3
 *          LCD_DATA020 -> LCD_R4
 *          LCD_DATA021 -> LCD_R5
 *          LCD_DATA022 -> LCD_R6
 *          LCD_DATA023 -> LCD_R7
 *
 *          LCD_CLK -> LCD_CLK
 *          LCD_VSYNC -> LCD_VSYNC
 *          LCD_HSYNC -> LCD_HSYNC
 *          LCD_DE -> LCD_DE
 *          LCD_BL -> GPIO1_IO08 
 */
 
/*
 * @description : LCD GPIO初始化
 * @param       : 无
 * @return      : 无
 */
void lcdgpio_init(void)
{
    gpio_pin_config_t gpio_config;
    

    /* 1、IO初始化复用功能 */
    IOMUXC_SetPinMux(IOMUXC_LCD_DATA00_LCDIF_DATA00,0);
    IOMUXC_SetPinMux(IOMUXC_LCD_DATA01_LCDIF_DATA01,0);
    IOMUXC_SetPinMux(IOMUXC_LCD_DATA02_LCDIF_DATA02,0);
    IOMUXC_SetPinMux(IOMUXC_LCD_DATA03_LCDIF_DATA03,0);
    IOMUXC_SetPinMux(IOMUXC_LCD_DATA04_LCDIF_DATA04,0);
    IOMUXC_SetPinMux(IOMUXC_LCD_DATA05_LCDIF_DATA05,0);
    IOMUXC_SetPinMux(IOMUXC_LCD_DATA06_LCDIF_DATA06,0);
    IOMUXC_SetPinMux(IOMUXC_LCD_DATA07_LCDIF_DATA07,0);
    
    IOMUXC_SetPinMux(IOMUXC_LCD_DATA08_LCDIF_DATA08,0);
    IOMUXC_SetPinMux(IOMUXC_LCD_DATA09_LCDIF_DATA09,0);
    IOMUXC_SetPinMux(IOMUXC_LCD_DATA10_LCDIF_DATA10,0);
    IOMUXC_SetPinMux(IOMUXC_LCD_DATA11_LCDIF_DATA11,0);
    IOMUXC_SetPinMux(IOMUXC_LCD_DATA12_LCDIF_DATA12,0);
    IOMUXC_SetPinMux(IOMUXC_LCD_DATA13_LCDIF_DATA13,0);
    IOMUXC_SetPinMux(IOMUXC_LCD_DATA14_LCDIF_DATA14,0);
    IOMUXC_SetPinMux(IOMUXC_LCD_DATA15_LCDIF_DATA15,0);

    IOMUXC_SetPinMux(IOMUXC_LCD_DATA16_LCDIF_DATA16,0);
    
IOMUXC_SetPinMux(IOMUXC_LCD_DATA17_LCDIF_DATA17,0);
    IOMUXC_SetPinMux(IOMUXC_LCD_DATA18_LCDIF_DATA18,0);
    IOMUXC_SetPinMux(IOMUXC_LCD_DATA19_LCDIF_DATA19,0);
    IOMUXC_SetPinMux(IOMUXC_LCD_DATA20_LCDIF_DATA20,0);
    IOMUXC_SetPinMux(IOMUXC_LCD_DATA21_LCDIF_DATA21,0);
    IOMUXC_SetPinMux(IOMUXC_LCD_DATA22_LCDIF_DATA22,0);
    IOMUXC_SetPinMux(IOMUXC_LCD_DATA23_LCDIF_DATA23,0);

    IOMUXC_SetPinMux(IOMUXC_LCD_CLK_LCDIF_CLK,0);   
    IOMUXC_SetPinMux(IOMUXC_LCD_ENABLE_LCDIF_ENABLE,0); 
    IOMUXC_SetPinMux(IOMUXC_LCD_HSYNC_LCDIF_HSYNC,0);
    IOMUXC_SetPinMux(IOMUXC_LCD_VSYNC_LCDIF_VSYNC,0);

    IOMUXC_SetPinMux(IOMUXC_GPIO1_IO08_GPIO1_IO08,0);           /* 背光BL引脚      */

    /* 2、配置LCD IO属性 
     *bit 16:0 HYS关闭
     *bit [15:14]: 0 默认22K上拉
     *bit [13]: 0 pull功能
     *bit [12]: 0 pull/keeper使能 
     *bit [11]: 0 关闭开路输出
     *bit [7:6]: 10 速度100Mhz
     *bit [5:3]: 111 驱动能力为R0/7
     *bit [0]: 1 高转换率
     */
    IOMUXC_SetPinConfig(IOMUXC_LCD_DATA00_LCDIF_DATA00,0xB9);
    IOMUXC_SetPinConfig(IOMUXC_LCD_DATA01_LCDIF_DATA01,0xB9);
    IOMUXC_SetPinConfig(IOMUXC_LCD_DATA02_LCDIF_DATA02,0xB9);
    IOMUXC_SetPinConfig(IOMUXC_LCD_DATA03_LCDIF_DATA03,0xB9);
    IOMUXC_SetPinConfig(IOMUXC_LCD_DATA04_LCDIF_DATA04,0xB9);
    IOMUXC_SetPinConfig(IOMUXC_LCD_DATA05_LCDIF_DATA05,0xB9);
    IOMUXC_SetPinConfig(IOMUXC_LCD_DATA06_LCDIF_DATA06,0xB9);
    IOMUXC_SetPinConfig(IOMUXC_LCD_DATA07_LCDIF_DATA07,0xB9);
    IOMUXC_SetPinConfig(IOMUXC_LCD_DATA08_LCDIF_DATA08,0xB9);
    IOMUXC_SetPinConfig(IOMUXC_LCD_DATA09_LCDIF_DATA09,0xB9);
    IOMUXC_SetPinConfig(IOMUXC_LCD_DATA10_LCDIF_DATA10,0xB9);
    IOMUXC_SetPinConfig(IOMUXC_LCD_DATA11_LCDIF_DATA11,0xB9);
    IOMUXC_SetPinConfig(IOMUXC_LCD_DATA12_LCDIF_DATA12,0xB9);
    IOMUXC_SetPinConfig(IOMUXC_LCD_DATA13_LCDIF_DATA13,0xB9);
    IOMUXC_SetPinConfig(IOMUXC_LCD_DATA14_LCDIF_DATA14,0xB9);
    IOMUXC_SetPinConfig(IOMUXC_LCD_DATA15_LCDIF_DATA15,0xB9);
    IOMUXC_SetPinConfig(IOMUXC_LCD_DATA16_LCDIF_DATA16,0xB9);
    IOMUXC_SetPinConfig(IOMUXC_LCD_DATA17_LCDIF_DATA17,0xB9);
    IOMUXC_SetPinConfig(IOMUXC_LCD_DATA18_LCDIF_DATA18,0xB9);
    IOMUXC_SetPinConfig(IOMUXC_LCD_DATA19_LCDIF_DATA19,0xB9);
    IOMUXC_SetPinConfig(IOMUXC_LCD_DATA20_LCDIF_DATA20,0xB9);
    IOMUXC_SetPinConfig(IOMUXC_LCD_DATA21_LCDIF_DATA21,0xB9);
    IOMUXC_SetPinConfig(IOMUXC_LCD_DATA22_LCDIF_DATA22,0xB9);
    IOMUXC_SetPinConfig(IOMUXC_LCD_DATA23_LCDIF_DATA23,0xB9);

    IOMUXC_SetPinConfig(IOMUXC_LCD_CLK_LCDIF_CLK,0xB9);
    IOMUXC_SetPinConfig(IOMUXC_LCD_ENABLE_LCDIF_ENABLE,0xB9);
    IOMUXC_SetPinConfig(IOMUXC_LCD_HSYNC_LCDIF_HSYNC,0xB9);
    IOMUXC_SetPinConfig(IOMUXC_LCD_VSYNC_LCDIF_VSYNC,0xB9);

    IOMUXC_SetPinConfig(IOMUXC_GPIO1_IO08_GPIO1_IO08,0xB9); /* 背光BL引脚       */

    /* GPIO初始化 */
    gpio_config.direction = kGPIO_DigitalOutput;            /* 输出           */
    gpio_config.outputLogic = 1;                            /* 默认关闭背光 */
    gpio_init(GPIO1, 8, &gpio_config);                      /* 背光默认打开 */
    gpio_pinwrite(GPIO1, 8, 1);                             /* 打开背光     */
}

/*
 * @description     : LCD时钟初始化, LCD时钟计算公式如下:
 *                    LCD CLK = 24 * loopDiv / prediv / div
 * @param - loopDiv : loopDivider值
 * @param - loopDiv : lcdifprediv值
 * @param - div     : lcdifdiv值
 * @return          : 无
 */
void lcdclk_init(unsigned char loopDiv, unsigned char prediv, unsigned char div)
{
    /* 先初始化video pll 
     * VIDEO PLL = OSC24M * (loopDivider + (denominator / numerator)) / postDivider
     *不使用小数分频器,因此denominator和numerator设置为0
     */
    CCM_ANALOG->PLL_VIDEO_NUM = 0;      /* 不使用小数分频器 */
    CCM_ANALOG->PLL_VIDEO_DENOM = 0;    

    /*
     * PLL_VIDEO寄存器设置
     * bit[13]:    1   使能VIDEO PLL时钟
     * bit[20:19]  2  设置postDivider为1分频
     * bit[6:0] : 32  设置loopDivider寄存器
     */
    CCM_ANALOG->PLL_VIDEO =  (2 << 19) | (1 << 13) | (loopDiv << 0); 

    /*
     * MISC2寄存器设置
     * bit[31:30]: 0  VIDEO的post-div设置,时钟源来源于postDivider,1分频
     */
    CCM_ANALOG->MISC2 &= ~(3 << 30);
    CCM_ANALOG->MISC2 = 0 << 30;

    /* LCD时钟源来源与PLL5,也就是VIDEO           PLL  */
    CCM->CSCDR2 &= ~(7 << 15);      
    CCM->CSCDR2 |= (2 << 15);           /* 设置LCDIF_PRE_CLK使用PLL5 */

    /* 设置LCDIF_PRE分频 */
    CCM->CSCDR2 &= ~(7 << 12);      
    CCM->CSCDR2 |= (prediv - 1) << 12;  /* 设置分频  */

    /* 设置LCDIF分频 */
    CCM->CBCMR &= ~(7 << 23);                   
    CCM->CBCMR |= (div - 1) << 23;              

    /* 设置LCD时钟源为LCDIF_PRE时钟 */
    CCM->CSCDR2 &= ~(7 << 9);                   /* 清除原来的设置          */
    CCM->CSCDR2 |= (0 << 9);                    /* LCDIF_PRE时钟源选择LCDIF_PRE时钟 */
}

/*
 * @description : 复位ELCDIF接口
 * @param       : 无
 * @return      : 无
 */
void lcd_reset(void)
{
    LCDIF->CTRL  = 1<<31; /* 强制复位 */
}

/*
 * @description : 结束复位ELCDIF接口
 * @param       : 无
 * @return      : 无
 */
void lcd_noreset(void)
{
    LCDIF->CTRL  = 0<<31; /* 取消强制复位 */
}

/*
 * @description : 使能ELCDIF接口
 * @param       : 无
 * @return      : 无
 */
void lcd_enable(void)
{
    LCDIF->CTRL |= 1<<0; /* 使能ELCDIF */
}

/*
 * @description     : 画点函数 
 * @param - x       : x轴坐标
 * @param - y       : y轴坐标
 * @param - color   : 颜色值
 * @return          : 无
 */
inline void lcd_drawpoint(unsigned short x,unsigned short y,unsigned int color)
{ 
    *(unsigned int*)((unsigned int)tftlcd_dev.framebuffer + 
                     tftlcd_dev.pixsize * (tftlcd_dev.width * y+x))=color;
}

/*
 * @description     : 读取指定点的颜色值
 * @param - x       : x轴坐标
 * @param - y       : y轴坐标
 * @return          : 读取到的指定点的颜色值
 */
inline unsigned int lcd_readpoint(unsigned short x,unsigned short y)
{ 
    return *(unsigned int*)((unsigned int)tftlcd_dev.framebuffer + 
           tftlcd_dev.pixsize * (tftlcd_dev.width * y + x));
}

/*
 * @description     : 清屏
 * @param - color   : 颜色值
 * @return          : 读取到的指定点的颜色值
 */
void lcd_clear(unsigned int color)
{
    unsigned int num;
    unsigned int i = 0; 

    unsigned int *startaddr=(unsigned int*)tftlcd_dev.framebuffer;  //指向帧缓存首地址
    num=(unsigned int)tftlcd_dev.width * tftlcd_dev.height;         //缓冲区总长度
    for(i = 0; i < num; i++)
    {
        startaddr[i] = color;
    }       
}

/*
 * @description     : 以指定的颜色填充一块矩形
 * @param - x0      : 矩形起始点坐标X轴
 * @param - y0      : 矩形起始点坐标Y轴
 * @param - x1      : 矩形终止点坐标X轴
 * @param - y1      : 矩形终止点坐标Y轴
 * @param - color   : 要填充的颜色
 * @return          : 读取到的指定点的颜色值
 */
void lcd_fill(unsigned    short x0, unsigned short y0, 
                 unsigned short x1, unsigned short y1, unsigned int color)
{ 
    unsigned short x, y;

    if(x0 < 0) x0 = 0;
    if(y0 < 0) y0 = 0;
    if(x1 >= tftlcd_dev.width) x1 = tftlcd_dev.width - 1;
    if(y1 >= tftlcd_dev.height) y1 = tftlcd_dev.height - 1;
    
    for(y = y0; y <= y1; y++)
    {
        for(x = x0; x <= x1; x++)
            lcd_drawpoint(x, y, color);
    }
}

//bsp_lcd.h

#ifndef _BSP_LCD_H
#define _BSP_LCD_H

#include "imx6ul.h"

/* 颜色 */
#define LCD_BLUE          0x000000FF
#define LCD_GREEN         0x0000FF00
#define LCD_RED           0x00FF0000
#define LCD_CYAN          0x0000FFFF
#define LCD_MAGENTA       0x00FF00FF
#define LCD_YELLOW        0x00FFFF00
#define LCD_LIGHTBLUE     0x008080FF
#define LCD_LIGHTGREEN    0x0080FF80
#define LCD_LIGHTRED      0x00FF8080
#define LCD_LIGHTCYAN     0x0080FFFF
#define LCD_LIGHTMAGENTA  0x00FF80FF
#define LCD_LIGHTYELLOW   0x00FFFF80
#define LCD_DARKBLUE      0x00000080
#define LCD_DARKGREEN     0x00008000
#define LCD_DARKRED       0x00800000
#define LCD_DARKCYAN      0x00008080
#define LCD_DARKMAGENTA   0x00800080
#define LCD_DARKYELLOW    0x00808000
#define LCD_WHITE         0x00FFFFFF
#define LCD_LIGHTGRAY     0x00D3D3D3
#define LCD_GRAY          0x00808080
#define LCD_DARKGRAY      0x00404040
#define LCD_BLACK         0x00000000
#define LCD_BROWN         0x00A52A2A
#define LCD_ORANGE        0x00FFA500
#define LCD_TRANSPARENT   0x00000000

/* LCD显存地址 */
#define LCD_FRAMEBUF_ADDR   (0x89000000)

/* LCD控制参数结构体 */
struct tftlcd_typedef{
    unsigned short height;      /* LCD屏幕高度 */
    unsigned short width;       /* LCD屏幕宽度 */
    unsigned char pixsize;      /* LCD每个像素所占字节大小 */
    unsigned short vspw;
    unsigned short vbpd;
    unsigned short vfpd;
    unsigned short hspw;
    unsigned short hbpd;
    unsigned short hfpd;
    unsigned int framebuffer;   /* LCD显存首地址       */
    unsigned int forecolor;     /* 前景色 */
    unsigned int backcolor;     /* 背景色 */
};

extern struct tftlcd_typedef tftlcd_dev;

/* 函数声明 */
void lcd_init(void);
void lcdgpio_init(void);
void lcdclk_init(unsigned char loopDiv, unsigned char prediv, unsigned char div);
void lcd_reset(void);
void lcd_noreset(void);
void lcd_enable(void);
void video_pllinit(unsigned char loopdivi, unsigned char postdivi);

inline void lcd_drawpoint(unsigned short x,unsigned short y,unsigned int color);
inline unsigned int lcd_readpoint(unsigned short x,unsigned short y);
void lcd_clear(unsigned int color);
void lcd_fill(unsigned    short x0, unsigned short y0, unsigned short x1, unsigned short y1, unsigned int color);
#endif

五、LCD操作API函数编写

//bsp_lcdapi.c

#include "bsp_lcdapi.h"
#include "font.h" 

/*
 * @description     : 画线函数
 * @param - x1      : 线起始点坐标X轴
 * @param - y1      : 线起始点坐标Y轴
 * @param - x2      : 线终止点坐标X轴
 * @param - y2      : 线终止点坐标Y轴
 * @return          : 无
 */ 
void lcd_drawline(unsigned short x1, unsigned short y1, unsigned short x2, unsigned short y2)
{
    u16 t; 
    int xerr = 0, yerr = 0, delta_x, delta_y, distance; 
    int incx, incy, uRow, uCol; 
    delta_x = x2 - x1;                  /* 计算坐标增量 */
    delta_y = y2 - y1; 
    uRow = x1; 
    uCol = y1; 
    if(delta_x > 0)                     /* 设置单步方向 */ 
        incx = 1;
    else if(delta_x==0)                 /* 垂直线 */
        incx = 0;       
    else 
    {
        incx = -1;
        delta_x = -delta_x;
    } 
    if(delta_y>0)
        incy=1; 
    else if(delta_y == 0)               /* 水平线 */
        incy=0;
    else
    {
        incy = -1;
        delta_y = -delta_y;
    } 
    if( delta_x > delta_y)              /*选取基本增量坐标轴  */
        distance = delta_x; 
    else 
        distance = delta_y; 
    for(t = 0; t <= distance+1; t++ )   /* 画线输出 */
    {  

        lcd_drawpoint(uRow, uCol, tftlcd_dev.forecolor);/* 画点 */
        xerr += delta_x ; 
        yerr += delta_y ; 
        if(xerr > distance) 
        { 
            xerr -= distance; 
            uRow += incx; 
        } 
        if(yerr > distance) 
        { 
            yerr -= distance; 
            uCol += incy; 
        } 
    }  
}   

/*
 * @description : 画矩形函数
 * @param - x1  : 矩形坐上角坐标X轴
 * @param - y1  : 矩形坐上角坐标Y轴
 * @param - x2  : 矩形右下角坐标X轴
 * @param - y2  : 矩形右下角坐标Y轴
 * @return      : 无
 */
void lcd_draw_rectangle(unsigned short x1, unsigned short y1, unsigned short x2, unsigned short y2)
{
    lcd_drawline(x1, y1, x2, y1);
    lcd_drawline(x1, y1, x1, y2);
    lcd_drawline(x1, y2, x2, y2);
    lcd_drawline(x2, y1, x2, y2);
}

/*
 * @description : 在指定位置画一个指定大小的圆
 * @param - x0  : 圆心坐标X轴
 * @param - y0  : 圆心坐标Y轴
 * @param - y2  : 圆形半径
 * @return      : 无
 */
void lcd_draw_Circle(unsigned short x0,unsigned short y0,unsigned char r)
{
    int mx = x0, my = y0;
    int x = 0, y = r;

    int d = 1 - r;   
    while(y > x)        /* y>x即第一象限的第1区八分圆 */
    {
        lcd_drawpoint(x  + mx, y  + my, tftlcd_dev.forecolor);
        lcd_drawpoint(y  + mx, x  + my, tftlcd_dev.forecolor);
        lcd_drawpoint(-x + mx, y  + my, tftlcd_dev.forecolor);
        lcd_drawpoint(-y + mx, x  + my, tftlcd_dev.forecolor);

        lcd_drawpoint(-x + mx, -y + my, tftlcd_dev.forecolor);
        lcd_drawpoint(-y + mx, -x + my, tftlcd_dev.forecolor);
        lcd_drawpoint(x  + mx, -y + my, tftlcd_dev.forecolor);
        lcd_drawpoint(y  + mx, -x + my, tftlcd_dev.forecolor);
        if( d < 0)
        {
            d = d + 2 * x + 3;
        }
        else
        {
            d= d + 2 * (x - y) + 5;
            y--;
        }
        x++;
    }
}

/*
 * @description : 在指定位置显示一个字符
 * @param - x   : 起始坐标X轴
 * @param - y   : 起始坐标Y轴
 * @param - num : 显示字符
 * @param - size: 字体大小, 可选12/16/24/32
 * @param - mode: 叠加方式(1)还是非叠加方式(0)
 * @return      : 无
 */
void lcd_showchar(unsigned     short x, unsigned short y,
                      unsigned char num, unsigned char size, 
                      unsigned char mode)
{                             
    unsigned char  temp, t1, t;
    unsigned short y0 = y;
    unsigned char csize = (size / 8+ ((size % 8) ? 1 : 0)) * (size / 2);    /* 得到字体一个字符对应点阵集所占的字节数   */ 
    num = num - ' ';    /*得到偏移后的值(ASCII字库是从空格开始取模,所以-' '就是对应字符的字库)  */
    for(t = 0; t < csize; t++)
    {   
        if(size == 12) temp = asc2_1206[num][t];        /* 调用1206字体 */
        else if(size == 16)temp = asc2_1608[num][t];    /* 调用1608字体 */
        else if(size == 24)temp = asc2_2412[num][t];    /* 调用2412字体 */
        else if(size == 32)temp = asc2_3216[num][t];    /* 调用3216字体 */
        else return;                                    /* 没有的字库        */
        for(t1 = 0; t1 < 8; t1++)
        {               
            if(temp & 0x80)lcd_drawpoint(x, y, tftlcd_dev.forecolor);
            else if(mode==0)lcd_drawpoint(x, y, tftlcd_dev.backcolor);
            temp <<= 1;
            y++;
            if(y >= tftlcd_dev.height) return;          /* 超区域了 */  
            if((y - y0) == size)
            {
                y = y0;
                x++;
                if(x >= tftlcd_dev.width) return;       /* 超区域了 */
                break;
            }
        }    
    }                         
} 

/*
 * @description : 计算m的n次方
 * @param - m   : 要计算的值
 * @param - n   : n次方
 * @return      : m^n次方.
 */
unsigned int lcd_pow(unsigned char m,unsigned char n)
{
    unsigned int result = 1;     
    while(n--) result *= m;    
    return result;
}

/*
 * @description : 显示指定的数字,高位为0的话不显示
 * @param - x   : 起始坐标点X轴。
 * @param - y   : 起始坐标点Y轴。
 * @param - num : 数值(0~999999999)。
 * @param - len : 数字位数。
 * @param - size: 字体大小
 * @return      : 无
 */
void lcd_shownum(unsigned     short x, 
                     unsigned short y, 
                     unsigned int num, 
                     unsigned char len,
                     unsigned char size)
{           
    unsigned char  t, temp;
    unsigned char  enshow = 0;                         
    for(t = 0; t < len; t++)
    {
        temp = (num / lcd_pow(10, len - t - 1)) % 10;
        if(enshow == 0 && t < (len - 1))
        {
            if(temp == 0)
            {
                lcd_showchar(x + (size / 2) * t, y, ' ', size, 0);
                continue;
            }else enshow = 1;    
        }
        lcd_showchar(x + (size / 2) * t, y, temp + '0', size, 0); 
    }
} 

/*
 * @description     : 显示指定的数字,高位为0,还是显示
 * @param - x       : 起始坐标点X轴。
 * @param - y       : 起始坐标点Y轴。
 * @param - num     : 数值(0~999999999)。
 * @param - len     : 数字位数。
 * @param - size    : 字体大小
 * @param - mode    : [7]:0,不填充;1,填充0.
 *                    [6:1]:保留
 *                    [0]:0,非叠加显示;1,叠加显示.
 * @return          : 无
 */
void lcd_showxnum(unsigned     short x, unsigned short y, 
                      unsigned int num, unsigned char len, 
                      unsigned char size, unsigned char mode)
{  
    unsigned char t, temp;
    unsigned char enshow = 0;                          
    for(t = 0; t < len; t++)
    {
        temp = (num / lcd_pow(10, len - t- 1)) % 10;
        if(enshow == 0 && t < (len - 1))
        {
            if(temp == 0)
            {
                if(mode & 0X80) lcd_showchar(x + (size / 2) * t, y, '0', size, mode & 0X01);  
                else  lcd_showchar(x + (size / 2) * t, y , ' ', size, mode & 0X01);  
                continue;
            }else enshow=1; 
             
        }
        lcd_showchar( x + (size / 2) * t, y, temp + '0' , size , mode & 0X01); 
    }
} 

/*
 * @description     : 显示一串字符串
 * @param - x       : 起始坐标点X轴。
 * @param - y       : 起始坐标点Y轴。
 * @param - width   : 字符串显示区域长度
 * @param - height  : 字符串显示区域高度
 * @param - size    : 字体大小
 * @param - p       : 要显示的字符串首地址
 * @return          : 无
 */
void lcd_show_string(unsigned short x,unsigned short y,
                          unsigned short width,unsigned short height,
                          unsigned char size,char *p)
{         
    unsigned char x0 = x;
    width += x;
    height += y;
    while((*p <= '~') &&(*p >= ' '))        /* 判断是不是非法字符! */ 
    {       
        if(x >= width) {x = x0; y += size;}
        if(y >= height) break;              /* 退出 */
        lcd_showchar(x, y, *p , size, 0);
        x += size / 2;
        p++;
    }  
}

//bsp_lcdapi.h

#ifndef BSP_LCDAPI_H
#define BSP_LCDAPI_H
#include "imx6ul.h"
#include "bsp_lcd.h"

/* 函数声明 */
void lcd_drawline(unsigned short x1, unsigned short y1, unsigned short x2, unsigned short y2);
void lcd_draw_rectangle(unsigned short x1, unsigned short y1, unsigned short x2, unsigned short y2);
void lcd_draw_Circle(unsigned short x0,unsigned short y0,unsigned char r);
void lcd_showchar(unsigned     short x,unsigned short y,unsigned char num,unsigned char size, unsigned char mode);
unsigned int lcd_pow(unsigned char m,unsigned char n);
void lcd_shownum(unsigned short x, unsigned short y, unsigned int num, unsigned char len,unsigned char size);
void lcd_showxnum(unsigned short x, unsigned short y, unsigned int num, unsigned char len, unsigned char size, unsigned char mode);
void lcd_show_string(unsigned short x,unsigned short y,
unsigned short width, unsigned short height, unsigned char size,char *p);

#endif

//main.c

#include "bsp_clk.h"
#include "bsp_delay.h"
#include "bsp_led.h"
#include "bsp_beep.h"
#include "bsp_key.h"
#include "bsp_int.h"
#include "bsp_uart.h"
#include "stdio.h"
#include "bsp_lcd.h"
#include "bsp_lcdapi.h"

/* 背景颜色索引 */
unsigned int backcolor[10] = {
    LCD_BLUE,       LCD_GREEN,      LCD_RED,    LCD_CYAN,   LCD_YELLOW, 
    LCD_LIGHTBLUE,  LCD_DARKBLUE,   LCD_WHITE,  LCD_BLACK,  LCD_ORANGE

}; 
    

/*
 * @description : main函数
 * @param       : 无
 * @return      : 无
 */
int main(void)
{
    unsigned char index = 0;
    unsigned char state = OFF;

    int_init();                 /* 初始化中断(一定要最先调用!) */
    imx6u_clkinit();            /* 初始化系统时钟          */
    delay_init();               /* 初始化延时            */
    clk_enable();               /* 使能所有的时钟          */
    led_init();                 /* 初始化led           */
    beep_init();                /* 初始化beep          */
    uart_init();                /* 初始化串口,波特率115200 */
    lcd_init();                 /* 初始化LCD           */

    tftlcd_dev.forecolor = LCD_RED;   
    lcd_show_string(10,10,400,32,32,(char*)"ZERO-IMX6UL ELCD TEST");  /* 显示字符串 */
    lcd_draw_rectangle(10, 52, 1014, 290);  /* 绘制矩形框        */
    lcd_drawline(10, 52,1014, 290);         /* 绘制线条         */
    lcd_drawline(10, 290,1014, 52);         /* 绘制线条         */
    lcd_draw_Circle(512, 171, 119);         /* 绘制圆形         */

    while(1)                
    {   
        index++;
        if(index == 10)
            index = 0;
        lcd_fill(0, 300, 1023, 599, backcolor[index]);
        lcd_show_string(800,10,240,32,32,(char*)"INDEX=");  /*显示字符串               */
        lcd_shownum(896,10, index, 2, 32);                  /* 显示数字,叠加显示    */
        
        state = !state;
        led_switch(LED0,state);
        delayms(1000);  /* 延时一秒 */
    }
    return 0;
}

//start.S

/***************************************************************
描述     : ZERO-I.MX6UL/I.MX6ULL开发板启动文件,完成C环境初始化,
         C环境初始化完成以后跳转到C代码。
**************************************************************/

.global _start                  /* 全局标号 */

/*
 * 描述:  _start函数,首先是中断向量表的创建
 * 参考文档:ARM Cortex-A(armV7)编程手册V4.0.pdf P42,3 ARM Processor Modes and Registers(ARM处理器模型和寄存器)
 *          ARM Cortex-A(armV7)编程手册V4.0.pdf P165 11.1.1 Exception priorities(异常)
 */
_start:
    ldr pc, =Reset_Handler      /* 复位中断                     */  
    ldr pc, =Undefined_Handler  /* 未定义中断                    */
    ldr pc, =SVC_Handler        /* SVC(Supervisor)中断        */
    ldr pc, =PrefAbort_Handler  /* 预取终止中断                   */
    ldr pc, =DataAbort_Handler  /* 数据终止中断                   */
    ldr pc, =NotUsed_Handler    /* 未使用中断                    */
    ldr pc, =IRQ_Handler        /* IRQ中断                    */
    ldr pc, =FIQ_Handler        /* FIQ(快速中断)未定义中断           */

/* 复位中断 */  
Reset_Handler:

    cpsid i                     /* 关闭全局中断 */

    /* 关闭I,DCache和MMU 
     * 采取读-改-写的方式。
     */
    mrc     p15, 0, r0, c1, c0, 0     /* 读取CP15的C1寄存器到R0中                           */
    bic     r0,  r0, #(0x1 << 12)     /* 清除C1寄存器的bit12位(I位),关闭I Cache               */
    bic     r0,  r0, #(0x1 <<  2)     /* 清除C1寄存器的bit2(C位),关闭D Cache                 */
    bic     r0,  r0, #0x2             /* 清除C1寄存器的bit1(A位),关闭对齐                      */
    bic     r0,  r0, #(0x1 << 11)     /* 清除C1寄存器的bit11(Z位),关闭分支预测                   */
    bic     r0,  r0, #0x1             /* 清除C1寄存器的bit0(M位),关闭MMU                     */
    mcr     p15, 0, r0, c1, c0, 0     /* 将r0寄存器中的值写入到CP15的C1寄存器中                    */

    
#if 0
    /* 汇编版本设置中断向量表偏移 */
    ldr r0, =0X87800000

    dsb
    isb
    mcr p15, 0, r0, c12, c0, 0
    dsb
    isb
#endif
    
    /* 设置各个模式下的栈指针,
     * 注意:IMX6UL的堆栈是向下增长的!
     * 堆栈指针地址一定要是4字节地址对齐的!!!
     * DDR范围:0X80000000~0X9FFFFFFF
     */
    /* 进入IRQ模式 */
    mrs r0, cpsr
    bic r0, r0, #0x1f   /* 将r0寄存器中的低5位清零,也就是cpsr的M0~M4  */
    orr r0, r0, #0x12   /* r0或上0x13,表示使用IRQ模式                   */
    msr cpsr, r0        /* 将r0 的数据写入到cpsr_c中                    */
    ldr sp, =0x80600000 /* 设置IRQ模式下的栈首地址为0X80600000,大小为2MB */

    /* 进入SYS模式 */
    mrs r0, cpsr
    bic r0, r0, #0x1f   /* 将r0寄存器中的低5位清零,也就是cpsr的M0~M4  */
    orr r0, r0, #0x1f   /* r0或上0x13,表示使用SYS模式                   */
    msr cpsr, r0        /* 将r0 的数据写入到cpsr_c中                    */
    ldr sp, =0x80400000 /* 设置SYS模式下的栈首地址为0X80400000,大小为2MB */

    /* 进入SVC模式 */
    mrs r0, cpsr
    bic r0, r0, #0x1f   /* 将r0寄存器中的低5位清零,也就是cpsr的M0~M4  */
    orr r0, r0, #0x13   /* r0或上0x13,表示使用SVC模式                   */
    msr cpsr, r0        /* 将r0 的数据写入到cpsr_c中                    */
    ldr sp, =0X80200000 /* 设置SVC模式下的栈首地址为0X80200000,大小为2MB */

    cpsie i             /* 打开全局中断 */
#if 0
    /* 使能IRQ中断 */
    mrs r0, cpsr        /* 读取cpsr寄存器值到r0中           */
    bic r0, r0, #0x80   /* 将r0寄存器中bit7清零,也就是CPSR中的I位清零,表示允许IRQ中断 */
    msr cpsr, r0        /* 将r0重新写入到cpsr中            */
#endif

    b main              /* 跳转到main函数                */

/* 未定义中断 */
Undefined_Handler:
    ldr r0, =Undefined_Handler
    bx r0

/* SVC中断 */
SVC_Handler:
    ldr r0, =SVC_Handler
    bx r0

/* 预取终止中断 */
PrefAbort_Handler:
    ldr r0, =PrefAbort_Handler  
    bx r0

/* 数据终止中断 */
DataAbort_Handler:
    ldr r0, =DataAbort_Handler
    bx r0

/* 未使用的中断 */
NotUsed_Handler:

    ldr r0, =NotUsed_Handler
    bx r0

/* IRQ中断!重点!!!!! */
IRQ_Handler:
    push {lr}                   /* 保存lr地址 */
    push {r0-r3, r12}           /* 保存r0-r3,r12寄存器 */

    mrs r0, spsr                /* 读取spsr寄存器 */
    push {r0}                   /* 保存spsr寄存器 */

    mrc p15, 4, r1, c15, c0, 0 /* 从CP15的C0寄存器内的值到R1寄存器中
                                * 参考文档ARM Cortex-A(armV7)编程手册V4.0.pdf P49
                                * Cortex-A7 Technical ReferenceManua.pdf P68 P138
                                */                          
    add r1, r1, #0X2000         /* GIC基地址加0X2000,也就是GIC的CPU接口端基地址 */
    ldr r0, [r1, #0XC]          /* GIC的CPU接口端基地址加0X0C就是GICC_IAR寄存器,
                                 * GICC_IAR寄存器保存这当前发生中断的中断号,我们要根据
                                 * 这个中断号来绝对调用哪个中断服务函数
                                 */
    push {r0, r1}               /* 保存r0,r1 */
    
    cps #0x13                   /* 进入SVC模式,允许其他中断再次进去 */
    
    push {lr}                   /* 保存SVC模式的lr寄存器 */
    ldr r2, =system_irqhandler  /* 加载C语言中断处理函数到r2寄存器中*/
    blx r2                      /* 运行C语言中断处理函数,带有一个参数,保存在R0寄存器中 */

    pop {lr}                    /* 执行完C语言中断服务函数,lr出栈 */
    cps #0x12                   /* 进入IRQ模式 */
    pop {r0, r1}                
    str r0, [r1, #0X10]         /* 中断执行完成,写EOIR */

    pop {r0}                        
    msr spsr_cxsf, r0           /* 恢复spsr */

    pop {r0-r3, r12}            /* r0-r3,r12出栈 */
    pop {lr}                    /* lr出栈 */
    subs pc, lr, #4             /* 将lr-4赋给pc */
    
    

/* FIQ中断 */
FIQ_Handler:

    ldr r0, =FIQ_Handler    
    bx r0         

//Makefile

CROSS_COMPILE   ?= arm-linux-gnueabihf-
TARGET          ?= lcd

CC              := $(CROSS_COMPILE)gcc
LD              := $(CROSS_COMPILE)ld
OBJCOPY         := $(CROSS_COMPILE)objcopy
OBJDUMP         := $(CROSS_COMPILE)objdump

LIBPATH         := -lgcc -L /usr/local/arm/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/lib/gcc/arm-linux-gnueabihf/4.9.4

INCDIRS         := imx6ul \
                   stdio/include \
                   bsp/clk \
                   bsp/led \
                   bsp/delay  \
                   bsp/beep \
                   bsp/gpio \
                   bsp/key \
                   bsp/exit \
                   bsp/int \
                   bsp/epittimer \
                   bsp/keyfilter \
                   bsp/uart \
                   bsp/lcd
                               
SRCDIRS         := project \
                   stdio/lib \
                   bsp/clk \
                   bsp/led \
                   bsp/delay \
                   bsp/beep \
                   bsp/gpio \
                   bsp/key \
                   bsp/exit \
                   bsp/int \
                   bsp/epittimer \
                   bsp/keyfilter \
                   bsp/uart \
                   bsp/lcd
                   
                   
INCLUDE         := $(patsubst %, -I %, $(INCDIRS))

SFILES          := $(foreach dir, $(SRCDIRS), $(wildcard $(dir)/*.S))
CFILES          := $(foreach dir, $(SRCDIRS), $(wildcard $(dir)/*.c))

SFILENDIR       := $(notdir  $(SFILES))
CFILENDIR       := $(notdir  $(CFILES))

SOBJS           := $(patsubst %, obj/%, $(SFILENDIR:.S=.o))
COBJS           := $(patsubst %, obj/%, $(CFILENDIR:.c=.o))
OBJS            := $(SOBJS) $(COBJS)

VPATH           := $(SRCDIRS)

.PHONY: clean
    
$(TARGET).bin : $(OBJS)
    $(LD) -Timx6ul.lds -o $(TARGET).elf $^ $(LIBPATH)
    $(OBJCOPY) -O binary -S $(TARGET).elf $@
    $(OBJDUMP) -D -m arm $(TARGET).elf > $(TARGET).dis

$(SOBJS) : obj/%.o : %.S
    $(CC) -Wall -nostdlib -fno-builtin -c -O2  $(INCLUDE) -o $@ $<

$(COBJS) : obj/%.o : %.c
    $(CC) -Wall -Wa,-mimplicit-it=thumb -nostdlib -fno-builtin -c -O2  $(INCLUDE) -o $@ $<
    
clean:
    rm -rf $(TARGET).elf $(TARGET).dis $(TARGET).bin $(COBJS) $(SOBJS)

http://www.kler.cn/a/527228.html

相关文章:

  • 【设计模式-行为型】备忘录模式
  • 基于Python的药物相互作用预测模型AI构建与优化(上.文字部分)
  • 浅谈AI的发展对IT行业的影响
  • 18、智能驾驶芯片外部接口要求
  • 金融级分布式数据库如何优化?PawSQL发布OceanBase专项调优指南
  • 【Numpy核心编程攻略:Python数据处理、分析详解与科学计算】1.30 性能巅峰:NumPy代码优化全攻略
  • 最新Python大数据之Python基础【十】学生管理系统面向对象版_python面向对象学生管理系统
  • JAVA实战开源项目:网上购物商城(Vue+SpringBoot) 附源码
  • 随笔 | 写在一月的最后一天
  • Vue-cli 脚手架搭建
  • 翻译: Anthropic CEO:DeepSeek-R1是人工智能领域的革命吗?一
  • gentoo中利用ollama运行DeepSeek-R1
  • Bash 基础与进阶实践指南
  • 【搜索回溯算法篇】:拓宽算法视野--BFS如何解决拓扑排序问题
  • FreeRTOS从入门到精通 第十五章(事件标志组)
  • Spring Boot 配置文件详解:YAML vs Properties
  • 边缘计算与ROS结合:如何实现分布式机器人智能决策?
  • C 语言实现计算一年中指定日期是第几天 题】
  • 【Linux】软硬链接
  • 英语语法 第一天
  • 【算法应用】基于鲸鱼优化算法求解OTSU多阈值图像分割问题
  • python 之 zip 和 * 解包操作
  • 微店的Flutter混合开发组件化与工程化架构
  • SQL NOW() 函数详解
  • Day52:range()函数
  • 精准化糖尿病知识问答(LLM+机器学习预测模型)