(Linux驱动学习 -13).SPI驱动实验

目录

一.SPI驱动相关结构体与函数

1.struct spi_master 结构体

2.申请 spi_master - spi_alloc_master

3.释放 spi_master - spi_master_put

4.向内核注册 spi_master - spi_register_master

5.注销掉 spi_master

6.struct spi_driver 结构体

7.向内核注册 spi_driver - spi_register_driver

8.注销 spi_driver - spi_unregister_driver

9.struct spi_transfer 结构体

10.struct spi_message 结构体

11.初始化 spi_message

12.将 spi_transfer 添加到 spi_message 队列中

13.SPI 同步传输数据 - spi_sync

14.SPI 异步传输数据 - spi_async

二.SPI实验 - ICM20608

1.设备树

(1).流程图

(2).设备树代码

​编辑

2.驱动部分

(1).流程图

​编辑

(2).驱动部分代码

3.应用程序

三.SPI实验 - OLED

1.设备树

(1).流程图

(2).设备树代码

2.驱动部分

(1).流程图

​编辑

(2).驱动代码


一.SPI驱动相关结构体与函数

1.struct spi_master 结构体

struct spi_master 
{
    struct device dev;

    struct list_head list;
    ......
    
    s16 bus_num;

    /* chipselects will be integral to many controllers; some others
    * might use board-specific GPIOs.
    */
    u16 num_chipselect;

    /* some SPI controllers pose alignment requirements on DMAable
    * buffers; let protocol drivers know about these requirements.
    */
    u16 dma_alignment;

    /* spi_device.mode flags understood by this controller driver */
    u16 mode_bits;

    /* bitmask of supported bits_per_word for transfers */
    u32 bits_per_word_mask;
    ......
    /* limits on transfer speed */
    u32 min_speed_hz;
    u32 max_speed_hz;

    /* other constraints relevant to this driver */
    u16 flags;

    /* lock and mutex for SPI bus locking */
    spinlock_t bus_lock_spinlock;
    struct mutex bus_lock_mutex;

    /* flag indicating that the SPI bus is locked for exclusive use */
    bool bus_lock_flag;
    ......
    int (*setup)(struct spi_device *spi);

    ......
    int (*transfer)(struct spi_device *spi,
    struct spi_message *mesg);
    ......
    int (*transfer_one_message)(struct spi_master *master,
    struct spi_message *mesg);
    ......
};

2.申请 spi_master - spi_alloc_master

/**
 * @description:            申请 spi_master
 * @param - dev     :       设备,一般是 platform_device 中的 dev 变量
 * @param - size    :       私有数据大小,可以通过 spi_master_get_devdata 函数获取到这些私有数据
 * @return          :       申请到的 spi_master
 */
struct spi_master *spi_alloc_master(struct device *dev,unsigned int size)

3.释放 spi_master - spi_master_put

/**
 * @description:            释放 spi_master
 * @param - master  :       要释放的 spi_master
 * @return          :       无
 */
void spi_master_put(struct spi_master *master)

4.向内核注册 spi_master - spi_register_master

/**
 * @description:            向内核注册 spi_master
 * @param - master      :   要向内核注册的 spi_master
 * @return              :   成功则返回(0),失败则返回(-1)
 */
int spi_register_master(struct spi_master *master)

5.注销掉 spi_master

/**
 * @description:            注销 spi_master
 * @param - master  :       要注销的 spi_master
 * @return          :       无
 */
void spi_unregister_master(struct spi_master *master)

6.struct spi_driver 结构体

struct spi_driver 
{
    const struct spi_device_id *id_table;
    int (*probe)(struct spi_device *spi);
    int (*remove)(struct spi_device *spi);
    void (*shutdown)(struct spi_device *spi);
    struct device_driver driver;
};

7.向内核注册 spi_driver - spi_register_driver

/**
 * @description:            向内核注册 spi_driver
 * @param - sdrv        :   要注册的 spi_driver
 * @return              :   成功则返回(0),失败则返回(负值)
 */
int spi_register_driver(struct spi_driver *sdrv)

8.注销 spi_driver - spi_unregister_driver

/**
 * @description:            注销 spi_driver
 * @param - sdrv        :   要注销的 spi_driver
 * @return              :   无
 */
void spi_unregister_driver(struct spi_driver *sdrv)

9.struct spi_transfer 结构体

struct spi_transfer 
{
    /* 发送数据缓冲区 */
    const void *tx_buf;

    /* 接收数据缓冲区 */         
    void *rx_buf;

    /* 数据长度 */
    unsigned len;

    dma_addr_t tx_dma;
    dma_addr_t rx_dma;
    struct sg_table tx_sg;
    struct sg_table rx_sg;

    unsigned cs_change:1;
    unsigned tx_nbits:3;
    unsigned rx_nbits:3;
    #define SPI_NBITS_SINGLE 0x01 /* 1bit transfer */
    #define SPI_NBITS_DUAL 0x02 /* 2bits transfer */
    #define SPI_NBITS_QUAD 0x04 /* 4bits transfer */
    u8 bits_per_word;
    u16 delay_usecs;
    u32 speed_hz;

    struct list_head transfer_list;
};

10.struct spi_message 结构体

