• SPI的控制器驱动由平台设备与平台驱动来实现. 驱动后用spi_master对象来...include/linux/spi/spi.h"//让spi->master指向的控制器对象发出len个字节数据,数据缓冲区地址由buf指针指向 static inline int sp

    SPI的控制器驱动由平台设备与平台驱动来实现. 驱动后用spi_master对象来描述.在设备驱动中就可以通过函数spi_write, spi_read, spi_w8r16, spi_w8r8等函数来调用控制器.

    "include/linux/spi/spi.h"
    
    //让spi->master指向的控制器对象发出len个字节数据,数据缓冲区地址由buf指针指向
    static inline int spi_write(struct spi_device *spi, const void *buf, size_t len);
    
    //让spi->master指向的控制器对象接收len个字节数据,由buf指向指向的数据缓冲区存放
    static inline int spi_read(struct spi_device *spi, void *buf, size_t len);
    
    //让spi->master指向的控制器对象发出数据后再接收数据
    int spi_write_then_read(struct spi_device *spi, const void *txbuf, unsigned n_tx,
            void *rxbuf, unsigned n_rx);
    
    //让spi->master控制器对象同时收发8位数据
    static inline ssize_t spi_w8r8(struct spi_device *spi, u8 cmd);
    
    //让spi->master控制器对象同时发8位,接收16位数据.
    static inline ssize_t spi_w8r16(struct spi_device *spi, u8 cmd);
    

    ////////////////////////////////////////////////////////////////
    屏:
    这里写图片描述

    流程: 命令/数据 –> spi控制器 —> 屏驱动ic的spi接口 —> ILI9340C(屏的驱动ic) –> 屏
    屏的驱动ic的作用:根据接收到的命令和数据,配置屏的时序参数及在屏上刷出相应的像素数据.
    也就是我们只要通过spi接口把屏的命令和数据交给屏的驱动ic即可, 让驱动IC完成刷屏的操作.
    //所有的lcd屏都会用到驱动IC的

    //ILI9340C驱动ic内部有配置寄存器,我们需要通过spi接口配置驱动ic内部寄存器的值
    模块的引脚与板的连接:

     reset --> PA8   //用于复位模块
     D/C   --> PA7   //通过高低电平来区分数据线上的数据类型, command:0, data:1 .
            //command其实就是表示数据线上发过去的是驱动ic内部寄存器的地址
                //data表示数据线上发过去的数据就是寄存器要设的值
     CS    --> spi0_CS0  //片选线
     SDI   --> spi0_MOSI   //数据线,发出驱动ic的寄存器地址和要设置的值
     SDO   --> spi0_MISO // 如不需要读取驱动ic寄存器的值,可不接
     SCLK  --> spi0_CLK  //时钟线
     LED   --> 3.3v      //背光电源
     VCC   --> 3.3v
     GND   --> GND  
    

    这里写图片描述

    //通过时序图可得知,模块支持三线/四线的工作方式,四线是用D/C线区分数据线上的数据是寄存器地址或数据. spi的工作时序方式是SPI_MODE_0(CPOL=0, CPHA=0), 也可以得知传输是以8位为单位.

    //在内核里描述spi屏设备,并通过spi_board_info的platform_data提供连接屏reset和D/C引脚的GPIO.
    描述设备的代码:

    #include <linux/spi/spi.h>
    #include <mach/gpio.h>
    
    struct sunxi_spi_config {
        int bits_per_word; //8bit
        int max_speed_hz;  //80MHz
        int mode; // pha,pol,LSB,etc..
    } sunxi_data =  {
        8, 10000000, SPI_MODE_0
    };
    
    struct myspi_lcd_pdata {
            int dc_io;
            int reset_io;
    }spi_lcd_pdata = { 
        GPIOA(7), GPIOA(8), 
    };
    
    
    struct spi_board_info spi_infos[] = { 
        {   
            .modalias = "myspi_lcd",
            .platform_data = &spi_lcd_pdata,    
            .controller_data = &sunxi_data,
            .max_speed_hz = 10000000,
            .bus_num = 0,
            .chip_select = 0,
            .mode = SPI_MODE_0,
        },
    
    };
    
    
    static void __init sunxi_dev_init(void)
    {
        ...
        // 在最后一行
        spi_register_board_info(spi_infos, ARRAY_SIZE(spi_infos));
    }
    

    //////////////////////////设备驱动的实现//////////////////////////////////

    店家提供的c51里初始化屏的驱动代码:
    
    void  write_command(uchar c)  //发送驱动ic的寄存器地址
    {
        cs=0;
    
        rs=0;  // D/C 低电平
        bitdata=c;
    
        sda=bit7;scl=0;scl=1;
        sda=bit6;scl=0;scl=1;
        sda=bit5;scl=0;scl=1;
        sda=bit4;scl=0;scl=1;
        sda=bit3;scl=0;scl=1;
        sda=bit2;scl=0;scl=1;
        sda=bit1;scl=0;scl=1;
        sda=bit0;scl=0;scl=1;
        cs=1;      
    }
    
    void  write_data(uchar d) //给驱动ic传输数据使用
    {
        cs=0;
        rs=1; //  D/C 高电平
        bitdata=d;
        sda=bit7;scl=0;scl=1;
        sda=bit6;scl=0;scl=1;
        sda=bit5;scl=0;scl=1;
        sda=bit4;scl=0;scl=1;
        sda=bit3;scl=0;scl=1;
        sda=bit2;scl=0;scl=1;
        sda=bit1;scl=0;scl=1;
        sda=bit0;scl=0;scl=1;
        cs=1;
    }
    
    void lcd_initial()
    {
    
            reset=0;
            delay(100);
            reset=1;
            delay(100);
    
    
            write_command(0xCB);  
            write_data(0x39); 
            write_data(0x2C); 
            write_data(0x00); 
            write_data(0x34); 
            write_data(0x02); 
    
            write_command(0xCF);  
            write_data(0x00); 
            write_data(0XC1); 
            write_data(0X30); 
    
            write_command(0xE8);  
            write_data(0x85); 
            write_data(0x00); 
            write_data(0x78); 
    
            write_command(0xEA);  
            write_data(0x00); 
            write_data(0x00); 
    
            write_command(0xED);  
            write_data(0x64); 
            write_data(0x03); 
            write_data(0X12); 
            write_data(0X81); 
    
            write_command(0xF7);  
            write_data(0x20); 
    
            write_command(0xC0);    //Power control 
            write_data(0x23);   //VRH[5:0] 
    
            write_command(0xC1);    //Power control 
            write_data(0x10);   //SAP[2:0];BT[3:0] 
    
            write_command(0xC5);    //VCM control 
            write_data(0x3e); //¶Ô±È¶Èµ÷œÚ
            write_data(0x28); 
    
            write_command(0xC7);    //VCM control2 
            write_data(0x86);  //--
    
            write_command(0x36);    // Memory Access Control 
            //ŽË²ÎÊýΪºáÆÁÊúÆÁÉšÃ跜ʜÇл»¹ØŒü²ÎÊý
            //0x48 0x68ÊúÆÁ
            //0x28 0xE8 ºáÆÁ
            write_data(0x48); //ÉèÖÃĬÈÏÊúÆÁÉšÃ跜ʜ
    
            write_command(0x3A);    
            write_data(0x55); 
    
            write_command(0xB1);    
            write_data(0x00);  
            write_data(0x18); 
    
            write_command(0xB6);    // Display Function Control 
            write_data(0x08); 
            write_data(0x82);
            write_data(0x27);  
    
            write_command(0xF2);    // 3Gamma Function Disable 
            write_data(0x00); 
    
            write_command(0x26);    //Gamma curve selected 
            write_data(0x01); 
    
            write_command(0xE0);    //Set Gamma 
            write_data(0x0F); 
            write_data(0x31); 
            write_data(0x2B); 
            write_data(0x0C); 
            write_data(0x0E); 
            write_data(0x08); 
            write_data(0x4E); 
            write_data(0xF1); 
            write_data(0x37); 
            write_data(0x07); 
            write_data(0x10); 
            write_data(0x03); 
            write_data(0x0E); 
            write_data(0x09); 
            write_data(0x00); 
    
            write_command(0XE1);    //Set Gamma 
            write_data(0x00); 
            write_data(0x0E); 
            write_data(0x14); 
            write_data(0x03); 
            write_data(0x11); 
            write_data(0x07); 
            write_data(0x31); 
            write_data(0xC1); 
            write_data(0x48); 
            write_data(0x08); 
            write_data(0x0F); 
            write_data(0x0C); 
            write_data(0x31); 
            write_data(0x36); 
            write_data(0x0F); 
    
            write_command(0x11);    //Exit Sleep 
            delay(120); 
    
            write_command(0x29);    //Display on 
            write_command(0x2c); 
    
    } 
    
    

    ///////////////////////////////////////
    参考上面驱动代码实现的linux设备驱动:

    
    #include <linux/init.h>
    #include <linux/module.h>
    #include <linux/spi/spi.h>
    #include <linux/delay.h>
    #include <linux/gpio.h>
    
    struct myspi_lcd_pdata {
            int dc_io;
            int reset_io;
    };
    
    struct spi_lcd_cmd{
        u8  reg_addr; // command
        u8  len;  //需要从spi_lcd_datas数组里发出数据字节数
        int delay_ms; //此命令发送数据完成后,需延时多久
    }cmds[] = {
        {0xCB, 5, 0},
        {0xCF, 3, 0},
        {0xEB, 3, 0},
        {0xEA, 2, 0},
        {0xED, 4, 0},
        {0xF7, 1, 0},
        {0xC0, 1, 0},
        {0xC1, 1, 0},
        {0xC5, 2, 0},
        {0xC7, 1, 0},
        {0x36, 1, 0},
        {0x3A, 1, 0},
        {0xB1, 2, 0},
        {0xB6, 3, 0},
        {0xF2, 1, 0},
        {0x26, 1, 0},
        {0xE0, 15, 0},
        {0xE1, 15, 0},
        {0x11, 0,  120},
        {0x29, 0, 0},
        {0x2c, 0, 0},
    };
    
    
    u8 spi_lcd_datas[] = {
        0x39, 0x2c, 0x00, 0x34, 0x20,           // command: 0xCB要发出的数据
        0x00, 0xC1, 0x30,                       // command: 0xCF
        0x85, 0x00, 0x78,                       // command: 0xEB
        0x00, 0x00,                             // command: 0xEA
        0x64, 0x03, 0x12, 0x81,                 // command: 0xED 
        0x20,                                   // command: 0xF7
        0x23,                                   // command: 0xC0
        0x10,                                   // command: 0xC1
        0x3e, 0x28,                             // command: 0xC5
        0x86,                                   // command: 0xC7
        0x48,                                   // command: 0x36
        0x55,                                   // command: 0x3A
        0x00, 0x18,                             // command: 0xB1
        0x08, 0x82, 0x27,                       // command: 0xB6
        0x00,                                   // command: 0xF2
        0x01,                                   // command: 0x26
        0x0F, 0x31, 0x2B, 0x0C, 0x0E, 0x08, 0x4E, 0xF1, 0x37, 0x07, 0x10, 0x03, 0x0E, 0x09, 0x00,                           //command: 0xE0
        0x00, 0x0E, 0x14, 0x03, 0x11, 0x07, 0x31, 0xC1, 0x48, 0x08, 0x0F, 0x0C, 0x31, 0x36, 0x0F, //command: 0xE1
    
    };
    
    void write_command(struct spi_device *spi, u8 cmd)
    {
        struct myspi_lcd_pdata *pdata = spi->dev.platform_data;
    
        // dc , command:0
        gpio_direction_output(pdata->dc_io, 0); 
        spi_write(spi, &cmd, 1);
    }
    
    void write_data(struct spi_device *spi, u8 data)
    {
        struct myspi_lcd_pdata *pdata = spi->dev.platform_data;
        // dc , data:1
        gpio_direction_output(pdata->dc_io, 1); 
        spi_write(spi, &data, 1);
    }
    
    //初始化spi_lcd
    void spi_lcd_init(struct spi_device *spi)
    {
        struct myspi_lcd_pdata *pdata = spi->dev.platform_data;
        int i, j, n;
    
        // 屏复位
        gpio_direction_output(pdata->reset_io, 0);
        mdelay(100);
        gpio_set_value(pdata->reset_io, 1);
        mdelay(100);
    
        n = 0; // n用于记录数据数组spi_lcd_datas的位置
        //发命令,并发出命令所需的数据
        for (i = 0; i < ARRAY_SIZE(cmds); i++) //命令
        {
            write_command(spi, cmds[i].reg_addr);
            for (j = 0; j < cmds[i].len; j++) //发出命令后,需要发出的数据
                write_data(spi, spi_lcd_datas[n++]);
    
            if (cmds[i].delay_ms) //如有延时则延时
                mdelay(cmds[i].delay_ms);
        }
    }
    
    //设置要刷屏的开始坐标
    void addset(struct spi_device *spi, unsigned int x,unsigned int y)
    {
            write_command(spi, 0x2a); //发出x坐标
            write_data(spi, x>>8);
            write_data(spi, x&0xff);
    
            write_command(spi, 0x2b); //发出y坐标
            write_data(spi, y>>8);
            write_data(spi, y&0xff);
    
            write_command(spi, 0x2c);
    }
    
    
    int myprobe(struct spi_device *spi)
    {   
        struct myspi_lcd_pdata *pdata = spi->dev.platform_data;
        int ret;
        int x, y;
        u16 color0 = 0x001f; // RGB565, blue    
        u16 color1 = 0xf800; // red
        u16 color2 = 0x07e0; // green
        u16 color3 = 0xffff; // white
        u16 color;
    
        ret = gpio_request(pdata->reset_io, spi->modalias);
        if (ret < 0)
            goto err0;
        ret = gpio_request(pdata->dc_io, spi->modalias);
        if (ret < 0)
            goto err1;
    
        spi_lcd_init(spi); //初始化屏
    
        addset(spi, 0, 0); //从屏的0,0坐标开始刷
    
    //刷屏, 把整屏分成4块,每块颜色不同
    //  gpio_direction_output(pdata->dc_io, 1); 
        for (y = 0; y < 320; y++)
        {
            for (x = 0; x < 240; x++)
            {
                if (x < 120)
                    color = (y < 160) ? color0 : color1; 
                else
                    color = (y < 160) ? color2 : color3; 
    
                write_data(spi, color >> 8);
                write_data(spi, color & 0xff);
            }
        }
    
    
        printk("probe ...%s\n", spi->modalias);
        return 0;
    err1:
        gpio_free(pdata->reset_io);
    err0:
        return ret;
    }
    
    int myremove(struct spi_device *spi)
    {
        struct myspi_lcd_pdata *pdata = spi->dev.platform_data;
    
        gpio_free(pdata->dc_io);
        gpio_free(pdata->reset_io);
        printk("%s remove\n", spi->modalias);
        return 0;
    }
    
    
    struct spi_device_id ids[] = {
        {"myspi_lcd"},
        {},
    };
    
    struct spi_driver myspi_drv = {
        .driver = {
            .owner = THIS_MODULE,
            .name = "myspi_drv",
        },
        .probe = myprobe,
        .remove = myremove,
        .id_table = ids,
    };
    
    module_spi_driver(myspi_drv);
    MODULE_LICENSE("GPL");
    
    

    效果图:
    这里写图片描述

    展开全文
  • 前面驱动的spi lcd仅仅是刷了一下图而已, 如果要让QT图形程序在此lcd上显示的话,还需要实现标准的framebuffer设备驱动才可以.实现一个fb设备驱动好, QT程序就可以在显存里显示出来。 只需要在设备驱动把显存的...

    前面驱动的spi lcd仅仅是刷了一下图而已, 如果要让QT图形程序在此lcd上显示的话,还需要实现标准的framebuffer设备驱动才可以.

    实现一个fb设备驱动好, QT程序就可以在显存里显示出来。 只需要在设备驱动把显存的数据通过spi控制器发送到屏的驱动ic,就可以让QT程序在spi lcd屏上显示出来. 但显存的数据有可能经常发生变化(界面切换), spi lcd屏也应跟着显示出改变过的画面。

    在设备驱动里用一个内核线程, 循环把显存的数据通过spi控制发送到屏的驱动ic. 这样应用程序只需改变显存里的数据就可以了,无需考虑屏幕的更新.

    同时, QT程序不支持16位色的fb设备里显示,所以只能在设备驱动把32位色的显存转换成rgb565后,再把数据发送到屏的驱动ic.
    /////////////////////////////////////////////////////////////////
    设备驱动由两个文件组成:

    fb_model.c主要实现fb设备驱动模型.

    
    #include <linux/init.h>
    #include <linux/module.h>
    #include <linux/fb.h>
    #include <linux/spi/spi.h>
    #include <linux/dma-mapping.h>
    #include <linux/sched.h>
    #include <linux/wait.h>
    #include <linux/delay.h>
    
    #define X 240
    #define Y 320
    
    
    typedef struct {
        struct spi_device *spi; //记录fb_info对象对应的spi设备对象
        struct task_struct *thread; //记录线程对象的地址,此线程专用于把显存数据发送到屏的驱动ic
    }lcd_data_t;
    
    
    struct fb_ops fops = {
    
    
    };
    
    extern void show_fb(struct fb_info *fbi, struct spi_device *spi);
    int thread_func(void *data)
    {
        struct fb_info *fbi = (struct fb_info *)data;
        lcd_data_t *ldata = fbi->par;
    
        while (1)
        {   
                if (kthread_should_stop())
                    break;
            show_fb(fbi, ldata->spi);
            //
        }
    
        return 0;
    }
    
    int myfb_new(struct spi_device *spi) //此函数在spi设备驱动的probe函数里被调用
    {
        struct fb_info *fbi;
        u8 *v_addr;
        u32 p_addr;
        lcd_data_t *data;
    
        v_addr = dma_alloc_coherent(NULL, X*Y*4, &p_addr, GFP_KERNEL);
    
        //额外分配lcd_data_t类型空间
        fbi = framebuffer_alloc(sizeof(lcd_data_t), NULL);
        //data = &fbi[1]; //data指针指向额外分配的空间
        data = fbi->par; //data指针指向额外分配的空间
    
        data->spi = spi;
    
        fbi->var.xres = X;
        fbi->var.yres = Y;
        fbi->var.xres_virtual = X;
        fbi->var.yres_virtual = Y;
        fbi->var.bits_per_pixel = 32; // 屏是rgb565, 但QT程序只能支持32位.还需要在刷图时把32位的像素数据转换成rgb565
        fbi->var.red.offset = 16;
        fbi->var.red.length = 8;
        fbi->var.green.offset = 8;
        fbi->var.green.length = 8;
        fbi->var.blue.offset = 0;
        fbi->var.blue.length = 8;
    
        strcpy(fbi->fix.id, "myfb");
        fbi->fix.smem_start = p_addr; //显存的物理地址
        fbi->fix.smem_len = X*Y*4; 
        fbi->fix.type = FB_TYPE_PACKED_PIXELS;
        fbi->fix.visual = FB_VISUAL_TRUECOLOR;
        fbi->fix.line_length = X*4;
    
        fbi->fbops = &fops;
        fbi->screen_base = v_addr; //显存虚拟地址
        fbi->screen_size = X*Y*4; //显存大小
    
            spi_set_drvdata(spi, fbi);
        register_framebuffer(fbi);  
        data->thread = kthread_run(thread_func, fbi, spi->modalias);
        return 0;    
    }
    
    void myfb_del(struct spi_device *spi) //此函数在spi设备驱动remove时被调用
    {
        struct fb_info *fbi = spi_get_drvdata(spi);
        lcd_data_t *data = fbi->par;
    
        kthread_stop(data->thread); //让刷图线程退出
        unregister_framebuffer(fbi);
        dma_free_coherent(NULL, fbi->screen_size, fbi->screen_base, fbi->fix.smem_start);
        framebuffer_release(fbi);
    
    
    }
    

    ///////////////////
    test.c 主要实现spi lcd的操作

    
    #include <linux/init.h>
    #include <linux/module.h>
    #include <linux/spi/spi.h>
    #include <linux/delay.h>
    #include <linux/gpio.h>
    #include <linux/fb.h>
    
    struct myspi_lcd_pdata {
            int dc_io;
            int reset_io;
    };
    
    struct spi_lcd_cmd{
        u8  reg_addr; // command
        u8  len;  //需要从spi_lcd_datas数组里发出数据字节数
        int delay_ms; //此命令发送数据完成后,需延时多久
    }cmds[] = {
        {0xCB, 5, 0},
        {0xCF, 3, 0},
        {0xEB, 3, 0},
        {0xEA, 2, 0},
        {0xED, 4, 0},
        {0xF7, 1, 0},
        {0xC0, 1, 0},
        {0xC1, 1, 0},
        {0xC5, 2, 0},
        {0xC7, 1, 0},
        {0x36, 1, 0},
        {0x3A, 1, 0},
        {0xB1, 2, 0},
        {0xB6, 3, 0},
        {0xF2, 1, 0},
        {0x26, 1, 0},
        {0xE0, 15, 0},
        {0xE1, 15, 0},
        {0x11, 0,  120},
        {0x29, 0, 0},
        {0x2c, 0, 0},
    };
    
    
    u8 spi_lcd_datas[] = {
        0x39, 0x2c, 0x00, 0x34, 0x20,           // command: 0xCB要发出的数据
        0x00, 0xC1, 0x30,                       // command: 0xCF
        0x85, 0x00, 0x78,                       // command: 0xEB
        0x00, 0x00,                             // command: 0xEA
        0x64, 0x03, 0x12, 0x81,                 // command: 0xED 
        0x20,                                   // command: 0xF7
        0x23,                                   // command: 0xC0
        0x10,                                   // command: 0xC1
        0x3e, 0x28,                             // command: 0xC5
        0x86,                                   // command: 0xC7
        0x48,                                   // command: 0x36
        0x55,                                   // command: 0x3A
        0x00, 0x18,                             // command: 0xB1
        0x08, 0x82, 0x27,                       // command: 0xB6
        0x00,                                   // command: 0xF2
        0x01,                                   // command: 0x26
        0x0F, 0x31, 0x2B, 0x0C, 0x0E, 0x08, 0x4E, 0xF1, 0x37, 0x07, 0x10, 0x03, 0x0E, 0x09, 0x00,                           //command: 0xE0
        0x00, 0x0E, 0x14, 0x03, 0x11, 0x07, 0x31, 0xC1, 0x48, 0x08, 0x0F, 0x0C, 0x31, 0x36, 0x0F, //command: 0xE1
    
    };
    
    
    extern int myfb_new(struct spi_device *);
    extern void myfb_del(struct spi_device *);
    void write_command(struct spi_device *spi, u8 cmd)
    {
        struct myspi_lcd_pdata *pdata = spi->dev.platform_data;
    
        // dc , command:0
        gpio_direction_output(pdata->dc_io, 0); 
        spi_write(spi, &cmd, 1);
    }
    
    void write_data(struct spi_device *spi, u8 data)
    {
        struct myspi_lcd_pdata *pdata = spi->dev.platform_data;
        // dc , data:1
        gpio_direction_output(pdata->dc_io, 1); 
        spi_write(spi, &data, 1);
    }
    
    //初始化spi_lcd
    void spi_lcd_init(struct spi_device *spi)
    {
        struct myspi_lcd_pdata *pdata = spi->dev.platform_data;
        int i, j, n;
    
        // 屏复位
        gpio_direction_output(pdata->reset_io, 0);
        mdelay(100);
        gpio_set_value(pdata->reset_io, 1);
        mdelay(100);
    
        n = 0; // n用于记录数据数组spi_lcd_datas的位置
        //发命令,并发出命令所需的数据
        for (i = 0; i < ARRAY_SIZE(cmds); i++) //命令
        {
            write_command(spi, cmds[i].reg_addr);
            for (j = 0; j < cmds[i].len; j++) //发出命令后,需要发出的数据
                write_data(spi, spi_lcd_datas[n++]);
    
            if (cmds[i].delay_ms) //如有延时则延时
                mdelay(cmds[i].delay_ms);
        }
    }
    
    //设置要刷屏的开始坐标
    void addset(struct spi_device *spi, unsigned int x,unsigned int y)
    {
            write_command(spi, 0x2a); //发出x坐标
            write_data(spi, x>>8);
            write_data(spi, x&0xff);
    
            write_command(spi, 0x2b); //发出y坐标
            write_data(spi, y>>8);
            write_data(spi, y&0xff);
    
            write_command(spi, 0x2c);
    }
    
    void show_fb(struct fb_info *fbi, struct spi_device *spi)
    {
        int x, y;
        u32 k;
        u32 *p = (u32 *)(fbi->screen_base);
        u16 c;
        u8 *pp;
    
        addset(spi, 0, 0); //从屏的0,0坐标开始刷
    //  gpio_direction_output(pdata->dc_io, 1); 
        for (y = 0; y < fbi->var.yres; y++)
        {
            for (x = 0; x < fbi->var.xres; x++)
            {
                k = p[y*fbi->var.xres+x];//取出一个像素点的32位数据
                // rgb8888 --> rgb565       
                pp = (u8 *)&k;  
                c = pp[0] >> 3; //蓝色
                c |= (pp[1]>>2)<<5; //绿色
                c |= (pp[2]>>3)<<11; //红色
    
                //发出像素数据的rgb565
                write_data(spi, c >> 8);
                write_data(spi, c & 0xff);
            }
        }
    
    }
    
    
    int myprobe(struct spi_device *spi)
    {   
        struct myspi_lcd_pdata *pdata = spi->dev.platform_data;
        int ret;
        int x, y;
        u16 color0 = 0x001f; // RGB565, blue    
        u16 color1 = 0xf800; // red
        u16 color2 = 0x07e0; // green
        u16 color3 = 0xffff; // white
        u16 color;
    
        ret = gpio_request(pdata->reset_io, spi->modalias);
        if (ret < 0)
            goto err0;
        ret = gpio_request(pdata->dc_io, spi->modalias);
        if (ret < 0)
            goto err1;
    
        spi_lcd_init(spi); //初始化屏
    
        printk("probe ...%s\n", spi->modalias);
        return myfb_new(spi); //fb设备初始化
    err1:
        gpio_free(pdata->reset_io);
    err0:
        return ret;
    }
    
    int myremove(struct spi_device *spi)
    {
        struct myspi_lcd_pdata *pdata = spi->dev.platform_data;
    
        myfb_del(spi); //fb设备回收
    
        gpio_free(pdata->dc_io);
        gpio_free(pdata->reset_io);
        printk("%s remove\n", spi->modalias);
    
    
        return 0;
    }
    
    
    struct spi_device_id ids[] = {
        {"myspi_lcd"},
        {},
    };
    
    struct spi_driver myspi_drv = {
        .driver = {
            .owner = THIS_MODULE,
            .name = "myspi_drv",
        },
        .probe = myprobe,
        .remove = myremove,
        .id_table = ids,
    };
    
    module_spi_driver(myspi_drv);
    MODULE_LICENSE("GPL");
    
    

    ///////
    Makefile:

    
    obj-m += test_fb.o
    test_fb-objs := test.o fb_model.o
    
    KSRC := /disk3/myown/h3/orangepi_sdk/source/linux-3.4.112/
    export ARCH := arm
    export CROSS_COMPILE := arm-linux-gnueabihf-
    
    all:
        make -C $(KSRC) modules M=`pwd` 
    
    
    .PHONY : clean
    clean:
        make -C $(KSRC) modules clean M=`pwd` 
    

    //////////////////////////////////////////
    编译模块后,加载模块。/dev/目录下多生成一个fb8设备文件.
    设置QT环境变量: export QT_QPA_PLATFORM=linuxfb:fb=/dev/fb8

    然后执行交叉编译过的QT程序, QT程序就会在屏上显示出来(刷新比较慢), 效果如图:
    这里写图片描述

    QT程序在pc上的效果:
    这里写图片描述

    展开全文
  • SPI LCD Linux驱动

    2020-06-06 23:30:22
    支持ST7735R、ILI9340、ST7735R、SSD1289、ILI9341、ILI9325等液晶。
  • 你好!这里是风筝的博客, ...68 linux framebuffer设备驱动之spi lcd屏驱动 【吐槽】结果问题就来了。。。。。。。 我把代码编译成模块,insmod 时就出现:Segmentation fault 段错误一般都是指针指向或者引用...

    你好!这里是风筝的博客,

    欢迎和我一起交流。


    最近入手了一块spi接口的tft彩屏,想着在我的h3板子上使用framebuffer驱动起来。
    我们知道:

    Linux抽象出FrameBuffer这个设备来供用户态进程实现直接写屏。Framebuffer机制模仿显卡的功能,将显卡硬件结构抽象掉,可以通过Framebuffer的读写直接对显存进行操作。用户可以将Framebuffer看成是显示内存的一个映像,将其映射到进程地址空间之后,就可以直接进行读写操作,而写操作可以立即反应在屏幕上。这种操作是抽象的,统一的。用户不必关心物理显存的位置、换页机制等等具体细节。这些都是由Framebuffer设备驱动来完成的。

    帧缓存有个地址,是在内存里。我们通过不停的向frame buffer中写入数据,CPU指定显示控制器工作, 显示控制器就自动的从frame buffer中取数据并显示出来。全部的图形都共享内存中同一个帧缓存。

    所以参考了这篇文章:
    68 linux framebuffer设备驱动之spi lcd屏驱动

    27 在H5上实现spi-tft屏的简单驱动
    【吐槽】结果问题就来了。。。。。。。
    我把代码编译成模块,insmod 时就出现:Segmentation fault
    段错误一般都是指针指向或者引用了错误的地址,dmesg查看信息也确实发现是这样:
    错误忘记复制下来了,懒得复现了,只有一些截图:
    error
    register
    查看dmesg可以发现,确实是指向虚拟地址的问题,而且就是使用register_framebuffer
    函数引起的Segmentation fault。
    我当时就纳闷了,以前在2440板子上使用register_framebuffer函数也没见啥问题啊,就百度了一下,发现有的帖子说直接把fb_notifier_call_chain函数注释掉…
    这不是扯淡吗,fb_notifier_call_chain函数最后会调用到notifier_call_chain函数,在我的错误堆栈信息也看到确实调用了这个函数,这函数路径在kernel/notifier.c里。
    kernel路径下的文件那是我等渣渣能乱动的吗!!
    我试着注释掉此函数,就发现板子在这里本卡住了。。。。。。
    最后为了这个问题,我只能自力更生了。
    根据错误信息,追踪到bit_clear_margins函数:

    static void bit_clear_margins(struct vc_data *vc, struct fb_info *info,
    			      int color, int bottom_only)
    {
    	unsigned int cw = vc->vc_font.width;
    	unsigned int ch = vc->vc_font.height;
    	unsigned int rw = info->var.xres - (vc->vc_cols*cw);
    	unsigned int bh = info->var.yres - (vc->vc_rows*ch);
    	unsigned int rs = info->var.xres - rw;
    	unsigned int bs = info->var.yres - bh;
    	struct fb_fillrect region;
    
    	region.color = color;
    	region.rop = ROP_COPY;
    
    	if (rw && !bottom_only) {
    		region.dx = info->var.xoffset + rs;
    		region.dy = 0;
    		region.width = rw;
    		region.height = info->var.yres_virtual;
    		info->fbops->fb_fillrect(info, &region);
    	}
    
    	if (bh) {
    		region.dx = info->var.xoffset;
    		region.dy = info->var.yoffset + bs;
    		region.width = rs;
    		region.height = bh;
    		info->fbops->fb_fillrect(info, &region);
    	}
    }
    

    这函数哪里会使用非法地址导致Segmentation fault呢?显而易见,就是:info->fbops->fb_fillrect(info, &region)!!!!
    我就是听信了那篇文章作者的话,fb_ops留了空,就悲剧了。

    struct fb_ops fops = { //这里不实现操作函数,使用fbmem.c里实现的功能函数
                   //如果这里实现了功能函数,则会调用这里实现的函数
    };
    

    后面我参考以前写的文章:
    嵌入式Linux驱动笔记(三)------LCD驱动程序
    填充了fb_ops:

    static struct fb_ops fops = {
    	.owner		= THIS_MODULE,
    	.fb_fillrect	= cfb_fillrect,
    	.fb_copyarea	= cfb_copyarea,
    	.fb_imageblit	= cfb_imageblit,
    };
    

    然后编译,发现还是出现Segmentation fault,dmesg查看错误信息的输出,发现错误出现在cfb_imageblit函数里,打开函数一看,确实是:

    void cfb_imageblit(struct fb_info *p, const struct fb_image *image)
    {
    	...
    	fgcolor = ((u32*)(p->pseudo_palette))[image->fg_color];
    	...
    }
    

    我一看,确实是使用register_framebuffer(fbi);函数注册时,fb_info结构体的pseudo_palette成员没有填充,填充之后顺利解决,可以在根文件系统里发现/dev/fb设备!

    可以尝试一下:
    echo hello > /dev/tty1
    这样,在LCD就会出现hello字样了
    hello
    在这里插入图片描述
    摆放问题,屏幕倒着的,转一下方向即可看出正确输出了。
    附上代码一份:

    #include <linux/init.h>
    #include <linux/fs.h>
    #include <linux/slab.h>
    #include <linux/module.h>
    #include <linux/kernel.h>
    #include <linux/device.h>
    #include <sound/core.h>
    #include <linux/spi/spi.h>
    #include <asm/uaccess.h>
    #include <linux/cdev.h>
    
    #include <linux/gpio.h>
    #include <linux/delay.h>
    
    #include <linux/fb.h>
    
    #include <linux/dma-mapping.h>
    #include <linux/sched.h>
    #include <linux/wait.h>
    
    #include <asm/mach/map.h>
    
    //#define USE_HORIZONTAL
    //#define __DEBUG__ 1
    
    #ifdef __DEBUG__
    #define DEBUG(format,...) \
            printk("DEBUG::"format,  ##__VA_ARGS__)
    #else
    #define DEBUG(format,...)
    #endif
    
    
    #define LCD_X_SIZE          176
    #define LCD_Y_SIZE          220
    
    #ifdef USE_HORIZONTAL//如果定义了横屏
    #define X_MAX_PIXEL         LCD_Y_SIZE
    #define Y_MAX_PIXEL         LCD_X_SIZE
    #else//竖屏
    #define X_MAX_PIXEL         LCD_X_SIZE
    #define Y_MAX_PIXEL         LCD_Y_SIZE
    #endif
    
    static int tft_lcdfb_setcolreg(unsigned int regno, unsigned int red,
    			     unsigned int green, unsigned int blue,
    			     unsigned int transp, struct fb_info *info);
    
    struct tft_lcd{
        struct gpio_desc *reset_gpio;   
    	struct gpio_desc *rs_gpio;
    
    };
    
    struct tft_lcd_fb{
    	struct spi_device *spi; //记录fb_info对象对应的spi设备对象
    	struct task_struct *thread; //记录线程对象的地址,此线程专用于把显存数据发送到屏的驱动ic
    };
    static struct fb_ops fops = {
    	.owner		= THIS_MODULE,
    	.fb_setcolreg	= tft_lcdfb_setcolreg,
    	.fb_fillrect	= cfb_fillrect,
    	.fb_copyarea	= cfb_copyarea,
    	.fb_imageblit	= cfb_imageblit,
    };
    
    
    
    struct regdata_t{
            u8  reg;
            u16 data;
            int delay_ms;
    }regdatas[] = {
        {0x10, 0x0000, 0}, {0x11, 0x0000, 0}, {0x12, 0x0000, 0},
        {0x13, 0x0000, 0}, {0x14, 0x0000, 40},
    
        {0x11, 0x0018, 0}, {0x12, 0x1121, 0}, {0x13, 0x0063, 0},
        {0x14, 0x3961, 0}, {0x10, 0x0800, 10}, {0x11, 0x1038, 30},
    
        {0x02, 0x0100, 0}, 
    #ifdef USE_HORIZONTAL//如果定义了横屏
    	{0x01, 0x001c, 0}, {0x03, 0x1038, 0},
    #else//竖屏
    	{0x01, 0x011c, 0}, {0x03, 0x1030, 0},
    #endif
    
        {0x07, 0x0000, 0}, {0x08, 0x0808, 0}, {0x0b, 0x1100, 0},
        {0x0c, 0x0000, 0}, {0x0f, 0x0501, 0}, {0x15, 0x0020, 0},
        {0x20, 0x0000, 0}, {0x21, 0x0000, 0},
    
        {0x30, 0x0000}, {0x31, 0x00db}, {0x32, 0x0000}, {0x33, 0x0000},
        {0x34, 0x00db}, {0x35, 0x0000}, {0x36, 0x00af}, {0x37, 0x0000},
        {0x38, 0x00db}, {0x39, 0x0000},
    
        {0x50, 0x0603}, {0x51, 0x080d}, {0x52, 0x0d0c}, {0x53, 0x0205},
        {0x54, 0x040a}, {0x55, 0x0703}, {0x56, 0x0300}, {0x57, 0x0400},
        {0x58, 0x0b00}, {0x59, 0x0017},
    
        {0x0f, 0x0701}, {0x07, 0x0012, 50}, {0x07, 0x1017},
    }; 
    
    static void Lcd_WriteIndex(struct spi_device *spi, u8 Index)
    {
    	struct tft_lcd *pdata = spi_get_drvdata(spi);
    
    	gpiod_set_value(pdata->rs_gpio, 0); //高电平
    	spi_write(spi, &Index, 1);
    }
    static void Lcd_WriteData_16Bit(struct spi_device *spi, u16 Data)
    {   
    	u8 buf[2];
    	struct tft_lcd *pdata = spi_get_drvdata(spi);
    
    	buf[0] = ((u8)(Data>>8));
    	buf[1] = ((u8)(Data&0x00ff));
    
    	gpiod_set_value(pdata->rs_gpio, 1); //高电平
    	spi_write(spi, &buf[0], 1);
    	spi_write(spi, &buf[1], 1);   
    }
    
    static void LCD_WriteReg(struct spi_device *spi, u8 Index, u16 Data)
    {
    	int addr;
    	addr = Index;
        Lcd_WriteIndex(spi, addr);
        Lcd_WriteData_16Bit(spi, Data);
    }
    
    static void Lcd_SetRegion(struct spi_device *spi, u8 xStar, u8 yStar,u8 xEnd,u8 yEnd)
    {
    #ifdef USE_HORIZONTAL//如果定义了横屏 
    		LCD_WriteReg(spi,0x38,xEnd);
    		LCD_WriteReg(spi,0x39,xStar);
    		LCD_WriteReg(spi,0x36,yEnd);
    		LCD_WriteReg(spi,0x37,yStar);
    		LCD_WriteReg(spi,0x21,xStar);
    		LCD_WriteReg(spi,0x20,yStar);
    #else//竖屏   
    		LCD_WriteReg(spi,0x36,xEnd);
    		LCD_WriteReg(spi,0x37,xStar);
    		LCD_WriteReg(spi,0x38,yEnd);
    		LCD_WriteReg(spi,0x39,yStar);
    		LCD_WriteReg(spi,0x20,xStar);
    		LCD_WriteReg(spi,0x21,yStar);
    #endif
    		Lcd_WriteIndex(spi,0x22);	
    
    }
    
    static int lcd_dt_parse(struct spi_device *spi, struct tft_lcd *lcd_data)
    {
    
        lcd_data->reset_gpio = devm_gpiod_get(&spi->dev, "rest", GPIOD_OUT_HIGH);
        if (IS_ERR(lcd_data->reset_gpio))
            goto err0;
    	gpio_direction_output(desc_to_gpio(lcd_data->reset_gpio), 1);
    
    	lcd_data->rs_gpio = devm_gpiod_get(&spi->dev, "rs", GPIOD_OUT_HIGH);
        if (IS_ERR(lcd_data->rs_gpio))
            goto err1;
    	gpio_direction_output(desc_to_gpio(lcd_data->rs_gpio), 1);
    	
    	return 0;
    
    err1:
    	devm_gpiod_put(&spi->dev, lcd_data->reset_gpio);
    err0:
    	DEBUG("[%s]:failed\n", __FUNCTION__);
    	return -1;
    
    }
    
    static void lcd_init(struct spi_device *spi, struct tft_lcd *pdata)
    {
    	int i =0;
    	gpiod_set_value(pdata->reset_gpio, 0); //设低电平
        msleep(100);
        gpiod_set_value(pdata->reset_gpio, 1); //设高电平
        msleep(50);
    
    	for (i = 0; i < ARRAY_SIZE(regdatas); i++)
        {
            LCD_WriteReg(spi, regdatas[i].reg, regdatas[i].data);
            if (regdatas[i].delay_ms)
                msleep(regdatas[i].delay_ms);
        }
    
    }
    
    void show_fb(struct fb_info *fbi, struct spi_device *spi)
    {
        int x, y;
        u32 k;
        u32 *p = (u32 *)(fbi->screen_base);
        u16 c;
        u8 *pp;
    
        //addset(spi, 0, 0); //从屏的0,0坐标开始刷
        Lcd_SetRegion(spi, 0,0,X_MAX_PIXEL-1,Y_MAX_PIXEL-1);
     	DEBUG("[%s] \n",__FUNCTION__);
        for (y = 0; y < fbi->var.yres; y++)
        {
            for (x = 0; x < fbi->var.xres; x++)
            {
                k = p[y*fbi->var.xres+x];//取出一个像素点的32位数据
                // rgb8888 --> rgb565       
                pp = (u8 *)&k;
                c = pp[0] >> 3; //蓝色
                c |= (pp[1]>>2)<<5; //绿色
                c |= (pp[2]>>3)<<11; //红色
    
                //发出像素数据的rgb565
                //write_data(spi, c >> 8);
                //write_data(spi, c & 0xff);
    			Lcd_WriteData_16Bit(spi, c);
            }
        }
    
    }
    
    int thread_func_fb(void *data)
    {
        struct fb_info *fbi = (struct fb_info *)data;
        struct tft_lcd_fb *lcd_fb = fbi->par;
    
        while (1)
        {
    		if (kthread_should_stop())
    			break;
            show_fb(fbi, lcd_fb->spi);
            //
        }
    
        return 0;
    }
    static u32 pseudo_palette[16];
    static inline unsigned int chan_to_field(unsigned int chan, struct fb_bitfield *bf)
    {
    	chan &= 0xffff;
    	chan >>= 16 - bf->length;
    	return chan << bf->offset;
    }
    
    static int tft_lcdfb_setcolreg(unsigned int regno, unsigned int red,
    			     unsigned int green, unsigned int blue,
    			     unsigned int transp, struct fb_info *info)
    {
    	unsigned int val;
    	
    	if (regno > 16)
    	{
    		DEBUG("[%S] the regno is %d !!\n",__FUNCTION__, regno);
    		return 1;
    	}
    		
    
    	/* 用red,green,blue三原色构造出val  */
    	val  = chan_to_field(red,	&info->var.red);
    	val |= chan_to_field(green, &info->var.green);
    	val |= chan_to_field(blue,	&info->var.blue);
    	
    	//((u32 *)(info->pseudo_palette))[regno] = val;
    	pseudo_palette[regno] = val;
    	return 0;
    }
    
    int tft_lcd_fb_register(struct spi_device *spi) //此函数在probe函数里被调用
    {
    	struct fb_info *fbi;
        u8 *v_addr;
        u32 p_addr;
    	struct tft_lcd_fb *lcd_fb;
    
    	v_addr = dma_alloc_coherent(NULL, LCD_X_SIZE*LCD_Y_SIZE*4, &p_addr, GFP_KERNEL);
    
    	//额外分配lcd_data_t类型空间
        fbi = framebuffer_alloc(sizeof(struct tft_lcd_fb), &spi->dev);
    	if(fbi == NULL)
    		DEBUG("[%s]:framebuffer_alloc failed\n", __FUNCTION__);
        //lcd_fb = &fbi[1]; //data指针指向额外分配的空间
        lcd_fb = fbi->par; //data指针指向额外分配的空间
    
    	lcd_fb->spi = spi;
    
    	fbi->pseudo_palette = pseudo_palette;
    	fbi->var.activate       = FB_ACTIVATE_NOW;
    
        fbi->var.xres = LCD_X_SIZE;
        fbi->var.yres = LCD_Y_SIZE;
        fbi->var.xres_virtual = LCD_X_SIZE;
        fbi->var.yres_virtual = LCD_Y_SIZE;
        fbi->var.bits_per_pixel = 32; // 屏是rgb565, 但QT程序只能支持32位.还需要在刷图时把32位的像素数据转换成rgb565
        fbi->var.red.offset = 16;
        fbi->var.red.length = 8;
        fbi->var.green.offset = 8;
        fbi->var.green.length = 8;
        fbi->var.blue.offset = 0;
        fbi->var.blue.length = 8;
    
    	strcpy(fbi->fix.id, "myfb");
        fbi->fix.smem_start = p_addr; //显存的物理地址
        fbi->fix.smem_len = LCD_X_SIZE*LCD_Y_SIZE*4; 
        fbi->fix.type = FB_TYPE_PACKED_PIXELS;
        fbi->fix.visual = FB_VISUAL_TRUECOLOR;
        fbi->fix.line_length = LCD_X_SIZE*4;
    
    	fbi->fbops = &fops;
        fbi->screen_base = v_addr; //显存虚拟地址
        //fbi->screen_base = dma_alloc_writecombine(NULL, fbi->fix.smem_len, &fbi->fix.smem_start, GFP_KERNEL);
        fbi->screen_size = LCD_X_SIZE*LCD_Y_SIZE*4; //显存大小
    
    	//spi_set_drvdata(spi, fbi);
        register_framebuffer(fbi);
        lcd_fb->thread = kthread_run(thread_func_fb, fbi, spi->modalias);
    	
        return 0; 
    
    }
    
    static void tft_fb_test(struct spi_device *spi)
    {
    	int i,j;
    	u16 color = 0x001f; /* rgb565,  蓝色 */
    	
    	Lcd_SetRegion(spi, 0,0,X_MAX_PIXEL-1,Y_MAX_PIXEL-1); //设置从屏哪个坐标开始显示,到哪个坐标结束
    
    	#define rgb(r, g, b)  ((((r>>3)&0x1f)<<11) | (((g>>2)&0x3f)<<5) | ((b>>3)&0x1f))
        for(i=0 ; i<Y_MAX_PIXEL/2 ; i++)
        {
    		
            color = rgb(255, 0, 255); 
            for(j=0; j<X_MAX_PIXEL/2; j++)
                Lcd_WriteData_16Bit(spi, color);//(u8 *)&
            color = rgb(255, 255, 0);
            for(j=X_MAX_PIXEL/2; j<X_MAX_PIXEL; j++)
                Lcd_WriteData_16Bit(spi, color);
        }
        for(i=Y_MAX_PIXEL/2 ; i<Y_MAX_PIXEL; i++)
        {
            color = rgb(0, 255, 255);
            for(j=0; j<X_MAX_PIXEL/2; j++)
                Lcd_WriteData_16Bit(spi, color);
    
            color = rgb(255, 0,0);
            for(j=X_MAX_PIXEL/2; j<X_MAX_PIXEL; j++)
                Lcd_WriteData_16Bit(spi, color);
        }
    }
    
    static int tft_lcd_probe(struct spi_device *spi)
    {
    	int ret;
    	
    	struct tft_lcd *lcd_data = devm_kzalloc(&spi->dev, sizeof(struct tft_lcd), GFP_KERNEL);
    
    	ret = lcd_dt_parse(spi, lcd_data);
    	if(ret !=0)
    		goto err0;
    
    	DEBUG("[%s]:success\n", __FUNCTION__);
    	spi_set_drvdata(spi, lcd_data);
    	lcd_init(spi, lcd_data);
    
    	tft_fb_test(spi);
    
    	ret = tft_lcd_fb_register(spi); //fb设备初始化
    	
        return 0;
    
    err0:
    	devm_gpiod_put(&spi->dev, lcd_data->rs_gpio);
    	DEBUG("[%s]:failed\n", __FUNCTION__);
    	return ret;
    	
    }
    
    int tft_lcd_remove(struct spi_device *spi)
    {
        struct tft_lcd *pdata = spi_get_drvdata(spi);
    
        DEBUG("[%s]:success\n", __FUNCTION__);
    	devm_gpiod_put(&spi->dev, pdata->rs_gpio);
        devm_gpiod_put(&spi->dev, pdata->reset_gpio);
        return 0;
    }
    
    
    struct of_device_id tft_lcd_ids[] = {
        {.compatible = "nanopi,tft_lcd_spi"},
        {},
    };
    
    struct spi_driver tft_lcd_drv = {
            .probe	= tft_lcd_probe,
            .remove = tft_lcd_remove,
    
            .driver = {
                .owner = THIS_MODULE,
                .name = "tft_lcd_drv",
                .of_match_table = tft_lcd_ids,
            },
    };
    
    module_spi_driver(tft_lcd_drv);
    MODULE_LICENSE("GPL");
    MODULE_DESCRIPTION("TFT LCD SPI driver");
    

    还是要感谢一下jklinux大佬的文章的~

    但是这驱动只是简单刷一下屏幕,而且是在线程里全局刷新,即使界面无更改,也要刷新,利用率非常低,为了提高效率,每次刷新只要刷更改过的界面即可,也就是刷新重绘区。
    可以参考这篇:嵌入式Linux驱动笔记(二十六)------framebuffer之使用spi-tft屏幕(下)

    展开全文
  • 问题:我们可以将qt移植到H3上,但是如果要将窗口通过spi屏进行显示应该怎么做呢,和framebuffer又有什么关系? 这里只说一下qt在spi屏上显示的技术原理,具体的实现还需要自己努力。 一、创建显存并实现显存...

    问题:我们可以将qt移植到H3上,但是如果要将窗口通过spi屏进行显示应该怎么做呢,和framebuffer又有什么关系?

    这里只说一下qt在spi屏上显示的技术原理,具体的实现还需要自己努力。

     

    一、创建显存并实现显存驱动

    在linux系统中,没有lcd驱动这种叫法,只有framebuffer设备驱动,也就是显存驱动。 这种驱动是用于实现提供应用程序的窗口图像的显示接口,如QT窗口程序的显示.

    加载模块后会在 /dev/ 下面出现fb8对象(可以通过它操作显存)。

    二、写qt程序,移植到H3上,并修改板子/etc/profile上的环境变量将fb0改为fb8,意思是将窗口显示数据写到显存中。

      

    三、通过spi协议将显存中的数据传送到spi屏幕上即可。

    转载于:https://www.cnblogs.com/edan/p/9270097.html

    展开全文
  • 液晶型号为JLX12864 COG液晶,资料网上有,linux下面通过SPI与IO控制,IO控制使用的是文件方式,SPI是开发板提供的驱动,这个SPI驱动应该每家提供的都不一样,需要自己去按照驱动文档操作,主要不通电就是底层的配置...

    液晶型号为JLX12864 COG液晶,资料网上有,linux下面通过SPI与IO控制,IO控制使用的是文件方式,SPI是开发板提供的驱动,这个SPI驱动应该每家提供的都不一样,需要自己去按照驱动文档操作,主要不通电就是底层的配置不一样。

    //SPI.c这个是打开SPI驱动

    /*
     * SPI.c
     *
     *  Created on: 2018年8月2日
     *      Author: cfan
     */
    #include <stdio.h>
    #include <unistd.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <fcntl.h>
    #include <unistd.h>
    #include <stdint.h>
    #include <termios.h>
    #include "SPI.h"
    #include <errno.h>  	// 包含errno所需要的头文件
    #include <string.h>  	// 包含strerror所需要的头文件
    #include "typedef.h"
    
    
    
    //SPI初始化
    int SPI_Init(SPI_HANDLE *pHandle, const char *pSpiDeviceName)
    {
    	pHandle->fd = -1;
    
    	if(pSpiDeviceName == NULL || pHandle==NULL || strlen(pSpiDeviceName)>SPI_DEVICE_NAME_MAX_LEN)
    	{
    		printf("%s(%d)Check the input parameters!\r\n",__FILE__ , __LINE__);
    		return -1;
    	}
    	strcpy(pHandle->SpiDeviceName, pSpiDeviceName);					//记录串口设备名称
    
    	//打开SPI
    	pHandle->fd = open(pSpiDeviceName, O_RDWR|O_NOCTTY|O_NDELAY);		//读写独占方式打开SPI
    	if (pHandle->fd < 0)
    	{
    		//打印错误信息
    		printf("Can't Open SPI(%s) : %s(%d)\n",pSpiDeviceName, strerror(errno), errno);
    		return errno;
    	}
    	else
    	{
    		printf("Open SPI OK!\r\n");
    
    	}
    
    	return 0;
    }
    
    

    //spi.h

    /*
     * SPI.h
     *
     *  Created on: 2018年8月2日
     *      Author: cfan
     */
    
    #ifndef HARDWARE_SPI_H_
    #define HARDWARE_SPI_H_
    #include "termios.h"
    #include "typedef.h"
    
    
    #define SPI_DEVICE_NAME_MAX_LEN	35	//SPI名称最大长度
    
    //SPI接口句柄
    typedef struct
    {
    	int fd;
    	char SpiDeviceName[SPI_DEVICE_NAME_MAX_LEN+1];		//SPI名称
    }SPI_HANDLE;
    
    
    
    //SPI初始化
    int SPI_Init(SPI_HANDLE *pHandle, const char *pSpiDeviceName);
    
    
    #endif /* HARDWARE_SPI_H_ */
    

    //SPI驱动调用,这个是开发板提供的,各个厂家应该都不一样,需要自己去查询

    #include "common.h"
    #include "spi_enum.h"
    #include "spidev.h"
    #include <sys/ioctl.h>
    #include "nano_pi_spi.h"
    //开发板官方提供的SPI驱动接口
    
    
    #define SPI_MAX_SPEED 25000000		//最大时钟速度
    
    
    /*************************************************************************************************************************
    *函数        			:	int setSPIWriteBitsPerWord(int spi_fd, int bits)
    *功能        			:	设置每次读SPI设备的字长,单位是比特.
    *参数        			:	spi_fd: SPI设备的文件描述符;bits: 字长,单位是比特
    *返回        			:	成功返回0,失败返回负数
    *依赖        			:	无
    *作者        			:	Friendly NanoPI-NEO(cp1300@139.com整理)
    *时间        			:	2018-08-12
    *最后修改时间	:	2018-08-12
    *说明        			:	虽然大部分SPI接口的字长是8或者16,仍然会有一些特殊的例子。
            			需要说明的是,如果这个成员为零的话,默认使用8作为字长(ioctl SPI_IOC_WR_BITS_PER_WORD)
    *************************************************************************************************************************/
    int setSPIWriteBitsPerWord(int spi_fd, int bits)
    {
        clearLastError();
    	int ret = ioctl(spi_fd, SPI_IOC_WR_BITS_PER_WORD, &bits);
    	if (ret < 0) {
    		setLastError("Can't ioctl SPI_IOC_WR_BITS_PER_WORD");
    	}
    	return ret;
    }
    
    
    /*************************************************************************************************************************
    *函数        			:	int setSPIReadBitsPerWord(int spi_fd, int bits)
    *功能        			:	设置每次写SPI设备的字长,单位是比特
    *参数        			:	spi_fd: SPI设备的文件描述符;bits: 字长,单位是比特
    *返回        			:	成功返回0,失败返回负数
    *依赖        			:	无
    *作者        			:	Friendly NanoPI-NEO(cp1300@139.com整理)
    *时间        			:	2018-08-12
    *最后修改时间	:	2018-08-12
    *说明        			:	虽然大部分SPI接口的字长是8或者16,仍然会有一些特殊的例子。
            			需要说明的是,如果这个成员为零的话,默认使用8作为字长(ioctl SPI_IOC_WR_BITS_PER_WORD)
    *************************************************************************************************************************/
    int setSPIReadBitsPerWord(int spi_fd, int bits)
    {
    	int ret = ioctl(spi_fd, SPI_IOC_RD_BITS_PER_WORD, &bits);
        clearLastError();
    	if (ret < 0) {
    		setLastError("Can't ioctl SPI_IOC_RD_BITS_PER_WORD");
    	}
    	return ret;
    }
    
    
    /*************************************************************************************************************************
    *函数        			:	int setSPIBitOrder(int spi_fd, int order)
    *功能        			:	设备SPI传输时是先传输低比特位还是高比特位
    *参数        			:	spi_fd: SPI设备的文件描述符;order: 传SPIEnum.MSBFIRST或SPIEnum.LSBFIRST
    *返回        			:	成功返回0,失败返回负数
    *依赖        			:	无
    *作者        			:	Friendly NanoPI-NEO(cp1300@139.com整理)
    *时间        			:	2018-08-12
    *最后修改时间	:	2018-08-12
    *说明        			:	可选的参数有SPIEnum.MSBFIRST和SPIEnum.LSBFIRST
    *************************************************************************************************************************/
    int setSPIBitOrder(int spi_fd, int order)
    {
    	int ret;
    	int spi_mode = 0;
        clearLastError();
    	if(order == LSBFIRST) {
    		spi_mode |=  SPI_LSB_FIRST;
    	} else {
    		spi_mode &= ~SPI_LSB_FIRST;
    	}
    	ret = ioctl(spi_fd, SPI_IOC_WR_MODE, &spi_mode);
    	if (ret < 0) {
    		setLastError("Can't ioctl SPI_IOC_WR_MODE");
    		return ret;
    	}
    	return ret;
    }
    
    
    /*************************************************************************************************************************
    *函数        			:	int setSPIMaxSpeed(int spi_fd, unsigned int spi_speed)
    *功能        			:	设备SPI传输速度
    *参数        			:	spi_fd: SPI设备的文件描述符;spi_speed: 速度(分频,越小速度越高)
    *返回        			:	成功返回0,失败返回负数
    *依赖        			:	无
    *作者        			:	Friendly NanoPI-NEO(cp1300@139.com整理)
    *时间        			:	2018-08-12
    *最后修改时间	:	2018-08-12
    *说明        			:
    *************************************************************************************************************************/
    int setSPIMaxSpeed(int spi_fd, unsigned int spi_speed)
    {
    	int ret;
    	unsigned int realSpeed;
        clearLastError();
        if (spi_speed<0 || spi_speed>SPI_MAX_SPEED) {
            setLastError("invalid spi speed %d", spi_speed);
        }
    	ret = ioctl(spi_fd, SPI_IOC_WR_MAX_SPEED_HZ, &spi_speed);
    	if (ret < 0) {
    		setLastError("Can't ioctl SPI_IOC_WR_MAX_SPEED_HZ");
    		return ret;
    	}
    	ret = ioctl(spi_fd, SPI_IOC_RD_MAX_SPEED_HZ, &realSpeed);
    	if (ret < 0) {
    		setLastError("Can't ioctl SPI_IOC_RD_MAX_SPEED_HZ");
    		return ret;
    	}
    	return ret;
    }
    
    /*************************************************************************************************************************
    *函数        			:	int setSPIDataMode(int spi_fd, int mode)
    *功能        			:	设置SPI设备的模式
    *参数        			:	spi_fd: SPI设备的文件描述符;mode: SPI设备的模式,可传入SPI_MODE0 ~ SPI_MODE3
    *返回        			:	成功返回0,失败返回负数
    *依赖        			:	无
    *作者        			:	Friendly NanoPI-NEO(cp1300@139.com整理)
    *时间        			:	2018-08-12
    *最后修改时间	:	2018-08-12
    *说明        			:
    *************************************************************************************************************************/
    int setSPIDataMode(int spi_fd, int mode)
    {
    	int ret;
    	int spi_mode = 0;
        clearLastError();
    	switch(mode) {
    		case SPI_MODE0:
    			spi_mode &= ~(SPI_CPHA|SPI_CPOL);
    			break;
    		case SPI_MODE1:
    			spi_mode &= ~(SPI_CPOL);
    			spi_mode |= (SPI_CPHA);
    			break;
    		case SPI_MODE2:
    			spi_mode |= (SPI_CPOL);
    			spi_mode &= ~(SPI_CPHA);
    			break;
    		case SPI_MODE3:
    			spi_mode |= (SPI_CPHA|SPI_CPOL);
    			break;
    		default:
    			setLastError("error SPIDataMode");
    			return -1;
    	}
    
    	ret = ioctl(spi_fd, SPI_IOC_WR_MODE, &mode);
    	if (ret < 0) {
    		setLastError("Can't ioctl SPI_IOC_WR_MODE");
    		return ret;
    	}
    
    	ret = ioctl(spi_fd, SPI_IOC_RD_MODE, &mode);
    	if (ret < 0) {
    		setLastError("Can't ioctl SPI_IOC_RD_MODE");
    		return ret;
    	}
    
    	return ret;
    }
    
    
    
    /*************************************************************************************************************************
    *函数        			:	int SPItransferOneByte(int spi_fd, unsigned char byteData, int spi_delay, int spi_speed, int spi_bits)
    *功能        			:	同时发送与接收一个字节的数据
    *参数        			:	spi_fd: SPI设备的文件描述符;byteData:要写入SPI设备的数据;spi_delay:延时;spi_speed:传输速度;spi_bits:字长,单位是比特
    *返回        			:	成功返回读到的数据,失败返回负数
    *依赖        			:	无
    *作者        			:	Friendly NanoPI-NEO(cp1300@139.com整理)
    *时间        			:	2018-08-12
    *最后修改时间	:	2018-08-12
    *说明        			:
    *************************************************************************************************************************/
    int SPItransferOneByte(int spi_fd , unsigned char byteData, int spi_delay, int spi_speed, int spi_bits)
    {
    	int ret;
    	unsigned char tx[1] = {0};
    	unsigned char rx[1] = {0};
    	tx[0] = byteData;
    
    	struct spi_ioc_transfer tr;
    	tr.tx_buf = (unsigned long)tx;
    	tr.rx_buf = (unsigned long)rx;
    	tr.len = 1;
    	tr.delay_usecs = spi_delay;
    	tr.speed_hz = spi_speed;
    	tr.bits_per_word = spi_bits;
    
        clearLastError();
    	ret = ioctl(spi_fd, SPI_IOC_MESSAGE(1), &tr);
    	if (ret < 0) {
    		setLastError("Can't ioctl SPI_IOC_MESSAGE");
    		return ret;
    	}
    	return rx[0];
    }
    
    
    /*************************************************************************************************************************
    *函数        			:	int SPItransferBytes(int spi_fd, unsigned char * writeData, int writeLen, unsigned char * readBuffer,
    *函数        					int readLen, int spi_delay, int spi_speed, int spi_bits)
    *功能        			:	同时发送与接收多个字节的数据
    *参数        			:	spi_fd: SPI设备的文件描述符;writeData:要写入的数据;readBuff: 存放读取数据的缓冲区;spi_delay:延时;spi_speed:传输速度;
            			spi_bits:字长,单位是比特
    *返回        			:	成功返回0,失败返回负数
    *依赖        			:	无
    *作者        			:	Friendly NanoPI-NEO(cp1300@139.com整理)
    *时间        			:	2018-08-12
    *最后修改时间	:	2018-08-12
    *说明        			:
    *************************************************************************************************************************/
    int SPItransferBytes(int spi_fd, unsigned char * writeData, int writeLen, unsigned char * readBuffer, int readLen, int spi_delay,
    		int spi_speed, int spi_bits)
    {
    	unsigned int len = writeLen;
    	if (len > readLen) {
    		len = readLen;
    	}
    
    	unsigned char * pWriteData = writeData;
    	unsigned char * pReadBuffer = readBuffer;
    
    	struct spi_ioc_transfer tr;
    	tr.tx_buf = (unsigned long)pWriteData;
    	tr.rx_buf = (unsigned long)pReadBuffer;
    	tr.len = len;
    	tr.delay_usecs = spi_delay;
    	tr.speed_hz = spi_speed;
    	tr.bits_per_word = spi_bits;
    
    	int ret = ioctl(spi_fd, SPI_IOC_MESSAGE(1), &tr);
    
        clearLastError();
    	if (ret < 0) {
    		setLastError("Can't ioctl SPI_IOC_MESSAGE");
    	}
    	return ret;
    }
    
    
    /*************************************************************************************************************************
    *函数        			:	int writeBytesToSPI(int spi_fd, unsigned char * writeData, int writeLen, int spi_delay, int spi_speed,
    *函数        					int spi_bits)
    *功能        			:	写多个字节的数据到SPI设 备
    *参数        			:	spi_fd: SPI设备的文件描述符;writeData:要写入的数据;spi_delay:延时;spi_speed:传输速度;spi_bits:字长,单位是比特
    *返回        			:	成功返回0,失败返回负数
    *依赖        			:	无
    *作者        			:	Friendly NanoPI-NEO(cp1300@139.com整理)
    *时间        			:	2018-08-12
    *最后修改时间	:	2018-08-12
    *说明        			:
    *************************************************************************************************************************/
    int writeBytesToSPI(int spi_fd, unsigned char * writeData, int writeLen, int spi_delay, int spi_speed, int spi_bits)
    {
    	unsigned int len = writeLen;
    
    	unsigned char * pWriteData = writeData;
    
    	struct spi_ioc_transfer tr;
    	tr.tx_buf = (unsigned long)pWriteData;
    	tr.rx_buf = (unsigned long)0;
    	tr.len = len;
    	tr.delay_usecs = spi_delay;
    	tr.speed_hz = spi_speed;
    	tr.bits_per_word = spi_bits;
    
    	int ret = ioctl(spi_fd, SPI_IOC_MESSAGE(1), &tr);
        clearLastError();
    	if (ret < 0) {
    		setLastError("Can't ioctl SPI_IOC_MESSAGE");
    	}
    	return ret;
    }
    
    /*************************************************************************************************************************
    *函数        			:	int readBytesFromSPI(int spi_fd, unsigned char * readBuffer, int readLen, int spi_delay, int spi_speed,
    *函数        					int spi_bits)
    *功能        			:	从SPI设备读取多个字节
    *参数        			:	spi_fd: SPI设备的文件描述符;readBuff: 存放读取数据的缓冲区;readLen:读取长度(不能超过缓冲区大小)
           				spi_delay:延时;spi_speed:传输速度;spi_bits:字长,单位是比特
    *返回        			:	成功返回0,失败返回负数
    *依赖        			:	无
    *作者        			:	Friendly NanoPI-NEO(cp1300@139.com整理)
    *时间        			:	2018-08-12
    *最后修改时间	:	2018-08-12
    *说明        			:
    *************************************************************************************************************************/
    int readBytesFromSPI(int spi_fd, unsigned char * readBuffer, int readLen, int spi_delay, int spi_speed, int spi_bits)
    {
    	unsigned int len = readLen;
    
    	unsigned char * pReadBuffer = readBuffer;
    
    	struct spi_ioc_transfer tr;
    	tr.tx_buf = (unsigned long)0;
    	tr.rx_buf = (unsigned long)pReadBuffer;
    	tr.len = len;
    	tr.delay_usecs = spi_delay;
    	tr.speed_hz = spi_speed;
    	tr.bits_per_word = spi_bits;
    
    	int ret = ioctl(spi_fd, SPI_IOC_MESSAGE(1), &tr);
        clearLastError();
    	if (ret < 0) {
    		setLastError("Can't ioctl SPI_IOC_MESSAGE");
    	}
    	return ret;
    }
    
    

    //JLX12864G.c 液晶硬件底层操作

    /*************************************************************************************************************
     * 文件名:		JLX12864G.c
     * 功能:		JLX12864G-0088 JLX12864G液晶驱动
     * 作者:		cp1300@139.com
     * 邮箱:		cp1300@139.com
     * 创建时间:	2012年5月30日20:40
     * 最后修改时间:2012年5月30日
     * 详细:		2016-02-01:增加获取显存函数
    *************************************************************************************************************/
    #include "ascii_8x16.h"
    #include "JLX12864G.H"
    #include <stdio.h>
    #include "typedef.h"
    
    
    //字模取模方式:阴码,列行式,逆向(低位在前)
    
    
    
    //汉字支持
    #define CHINESE_ENABLE		0
    
    
    
    
    
    
    
    #if LCD_BUFF_ENABLE		//使能了显存
    //获取显存地址
    //2016-02-01:增加获取显存函数
    u8 *JLX12864G_GetGramBuff(JLX12864G_HANDLE *pHandle)
    {
    	return (u8 *)&pHandle->LCD_BUFF[0][0];
    }
    
    
    #endif
    
    
    
    
    
    /*************************************************************************************************************************
    * 函数			:	void JLX12864G_WriteCommand(JLX12864G_HANDLE *pHandle, u8 cmd)
    * 功能			:	向JLX12864G写入一字节命令
    * 参数			:	pHandle:句柄;cmd:命令
    * 返回			:	无
    * 依赖			:	底层宏定义
    * 作者			:	cp1300@139.com
    * 时间			:	20120530
    * 最后修改时间 	: 	2018-08-12
    * 说明			: 	RS=0,时钟上升沿数据有效,先发送高位
    *************************************************************************************************************************/
    void JLX12864G_WriteCommand(JLX12864G_HANDLE *pHandle, u8 cmd)
    {
    	pHandle->SetRS(0);				//RS=0
    	pHandle->WriteData(&cmd, 1);	//发送数据
    	pHandle->SetRS(1);				//RS=1
    }
    
    
    /*************************************************************************************************************************
    * 函数	:	static void JLX12864G_SetPageAdd(JLX12864G_HANDLE *pHandle, u8 PageAdd)
    * 功能	:	设置光标页地址
    * 参数	:	pHandle:句柄;PageAdd:页地址,0-7
    * 返回	:	无
    * 依赖	:	底层宏定义
    * 作者	:	cp1300@139.com
    * 时间	:	20120531
    * 最后修改时间 : 20120531
    * 说明	: 	共64行,没8行为一页,共8页
    *************************************************************************************************************************/
    static void JLX12864G_SetPageAdd(JLX12864G_HANDLE *pHandle, u8 PageAdd)
    {
    	JLX12864G_WriteCommand(pHandle, 0xb0 + PageAdd);
    }
    
    
    /*************************************************************************************************************************
    * 函数	:	static void JLX12864G_SetLineAdd(JLX12864G_HANDLE *pHandle, u8 LineAdd)
    * 功能	:	设置光标列地址
    * 参数	:	pHandle:句柄;LineAdd:列地址,0-127
    * 返回	:	无
    * 依赖	:	底层宏定义
    * 作者	:	cp1300@139.com
    * 时间	:	20120531
    * 最后修改时间 : 20120531
    * 说明	: 	共128列
    *************************************************************************************************************************/
    static void JLX12864G_SetLineAdd(JLX12864G_HANDLE *pHandle, u8 LineAdd)
    {
    	LineAdd += JLX12864G_X_OFFSET;
    	JLX12864G_WriteCommand(pHandle, 0x10 + (LineAdd >> 4));		//列地址高4位
    	JLX12864G_WriteCommand(pHandle, 0x00 + (LineAdd & 0x0f)); 	//列地址低4位
    }
    
    
    
    
    /*************************************************************************************************************************
    * 函数			:	void JLX12864G_ClearAll(JLX12864G_HANDLE *pHandle)
    * 功能			:	JLX12864G液晶清屏
    * 参数			:	pHandle:句柄;
    * 返回			:	无
    * 依赖			:	底层宏定义
    * 作者			:	cp1300@139.com
    * 时间			:	20120530
    * 最后修改时间 	: 	20120530
    * 说明			: 	无
    *************************************************************************************************************************/
    void JLX12864G_ClearAll(JLX12864G_HANDLE *pHandle)
    {
    	u8 i,j;
    	u32 data = 0;
    
    	for(i = 0;i < 9;i ++)
    	{
    		JLX12864G_SetPageAdd(pHandle, i);
    		JLX12864G_SetLineAdd(pHandle, 0);
    		for(j = 0;j < 132/4;j ++)
    		{
    			pHandle->WriteData((u8 *)&data, 4);
    		}
    	}
    }
    
    
    /*************************************************************************************************************************
    * 函数			:	void JLX12864G_FillAll(JLX12864G_HANDLE *pHandle)
    * 功能			:	JLX12864G液晶填充
    * 参数			:	pHandle:句柄;
    * 返回			:	无
    * 依赖			:	底层宏定义
    * 作者			:	cp1300@139.com
    * 时间			:	20120530
    * 最后修改时间 	: 	20120530
    * 说明			: 	无
    *************************************************************************************************************************/
    void JLX12864G_FillAll(JLX12864G_HANDLE *pHandle)
    {
    	u8 i,j;
    	u32 data = 0xffffffff;
    
    	for(i = 0;i < 9;i ++)
    	{
    		JLX12864G_SetPageAdd(pHandle, i);
    		JLX12864G_SetLineAdd(pHandle, 0);
    		for(j = 0;j < 132/4;j ++)
    		{
    			pHandle->WriteData((u8 *)&data, 4);
    		}
    	}
    }
    
    
    /*************************************************************************************************************************
    * 函数	:	void JLX12864G_ShowOneChar(JLX12864G_HANDLE *pHandle,u8 PageAdd,u8 LineAdd,u8 CHAR,u8 FontSize)
    * 功能	:	在指定位置显示一个字符
    * 参数	:	pHandle:句柄;PageAdd:页,0~7,共8页;L:0~127共128列,CHAR:需要显示的字符,FontSize:字体大小
    * 返回	:	无
    * 依赖	:	底层宏定义
    * 作者	:	cp1300@139.com
    * 时间	:	20120530
    * 最后修改时间 : 20120530
    * 说明	: 	显示一个ASCII字符
    *************************************************************************************************************************/
    void JLX12864G_ShowOneChar(JLX12864G_HANDLE *pHandle,u8 PageAdd,u8 LineAdd,u8 CHAR,u8 FontSize)
    {
    
    	u8 i,j,k;
    	const unsigned char *p;
    
    
    	CHAR -= 32;
    	if(CHAR > ASCII_MAX - 1)
    		return;
    
    	if(FontSize == 12)
    		p = ASCII_8X12[CHAR];		//12号
    	else
    		p = ASCII_8X16[CHAR];		//16号
    
    	for(i = 0;i < 2;i ++)
    	{
    		JLX12864G_SetPageAdd(pHandle, PageAdd + i);
    		JLX12864G_SetLineAdd(pHandle, LineAdd);
    		k = i * 8;
    		pHandle->WriteData((u8 *)&p[k+j], 8);
    		/*for(j = 0;j < 8;j ++)
    		{
    			pHandle->WriteByteData(p[k+j]);
    		}*/
    	}
    }
    
    
    
    /*************************************************************************************************************************
    * 函数	:	void LCD_PrintfChar(JLX12864G_HANDLE *pHandle,u8 PageAdd,u8 LineAdd,const char *p,u8 FontSize)
    * 功能	:	在指定位置显示字符串
    * 参数	:	pHandle:句柄;PageAdd:页,0~7,共8页;L:0~127共128列;p:字符指针,FontSize:子大小;16或者12
    * 返回	:	无
    * 依赖	:	JLX12864G_ShowOneChar
    * 作者	:	cp1300@139.com
    * 时间	:	20120601
    * 最后修改时间 : 20120601
    * 说明	: 	FontSize = 16或者 12
    *************************************************************************************************************************/
    void JLX12864G_PrintfChar(JLX12864G_HANDLE *pHandle,u8 PageAdd,u8 LineAdd,const char *p,u8 FontSize)
    {
    	while(*p != 0)
    	{
    		JLX12864G_ShowOneChar(pHandle, PageAdd,LineAdd,*p,FontSize);
    		p ++;
    		LineAdd += 8;
    	}
    }
    
    
    /*************************************************************************************************************************
    * 函数	:	void JLX12864G_SetConAdj(JLX12864G_HANDLE *pHandle,u8 cont)
    * 功能	:	设置液晶的对比度
    * 参数	:	pHandle:句柄;cont:对比度值
    * 返回	:	无
    * 依赖	:	底层宏定义
    * 作者	:	cp1300@139.com
    * 时间	:	2014-08-24
    * 最后修改时间 : 2014-08-24
    * 说明	: 	需要先初始化LCD
    *************************************************************************************************************************/
    void JLX12864G_SetConAdj(JLX12864G_HANDLE *pHandle,u8 cont)
    {
    	if(cont < 25)cont = 25;
    	if(cont > 60)cont = 60;
    
    	JLX12864G_WriteCommand(pHandle, 0x81); /*微调对比度*/
    	JLX12864G_WriteCommand(pHandle, cont); /*微调对比度的值,可设置范围0~63*/
    	pHandle->LCD_Cont = cont;		//更新对比度
    }
    
    
    /*************************************************************************************************************************
    * 函数			:	void JLX12864G_Init(JLX12864G_HANDLE *pHandle,
    						void (*WriteData)(u8 data,u8 len),		//写数据接口
    						void (*SetRS)(u8 level),		//设置RS电平
    						void (*SetRST)(u8 level),		//设置RST电平
    						void (*DelayMS)(u8 ms),			//ms延时
    						u8 LCDCont)
    * 功能			:	初始化JLX12864G液晶
    * 参数			:	pHandle:句柄;WriteByteData:写一字节函数;SetRS:设置RS电平;SetRST:设置RST电平;DelayMS:系统ms延时;LCDCont:对比度
    * 返回			:	无
    * 依赖			:	底层宏定义
    * 作者			:	cp1300@139.com
    * 时间			:	20120530
    * 最后修改时间 	: 2018-08-12
    * 说明			: 	初始化JLX12864G液晶
    *************************************************************************************************************************/
    void JLX12864G_Init(JLX12864G_HANDLE *pHandle,
    		void (*WriteData)(u8 *data,u8 len),		//写数据接口
    		void (*SetRS)(u8 level),		//设置RS电平
    		void (*SetRST)(u8 level),		//设置RST电平
    		void (*DelayMS)(u8 ms),			//ms延时
    		u8 LCDCont)
    {
    	if(pHandle == NULL)
    	{
    		printf("JLX12864G:ERROR *pHandle is NULL!\r\n");
    		return;
    	}
    	if(WriteData == NULL)
    	{
    		printf("JLX12864G:ERROR *WriteData is NULL!\r\n");
    		return;
    	}
    	if(SetRS == NULL)
    	{
    		printf("JLX12864G:ERROR *SetRS is NULL!\r\n");
    		return;
    	}
    	if(DelayMS == NULL)
    	{
    		printf("JLX12864G:ERROR *DelayMS is NULL!\r\n");
    		return;
    	}
    
    	pHandle->WriteData = WriteData;
    	pHandle->SetRS = SetRS;
    	pHandle->SetRST = SetRST;
    	pHandle->DelayMS = DelayMS;
    
    	if(pHandle->SetRST != NULL)
    	{
    		pHandle->SetRST(0);//液晶复位开始
    		pHandle->DelayMS(1);
    		pHandle->SetRST(1);//液晶复位结束
    
    	}
    	pHandle->DelayMS(1);
    
    	JLX12864G_WriteCommand(pHandle, 0xe2); /*软复位*/
    	JLX12864G_WriteCommand(pHandle, 0x2c); /*升压步聚1*/
    	JLX12864G_WriteCommand(pHandle, 0x2e); /*升压步聚2*/
    	JLX12864G_WriteCommand(pHandle, 0x2f); /*升压步聚3*/
    	JLX12864G_WriteCommand(pHandle, 0x23); /*粗调对比度,可设置范围20~27*/
    //	JLX12864G_WriteCommand(0x81); /*微调对比度*/
    //	JLX12864G_WriteCommand(0x30); /*微调对比度的值,可设置范围0~63*/
    	JLX12864G_SetConAdj(pHandle, LCDCont);
    	JLX12864G_WriteCommand(pHandle, 0xa2); /*1/9 偏压比(bias)*/
    #if(LCD_ROTATE_180)	//旋转180度显示
    	JLX12864G_WriteCommand(pHandle, 0xc0); /*行扫描顺序:从下到上*/
    	JLX12864G_WriteCommand(pHandle, 0xa1); /*列扫描顺序:从右到左*/
    #else
    	JLX12864G_WriteCommand(pHandle, 0xc8); /*行扫描顺序:从上到下*/
    	JLX12864G_WriteCommand(pHandle, 0xa0); /*列扫描顺序:从左到右*/
    #endif
    	JLX12864G_WriteCommand(pHandle, 0x40); //初始化显示行为0
    
    	JLX12864G_WriteCommand(pHandle, 0xa4); //常规显示
    	JLX12864G_WriteCommand(pHandle, 0xaf); /*开显示*/
    	JLX12864G_ClearAll(pHandle);
    
    	pHandle->LCD_Cont = LCDCont;
    	//isPowerStatus = TRUE;	//上电完成
    }
    
    
    
    
    /*************************************************************************************************************************
    * 函数			:	void JLX12864G_GRAM_Up(JLX12864G_HANDLE *pHandle, u8 LCD_BUFF[8][128], u8 x1,u8 y1,u8 x2,u8 y2)
    * 功能			:	更新显存至液晶
    * 参数			:	pHandle:句柄;LCD_BUFF:显存地址;x1,y1:起始坐标;x2,y2:终点坐标
    * 返回			:	无
    * 依赖			:	底层宏定义
    * 作者			:	cp1300@139.com
    * 时间			:	20120531
    * 最后修改时间 	: 	2018-08-12
    * 说明			: 	y坐标会页对齐
    *************************************************************************************************************************/
    void JLX12864G_GRAM_Up(JLX12864G_HANDLE *pHandle, u8 LCD_BUFF[8][128], u8 x1,u8 y1,u8 x2,u8 y2)
    {
     	u8 i,j;
    
    
    	if(x2 > 127) x2 = 127;
    	y1 /= 8;  //计算页地址
    	y2 /= 8;
    
    	for(i = 0;i < (y2 - y1 + 1);i ++)
    	{
    		JLX12864G_SetPageAdd(pHandle, y1 + i);	//写入页地址
    		JLX12864G_SetLineAdd(pHandle, x1);  		//写入行地址
    		pHandle->WriteData(&LCD_BUFF[y1 + i][x1 + 0], (x2 - x1 + 1));
    		/*for(j = 0;j < (x2 - x1 + 1);j ++)
    		{
    			LCD12864_WriteData(pHandle, LCD_BUFF[y1 + i][x1 + j]);
    		}*/
    	}
    }
    

    //LCD12864.c 这个是个内存中的虚拟LCD,每次操作都在内存中操作,然后同步到实际LCD

    /*
     * LCD12864_Virtual.c
     * 虚拟LCD12864
     *  Created on: 2018年8月12日
     *      Author: cfan
     */
    #include "LCD12864.h"
    #include "typedef.h"
    #include "ASCII_8x16.h"
    #include <stdio.h>
    #include <string.h>
    
    /*************************************************************************************************************************
    * 函数			:	void LCD12864_GRAM_Init(LCD12864_HANDLE *pHandle)
    * 功能			:	LCD12864显存模式初始化
    * 参数			:	pHandle:句柄;
    * 返回			:	无
    * 依赖			:	底层宏定义
    * 作者			:	cp1300@139.com
    * 时间			:	20120531
    * 最后修改时间 	: 	2018-08-12
    * 说明			: 	无
    *************************************************************************************************************************/
    void LCD12864_GRAM_Init(LCD12864_HANDLE *pHandle)
    {
    	if(pHandle == NULL)
    	{
    		printf("LCD12864:ERROR *pHandle is NULL!\r\n");
    		return;
    	}
    	memset(pHandle->LCD_BUFF, 0, 8*128);
    }
    
    
    
    /*************************************************************************************************************************
    * 函数			:	void LCD12864_GRAM_DrawPoint(LCD12864_HANDLE *pHandle, u8 x,u8 y)
    * 功能			:	在显存里面指定位置画点
    * 参数			:	pHandle:句柄;x:X坐标,0-127;y:y坐标,0-63
    * 返回			:	无
    * 依赖			:	底层宏定义
    * 作者			:	cp1300@139.com
    * 时间			:	20120531
    * 最后修改时间 	: 	2018-08-12
    * 说明			: 	无
    *************************************************************************************************************************/
    void LCD12864_GRAM_DrawPoint(LCD12864_HANDLE *pHandle, u8 x,u8 y)
    {
    	if(x > 127 || y > 63)
    		return;
    	pHandle->LCD_BUFF[y / 8][x] |= (1 << (y % 8));
    }
    
    
    
    
    /*************************************************************************************************************************
    * 函数			:	void LCD12864_GRAM_ClearPoint(LCD12864_HANDLE *pHandle, u8 x,u8 y)
    * 功能			:	擦除显存里面指定位置的点
    * 参数			:	pHandle:句柄;x:X坐标,0-127;y:y坐标,0-63
    * 返回			:	无
    * 依赖			:	底层宏定义
    * 作者			:	cp1300@139.com
    * 时间			:	20120531
    * 最后修改时间 	: 	2018-08-12
    * 说明			: 	无
    *************************************************************************************************************************/
    void LCD12864_GRAM_ClearPoint(LCD12864_HANDLE *pHandle, u8 x, u8 y)
    {
    	if(x > 127 || y > 63)
    		return;
    	pHandle->LCD_BUFF[y / 8][x] &= ~(1 << (y % 8));
    }
    
    
    
    /*************************************************************************************************************************
    * 函数			:	void LCD12864_GRAM_ClearAll(LCD12864_HANDLE *pHandle)
    * 功能			:	清除全部显存
    * 参数			:	pHandle:句柄;
    * 返回			:	无
    * 依赖			:	底层宏定义
    * 作者			:	cp1300@139.com
    * 时间			:	20120531
    * 最后修改时间 	: 	2018-08-12
    * 说明			: 	无
    *************************************************************************************************************************/
    void LCD12864_GRAM_ClearAll(LCD12864_HANDLE *pHandle)
    {
    	u8 i,j;
    
    	for(i = 0;i < 8;i ++)
    	{
    		for(j = 0;j < 128;j ++)
    		{
    			pHandle->LCD_BUFF[i][j] = 0x00;
    		}
    	}
    }
    
    
    /*************************************************************************************************************************
    * 函数			:	void LCD12864_GRAM_ShowChar(LCD12864_HANDLE *pHandle, u8 x,u8 y,u8 CHAR,LCD12864_FONT_MODE FontMode)
    * 功能			:	在指定位置显示一个指定大小的字符
    * 参数			:	pHandle:句柄;x,y:显示开始坐标,p:汉子点阵缓冲区;FontMode:汉子显示模式,
    * 返回			:	无
    * 依赖			:	画点函数
    * 作者			:	cp1300@139.com
    * 时间			:	20120603
    * 最后修改时间 	: 	2018-08-12
    * 说明			:
    *************************************************************************************************************************/
    void LCD12864_GRAM_ShowChar(LCD12864_HANDLE *pHandle, u8 x,u8 y,u8 CHAR,LCD12864_FONT_MODE FontMode)
    {
    	u8 i,j;
    	u8 FontSize = (u8)FontMode&0x0f;	//获取字体大小
    	u8 *p;
    	void (*DrawPoint)(LCD12864_HANDLE *pHandle, u8 i,u8 j);
    	void (*ClearPoint)(LCD12864_HANDLE *pHandle, u8 i,u8 j);
    
    	CHAR -= 32;
    	if(CHAR > 95 - 1) //限制ASCII范围
    		return;
    
    	if(FontSize)
    	{
    		FontSize = 12;
    		p = (u8 *)ASCII_8X12[CHAR];		//12号
    	}
    	else
    	{
    		FontSize = 16;
    		p = (u8 *)ASCII_8X16[CHAR];		//16号
    	}
    
    	if(FontMode & 0x40)	//反显
    	{
    		DrawPoint = LCD12864_GRAM_ClearPoint;
    		ClearPoint =  LCD12864_GRAM_DrawPoint;
    	}
    	else //正常模式
    	{
    		ClearPoint =  LCD12864_GRAM_ClearPoint;
    		DrawPoint =  LCD12864_GRAM_DrawPoint;
    	}
    
    	if(FontMode & 0x80)	//叠加显示
    	{
    		for(j = 0;j < 8;j ++)
    		{
    		 	for(i = 0;i < 8;i ++)
    			{
    			 	if(*p & (1 << i))
    					(*DrawPoint)(pHandle, x + j,y + i);
    			}
    			p ++;
    		}
    		for(j = 0;j < 8;j ++)
    		{
    		 	for(i = 0;i < FontSize - 8;i ++)
    			{
    				if(*p & (1 << i))
    					(*DrawPoint)(pHandle, x + j,y + 8 + i);
    			}
    			p ++;
    		}
    	}
    	else	//非叠加显示
    	{
    		for(j = 0;j < 8;j ++)
    		{
    		 	for(i = 0;i < 8;i ++)
    			{
    			 	if(*p & (1 << i))
    					(*DrawPoint)(pHandle, x + j,y + i);
    				else
    					(*ClearPoint)(pHandle, x + j,y + i);
    			}
    			p ++;
    		}
    		for(j = 0;j < 8;j ++)
    		{
    		 	for(i = 0;i < FontSize - 8;i ++)
    			{
    				if(*p & (1 << i))
    					(*DrawPoint)(pHandle, x + j,y + 8 + i);
    				else
    					(*ClearPoint)(pHandle, x + j,y + 8 + i);
    			}
    			p ++;
    		}
    	}
    }
    
    
    
    /*************************************************************************************************************************
    * 函数			:	void LCD12864_GRAM_Fill(LCD12864_HANDLE *pHandle, u16 xStart, u16 yStart, u16 xEnd, u16 yEnd)
    * 功能			:	指定位置填充
    * 参数			:	pHandle:句柄;范围
    * 返回			:	无
    * 依赖			:	底层函数
    * 作者			:	cp1300@139.com
    * 时间			:	20110920
    * 最后修改时间 	: 	2018-08-12
    * 说明			: 	无
    *************************************************************************************************************************/
    void LCD12864_GRAM_Fill(LCD12864_HANDLE *pHandle, u16 xStart, u16 yStart, u16 xEnd, u16 yEnd)
    {
     	u16 i,j;
    
    	for(i = xStart;i < xEnd; i ++)
    	{
    		for(j = yStart;j < yEnd;j ++)
    		{
    			LCD12864_GRAM_DrawPoint(pHandle,i,j);
    		}
    	}
    }
    
    
    /*************************************************************************************************************************
    * 函数			:	void LCD12864_GRAM_Clear(LCD12864_HANDLE *pHandle, u16 xStart, u16 yStart, u16 xEnd, u16 yEnd)
    * 功能			:	清除指定位置
    * 参数			:	pHandle:句柄;范围
    * 返回			:	无
    * 依赖			:	底层函数
    * 作者			:	cp1300@139.com
    * 时间			:	20110920
    * 最后修改时间 	: 	2018-08-12
    * 说明			: 	无
    *************************************************************************************************************************/
    void LCD12864_GRAM_Clear(LCD12864_HANDLE *pHandle, u16 xStart, u16 yStart, u16 xEnd, u16 yEnd)
    {
     	u16 i,j;
    
    	for(i = xStart;i < xEnd; i ++)
    	{
    		for(j = yStart;j < yEnd;j ++)
    		{
    			LCD12864_GRAM_ClearPoint(pHandle,i,j);
    		}
    	}
    }
    
    
    
    /*************************************************************************************************************************
    * 函数			:	void LCD12864_GRAM_DrawLine(LCD12864_HANDLE *pHandle,u16 x1, u16 y1, u16 x2, u16 y2)
    * 功能			:	画线函数
    * 参数			:	pHandle:句柄;起点终点坐标
    * 返回			:	无
    * 依赖			:	画点函数
    * 作者			:	cp1300@139.com
    * 时间			:	20110920
    * 最后修改时间 	: 	2018-08-12
    * 说明			: 	无
    *************************************************************************************************************************/
    void LCD12864_GRAM_DrawLine(LCD12864_HANDLE *pHandle,u16 x1, u16 y1, u16 x2, u16 y2)
    {
    	u16 t;
    	int xerr=0,yerr=0,delta_x,delta_y,distance;
    	int incx,incy,uRow,uCol;
    
    	//TFT_LCD_SetRamAddr(0,239,0,319);//设置显示窗口
    
    	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++ )//画线输出
    	{
    		LCD12864_GRAM_DrawPoint(pHandle, uRow,uCol);//画点
    		xerr+=delta_x ;
    		yerr+=delta_y ;
    		if(xerr>distance)
    		{
    			xerr-=distance;
    			uRow+=incx;
    		}
    		if(yerr>distance)
    		{
    			yerr-=distance;
    			uCol+=incy;
    		}
    	}
    }
    
    
    
    /*************************************************************************************************************************
    * 函数			:	void LCD12864_GRAM_DrawRectangle(LCD12864_HANDLE *pHandle,u16 x1, u16 y1, u16 x2, u16 y2)
    * 功能			:	在指定位置画一个矩形
    * 参数			:	pHandle:句柄;多边形的两个坐标
    * 返回			:	无
    * 依赖			:	画线函数
    * 作者			:	cp1300@139.com
    * 时间			:	20110920
    * 最后修改时间 	: 	2018-08-12
    * 说明			: 	无
    *************************************************************************************************************************/
    void LCD12864_GRAM_DrawRectangle(LCD12864_HANDLE *pHandle,u16 x1, u16 y1, u16 x2, u16 y2)
    {
    	LCD12864_GRAM_DrawLine(pHandle, x1,y1,x2,y1);
    	LCD12864_GRAM_DrawLine(pHandle, x1,y1,x1,y2);
    	LCD12864_GRAM_DrawLine(pHandle, x1,y2,x2,y2);
    	LCD12864_GRAM_DrawLine(pHandle, x2,y1,x2,y2);
    }
    
    
    
    
    
    /*************************************************************************************************************************
    * 函数			:	void LCD12864_GRAM_ShowString(LCD12864_HANDLE *pHandle, u16 x,u16 y,const char *pStr,LCD12864_FONT_MODE Font_MODE)
    * 功能			:	在显存指定位置显示字符串
    * 参数			:	pHandle:句柄;x,y:显示开始坐标,pStr:字符串缓冲区;FontMode:显示模式,
    * 返回			:	无
    * 依赖			:	画点函数
    * 作者			:	cp1300@139.com
    * 时间			:	2014-08-20
    * 最后修改时间 	: 	2018-08-12
    * 说明			:
    *************************************************************************************************************************/
    void LCD12864_GRAM_ShowString(LCD12864_HANDLE *pHandle, u16 x,u16 y,const char *pStr,LCD12864_FONT_MODE Font_MODE)
    {
    	u8 Font_Size = Font_MODE & 0x0f;
    #if	CHINESE_ENABLE
    	u8 buff[32];
    #endif
    
    	while(*pStr != 0)
    	{
    #if	CHINESE_ENABLE
    		if(*pStr > 0x80)//汉字
    		{
    			FONT_GetFontLattice(buff, (u8*)pStr, ST16X16);	//获取汉字点阵
    			LCD12864_GRAM_ShowChina(x,y,buff,Font_MODE);	//显示汉字
    			pStr += 2;
    			if(x > 127 - 16)  //自动换行
    			{
    			 	x = 0;
    				y += 16;
    			}
    			else
    			{
    				x += 16;
    			}
    		}
    		else //ASCII
    #endif
    		{
    			LCD12864_GRAM_ShowChar(pHandle, x,y,*pStr,Font_MODE);
    			pStr++;
    			if(x > 127 - 8)  //自动换行
    			{
    			 	x = 0;
    				y += Font_Size;
    			}
    			else
    			{
    				x += 8;
    			}
    		}
    
    	}
    }
    
    

    //LCD12864.h

    /*
     * LCD12864_Virtual.h
     *
     *  Created on: 2018年8月12日
     *      Author: cfan
     */
    
    #ifndef PROGRAM_LCD_LCD12864_H_
    #define PROGRAM_LCD_LCD12864_H_
    #include "typedef.h"
    
    
    typedef enum
    {
    	FONT16_DEFAULT 	= (0x80+16),	//16号,叠加显示
    	FONT12_DEFAULT 	= (0x80+12),	//12号,叠加显示
    	FONT16_COVER 	= 	(16),		//16号,覆盖显示
    	FONT12_COVER 	= 	(12),		//12号,覆盖显示
    	FONT16_REVERSE 	= (0x40+16),	//16号,反显显示
    	FONT12_REVERSE 	= (0x40+12),	//12号,反显显示
    }LCD12864_FONT_MODE;
    
    
    //LCD12864 句柄
    typedef struct
    {
    	u8 LCD_BUFF[8][128];//显存
    	//void (*UpdateGRAM)(u8 LCD_BUFF[8][128], u8 x1,u8 y1,u8 x2,u8 y2);
    }LCD12864_HANDLE;
    
    
    void LCD12864_GRAM_Init(LCD12864_HANDLE *pHandle);//LCD12864显存模式初始化
    void LCD12864_GRAM_ShowString(LCD12864_HANDLE *pHandle, u16 x,u16 y,const char *pStr,LCD12864_FONT_MODE Font_MODE);	//在显存指定位置显示字符串
    
    #endif /* PROGRAM_LCD_LCD12864_H_ */
    

    //测试线程

    
    //测试线程
    void *func(void *arg)
    {
    
    	float ftemp = 0;
    	char buff[64];
    
    	SPI_Init(&SPI_Handle, "/dev/spidev0.0");			//打开SPI驱动
    	setSPIReadBitsPerWord(SPI_Handle.fd, 8);			//8bit模式
    	setSPIBitOrder(SPI_Handle.fd, MSBFIRST);			//高位在前
    	//setSPIMaxSpeed(SPI_Handle.fd, 500);				//设备SPI传输速度 10K
    	setSPIDataMode(SPI_Handle.fd, SPI_MODE0);			//模式0
    
    	if(initPinGPIO(BOARD_NANOPI_M1) < 0)				//初始化开发板型号
    	{
    		printf("error:gpio init error!\r\n");
    	}
    	exportGPIOPin(LCD_RST_PIN_INDEX);					//导出IO文件 RST接口
    	setGPIODirection(LCD_RST_PIN_INDEX,  GPIO_OUT);		//输出
    
    	exportGPIOPin(LCD_RS_PIN_INDEX);					//导出IO文件 RS接口
    	setGPIODirection(LCD_RS_PIN_INDEX,  GPIO_OUT);		//输出
    
    	//初始化JLX12864G硬件
    	JLX12864G_Init(&mJLX12864G_Handle, JLX12864G_WriteData, JLX12864G_SetRS, JLX12864G_SetRST, JLX12864G_DelayMS, 40);
    	LCD12864_GRAM_Init(&g_LCD12864_Handle);				//初始化虚拟LCD12864屏幕
    
    	while(1)
    	{
    		ftemp = GetCPU_Temp();							//获取CPU温度
    		sprintf(buff,"CPU TEMP:%.02f",ftemp);			//格式化字符串
    		LCD12864_GRAM_ShowString(&g_LCD12864_Handle, 0 ,0, buff, FONT16_COVER);			//覆盖显示-将字符串在虚拟LCD12864中显示
    		JLX12864G_GRAM_Up(&mJLX12864G_Handle, g_LCD12864_Handle.LCD_BUFF, 0,0,128,64);	//更新显存到JLX12864G
    		sleep(3);
    	}
    }

    示例代码:https://download.csdn.net/download/cp1300/10611732

    展开全文
  • 上一篇文章,描述的是如何驱动spi屏幕,嵌入式Linux驱动笔记(二十四)------framebuffer之使用spi-tft屏幕 但是是使用的是在内核里开一个线程来不停的绘制图形,CPU占用率非常高,效率低。 有种较为方便的办法,...
  • Linux内核调用SPI驱动_实现OLED显示功能 0. 导语 进入Linux的世界,发现真的是无比的有趣,也发现搞Linux驱动从底层嵌入式搞起真的是很有益处。我们在单片机、DSP这些无操作系统的裸机中学习了这些最基本的驱动,...
  • linux中的spi驱动开发中,在写好某个spi驱动的情况下,我们不得不根据自己的spi设备,选择适合自己的spi的参数值,比如要不要片选,四种通信方式的哪一种,几线通信等。这时候我们就需要使用带参数调试方式了。 ...
  • 属于四线SPI通信方式,但是它的MISO端口没有被接出来,所以spi的读取是不用实现的,但是又多出来一条线(RS),这个端口的作用是为OLED进行写命令或者写数据的控制。 时序如下所示: spi控制时序如下: ...
  • 简介:  本文主要讲解使用2440裸机的GPIO模拟SPI来控制OLED显示我们想显示的字符串。而我使用了两种方法来实现SPI...所用OLED 屏幕:韦东山老师淘宝所用屏幕以及另一种淘宝所用屏幕。 所用OLED 驱动芯片:SSD13...
  • 正点原子Linux开发板 spi内核驱动 0.96寸ips屏教程首先选择模块设备树配置spi驱动程序(用的spi_driver)app 最近做下底层SPI驱动来驱动IPS屏,本来想实现这种效果: 后面想着暂时不做frambuffer,用用spi巩固一下吧...
  • 代码
  • 前段时间看到B站大佬稚晖君发布的B站小电视,心中无限崇拜,刚好手中有一块NanoPi-NEO开发板和一块ST7735S的128X128TFT屏幕,近期又学了朱有鹏老师的嵌入式核心课程,刚好拿这个来练练手! 项目涉及到kernel配置、...
  • 简介:  本文主要讲解使用2440中的SPI控制器来控制SPI传输命令和数据,以实现对OLED和FLASH的控制。同时本文会分为两部分,...所用OLED 屏幕:韦东山老师淘宝所用屏幕 所用OLED 驱动芯片:SSD1306 FLASH:W25Q1...
  • SPI写寄存器操作:  staticvoid mcp251x_write_reg(struct spi_device *spi, uint8_t reg, uint8_t val) { struct mcp251x *chip = dev_get_drvdata(&amp;spi-&gt;dev); int ret; down(&...
  • MT7688 SPI 控制器的诡异之处:  在SPI接口的设备驱动中进行发数的过程中利用示波器查看发送数据的SDA和SCL发现,数据总是在发送的过程中右移了一位,比如发送的数据是0xaa 二进制为10101010,然而在示波器中读出的...
  • 这里写自定义目录标题欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题,有助于目录的生成基于博通芯片gpio模拟spi挂载tf卡流程如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的...
  • linux/types.h> #include <linux/kernel.h> #include <linux/delay.h> #include <linux/ide.h> #include <linux/init.h> #include <linux/module.h> #include <linux/...
1 2 3 4 5 ... 20
收藏数 2,511
精华内容 1,004
热门标签
关键字:

spi屏幕linux