rk3288 点亮SPI接口的OLED屏幕

对于rk3288,内核已经包含了spi_master的驱动,只需要在设备树上配置即可,在使用的时候将状态status改为ok即可。

    spi0: spi@ff110000 {
            compatible = "rockchip,rockchip-spi";
            reg = <0xff110000 0x1000>;
            interrupts = ;
            #address-cells = <1>;
            #size-cells = <0>;
            pinctrl-names = "default";
            pinctrl-0 = <&spi0_txd &spi0_rxd &spi0_clk &spi0_cs0 &spi0_cs1>;
            rockchip,spi-src-clk = <0>;
            num-cs = <2>;
            clocks =<&clk_spi0>, <&clk_gates6 4>;
            clock-names = "spi","pclk_spi0";
            dmas = <&pdma1 11>, <&pdma1 12>;
            #dma-cells = <2>;
            dma-names = "tx", "rx";
            status = "disabled";
    };

有了spi_master,还需要spi_driver和spi_board_info,spi_board_info是对spi设备硬件的资源描述,

&spi0 {
        status = "okay";
        max-freq = <48000000>;

    spidev@00 {
        compatible = "linux,spidev";
        reg = <0x00>;
        spi-max-frequency = <48000000>;
    };

    spi_oled@01 {
        compatible = "rk3288,oled";
        reg = <0x01>;
        spi-max-frequency = <10000000>;
        cd-gpio =  <&gpio7 GPIO_A3 GPIO_ACTIVE_HIGH>;
        //spi-cpha = <0>;
        //spi-cpol = <0>;

     };

rk3288 点亮SPI接口的OLED屏幕_第1张图片
在spi控制器下面定义一个spi设备:spi_oled。我手上的这个oled屏幕的spi需要3根线,SPICLK,SPIMOSI,OLED_DC,OLED_CSn。对于rk3288已经把这些接口定义出来了,在spi控制器中通过pinctrl来进行引用既可以。对于spi的片选信号,rk3288静态的定义个两个引脚cs0和cs1,这里使用cs1。对于设备地址0使用cs0,设备地址1使用cs1。

master->dev.parent = dws->parent_dev;
master->dev.of_node = dws->parent_dev->of_node;	
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LOOP;
master->bus_num = dws->bus_num;				//挂载在那一条总线上面
master->num_chipselect = dws->num_cs;		//最大支持的片选数量
master->cleanup = dw_spi_cleanup;
master->setup = dw_spi_setup;				//设置频率,模式等操作
master->transfer_one_message = dw_spi_transfer_one_message;	//	用于发起spi传输
master->prepare_transfer_hardware = dw_spi_prepare_transfer;
master->unprepare_transfer_hardware = dw_spi_unprepare_transfer;
tasklet_init(&dws->pump_transfers, pump_transfers,	(unsigned long)dws); //初始化任务队列,在transfer_one_message中被调用
ret = spi_register_master(master);	//注册spi_master

内核中会去设备spi_master,其中关键的由master->transfer_hardware负责发起spi传输,master->dw_spi_setup用于初始化spi的配置,最后通过spi_register_master注册进内核。

然后对于spi设备的初始化,它的流程如下
spi_register_master
	of_spi_register_master(master);
	of_register_spi_devices(master); (Register child devices onto the SPI bus)
		for_each_available_child_of_node(master->dev.of_node, nc) {
    		spi = spi_alloc_device(master); //分配一个spi_device
    		//设置spi_device
    		rc = spi_add_device(spi);	//向内核添加spi设备
		}

在设备树中添加了spi设备之后,还需要在内核中添加驱动文件,驱动程序的compatible需要和spi设备的compatible匹配才会调用probe函数。在probe函数中去把oled屏注册为一个字符设备:

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

//#include 
//#include 

#include 
//#include 

#include 

#include 

/* 构造注册 spi_driver */
#define GPIO_HIGH 1
#define GPIO_LOW 0

static int major;
static struct class *class;

//static int spi_oled_dc_pin;
static struct spi_device *spi_oled_dev;
static unsigned char *ker_buf;

enum of_gpio_flags cd_flags;

struct platform_oled_data {
	int cd_gpio;
	unsigned long cd_gpio_flags;
};
struct platform_oled_data oledData;

static void OLED_Set_DC(char val)
{
    gpio_set_value(oledData.cd_gpio,val);
}

static void OLEDWriteCmd(unsigned char cmd)
{
    OLED_Set_DC(0); /* command */
    spi_write(spi_oled_dev, &cmd, 1);
    OLED_Set_DC(1); /*  */
}

static void OLEDWriteDat(unsigned char dat)
{
    OLED_Set_DC(1); /* data */
    spi_write(spi_oled_dev, &dat, 1);
    OLED_Set_DC(1); /*  */
}

static void OLEDSetPageAddrMode(void)
{
    OLEDWriteCmd(0x20);
    OLEDWriteCmd(0x02);
}

static void OLEDSetPos(int page, int col)
{
    OLEDWriteCmd(0xB0 + page); /* page address */

    OLEDWriteCmd(col & 0xf);   /* Lower Column Start Address */
    OLEDWriteCmd(0x10 + (col >> 4));   /* Lower Higher Start Address */
}


static void OLEDClear(void)
{
    int page, i;
    for (page = 0; page < 8; page ++)
    {
        OLEDSetPos(page, 0);
        for (i = 0; i < 128; i++)
            OLEDWriteDat(0);
    }
}

void OLEDClearPage(int page)
{
    int i;
    OLEDSetPos(page, 0);
    for (i = 0; i < 128; i++)
        OLEDWriteDat(0);    
}

void OLEDInit(void)
{
    /* 向OLED发命令以初始化 */
    OLEDWriteCmd(0xAE); /*display off*/ 
    OLEDWriteCmd(0x00); /*set lower column address*/ 
    OLEDWriteCmd(0x10); /*set higher column address*/ 
    OLEDWriteCmd(0x40); /*set display start line*/ 
    OLEDWriteCmd(0xB0); /*set page address*/ 
    OLEDWriteCmd(0x81); /*contract control*/ 
    OLEDWriteCmd(0x66); /*128*/ 
    OLEDWriteCmd(0xA1); /*set segment remap*/ 
    OLEDWriteCmd(0xA6); /*normal / reverse*/ 
    OLEDWriteCmd(0xA8); /*multiplex ratio*/ 
    OLEDWriteCmd(0x3F); /*duty = 1/64*/ 
    OLEDWriteCmd(0xC8); /*Com scan direction*/ 
    OLEDWriteCmd(0xD3); /*set display offset*/ 
    OLEDWriteCmd(0x00); 
    OLEDWriteCmd(0xD5); /*set osc division*/ 
    OLEDWriteCmd(0x80); 
    OLEDWriteCmd(0xD9); /*set pre-charge period*/ 
    OLEDWriteCmd(0x1f); 
    OLEDWriteCmd(0xDA); /*set COM pins*/ 
    OLEDWriteCmd(0x12); 
    OLEDWriteCmd(0xdb); /*set vcomh*/ 
    OLEDWriteCmd(0x30); 
    OLEDWriteCmd(0x8d); /*set charge pump enable*/ 
    OLEDWriteCmd(0x14); 

    OLEDSetPageAddrMode();

    OLEDClear();
    
    OLEDWriteCmd(0xAF); /*display ON*/    
}


#define OLED_CMD_INIT       0x100001
#define OLED_CMD_CLEAR_ALL  0x100002
#define OLED_CMD_CLEAR_PAGE 0x100003
#define OLED_CMD_SET_POS    0x100004

static long oled_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
    int page;
    int col;
    
    switch (cmd)
    {
        case OLED_CMD_INIT:
        {
            OLEDInit();
            break;
        }
        case OLED_CMD_CLEAR_ALL:
        {
            OLEDClear();
            break;
        }
        case OLED_CMD_CLEAR_PAGE:
        {
            page = arg;
            OLEDClearPage(page);
            break;
        }
        case OLED_CMD_SET_POS:
        {
            page = arg & 0xff;
            col  = (arg >> 8) & 0xff;
            OLEDSetPos(page, col);
            break;
        }
    }
    return 0;
}