struct spi_message 
{
    struct list_head transfers;

    struct spi_device *spi;

    unsigned is_dma_mapped:1;
    ......
    /* completion is reported through a callback */
    void (*complete)(void *context);原子哥在线教学:www.yuanzige.com 论坛:www.openedv.com

    void *context;
    unsigned frame_length;
    unsigned actual_length;
    int status;
    
    struct list_head queue;
    void *state;
};

11.初始化 spi_message

/**
 * @description:            初始化 spi_message
 * @param - m       :       要初始化的 spi_message
 * @return          :       无
 */
void spi_message_init(struct spi_message *m)

12.将 spi_transfer 添加到 spi_message 队列中

/**
 * @description:            将 spi_transfer 添加到 spi_message 队列中
 * @param - t       :       要添加至 spi_message 队列中的 spi_transfer
 * @param - m       :       spi_transfer 要加入的 spi_message
 * @return          :       无
 */
void spi_message_add_tail(struct spi_transfer *t,struct spi_message *m)

13.SPI 同步传输数据 - spi_sync

/**
 * @description:            SPI 同步传输数据
 * @param - spi         :   要进行数据传输的 SPI
 * @param - message     :   要传输的 spi_message
 * @return              :   无       
 */
int spi_sync(struct spi_device *spi,struct spi_message *message)

14.SPI 异步传输数据 - spi_async

/**
 * @description:            SPI 异步传输数据
 * @param - spi     :       要进行数据传输的 SPI
 * @param - message :       要传输的 spi_message
 * @return          :       无
 */
int spi_async(struct spi_device *spi,struct spi_message *message)

二.SPI实验 - ICM20608

1.设备树

(1).流程图

(2).设备树代码

2.驱动部分

(1).流程图

(2).驱动部分代码

#include <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/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of_gpio.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <linux/i2c.h>
#include <linux/spi/spi.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/platform_device.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include "icm20608reg.h"

#define ICM20608_CNT	1
#define ICM20608_NAME	"icm20608"

struct icm20608_dev {
	dev_t devid;				/* 设备号 	 */
	struct cdev cdev;			/* cdev 	*/
	struct class *class;		/* 类 		*/
	struct device *device;		/* 设备 	 */
	struct device_node	*nd; 	/* 设备节点 */
	int major;					/* 主设备号 */
	void *private_data;			/* 私有数据 		*/
	signed int gyro_x_adc;		/* 陀螺仪X轴原始值 	 */
	signed int gyro_y_adc;		/* 陀螺仪Y轴原始值		*/
	signed int gyro_z_adc;		/* 陀螺仪Z轴原始值 		*/
	signed int accel_x_adc;		/* 加速度计X轴原始值 	*/
	signed int accel_y_adc;		/* 加速度计Y轴原始值	*/
	signed int accel_z_adc;		/* 加速度计Z轴原始值 	*/
	signed int temp_adc;		/* 温度原始值 			*/
};

static struct icm20608_dev icm20608dev;

/*
 * @description	: 从icm20608读取多个寄存器数据
 * @param - dev:  icm20608设备
 * @param - reg:  要读取的寄存器首地址
 * @param - val:  读取到的数据
 * @param - len:  要读取的数据长度
 * @return 		: 操作结果
 */
static int icm20608_read_regs(struct icm20608_dev *dev, u8 reg, void *buf, int len)
{

	int ret = -1;
	unsigned char txdata[1];
	unsigned char * rxdata;
	struct spi_message m;
	struct spi_transfer *t;
	struct spi_device *spi = (struct spi_device *)dev->private_data;
    
	t = kzalloc(sizeof(struct spi_transfer), GFP_KERNEL);	/* 申请内存 */
	if(!t) {
		return -ENOMEM;
	}

	rxdata = kzalloc(sizeof(char) * len, GFP_KERNEL);	/* 申请内存 */
	if(!rxdata) {
		goto out1;
	}

	/* 一共发送len+1个字节的数据,第一个字节为
	寄存器首地址,一共要读取len个字节长度的数据,*/
	txdata[0] = reg | 0x80;		/* 写数据的时候首寄存器地址bit8要置1 */			
	t->tx_buf = txdata;			/* 要发送的数据 */
    t->rx_buf = rxdata;			/* 要读取的数据 */
	t->len = len+1;				/* t->len=发送的长度+读取的长度 */
	spi_message_init(&m);		/* 初始化spi_message */
	spi_message_add_tail(t, &m);/* 将spi_transfer添加到spi_message队列 */
	ret = spi_sync(spi, &m);	/* 同步发送 */
	if(ret) {
		goto out2;
	}
	
    memcpy(buf , rxdata+1, len);  /* 只需要读取的数据 */

out2:
	kfree(rxdata);					/* 释放内存 */
out1:	
	kfree(t);						/* 释放内存 */
	
	return ret;
}

/*
 * @description	: 向icm20608多个寄存器写入数据
 * @param - dev:  icm20608设备
 * @param - reg:  要写入的寄存器首地址
 * @param - val:  要写入的数据缓冲区
 * @param - len:  要写入的数据长度
 * @return 	  :   操作结果
 */
