• IMX6ULL + SPI LCD(驱动IC ILI9341)显示简单的QT界面


    1. 硬件:

    使用正点原子的IMX6ULL Linux开发板

    开发板底板原理图版本:V2.1

    核心板原理图版本:V1.6

    LCD :MSP2402 (IC ILI9341)

    2. 查找可用引脚

    开发板上引出的引脚是在JP6上,只看JP6会发现没有可用的SPI引脚,但是查看底板原理图中与核心板相连的位置会发现其实JP6上的UART2的TX/RX/CTS/RTS 四个引脚正好可以复用为ECSPI3的 MISO/MOSI/CLK/SS0四个引脚,SPI LCD还需要三个IO口作为Reset/DC/背光的控制引脚,如

    下图所示(但是我是偷懒了,将背光引脚直接接的V3.3)

    3.添加支持SPI LCD的设备树节点(不废话,直接上干货)

        pinctrl_ecspi3: ecspi3grp {

                fsl,pins = <

                        MX6UL_PAD_UART2_RTS_B__ECSPI3_MISO        0x100b1  /* MISO*/

                        MX6UL_PAD_UART2_CTS_B__ECSPI3_MOSI        0x100b1  /* MOSI*/

                        MX6UL_PAD_UART2_RX_DATA__ECSPI3_SCLK      0x100b1  /* CLK*/

                        MX6UL_PAD_UART2_TX_DATA__GPIO1_IO20       0x100b0  /* CS*/

                        MX6UL_PAD_GPIO1_IO00__GPIO1_IO00          0x17059  /*back light*/

                        MX6UL_PAD_GPIO1_IO03__GPIO1_IO03          0x17059  /*data&command*/

                        MX6UL_PAD_GPIO1_IO08__GPIO1_IO08          0x17059 /* Reset IO */

                >;

        };


     

    &ecspi3 {

            fsl,spi-num-chipselects = <1>;

            cs-gpios = <&gpio1 20 GPIO_ACTIVE_LOW>;    ###特别注意,ATK出厂的设备树这里有问题,原有的cs-gpio 需要改成cs-gpios不然SPI将无法工作

            pinctrl-names = "default";

            pinctrl-0 = <&pinctrl_ecspi3>;

            status = "okay";

        spidev: icm20608@0 {

            compatible = "alientek,icm20608";

                spi-max-frequency = <8000000>;

                reg = <0>;

        };

        spi_lcd_msp2402@0 {

            compatible = "spi_lcd_msp2402";

            spi-max-frequency = <8000000>;  

            reg = <0>;            

            msp2402-bl-gpio = <&gpio1 0 GPIO_ACTIVE_HIGH>;          

            msp2402-dc-gpio = <&gpio1 3 GPIO_ACTIVE_HIGH>;    

            msp2402-rst-gpio = <&gpio1 8 GPIO_ACTIVE_HIGH>;                

        };

    4.关键代码:

    1.请先分析一下IMX6ULL的SPI设备驱动,网上太多了此处不再赘述

    2. LCD初始化命令:

        命令序列是从淘宝客服给的单片机代码里摘出来的(没有去看ILI的芯片手册,太长了,五的初衷是学习驱动,而不是研究LCD,因此请不要问我命令都是实现什么功能的,因为我不知道)

    struct spi_lcd_cmd{

        u8  reg_addr; // command

        u8  len;  //需要从spi_lcd_datas数组里发出数据字节数

        int delay_ms; //此命令发送数据完成后,需延时多久

    }cmds[] = {

        {0xCF, 3, 0},

        {0xED, 4, 0},

        {0xE8, 3, 0},

        {0xCB, 5, 0},

        {0xF7, 1, 0},

        {0xEA, 2, 0},

        {0xC0, 1, 0},    

        {0xC1, 1, 0},

        {0xC5, 2, 0},    

        {0xC7, 1, 0},

        {0x36, 1, 0},

        {0x3A, 1, 0},

        {0xB1, 2, 0},

        {0xB6, 2, 0},

        {0xF2, 1, 0},

        {0x26, 1, 0},

        {0xE0, 15, 0},

        {0xE1, 15, 0},

        {0x2B, 9, 0},

        {0x11, 0, 120},  

        {0x29, 0, 0},  

    };


     

    u8 spi_lcd_datas[] = {

        0x00, 0xD9, 0x30,                       // command: 0xCF

        0x64, 0x03, 0x12, 0x81,                 // command: 0xED

        0x85, 0x10, 0x7A,                       // command: 0xE8

        0x39, 0x2C, 0x00, 0x34, 0x02,           // command: 0xCB

        0x20,                                   // command: 0xF7

        0x00, 0x00,                             // command: 0xEA

        0x1B,                                   // command: 0xC0      

        0x12,                                   // command: 0xC1

        0x08, 0x26,                             // command: 0xC5

        0xB7,                                   // command: 0xC7

        0x08,                                   // command: 0x36

        0x55,                                   // command: 0x3A

        0x00, 0x1A,                             // command: 0xB1

        0x0A, 0xA2,                             // command: 0xB6

        0x00,                                   // command: 0xF2

        0x01,                                   // command: 0x26

        0x0F, 0x1D, 0x1A, 0x0A, 0x0D, 0x07, 0x49, 0x66, 0x3B, 0x07, 0x11, 0x01, 0x09, 0x05, 0x04, //command: 0xE0

        0x00, 0x18, 0x1D, 0x02, 0x0F, 0x04, 0x36, 0x13, 0x4C, 0x07, 0x13, 0x0F, 0x2E, 0x2F, 0x05, //command: 0xE1

        0x00, 0x00, 0x01, 0x3F, 0x2A, 0x00, 0x00, 0x00, 0xEF, //command: 0x2B

    };

    3.大名鼎鼎的probe函数:

    static int msp2402_probe(struct spi_device *spi)

    {

        int ret =0;

        struct fb_info *fb;

        /*初始化spi_device */

        printk("File:%s Function:%s Line:%d \n",__FILE__,__FUNCTION__,__LINE__);

        if (msp2402_gpio_parse_dt(&msp2402lcd, &spi->dev) < 0) {

            printk(KERN_ERR "msp2402lcd gpio parse dt fail!\n");

            goto params_parse_fail;

        }

        /*

        Mode 0 CPOL=0, CPHA=0

        Mode 1 CPOL=0, CPHA=1

        Mode 2 CPOL=1, CPHA=0

        Mode 3 CPOL=1, CPHA=1

        */

        spi->mode = SPI_MODE_0; /*MODE0,CPOL=0,CPHA=0*/

        spi->bits_per_word = 8;

       // spi->dev.platform_data = msp2402lcd;

        ret = spi_setup(spi);

        if (ret < 0)

        {

            printk("spi_setup failed.\n");

        }

        /* 初始化msp2402内部寄存器 */

        msp2402lcd.spi = spi;

        msp2402_reginit(spi);

       lcd_fill_rect(spi,0, 0,LCD_W,LCD_H,RED);

       LCD_DrawLine(spi,0, 50, 240,50);

    //至此,LCD的驱动部分就初始化完成了,屏幕上应该是红色背景,然后显示一条横线

    // 下边是为了实现Framwbuffer的必要步骤:

       /*填充frambuffer对应项目 */

        fb = framebuffer_alloc(0, NULL);

        if (!fb) {

            printk(KERN_ERR "msp2402 lcd framebuffer alloc fail!\n");

            goto dma_alloc_fail;

        }

        //LCD基本参数设置

        fb->var.xres   = LCD_W;

        fb->var.yres   = LCD_H;

        fb->var.xres_virtual = LCD_W;

        fb->var.yres_virtual = LCD_H;

        fb->var.bits_per_pixel = 32;

        //LCD RGB格式设置, RGB888

        fb->var.red.offset = 16;

        fb->var.red.length = 8;

        fb->var.green.offset = 8;

        fb->var.green.length = 8;

        fb->var.blue.offset = 0;

        fb->var.blue.length = 8;

        //设置固定参数

        strcpy(fb->fix.id, "MSP2402,spilcd");

        fb->fix.type   = FB_TYPE_PACKED_PIXELS;

        fb->fix.visual = FB_VISUAL_TRUECOLOR;

        //设置显存

        fb->fix.line_length = LCD_W * 32 / 8;

        fb->fix.smem_len    = LCD_W * LCD_H * 32 / 8;

        printk("fb->fix.smem_len is %d\n", fb->fix.smem_len);

        dma_set_coherent_mask(&spi->dev, DMA_BIT_MASK(32));

        fb->screen_base = dma_alloc_coherent(&spi->dev, fb->fix.smem_len, (dma_addr_t*)&fb->fix.smem_start, GFP_KERNEL);

        if (!fb->screen_base) {

            printk(KERN_ERR "dma_alloc_coherent %d bytes fail!\n", fb->fix.smem_len);

            goto dma_alloc_fail;

        }

        printk("fb->screen_base is 0x%08x\n", (uint32_t)fb->screen_base);

        fb->screen_size = LCD_W * LCD_H * 32 / 8;

        printk("fb->screen_size is %ld\n", fb->screen_size);

        //操作函数集

        fb->fbops = &msp2402_fb_ops;

        spi_set_drvdata(spi, fb);

        ret = register_framebuffer(fb);

        if (ret < 0) {

            printk(KERN_ERR "register framebuffer fail!\n");

            goto register_fail;

        }

        msp2402lcd.thread = kthread_run(msp2402_refresh_kthread_func, fb, spi->modalias);

        printk("probe run successfully ...%s\n", spi->modalias);

        return 0;

    register_fail:

        dma_free_coherent(&spi->dev, fb->fix.smem_len, fb->screen_base, fb->fix.smem_start);

    dma_alloc_fail:

        framebuffer_release(fb);

    params_parse_fail:

        gpio_free(msp2402lcd.data_command_io);

        gpio_free(msp2402lcd.reset_io);

        gpio_free(msp2402lcd.backlight_io);

    return -1;

    }

    4. 清屏代码:

    有两种写法建议都尝试以下就会发下差异(我就郁闷了一会)

    方法一(这种方法清屏比较慢,需要7s左右,根本无法接受):

    void LCD_Clear(struct spi_device *spi,u16 Color)

    {

        unsigned int i,m;    

        LCD_SetWindows(spi,0, 0,LCD_W - 1,LCD_H - 1);

        gpio_set_value(msp2402lcd.reset_io, 1);

        for(i=0;i

            for(m=0;m

                Lcd_WriteData_16Bit(spi,Color);

            }

        }

    }

    方法二:

    static void lcd_fill_rect(struct spi_device *spi,uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color)

    {

        uint32_t size, i;

        uint8_t data[] = {0};

        uint8_t *mem;

        size = (x2 - x1 + 1) * (y2 - y1 + 1) * 2;   // RGB888 3个字节

        data[1] = color >> 8;

        data[0] = color;

        mem = (uint8_t *)kzalloc(size, GFP_KERNEL);

        if (!mem) {

            printk(KERN_ERR "lcd_fill_rect: alloc %d bytes mem fail!\n", size);

        }

        for (i = 0; i < size/2 ; i++) {

            mem[i * 2] =  data[1];

            mem[i * 2 + 1] =  data[0];

        }

        LCD_SetWindows(spi,0, 0,LCD_W - 1,LCD_H - 1);

        gpio_set_value(msp2402lcd.data_command_io, 1);

        spi_write(spi,mem, size);

        kfree(mem);

    }

    完整源码地址(如果硬件相同可直接运行):

    (48条消息) IMX6ULL+SPILCDMSP2402(驱动ICILI9341)显示简单的QT界面-C文档类资源-CSDN文库

  • 相关阅读:
    图的遍历-DFS,BFS(代码详解)
    Android WebSocket 使用指南:详细步骤与实践
    C# using的几个用途
    ubantu 安装openssh
    大数据运维实战第五课 手动模式构建双 Namenode+Yarn 的 Hadoop 集群(上)
    赶紧进来!!!教你用C语言写三子棋小游戏
    tkinter使用xaml编写wpf
    【kubernetes】关于云原生之k8s集群中pod的容器资源限制和三种探针
    SpringCloud 微服务全栈体系(六)
    Hadoop分布式模式配置
  • 原文地址:https://blog.csdn.net/niepangu/article/details/128028038