目录
2.申请 spi_master - spi_alloc_master
3.释放 spi_master - spi_master_put
4.向内核注册 spi_master - spi_register_master
7.向内核注册 spi_driver - spi_register_driver
8.注销 spi_driver - spi_unregister_driver
12.将 spi_transfer 添加到 spi_message 队列中
一.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");