GPIO模拟SPI时序通信实例程序(屏幕RGB模式)

GPIO模拟spi通信总结

      • spi四种工作模式
      • 本文模拟spi时序代码
      • 可参考项
      • 本文作为总结和分享

spi四种工作模式

工作方式1:

当CPHA=0、CPOL=0时SPI总线工作在方式1。MISO引脚上的数据在第一个SPSCK沿跳变之前已经上线了,而为了保证正确传输,MOSI引脚的MSB位必须与SPSCK的第一个边沿同步,在SPI传输过程中,首先将数据上线,然后在同步时钟信号的上升沿时,SPI的接收方捕捉位信号,在时钟信号的一个周期结束时(下降沿),下一位数据信号上线,再重复上述过程,直到一个字节的8位信号传输结束。

工作方式2:

当CPHA=0、CPOL=1时SPI总线工作在方式2。与前者唯一不同之处只是在同步时钟信号的下降沿时捕捉位信号,上升沿时下一位数据上线。

工作方式3:

当CPHA=1、CPOL=0时SPI总线工作在方式3。MISO引脚和MOSI引脚上的数据的MSB位必须与SPSCK的第一个边沿同步,在SPI传输过程中,在同步时钟信号周期开始时(上升沿)数据上线,然后在同步时钟信号的下降沿时,SPI的接收方捕捉位信号,在时钟信号的一个周期结束时(上升沿),下一位数据信号上线,再重复上述过程,直到一个字节的8位信号传输结束。

工作方式4:

当CPHA=1、CPOL=1时SPI总线工作在方式4。与前者唯一不同之处只是在同步时钟信号的上升沿时捕捉位信号,下降沿时下一位数据上线。

本文模拟spi时序代码

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define GPIO_PB_8 24
#define GPIO_PH_4 116
#define GPIO_PH_5 117
#define GPIO_PE_11 75
#define GPIO_PE_12 76

#define SCLK GPIO_PH_4
#define MOSI GPIO_PH_5
#define CS GPIO_PE_11
#define Reset GPIO_PE_12
#define LCDPWM GPIO_PB_8

#define OUTP 1 //表示GPIO接口方向为输出
#define INP 0  //表示GPIO接口方向为输入

#define set_gpio_value gpio_set_value
#define get_gpio_value gpio_get_value

//默认为1则一直关比背光灯,默认为0一直打开背光灯
#define LCDdefault 0

//数据循环发送次数
#define sendnum 60000

//先释放一下几个gpio口为了方便以后的占用
static void spi_free_gpio(void)
{
    gpio_free(MOSI);
    gpio_free(SCLK);
    gpio_free(CS);
    gpio_free(Reset);
    gpio_free(LCDPWM);
}

//申请GPIO口资源
static int spi_request_gpio(void)
{
    if (gpio_request(MOSI, "spi_mosi") < 0)
    {
        printk("fail to request mosi\n");
        return -1;
    }
    if (gpio_request(SCLK, "spi_sclk\n") < 0)
    {
        printk("fail to request sclk\n");
        return -1;
    }
    if (gpio_request(CS, "spi_cs") < 0)
    {
        printk("fail to request cs\n");
        return -1;
    }
    if (gpio_request(Reset, "spi_reset") < 0)
    {
        printk("fail to request reset\n");
        return -1;
    }
    if (gpio_request(LCDPWM, "spi_lcdpwm") < 0)
    {
        printk("fail to request lcdpwm\n");
        return -1;
    }
    return 0;
}

/* SPI端口初始化 用于每次传递参数 */
static void spi_init(void)
{
    // //spi模拟通信初始化
    // gpio_direction_output(CS, 1);
    // gpio_direction_output(SCLK, 1);
    // gpio_direction_output(MOSI, 0);
    gpio_set_value(SCLK, 0);
    gpio_set_value(MOSI, 0);
}

/*  
  从设备使能  
  enable:为1时,使能信号有效,SS低电平  
  为0时,使能信号无效,SS高电平  
  */
//因为不确定cs为高低才是使能,所以先保持原样
void ss_enable(int enable)
{
    if (enable)
        set_gpio_value(CS, 0); //SS低电平,从设备使能有效
    else
        set_gpio_value(CS, 1); //SS高电平,从设备使能无效
}