static ssize_t oled_write(struct file *file,
				      const char __user *buf,
				      size_t count, loff_t *ppos)
{
    int ret;
    
    if (count > 4096)
        return -EINVAL;
    ret = copy_from_user(ker_buf, buf, count);
    OLED_Set_DC(1); /* data */
    spi_write(spi_oled_dev, ker_buf, count);
    return 0;
}

static struct file_operations oled_ops = {
	.owner            = THIS_MODULE,
	.unlocked_ioctl   = oled_ioctl,
	.write            = oled_write,
};

static int  spi_oled_probe(struct spi_device *spi)
{
	struct device_node *np = spi->dev.of_node;
	int ret;
	spi_oled_dev = spi;
	printk("%s, %s, %d\n", __FILE__, __FUNCTION__, __LINE__);
	ker_buf = kmalloc(4096, GFP_KERNEL);
	
	/* 注册一个 file_operations */
	major = register_chrdev(0, "oled", &oled_ops);
	
	class = class_create(THIS_MODULE, "oled");

	/* 为了让mdev根据这些信息来创建设备节点 */
	device_create(class, NULL, MKDEV(major, 0), NULL, "oled"); /* /dev/oled */

	oledData.cd_gpio= of_get_named_gpio_flags(np, "cd-gpio", 0, &cd_flags);
	if (gpio_is_valid(oledData.cd_gpio)) {
		ret = devm_gpio_request_one(&spi->dev, oledData.cd_gpio, (cd_flags & OF_GPIO_ACTIVE_LOW) ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW, "oled cmd/data pin");
		if (ret != 0) {
			dev_err(&spi->dev, "oled cd gpio_request error\n");
			return -EIO;
		}
		gpio_direction_output(oledData.cd_gpio, 0);
		gpio_set_value(oledData.cd_gpio,GPIO_HIGH);
		msleep(20);
	} else {
		dev_info(&spi->dev, "oled cd pin invalid\n");
	}

    return 0;
}

static int  spi_oled_remove(struct spi_device *spi)
{

	device_destroy(class, MKDEV(major, 0));
	class_destroy(class);
	unregister_chrdev(major, "oled");
    kfree(ker_buf);
	return 0;
}

static const struct of_device_id spidev_dt_ids[] = {
    { .compatible = "rk3288,oled" },
    {},
};

static struct spi_driver spi_oled_drv = {
	.driver = {
		.name	= "oled",
		.owner	= THIS_MODULE,
		.of_match_table = of_match_ptr(spidev_dt_ids),		
	},
	.probe		= spi_oled_probe,
	.remove		= spi_oled_remove,
};

static int spi_oled_init(void)
{
    return spi_register_driver(&spi_oled_drv);
}

static void spi_oled_exit(void)
{
    spi_unregister_driver(&spi_oled_drv);
}

你可能感兴趣的:(linux驱动)