static s32 icm20608_write_regs(struct icm20608_dev *dev, u8 reg, u8 *buf, u8 len)
{
	int ret = -1;
	unsigned char *txdata;
	struct spi_message m;
	struct spi_transfer *t;
	struct spi_device *spi = (struct spi_device *)dev->private_data;
	
	t = kzalloc(sizeof(struct spi_transfer), GFP_KERNEL);	/* 申请内存 */
	if(!t) {
		return -ENOMEM;
	}
	
	txdata = kzalloc(sizeof(char)+len, GFP_KERNEL);
	if(!txdata) {
		goto out1;
	}
	
	/* 一共发送len+1个字节的数据,第一个字节为
	寄存器首地址,len为要写入的寄存器的集合,*/
	*txdata = reg & ~0x80;	/* 写数据的时候首寄存器地址bit8要清零 */
    memcpy(txdata+1, buf, len);	/* 把len个寄存器拷贝到txdata里,等待发送 */
	t->tx_buf = txdata;			/* 要发送的数据 */
	t->len = len+1;				/* t->len=发送的长度+读取的长度 */
	spi_message_init(&m);		/* 初始化spi_message */
	spi_message_add_tail(t, &m);/* 将spi_transfer添加到spi_message队列 */
	ret = spi_sync(spi, &m);	/* 同步发送 */
    if(ret) {
        goto out2;
    }
	
out2:
	kfree(txdata);				/* 释放内存 */
out1:
	kfree(t);					/* 释放内存 */
	return ret;
}

/*
 * @description	: 读取icm20608指定寄存器值,读取一个寄存器
 * @param - dev:  icm20608设备
 * @param - reg:  要读取的寄存器
 * @return 	  :   读取到的寄存器值
 */
static unsigned char icm20608_read_onereg(struct icm20608_dev *dev, u8 reg)
{
	u8 data = 0;
	icm20608_read_regs(dev, reg, &data, 1);
	return data;
}

/*
 * @description	: 向icm20608指定寄存器写入指定的值,写一个寄存器
 * @param - dev:  icm20608设备
 * @param - reg:  要写的寄存器
 * @param - data: 要写入的值
 * @return   :    无
 */	

static void icm20608_write_onereg(struct icm20608_dev *dev, u8 reg, u8 value)
{
	u8 buf = value;
	icm20608_write_regs(dev, reg, &buf, 1);
}

/*
 * @description	: 读取ICM20608的数据,读取原始数据,包括三轴陀螺仪、
 * 				: 三轴加速度计和内部温度。
 * @param - dev	: ICM20608设备
 * @return 		: 无。
 */
void icm20608_readdata(struct icm20608_dev *dev)
{
	unsigned char data[14] = { 0 };
	icm20608_read_regs(dev, ICM20_ACCEL_XOUT_H, data, 14);

	dev->accel_x_adc = (signed short)((data[0] << 8) | data[1]); 
	dev->accel_y_adc = (signed short)((data[2] << 8) | data[3]); 
	dev->accel_z_adc = (signed short)((data[4] << 8) | data[5]); 
	dev->temp_adc    = (signed short)((data[6] << 8) | data[7]); 
	dev->gyro_x_adc  = (signed short)((data[8] << 8) | data[9]); 
	dev->gyro_y_adc  = (signed short)((data[10] << 8) | data[11]);
	dev->gyro_z_adc  = (signed short)((data[12] << 8) | data[13]);
}

/*
 * @description		: 打开设备
 * @param - inode 	: 传递给驱动的inode
 * @param - filp 	: 设备文件,file结构体有个叫做pr似有ate_data的成员变量
 * 					  一般在open的时候将private_data似有向设备结构体。
 * @return 			: 0 成功;其他 失败
 */
static int icm20608_open(struct inode *inode, struct file *filp)
{
	filp->private_data = &icm20608dev; /* 设置私有数据 */
	return 0;
}

/*
 * @description		: 从设备读取数据 
 * @param - filp 	: 要打开的设备文件(文件描述符)
 * @param - buf 	: 返回给用户空间的数据缓冲区
 * @param - cnt 	: 要读取的数据长度
 * @param - offt 	: 相对于文件首地址的偏移
 * @return 			: 读取的字节数,如果为负值,表示读取失败
 */
static ssize_t icm20608_read(struct file *filp, char __user *buf, size_t cnt, loff_t *off)
{
	signed int data[7];
	long err = 0;
	struct icm20608_dev *dev = (struct icm20608_dev *)filp->private_data;

	icm20608_readdata(dev);
	data[0] = dev->gyro_x_adc;
	data[1] = dev->gyro_y_adc;
	data[2] = dev->gyro_z_adc;
	data[3] = dev->accel_x_adc;
	data[4] = dev->accel_y_adc;
	data[5] = dev->accel_z_adc;
	data[6] = dev->temp_adc;
	err = copy_to_user(buf, data, sizeof(data));
	return 0;
}

/*
 * @description		: 关闭/释放设备
 * @param - filp 	: 要关闭的设备文件(文件描述符)
 * @return 			: 0 成功;其他 失败
 */
static int icm20608_release(struct inode *inode, struct file *filp)
{
	return 0;
}