/* SPI字节写 */
void spi_write_cmd(unsigned char b)
{
    //变量初始化
    int i;

    /* SPI端口初始化 */
    spi_init();
    // udelay(5);

    ss_enable(1); //从设备使能有效,通信开始
    // udelay(5);

    for (i = 8; i >= 0; i--)
    {
        if (i == 8)
        {
            set_gpio_value(SCLK, 0);
            // udelay(5);               //延时
            set_gpio_value(MOSI, 0); //如果是写命令则首位先写0
            // udelay(5);               //延时
            set_gpio_value(SCLK, 1); // CPHA=1,在时钟的第一个跳变沿采样
            // udelay(5);
        }
        else
        {
            set_gpio_value(SCLK, 0);
            // udelay(5);                        //延时
            set_gpio_value(MOSI, b & 1 << i); //从高位7到低位0进行串行写入
            // udelay(5);                        //延时
            set_gpio_value(SCLK, 1);          // CPHA=1,在时钟的第一个跳变沿采样
            // udelay(5);                        //延时
        }                                     //延时
    }

    set_gpio_value(SCLK, 0);
    // udelay(5);

    //结束单次通信
    ss_enable(0);
}

void spi_write_data(unsigned char b)
{
    //变量初始化
    int i;

    /* SPI端口初始化 */
    spi_init();
    // udelay(5);

    ss_enable(1); //从设备使能有效,通信开始
    // udelay(5);    //延时

    for (i = 8; i >= 0; i--)
    {
        if (i == 8)
        {
            set_gpio_value(SCLK, 0);
            // udelay(5);               //延时
            set_gpio_value(MOSI, 1); //如果是写数据则首位先写1
            // udelay(5);               //延时
            set_gpio_value(SCLK, 1); // CPHA=1,在时钟的第一个跳变沿采样
            // udelay(5);
        }
        else
        {
            set_gpio_value(SCLK, 0);
            // udelay(5);                        //延时
            set_gpio_value(MOSI, b & 1 << i); //从高位7到低位0进行串行写入
            // udelay(5);                        //延时
            set_gpio_value(SCLK, 1);          // CPHA=1,在时钟的第一个跳变沿采样
            // udelay(5);                        //延时
        }
    }

    set_gpio_value(SCLK, 0);
    // udelay(5);

    //结束单次通信
    ss_enable(0);
}


//屏幕初始化函数
static void spi_lcd_init(void)
{
    //spi模拟通信初始化
    gpio_direction_output(CS, 1);
    gpio_direction_output(SCLK, 1);
    gpio_direction_output(MOSI, 0);

    //屏幕初始化
    gpio_direction_output(LCDPWM, 1);
    gpio_direction_output(Reset, 1);
    gpio_set_value(LCDPWM, LCDdefault);
    gpio_set_value(Reset, 1);
    mdelay(10);
    gpio_set_value(Reset, 0);
    mdelay(100);
    gpio_set_value(Reset, 1);
    mdelay(500);
    spi_write_cmd(0x11);
    mdelay(120);
 //--------------------------------ST7789S Frame rate setting----------------------------------// 
spi_write_cmd(0xb2); 
spi_write_data(0x0c); 
spi_write_data(0x0c); 
spi_write_data(0x00); 
spi_write_data(0x33); 
spi_write_data(0x33); 
 
spi_write_cmd(0xb7); 
spi_write_data(0x35); 
 
spi_write_cmd(0x3A); 
spi_write_data(0x55); 
//---------------------------------ST7789S Power setting--------------------------------------// 
spi_write_cmd(0xbb); 
spi_write_data(0x30);//vcom 
 
spi_write_cmd(0xc3); 
spi_write_data(0x1C); 
 
spi_write_cmd(0xc4); 
spi_write_data(0x18); 
 
spi_write_cmd(0xc6); 
spi_write_data(0x0f); 
 
spi_write_cmd(0xd0); 
spi_write_data(0xa4); 
spi_write_data(0xa2); 
//--------------------------------ST7789S gamma setting---------------------------------------// 
spi_write_cmd(0xe0); 
spi_write_data(0xf0); 
spi_write_data(0x00); 
spi_write_data(0x0a); 
spi_write_data(0x10); 
spi_write_data(0x12); 
spi_write_data(0x1b); 
spi_write_data(0x39); 
spi_write_data(0x44); 
spi_write_data(0x47); 
spi_write_data(0x28); 
spi_write_data(0x12); 
spi_write_data(0x10); 
spi_write_data(0x16); 
spi_write_data(0x1b); 
 
spi_write_cmd(0xe1); 
spi_write_data(0xf0); 
spi_write_data(0x00); 
spi_write_data(0x0a); 
spi_write_data(0x10); 
spi_write_data(0x11); 
spi_write_data(0x1a); 
spi_write_data(0x3b); 
spi_write_data(0x34); 
spi_write_data(0x4e); 
spi_write_data(0x3a); 
spi_write_data(0x17); 
spi_write_data(0x16); 
spi_write_data(0x21); 
spi_write_data(0x22); 
 
//*********SET RGB Interfae*************** 
spi_write_cmd(0xB0); 
spi_write_data(0x11); //set RGB interface and DE mode. 
spi_write_data(0x00); 
spi_write_data(0x00); 
 
spi_write_cmd(0xB1); 
spi_write_data(0x40); // set DE mode ; SET Hs,Vs,DE,DOTCLK signal polarity 
spi_write_data(0x00); 
spi_write_data(0x00); 
 
spi_write_cmd(0x3a); 
spi_write_data(0x55); //18 RGB ,55-16BIT RGB 
 
//************************ 
spi_write_cmd(0x11); 
mdelay(120);      //Delay 120ms 
 
spi_write_cmd(0x29); //display on 
spi_write_cmd(0x2c); 
    printk("finished invitialized.....\n");
}

