之前移植过MPU6050(I2C协议)和0.96寸oled(SPI协议),这次移植一个I2C协议的oled。
I2C的介绍
可以参考上一篇I2C的文章:
linux移植MPU6050的I2C驱动 —— AURORA1997
注意:从机地址不是
0x78
,而是0x3c
驱动的编写
其实这个1.3寸的oled屏在MPU6050之前就买了,店家只有另一款SSD1306的demo,不过在我上个买0.96寸oled的店铺给的资料里翻到了SH1106的demo。但是上手测试发现花屏,并且最好的时候是出现严重错位的情况,不过终于能显示点东西了。
调试了好久,换了很多次初始化reset代码,还是无济于事。后来上GitHub找代码,发现是列地址的设置问题,最早用的是反的,导致后面一直不行。。。
/*后来修改后的*/
static void Column_set(unsigned char column)
{
column+=2;
OLED_send_cmd(0x00); //设置列地址高位
OLED_send_cmd(0x10); //设置列地址低位
}
/*之前最早用的demo的*/
void Column_set(unsigned char column)
{
column+=OLED_COLUMN_OFFSET;
OLED_send_cmd(0x10|(column>>4)); //设置列地址高位
OLED_send_cmd(0x00|(column&0x0f)); //设置列地址低位
}
这里直接贴代码:
#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 <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
/***************************************************************
文件名 : oled1p3i2c.c
作者 : 1997AURORA
版本 : V1.0
描述 : oled 1.3寸 SH1106 I2C驱动程序
其他 : 无
日志 : 初版V1.0 2021/8/14 1997AURORA创建
***************************************************************/
#define OLED1P3_CNT 1
#define OLED1P3_NAME "oled1p3i2c"
const unsigned char BMP2[] =
{
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x84,0xC4,0x66,0x36,0x3E,0x1C,0x3C,0x7C,0xE6,0x06,0x06,0x03,0x03,0x03,
0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x06,0x06,0x06,0xFC,0x3C,0x1C,0x3E,
0x3E,0x66,0xC6,0x84,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x40,0x60,0xE0,0x00,0x00,0xE0,0x20,0xE0,0xC0,0xC0,0x60,0x20,0xE0,0x00,
0x20,0x20,0xE0,0x60,0x00,0x80,0xE0,0xE0,0x00,0x00,0xE0,0x00,0x00,0xE0,0xE0,0x00,
0xE0,0x20,0xE0,0x40,0xC0,0xE0,0x20,0x20,0xE0,0xC0,0xC0,0xE0,0x20,0xE0,0x00,0x00,
0xC0,0x60,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x78,0xF8,0xF8,
0xFE,0x77,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x70,0xF8,0xF8,0xF8,0x70,0x00,0x00,0x00,
0x00,0x00,0x00,0x01,0x07,0x06,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x0C,0x0F,0x0C,0x00,0x0D,0x0D,0x07,0x03,0x00,0x0D,0x0D,0x07,0x00,
0x00,0x06,0x03,0x00,0x04,0x07,0x02,0x03,0x07,0x00,0x07,0x0C,0x0C,0x07,0x03,0x00,
0x07,0x01,0x07,0x04,0x03,0x06,0x0C,0x0C,0x07,0x03,0x07,0x07,0x01,0x07,0x04,0x0E,
0x03,0x02,0x03,0x0E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF8,0xFE,0x07,0x03,0x01,
0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x00,0x00,0x00,0x01,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x02,0x06,0xFF,0xF8,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x20,0xE8,0x00,0x08,0x08,0xF8,0x08,0x08,0x00,0x00,0x28,0xDC,0x68,0x98,
0x78,0xE8,0x38,0x00,0x00,0xF0,0x1C,0xF0,0x20,0xDC,0x10,0xF0,0x00,0x00,0x00,0x00,
0x00,0x08,0xF8,0x08,0x20,0x10,0xF0,0x00,0xF0,0x18,0x08,0x30,0x00,0x20,0x10,0xD0,
0x30,0xFC,0x10,0x10,0x10,0x00,0x40,0x78,0x78,0xC0,0x30,0xCC,0x30,0xA0,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x00,0x00,0x00,
0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x00,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x07,0x04,0x04,0x04,0x07,0x04,0x04,0x00,0x00,0x00,0x07,0x06,0x03,
0x00,0x07,0x04,0x00,0x00,0x07,0x00,0x07,0x00,0x00,0x04,0x07,0x00,0x00,0x00,0x00,
0x00,0x04,0x07,0x04,0x04,0x06,0x05,0x04,0x03,0x04,0x06,0x02,0x01,0x00,0x01,0x05,
0x07,0x03,0x03,0x07,0x05,0x00,0x01,0x05,0x07,0x01,0x07,0x05,0x07,0x04,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0F,0x3E,0xF0,0x80,
0x03,0x0E,0x38,0x70,0xC0,0xC0,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,
0x80,0xC0,0x60,0x30,0x1C,0x07,0x80,0xF0,0x7E,0x0F,0x01,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,
0x07,0x1C,0x38,0x70,0x60,0xC1,0xC3,0x87,0x06,0x0E,0x0C,0x1C,0x1C,0x18,0x38,0x38,
0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x1C,0x1C,0x0C,0x0E,0x06,0x87,0xC3,
0xC1,0xE0,0x70,0x38,0x1C,0x0F,0x03,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x03,0x07,0x06,0x06,0x0E,0x0C,0x0C,0x0C,
0x1C,0x1C,0x18,0x1C,0x1C,0x1C,0x0C,0x0C,0x0C,0x0E,0x06,0x06,0x07,0x03,0x03,0x01,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,/*"C:\Users\zjh46\Desktop\2.BMP",0*/
/* (128 X 64 )*/
};
const unsigned char OLED_init_cmd[22]= //SH1106
{
0xAE,
0xA4,
/* Multiplex Ratio Data Set: 64 (POR = 0x3f, 64) */
0xA8,0x3f,
/* Display OffsetData Set: 0 (POR = 0x00) */
0xD3,0x00,
0x40,
0xA0 + 1,
0xC8,
/* DC-DC ON/OFF Mode Set: Built-in DC-DC is used, Normal Display (POR = 0x8b) */
0xAD,0x8B,
/* Dis-charge/Pre-charge PeriodData Set: pre-charge 2 DCLKs,
* dis-charge 2 DCLKs (POR = 0x22, pre-charge 2 DCLKs, dis-charge 2 DCLKs)
* */
0xD5,0xf0,
/* VCOM Deselect LevelData Set: 0,770V (POR = 0x35, 0,770 V) */
0xDB,0x20,
/* Set Pump voltage value: 8,0 V (POR = 0x32, 8,0 V) */
0x30 | 0x0,
/* Contrast Data Register Set: 255 (large) (POR = 0x80) */
0x81,0x7f,
0xA6,
/* com pin HW config, sequential com pin config (bit 4), disable left/right remap (bit 5) */
0xDA, 0x12,
0xAF
};
struct oled1p3_dev {
dev_t devid; /* 设备号 */
struct cdev cdev; /* cdev */
struct class *class; /* 类 */
struct device *device; /* 设备 */
struct device_node *nd; /* 设备节点 */
int major; /* 主设备号 */
void *private_data; /* 私有数据 */
};
static struct oled1p3_dev oled1p3dev;
/*
* @description : 从oled1p3读取多个寄存器数据
* @param - dev: oled1p3设备
* @param - reg: 要读取的寄存器首地址
* @param - val: 读取到的数据
* @param - len: 要读取的数据长度
* @return : 操作结果
*/
static int oled1p3_read_regs(struct oled1p3_dev *dev, u8 reg, void *val, int len)
{
return 0;
}
/*
* @description : 向oled1p3多个寄存器写入数据
* @param - dev: oled1p3设备
* @param - reg: 要写入的寄存器首地址
* @param - val: 要写入的数据缓冲区
* @param - len: 要写入的数据长度
* @return : 操作结果
*/
static s32 oled1p3_write_regs(struct oled1p3_dev *dev,u8 *buf, u8 len)
{
struct i2c_msg msg;
struct i2c_client *client = (struct i2c_client *)dev->private_data;
msg.addr = client->addr; /* oled1p3地址 */
msg.flags = 0; /* 标记为写数据 */
msg.buf = buf; /* 要写入的数据缓冲区 */
msg.len = len; /* 要写入的数据长度 */
return i2c_transfer(client->adapter, &msg, 1);
}
/*
* @description : 读取oled1p3指定寄存器值,读取一个寄存器
* @param - dev: oled1p3设备
* @param - reg: 要读取的寄存器
* @return : 读取到的寄存器值
*/
static unsigned char oled1p3_read_reg(struct oled1p3_dev *dev, u8 reg)
{
u8 data = 0;
oled1p3_read_regs(dev, reg, &data, 1);
return data;
#if 0
struct i2c_client *client = (struct i2c_client *)dev->private_data;
return i2c_smbus_read_byte_data(client, reg);
#endif
}
/*
* @description : 向oled1p3指定寄存器写入指定的值,写一个寄存器
* @param - dev: oled1p3设备
* @param - reg: 要写的寄存器
* @param - data: 要写入的值
* @return : 无
*/
static void oled1p3_write_reg(u8* data)
{
oled1p3_write_regs(&oled1p3dev, data, 2);
}
static void OLED_send_cmd(unsigned char o_command)
{
u8 b[2];
b[0] = 0x00;
b[1] = o_command;
oled1p3_write_reg(b);
}
static void OLED_send_data(unsigned char o_data)
{
u8 b[2];
b[0] = 0x40;
b[1] = o_data;
oled1p3_write_reg(b);
}
static void OLED_init(void)
{
unsigned char i;
for(i=0;i<22;i++)
{
OLED_send_cmd(OLED_init_cmd[i]);
}
}
static void Page_set(unsigned char page)
{
OLED_send_cmd(0xb0+page);
}
static void Column_set(unsigned char column)
{
column+=2;
OLED_send_cmd(0x00); //设置列地址高位
OLED_send_cmd(0x10); //设置列地址低位
}
static void OLED_clear(void)
{
unsigned char page,column;
for(page=0;page<8;page++) //page loop
{
Page_set(page);
Column_set(0);
for(column=0;column<132;column++) //column loop
{
OLED_send_data(0x00);
}
}
}
static void OLED_full(void)
{
unsigned char page,column;
for(page=0;page<8;page++) //page loop
{
Page_set(page);
Column_set(0);
for(column=0;column<132;column++) //column loop
{
OLED_send_data(0xff);
}
}
}
static void Picture_display(const unsigned char *ptr_pic)
{
unsigned char page,column;
for(page=0;page<8;page++) //page loop
{
Page_set(page);
Column_set(0);
for(column=0;column<128;column++) //column loop
{
OLED_send_data(*ptr_pic++);
}
}
}
/*
* @description : 打开设备
* @param - inode : 传递给驱动的inode
* @param - filp : 设备文件,file结构体有个叫做private_data的成员变量
* 一般在open的时候将private_data指向设备结构体。
* @return : 0 成功;其他 失败
*/
static int oled1p3_open(struct inode *inode, struct file *filp)
{
filp->private_data = &oled1p3dev;
return 0;
}
/*
* @description : 从设备读取数据
* @param - filp : 要打开的设备文件(文件描述符)
* @param - buf : 返回给用户空间的数据缓冲区
* @param - cnt : 要读取的数据长度
* @param - offt : 相对于文件首地址的偏移
* @return : 读取的字节数,如果为负值,表示读取失败
*/
static ssize_t oled1p3_read(struct file *filp, char __user *buf, size_t cnt, loff_t *off)
{
return 0;
}
/*
* @description : 从设备读取数据
* @param - filp : 要打开的设备文件(文件描述符)
* @param - buf : 返回给用户空间的数据缓冲区
* @param - cnt : 要读取的数据长度
* @param - offt : 相对于文件首地址的偏移
* @return : 读取的字节数,如果为负值,表示读取失败
*/
static ssize_t oled1p3_write(struct file *filp, char __user *buf, size_t cnt, loff_t *off)
{
unsigned char databuf[1];
unsigned char oledstat;
long err = 0;
struct oled96_dev *dev = (struct oled96_dev *)filp->private_data;
err = copy_from_user(databuf, buf, sizeof(cnt));
if(err < 0) {
printk("kernel write failed!\r\n");
return -EFAULT;
}
oledstat = databuf[0]; /* 获取状态值 */
if(oledstat == 0) {
OLED_full();
printk("draw full screen\r\n");
} else if(oledstat == 1) {
OLED_clear();
Picture_display(&BMP2);
printk("draw BMP2\r\n");
}
return 0;
}
/*
* @description : 关闭/释放设备
* @param - filp : 要关闭的设备文件(文件描述符)
* @return : 0 成功;其他 失败
*/
static int oled1p3_release(struct inode *inode, struct file *filp)
{
return 0;
}
/* OLED1P3操作函数 */
static const struct file_operations oled1p3_ops = {
.owner = THIS_MODULE,
.open = oled1p3_open,
.read = oled1p3_read,
.write = oled1p3_write,
.release = oled1p3_release,
};
/*
* @description : i2c驱动的probe函数
* @param - client : i2c设备
* @param - id : i2c设备ID
* @return : 0,成功;其他负值,失败
*/
static int oled1p3_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
/* 1、构建设备号 */
if (oled1p3dev.major) {
oled1p3dev.devid = MKDEV(oled1p3dev.major, 0);
register_chrdev_region(oled1p3dev.devid, OLED1P3_CNT, OLED1P3_NAME);
} else {
alloc_chrdev_region(&oled1p3dev.devid, 0, OLED1P3_CNT, OLED1P3_NAME);
oled1p3dev.major = MAJOR(oled1p3dev.devid);
}
/* 2、注册设备 */
cdev_init(&oled1p3dev.cdev, &oled1p3_ops);
cdev_add(&oled1p3dev.cdev, oled1p3dev.devid, OLED1P3_CNT);
/* 3、创建类 */
oled1p3dev.class = class_create(THIS_MODULE, OLED1P3_NAME);
if (IS_ERR(oled1p3dev.class)) {
return PTR_ERR(oled1p3dev.class);
}
/* 4、创建设备 */
oled1p3dev.device = device_create(oled1p3dev.class, NULL, oled1p3dev.devid, NULL, OLED1P3_NAME);
if (IS_ERR(oled1p3dev.device)) {
return PTR_ERR(oled1p3dev.device);
}
oled1p3dev.private_data = client;
OLED_init();
return 0;
}
/*
* @description : i2c驱动的remove函数,移除i2c驱动的时候此函数会执行
* @param - client : i2c设备
* @return : 0,成功;其他负值,失败
*/
static int oled1p3_remove(struct i2c_client *client)
{
/* 删除设备 */
cdev_del(&oled1p3dev.cdev);
unregister_chrdev_region(oled1p3dev.devid, OLED1P3_CNT);
/* 注销掉类和设备 */
device_destroy(oled1p3dev.class, oled1p3dev.devid);
class_destroy(oled1p3dev.class);
return 0;
}
/* 传统匹配方式ID列表 */
static const struct i2c_device_id oled1p3_id[] = {
{"alientek,oled1p3i2c", 0},
{}
};
/* 设备树匹配列表 */
static const struct of_device_id oled1p3_of_match[] = {
{ .compatible = "alientek,oled1p3i2c" },
{ /* Sentinel */ }
};
/* i2c驱动结构体 */
static struct i2c_driver oled1p3_driver = {
.probe = oled1p3_probe,
.remove = oled1p3_remove,
.driver = {
.owner = THIS_MODULE,
.name = "oled1p3i2c",
.of_match_table = oled1p3_of_match,
},
.id_table = oled1p3_id,
};
/*
* @description : 驱动入口函数
* @param : 无
* @return : 无
*/
static int __init oled1p3_init(void)
{
int ret = 0;
ret = i2c_add_driver(&oled1p3_driver);
return ret;
}
/*
* @description : 驱动出口函数
* @param : 无
* @return : 无
*/
static void __exit oled1p3_exit(void)
{
i2c_del_driver(&oled1p3_driver);
}
/* module_i2c_driver(oled1p3_driver) */
module_init(oled1p3_init);
module_exit(oled1p3_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("1997AURORA");
应用程序
应用程序同这个实验的应用程序,改个名即可。
0.96寸OLED屏移植到搭载mx6ull的linux系统
图片是显示的效果,是取图片的模取的。
谢谢观看