分享

65 linux spi设备驱动之spi LCD屏驱动

 昵称3554661 2020-04-03

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");

效果图:
这里写图片描述

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多