/* icm20608操作函数 */
static const struct file_operations icm20608_ops = {
	.owner = THIS_MODULE,
	.open = icm20608_open,
	.read = icm20608_read,
	.release = icm20608_release,
};

/*
 * ICM20608内部寄存器初始化函数 
 * @param  	: 无
 * @return 	: 无
 */
void icm20608_reginit(void)
{
	u8 value = 0;
	
	icm20608_write_onereg(&icm20608dev, ICM20_PWR_MGMT_1, 0x80);
	mdelay(50);
	icm20608_write_onereg(&icm20608dev, ICM20_PWR_MGMT_1, 0x01);
	mdelay(50);

	value = icm20608_read_onereg(&icm20608dev, ICM20_WHO_AM_I);
	printk("ICM20608 ID = %#X\r\n", value);	

	icm20608_write_onereg(&icm20608dev, ICM20_SMPLRT_DIV, 0x00); 	/* 输出速率是内部采样率					*/
	icm20608_write_onereg(&icm20608dev, ICM20_GYRO_CONFIG, 0x18); 	/* 陀螺仪±2000dps量程 				*/
	icm20608_write_onereg(&icm20608dev, ICM20_ACCEL_CONFIG, 0x18); 	/* 加速度计±16G量程 					*/
	icm20608_write_onereg(&icm20608dev, ICM20_CONFIG, 0x04); 		/* 陀螺仪低通滤波BW=20Hz 				*/
	icm20608_write_onereg(&icm20608dev, ICM20_ACCEL_CONFIG2, 0x04); /* 加速度计低通滤波BW=21.2Hz 			*/
	icm20608_write_onereg(&icm20608dev, ICM20_PWR_MGMT_2, 0x00); 	/* 打开加速度计和陀螺仪所有轴 				*/
	icm20608_write_onereg(&icm20608dev, ICM20_LP_MODE_CFG, 0x00); 	/* 关闭低功耗 						*/
	icm20608_write_onereg(&icm20608dev, ICM20_FIFO_EN, 0x00);		/* 关闭FIFO						*/
}

 /*
  * @description     : spi驱动的probe函数,当驱动与
  *                    设备匹配以后此函数就会执行
  * @param - client  : i2c设备
  * @param - id      : i2c设备ID
  * 
  */	
static int icm20608_probe(struct spi_device *spi)
{
	/* 1、构建设备号 */
	if (icm20608dev.major) {
		icm20608dev.devid = MKDEV(icm20608dev.major, 0);
		register_chrdev_region(icm20608dev.devid, ICM20608_CNT, ICM20608_NAME);
	} else {
		alloc_chrdev_region(&icm20608dev.devid, 0, ICM20608_CNT, ICM20608_NAME);
		icm20608dev.major = MAJOR(icm20608dev.devid);
	}

	/* 2、注册设备 */
	cdev_init(&icm20608dev.cdev, &icm20608_ops);
	cdev_add(&icm20608dev.cdev, icm20608dev.devid, ICM20608_CNT);

	/* 3、创建类 */
	icm20608dev.class = class_create(THIS_MODULE, ICM20608_NAME);
	if (IS_ERR(icm20608dev.class)) {
		return PTR_ERR(icm20608dev.class);
	}

	/* 4、创建设备 */
	icm20608dev.device = device_create(icm20608dev.class, NULL, icm20608dev.devid, NULL, ICM20608_NAME);
	if (IS_ERR(icm20608dev.device)) {
		return PTR_ERR(icm20608dev.device);
	}

	/*初始化spi_device */
	spi->mode = SPI_MODE_0;	/*MODE0,CPOL=0,CPHA=0*/
	spi_setup(spi);
	icm20608dev.private_data = spi; /* 设置私有数据 */

	/* 初始化ICM20608内部寄存器 */
	icm20608_reginit();		
	return 0;
}

/*
 * @description     : i2c驱动的remove函数,移除i2c驱动的时候此函数会执行
 * @param - client 	: i2c设备
 * @return          : 0,成功;其他负值,失败
 */
static int icm20608_remove(struct spi_device *spi)
{
	/* 删除设备 */
	cdev_del(&icm20608dev.cdev);
	unregister_chrdev_region(icm20608dev.devid, ICM20608_CNT);

	/* 注销掉类和设备 */
	device_destroy(icm20608dev.class, icm20608dev.devid);
	class_destroy(icm20608dev.class);
	return 0;
}

/* 传统匹配方式ID列表 */
static const struct spi_device_id icm20608_id[] = {
	{"alientek,icm20608", 0},  
	{}
};

/* 设备树匹配列表 */
static const struct of_device_id icm20608_of_match[] = {
	{ .compatible = "alientek,icm20608" },
	{ /* Sentinel */ }
};

/* SPI驱动结构体 */	
static struct spi_driver icm20608_driver = {
	.probe = icm20608_probe,
	.remove = icm20608_remove,
	.driver = {
			.owner = THIS_MODULE,
		   	.name = "icm20608",
		   	.of_match_table = icm20608_of_match, 
		   },
	.id_table = icm20608_id,
};
		   
/*
 * @description	: 驱动入口函数
 * @param 		: 无
 * @return 		: 无
 */
static int __init icm20608_init(void)
{
	return spi_register_driver(&icm20608_driver);
}