//设置写入色块的坐标
void addset(unsigned int x,unsigned int y)
{
        spi_write_cmd(0x2a); //发出x坐标
        spi_write_data(x>>8);
        spi_write_data(x&0xff);

        spi_write_cmd(0x2b); //发出y坐标
        spi_write_data(y>>8);
        spi_write_data(y&0xff);

        spi_write_cmd(0x2c);
}



//写入色块的函数
void setcolor(void)
{   
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(); //初始化屏

addset(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; 

            spi_write_data(color >> 8);
            spi_write_data(color & 0xff);
        }
    }


printk("set color finished! ...\n");

}


//入口主函数
static int __init ya15c_spi_init(void)
{
    //spi端口先释放,防止之前有人使用
    spi_free_gpio();

    //占用函数要用的几个IO口
    if (spi_request_gpio())
    {
        printk("ya15c spi initializer failure...\n");
        return -1;
    }

    //发送命令次数
    int num = sendnum;
    unsigned char datawhite = 0xff;
    unsigned char datablack = 0x00; 

    //屏幕初始化
    printk("start to set color! ...\n");
    // setcolor();
    spi_lcd_init();

    //开始循环发送信息
    // printk("start send message... \n");
    // while (num >= 0)
    // {
    //     spi_write_data(datawhite);
    //     spi_write_data(datawhite);
    //     spi_write_data(datawhite);
    //     // udelay(20);
    //     num--;
    // }
    // num = 60000;
    // while (num >= 0)
    // {
    //     spi_write_data(datablack);
    //     spi_write_data(datablack);
    //     spi_write_data(datablack);
    //     // udelay(20);
    //     num--;
    // }


    return 0;
}

static void __exit ya15c_spi_exit(void)
{
    printk("ya15c exit!\n");
}

module_init(ya15c_spi_init);
module_exit(ya15c_spi_exit);
MODULE_LICENSE("GPL");

可参考项

  • 本文的时序是先拉低然后放数据,上升自动捕获,这个主要还是看对应的spi通信的协议,或者可以自己设置对应的协议,本文是因为已经定了,所以就只能用这种通信方式。

  • 模拟时序的方式就是根据拉高拉低几个io口,具体要用哪几个io口要看电路图

  • spi_request_gpio之前先free的原因是,已经确认这几个io口就是要给这个驱动使用,所以就可以放心free,不然可能会因为别的程序已经占用而没有办法申请资源

  • spi_write_cmd和spi_write_data的区别就是一共发送九个bit位,但是第一位是0还是1,这个就要看接受spi数据的手册要求了,不过一般都是一样的。

  • spi_lcd_init这个是LCD屏幕初始化的函数,这个因为不同的屏幕内容可能会有差异,可以根据自己的屏幕查找(上网搜索)相应的函数数据以及命令内容

  • setcolor(void) 这个函数包含LCD初始化函数,是用于spi模拟的初始化之后写入色块的,但是由于这样模拟spi写入太过于慢,所以就换了RGB通信也就是现在的LCD初始化参数

  • 原本的spi通信的屏幕参数可以参考这个网页:

      https://blog.csdn.net/jklinux/article/details/74411470?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522159418532019724811861656%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=159418532019724811861656&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~first_rank_v1~rank_blog_v1-1-74411470.pc_v1_rank_blog_v1&utm_term=spi
    
  • 循环发送信息,发送的就是纯白和纯黑的颜色,只适用于测试

本文作为总结和分享

如果有在做类似项目的人可以作为参考使用,如果有什么问题,可以评论一起交流探讨,一起进步

你可能感兴趣的:(MYD)