/*
 * @description	: 驱动出口函数
 * @param 		: 无
 * @return 		: 无
 */
static void __exit icm20608_exit(void)
{
	spi_unregister_driver(&icm20608_driver);
}

module_init(icm20608_init);
module_exit(icm20608_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("kaneki");



3.应用程序

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>


int main(int argc, char  *argv[])
{
    int ret,fd;
    char *filename;
    signed int databuf[7];
    unsigned char data[14];
    /* icm20608的原始值 */
    signed int gyro_x_adc,gyro_y_adc,gyro_z_adc;
    signed int accel_x_adc,accel_y_adc,accel_z_adc;
    signed int temp_adc;

    /* icm20608的实际值 */ 
    float gyro_x_act,gyro_y_act,gyro_z_act;
    float accel_x_act,accel_y_act,accel_z_act;
    float temp_act;

    if(2 != argc)
    {
        printf("Usage : ./%s <dev_path>\n",argv[0]);
        return -1;
    }

    filename = argv[1];

    fd = open(filename,O_RDONLY);
    if(0 > fd)
    {
        perror("open error");
        return -1;
    }

    while(1)
    {
        ret = read(fd,databuf,sizeof(databuf));
        /* 读取成功 */
        if(0 == ret)
        {
            /* 得到原始值 */
            gyro_x_adc = databuf[0];
            gyro_y_adc = databuf[1];
            gyro_z_adc = databuf[2];

            accel_x_adc = databuf[3];
            accel_y_adc = databuf[4];
            accel_z_adc = databuf[5];

            temp_adc = databuf[6];

            /* 计算得到实际值 */
            gyro_x_act = (float)(gyro_x_adc) / 16.4;
            gyro_y_act = (float)(gyro_y_adc) / 16.4;
            gyro_z_act = (float)(gyro_z_adc) / 16.4;

            accel_x_act = (float)(accel_x_adc) / 2048;
            accel_y_act = (float)(accel_y_adc) / 2048;
            accel_z_act = (float)(accel_z_adc) / 2048;

            temp_act = ((float)(temp_adc - 25) / 326.8 + 25);

            printf("----原始值-----\n");
            printf("gx = %d,gy = %d,gz = %d\n",gyro_x_adc,gyro_y_adc,gyro_z_adc);
            printf("ax = %d,ay = %d,az = %d\n",accel_x_adc,accel_y_adc,accel_z_adc);
            printf("temp = %d\n",temp_adc);

            printf("----实际值----\n");
            printf("act gx = %.2f,act gy = %.2f,act gz = %.2f\n",gyro_x_act,gyro_y_act,gyro_z_act);
            printf("act ax = %.2f,act ay = %.2f,act az = %.2f\n",accel_x_act,accel_y_act,accel_z_act);
            printf("act temp = %.2f\n",temp_act);

        }

        usleep(100000); /* 100ms*/
    }
    
    close(fd);

    return 0;
}

三.SPI实验 - OLED

接口说明:

        DO->SCLK

        DI->MOSI

        RES:复位引脚,低电平有效

        DC:低电平表示进行写命令操作,高电平表示进行写数据操作

        CS:片选信号,低电平有效

1.设备树

(1).流程图

(2).设备树代码

2.驱动部分

(1).流程图

(2).驱动代码

#include <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/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of_gpio.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <linux/i2c.h>
#include <linux/spi/spi.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/platform_device.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include "oledfont.h"


#define OLED_CNT    1           /* 设备数量 */
#define OLED_NAME   "spi_oled"  /* 设备名字 */

#define OLED_RES_Clr() gpio_direction_output(oleddev.RST_gpio,0)      //RES
#define OLED_RES_Set() gpio_direction_output(oleddev.RST_gpio,1)

#define OLED_DC_Clr()  gpio_direction_output(oleddev.DC_gpio,0)       //DC
#define OLED_DC_Set()  gpio_direction_output(oleddev.DC_gpio,1)


#define OLED_CMD  0	//写命令
#define OLED_DATA 1	//写数据


/* 设备结构体 */
struct oled_dev
{
    dev_t devid;                /* 设备号 */
    struct cdev cdev;           /* cdev */
    struct class *class;        /* 类 */
    struct device *device;      /* 设备 */
    struct device_node *nd;     /* 设备结点 */
    int major;                  /* 主设备号 */

    void *private_data;         /* 私有数据 */

    int RST_gpio;               /* 复位引脚 */
    int DC_gpio;                /* DC引脚 */
};


struct oled_dev oleddev;


u8 OLED_GRAM[144][8];



/**
 * @description:            向oled多个寄存器写入数据
 * @param - dev     :       oled 设备
 * @param - reg     :       要写的寄存器首地址
 * @param - buf     :       要写入的数据缓冲区
 * @param - len     :       要写入数据的长度
 * @return          :       操作结果
 */
static s32 oled_write_regs(struct oled_dev *dev,u8 *buf,u8 len)
{
    int ret = -1;
    unsigned char *txdata;
    struct spi_message m;
    struct spi_transfer *t;
    struct spi_device *spi = (struct spi_device *)dev->private_data;

    t = kzalloc(sizeof(struct spi_transfer),GFP_KERNEL);
    if(!t)
    {
        return -ENOMEM;
    }

    txdata = kzalloc(len,GFP_KERNEL);        //第一个字节为寄存器首地址,后面的内容为要写的内容
    if(!txdata)
    {
        goto out1;
    }

    /* 一共要发送 len 个字节,len 为要写入寄存器的集合 */
    memcpy(txdata,buf,len);               /* 将要写入的数据拷贝到缓冲区中 */
    t->tx_buf = txdata;                   /* 要发送的数据 */
    t->len = len;                         /* t->len = 发送的数据段长度+一个字节的寄存器首地址 */
    spi_message_init(&m);                 /* 初始化 spi_message */
    spi_message_add_tail(t,&m);           /* 将 spi_transfer 添加到 spi_message 队列中 */
    ret = spi_sync(spi,&m);
    if(ret)
    {
        goto out2;
    }

out2:
    kfree(txdata);
out1:
    kfree(t);

    return ret;
}




/**
 * @description:            向oled指定寄存器写入一个字节的数据
 * @param - dev     :       oled设备
 * @param - reg     :       要写入的寄存器首地址
 * @param - value   :       要写入的值
 */
void oled_write_onereg(struct oled_dev *dev,u8 value)
{
    u8 data = value;

    oled_write_regs(dev,&data,1);
}


void OLED_WR_Byte(u8 dat,u8 cmd)
{			  
	if(cmd)
	  OLED_DC_Set();
	else
	  OLED_DC_Clr();

	oled_write_onereg(&oleddev,dat);

    OLED_DC_Set();	   	  
}


//画点 
//x:0~127
//y:0~63
//t:1 填充 0,清空	
void OLED_DrawPoint(u8 x,u8 y,u8 t)
{
    OLED_WR_Byte(0xb0+y,OLED_CMD);
	OLED_WR_Byte(((x&0xf0)>>4)|0x10,OLED_CMD);
	OLED_WR_Byte((x&0x0f)|0x01,OLED_CMD); 
}



//反显函数
void OLED_ColorTurn(u8 i)
{
	if(i==0)
		{
			OLED_WR_Byte(0xA6,OLED_CMD);//正常显示
		}
	if(i==1)
		{
			OLED_WR_Byte(0xA7,OLED_CMD);//反色显示
		}
}

//屏幕旋转180度
void OLED_DisplayTurn(u8 i)
{
	if(i==0)
		{
			OLED_WR_Byte(0xC8,OLED_CMD);//正常显示
			OLED_WR_Byte(0xA1,OLED_CMD);
		}
	if(i==1)
		{
			OLED_WR_Byte(0xC0,OLED_CMD);//反转显示
			OLED_WR_Byte(0xA0,OLED_CMD);
		}
}



//开启OLED显示 
void OLED_DisPlay_On(void)
{
	OLED_WR_Byte(0x8D,OLED_CMD);//电荷泵使能
	OLED_WR_Byte(0x14,OLED_CMD);//开启电荷泵
	OLED_WR_Byte(0xAF,OLED_CMD);//点亮屏幕
}

//关闭OLED显示 
void OLED_DisPlay_Off(void)
{
	OLED_WR_Byte(0x8D,OLED_CMD);//电荷泵使能
	OLED_WR_Byte(0x10,OLED_CMD);//关闭电荷泵
	OLED_WR_Byte(0xAE,OLED_CMD);//关闭屏幕
}

//更新显存到OLED	
void OLED_Refresh(void)
{
	u8 i,n;
	for(i=0;i<8;i++)
	{
	   OLED_WR_Byte(0xb0+i,OLED_CMD); //设置行起始地址
	   OLED_WR_Byte(0x00,OLED_CMD);   //设置低列起始地址
	   OLED_WR_Byte(0x10,OLED_CMD);   //设置高列起始地址
	   for(n=0;n<128;n++)
		 OLED_WR_Byte(OLED_GRAM[n][i],OLED_DATA);
  }
}
//清屏函数
void OLED_Clear(void)
{
	u8 i,n;
	for(i=0;i<8;i++)
	{
	   for(n=0;n<128;n++)
			{
			 OLED_GRAM[n][i]=0;//清除所有数据
			}
  }
	OLED_Refresh();//更新显示
}



//x,y:圆心坐标
//r:圆的半径
void OLED_DrawCircle(u8 x,u8 y,u8 r)
{
	int a, b,num;
    a = 0;
    b = r;
    while(2 * b * b >= r * r)      
    {
        OLED_DrawPoint(x + a, y - b,1);
        OLED_DrawPoint(x - a, y - b,1);
        OLED_DrawPoint(x - a, y + b,1);
        OLED_DrawPoint(x + a, y + b,1);
 
        OLED_DrawPoint(x + b, y + a,1);
        OLED_DrawPoint(x + b, y - a,1);
        OLED_DrawPoint(x - b, y - a,1);
        OLED_DrawPoint(x - b, y + a,1);
        
        a++;
        num = (a * a + b * b) - r*r;//计算画的点离圆心的距离
        if(num > 0)
        {
            b--;
            a--;
        }
    }
}



void OLED_ShowChar(u8 x,u8 y,u8 chr)
{      	
	unsigned char c=0,i=0;	
		c=chr-' ';//得到偏移后的值			
		if(x>128-1)
        {
            x=0;y=y+2;
        }

			OLED_DrawPoint(x,y,1);	
			for(i=0;i<8;i++)
			OLED_WR_Byte(F8X16[c*16+i],OLED_DATA);
			OLED_DrawPoint(x,y+1,1);
			for(i=0;i<8;i++)
			OLED_WR_Byte(F8X16[c*16+i+8],OLED_DATA);


}


//显示字符串
//x,y:起点坐标  
//size1:字体大小 
//*chr:字符串起始地址 
//mode:0,反色显示;1,正常显示
void OLED_ShowString(u8 x,u8 y,u8 *chr)
{
	while((*chr>=' ')&&(*chr<='~'))//判断是不是非法字符!
	{
		OLED_ShowChar(x,y,*chr);
	
		x+=8;

		chr++;
  }
}

//m^n
u32 OLED_Pow(u8 m,u8 n)
{
	u32 result=1;
	while(n--)
	{
	  result*=m;
	}
	return result;
}



//OLED的初始化
void OLED_REGInit(void)
{	
	OLED_RES_Clr();
	mdelay(200);
	OLED_RES_Set();
	
	OLED_WR_Byte(0xAE,OLED_CMD);//--turn off oled panel
	OLED_WR_Byte(0x00,OLED_CMD);//---set low column address
	OLED_WR_Byte(0x10,OLED_CMD);//---set high column address
	OLED_WR_Byte(0x40,OLED_CMD);//--set start line address  Set Mapping RAM Display Start Line (0x00~0x3F)
	OLED_WR_Byte(0x81,OLED_CMD);//--set contrast control register
	OLED_WR_Byte(0xCF,OLED_CMD);// Set SEG Output Current Brightness
	OLED_WR_Byte(0xA1,OLED_CMD);//--Set SEG/Column Mapping     0xa0左右反置 0xa1正常
	OLED_WR_Byte(0xC8,OLED_CMD);//Set COM/Row Scan Direction   0xc0上下反置 0xc8正常
	OLED_WR_Byte(0xA6,OLED_CMD);//--set normal display
	OLED_WR_Byte(0xA8,OLED_CMD);//--set multiplex ratio(1 to 64)
	OLED_WR_Byte(0x3f,OLED_CMD);//--1/64 duty
	OLED_WR_Byte(0xD3,OLED_CMD);//-set display offset	Shift Mapping RAM Counter (0x00~0x3F)
	OLED_WR_Byte(0x00,OLED_CMD);//-not offset
	OLED_WR_Byte(0xd5,OLED_CMD);//--set display clock divide ratio/oscillator frequency
	OLED_WR_Byte(0x80,OLED_CMD);//--set divide ratio, Set Clock as 100 Frames/Sec
	OLED_WR_Byte(0xD9,OLED_CMD);//--set pre-charge period
	OLED_WR_Byte(0xF1,OLED_CMD);//Set Pre-Charge as 15 Clocks & Discharge as 1 Clock
	OLED_WR_Byte(0xDA,OLED_CMD);//--set com pins hardware configuration
	OLED_WR_Byte(0x12,OLED_CMD);
	OLED_WR_Byte(0xDB,OLED_CMD);//--set vcomh
	OLED_WR_Byte(0x40,OLED_CMD);//Set VCOM Deselect Level
	OLED_WR_Byte(0x20,OLED_CMD);//-Set Page Addressing Mode (0x00/0x01/0x02)
	OLED_WR_Byte(0x02,OLED_CMD);//
	OLED_WR_Byte(0x8D,OLED_CMD);//--set Charge Pump enable/disable
	OLED_WR_Byte(0x14,OLED_CMD);//--set(0x10) disable
	OLED_WR_Byte(0xA4,OLED_CMD);// Disable Entire Display On (0xa4/0xa5)
	OLED_WR_Byte(0xA6,OLED_CMD);// Disable Inverse Display On (0xa6/a7) 
	OLED_Clear();
	OLED_WR_Byte(0xAF,OLED_CMD);
}













/**
 * @description:            打开设备
 * @param - inode   :       传递给驱动的inode
 * @param - filp    :       设备文件,一般使 filp->private_data 指向设备结构体
 * @return          :       0成功,其他失败
 */
static int oled_open(struct inode *inode,struct file *filp)
{
    /* 设置私有数据 */
    filp->private_data = &oleddev;

    return 0;
}



/**
 * @description:            向设备内写入数据
 * @param - filp    :       要打开的设备
 * @param - buf     :       用户要写入的数据
 * @param - cnt     :       要写入的数据的长度
 * @param - offt    :       相对于文件首地址的偏移量
 */
static ssize_t oled_write(struct file *filp,const char __user *buf,size_t cnt,loff_t *offt)
{
    int ret = -1;
    char databuf[128] = {0};

    ret = copy_from_user(databuf,buf,cnt);
    if(0 > ret)
    {
        printk("kernel data write failed!\r\n");
        return -EFAULT;
    }

    /* 可自行完善此处,用户传入x、y轴坐标和数据以显示 */
    OLED_ShowString(0,0,"hello world");

    return 0;
}


/**
 * @description:            关闭/释放设备
 * @param           :       无
 * @return          :       0成功,其他失败
 */
static int oled_release(struct inode *inode,struct file *filp)
{
    return 0;
}



/* 设备操作函数  */
static const struct file_operations oled_ops = 
{
    .owner = THIS_MODULE,
    .open = oled_open,
    .write = oled_write,
    .release = oled_release,
};



/**
 * @description:            probe函数,当设备与驱动匹配成功后就会执行此函数
 * @param - spi     :       spi 设备
 * @return          :       0成功,其他失败
 */
static int oled_probe(struct spi_device *spi)
{
    printk("driver and device matched!\r\n");
    /* 1.构建设备号 */
    if(oleddev.major)
    {
        oleddev.devid = MKDEV(oleddev.major,0);
        register_chrdev_region(oleddev.devid,OLED_CNT,OLED_NAME);
    }
    else
    {
        alloc_chrdev_region(&oleddev.devid,0,OLED_CNT,OLED_NAME);
        oleddev.major = MAJOR(oleddev.devid);
    }

    /* 2.注册cdev */
    cdev_init(&oleddev.cdev,&oled_ops);
    cdev_add(&oleddev.cdev,oleddev.devid,OLED_CNT);

    /* 3.创建类 */
    oleddev.class = class_create(THIS_MODULE,OLED_NAME);
    if(IS_ERR(oleddev.class))
    {
        return PTR_ERR(oleddev.class);
    }

    /* 4.创建设备 */
    oleddev.device = device_create(oleddev.class,NULL,oleddev.devid,NULL,OLED_NAME);
    if(IS_ERR(oleddev.device))
    {
        return PTR_ERR(oleddev.device);
    }

    /* 初始化 spi_device */
    spi->mode = SPI_MODE_0;     /* MODE0 : CPOL=0,CPHA=0 */
    spi_setup(spi);
    oleddev.private_data = spi;     //设置私有数据

    /* 获取 oled 的 RST引脚和DC引脚 */
    /* 1.获取软件片选引脚 */
    oleddev.nd = of_get_parent(spi->dev.of_node);                     //从SPI父结点中获取子节点的信息
    if(NULL == oleddev.nd)
    {
        printk("can not get dev_node\r\n");
    }
    oleddev.RST_gpio = of_get_named_gpio(oleddev.nd,"RST-gpios",0);   //获取片选信号的GPIO
    if(0 > oleddev.RST_gpio)
    {
        printk("can not get RST-gpio : %d\r\n",oleddev.RST_gpio);
    } 
    oleddev.DC_gpio = of_get_named_gpio(oleddev.nd,"DC-gpios",0);
    if(0 > oleddev.DC_gpio)
    {
        printk("can not get DC-gpio : %d\r\n",oleddev.DC_gpio);
    }
    gpio_request(oleddev.RST_gpio,"RST");                 //申请 GPIO
    gpio_request(oleddev.DC_gpio,"DC");                 //申请 GPIO

    gpio_direction_output(oleddev.RST_gpio,1);           //默认高电平
    gpio_direction_output(oleddev.DC_gpio,1);           //默认高电平


    /* 初始化 OLED 内部寄存器 */
    OLED_REGInit();

    /* 用于OLED显示测试 */
    OLED_ShowString(0,0,"hello world");

    return 0;
}



/**
 * @description:            remove函数,移除SPI驱动的时候会执行此函数
 * @param - spi     :       spi设备
 * @return          :       成功则返回(0),失败则返回(其他值)
 */
static int oled_remove(struct spi_device *spi)
{
    /* 1.删除设备 */
    cdev_del(&oleddev.cdev);
    unregister_chrdev_region(oleddev.devid,OLED_CNT);

    /* 注销掉类和设备 */
    device_destroy(oleddev.class,oleddev.devid);
    class_destroy(oleddev.class);

    gpio_free(oleddev.RST_gpio);
    gpio_free(oleddev.DC_gpio);

    return 0;
}



/* 无设备树下的匹配方式 - ID列表 */
static const struct spi_device_id oled_id[] = 
{
    {"alientek,spi_oled",0},
    {}
};

/* 设备树下的匹配方式 - 匹配列表 */
static const struct of_device_id oled_of_match[] = 
{
    {.compatible = "alientek,spi_oled"},
    {}
};

/* SPI驱动结构体 */
static struct spi_driver oled_driver = 
{
    .probe = oled_probe,
    .remove = oled_remove,
    .driver = 
    {
        .owner = THIS_MODULE,
        .name = "spi_oled",
        .of_match_table = oled_of_match,        //设备树下的匹配列表
    },
    .id_table = oled_id,                        //无设备树下的 ID列表
};




/**
 * @description:            驱动入口函数
 * @param           :       无
 * @return          :       无
 */
static int __init oled_init(void)
{
    return spi_register_driver(&oled_driver);
}


/**
 * @description:            驱动出口函数
 * @param           :       无
 * @return          :       无
 */
static void __exit oled_exit(void)
{
    spi_unregister_driver(&oled_driver);
}



module_init(oled_init);
module_exit(oled_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("kaneki");

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值