i.MX6ULL移植linux-6.6和驱动基础(五)IIC - OLED驱动
本文介绍了IIC从设备驱动框架及其在OLED屏幕驱动中的应用。主要内容包括:1)IIC从设备驱动的核心结构体i2c_driver和i2c_client,以及读写数据的i2c_msg结构体;2)以SSD1306 OLED屏幕为例,详细讲解IIC设备地址确认、初始化命令设置和清屏操作。通过i2c_transfer函数实现数据传输,为IIC设备驱动开发提供了实用参考。
本篇主要内容:
一、IIC从设备驱动框架
📌和SPI驱动一样,这里我们只学习IIC从设备的驱动框架,主设备驱动一般都由内核提供,我们以能驱动/应用IIC设备为主要目的来学习。和SPI从机驱动类似,IIC从机驱动框架主要内容就是i2c_client和i2c_driver结构体,涉及到的API也是注册、注销功能,使用上和前篇的SPI大差不差。
1.1 核心结构体i2c_driver
📌i2c_driver结构体的使用主要是实现对应的probe和remove函数,使用上大致的框架如下:
static int ap3216c_probe(struct i2c_client *client, const struct i2c_device_id *id){
return 0;
}
static int ap3216c_remove(struct i2c_client *client){
}
static const struct i2c_device_id ap3216c_id[] = {
{"wo002582,ap3216c", 0},
{}
};
static const struct of_device_id ap3216c_of_match[] = {
{ .compatible = "wo002582,ap3216c" },
{ /* Sentinel */ }
};
static struct i2c_driver ap3216c_driver = {
.probe = ap3216c_probe,
.remove = ap3216c_remove,
.driver = {
.owner = THIS_MODULE,
.name = "ap3216c",
.of_match_table = ap3216c_of_match,
},
.id_table = ap3216c_id,
};
📌和spi从设备驱动框架一样,描述i2c驱动的结构体需要我们做的就是实现probe和remove函数,并通过i2c_add_driver(&ap3216c_driver)注册,i2c_del_driver(&at24c02_driver)注销;在实现probe或remove时,离不开结构体i2c_client,这个结构体描述的是一个IIC从设备实例,使用方式和上篇的spi一样,这里不再赘述,具体看代码会更好理解,其主要成员如下:
struct i2c_client {
unsigned short flags; // 设备标志(如I2C_CLIENT_TEN表示10位从地址)
unsigned short addr; // IIC从设备地址(7位或10位,由flags决定)
char name[I2C_NAME_SIZE]; // 设备名称(与驱动id_table或设备树匹配)
struct i2c_adapter *adapter; // 指向设备所属的IIC适配器(即IIC控制器)
struct device dev; // 内核通用设备结构体,用于设备模型管理
int irq; // 设备使用的中断号(若设备支持中断)
struct list_head detected; // 链表节点,用于内核管理已探测的设备
#if IS_ENABLED(CONFIG_I2C_SLAVE)
struct i2c_slave_client *slave; // 作为IIC从机模式时的配置(可选)
#endif
const struct i2c_client_address_data *addr_data; // 地址相关数据(如多地址设备)
struct list_head list; // 链表节点,用于挂载到IIC适配器的设备列表
};
1.2 IIC读写数据的结构体i2c_msg
truct i2c_msg {
__u16 addr;
__u16 flags;
#define I2C_M_RD 0x0001 /* guaranteed to be 0x0001! */
#define I2C_M_TEN 0x0010 /* use only if I2C_FUNC_10BIT_ADDR */
#define I2C_M_DMA_SAFE 0x0200 /* use only in kernel space */
#define I2C_M_RECV_LEN 0x0400 /* use only if I2C_FUNC_SMBUS_READ_BLOCK_DATA */
#define I2C_M_NO_RD_ACK 0x0800 /* use only if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_IGNORE_NAK 0x1000 /* use only if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_REV_DIR_ADDR 0x2000 /* use only if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_NOSTART 0x4000 /* use only if I2C_FUNC_NOSTART */
#define I2C_M_STOP 0x8000 /* use only if I2C_FUNC_PROTOCOL_MANGLING */
__u16 len;
__u8 *buf;
};
📌写数据实际上就是往对应地址写值,数据格式为地址+数据;读数据实际上是先写数据再读,也体现了半双工通信:
static int ap3216c_i2c_read(struct ap3216c *ap3216, uint8_t start_address, void *data,int size){
struct i2c_msg msg[2];
struct i2c_client *client = (struct i2c_client *)ap3216->private_data;
/* msg[0]为发送要读取的首地址 */
msg[0].addr = client->addr;
msg[0].flags = 0;
msg[0].buf = &start_address;
msg[0].len = 1;
/* msg[1]读取数据 */
msg[1].addr = client->addr;
msg[1].flags = I2C_M_RD;
msg[1].buf = data;
msg[1].len = size;
int ret = i2c_transfer(client->adapter, msg, 2);
if(ret == 2) {
return ret;
} else {
printk("i2c rd failed=%d reg=%06x len=%d\n",ret, start_address, size);
return -EREMOTEIO;
}
}
static int ap3216c_i2c_write(struct ap3216c *ap3216, uint8_t address, void *data,int size){
struct i2c_client *client = ap3216->private_data;
uint8_t *buf = kmalloc(sizeof(address)+size,GFP_KERNEL);
memcpy(buf,&address,sizeof(address));
memcpy(buf+1,data,size);
struct i2c_msg msg = {
.addr = client->addr,
.buf = buf,
.flags = 0,
.len = size + 1
};
int ret = i2c_transfer(client->adapter, &msg, 1);
if(ret < 0){
printk("i2c_transfer() occurred error.\n");
return ret;
}
return 0;
}
二、OLED屏幕驱动
2.1 效果展示

2.2 关于使用OLED屏的必要信息
📌本节使用的是1306 0.96寸的oled屏幕,也就是学习51/32常用的,这里只是想用来学习i2c驱动翻出来的"npc",关于这个oled屏幕的其它详细信息可以看模块的芯片手册和数据手册,下面是使用i2c简单驱动屏幕的一些关键信息。
(1)oled屏幕地址

📌把文档扔给豆包,我们可以知道,SSD1306的I2C从地址为7位,格式固定为 011110x(二进制),其中最后一位x由 SA0引脚(地址选择引脚)的电平决定(多数模块SA0默认接GND,默认地址为0x3C)
- 当 SA0 引脚接GND(电平为 0)时,地址为0111100(十六进制 0x3C)
- 当 SA0 引脚接VCC(电平为 1)时,地址为0111101(十六进制 0x3D)
(2)oled屏幕的初始化
📌在驱动屏幕显示我们所需的内容之前,初始化这个工作是必须的,关于这个屏幕的初始化,简单来说就是通过i2c向模块发送对应的命令或数据。在这个模块中,定义了两个寄存器地址,0x00表示写命令,0x40表示写数据;以及定义了一系列命令,具体的功能这里就不详细列出了,简单总结为以下初始化oled屏幕所需要的命令。也就是说通过i2c往0x00写入以下内容即可完成这块oled屏幕的初始化
uint8_t cmd[] ={0xAE,0x00,0x10,0x40,0xB0,0x81,0xFF,0xA1,0xA6,0xA8,0x3F,0xC8,0xD3,0x00,0xD5,0x80,0xD8,0x05,0xD9,0xF1,0xDA,0x12,0xDB,0x30,0x8D,0x14,0xAF};
📌功能含义大致如下,详细的解释可见模块手册

(3)清屏
📌清屏实际上就是全屏填充数据0x00,全屏对于0.96寸的oled来说,就是128列X64行,每8行称作1页(page0:地址0xb0 ~ page7:地址0xb7),每一页的列地址由低4位和高4位组成(low_bits从0x00到0x0F,high_bits从0x10到0x1F,一页有127列),列的高低地址需要分两次发送,论起始列是多少,后续写入的数据都会从起始列开始,按顺序向后覆盖(直到写满 128 列或停止写入)。比如:填充第2页的所有列
oled_i2c_write(&oled_st,OLED_CMD,0xb0+2); //第i页
oled_i2c_write(&oled_st,OLED_CMD,0x00); //从第0列开始
oled_i2c_write(&oled_st,OLED_CMD,0x10);
for(j=0;j<128;j++){
oled_i2c_write(&oled_st,OLED_DATA,0xff);
}
📌可能解释得还不是太清晰,下面我们来实现一个效果,从page0的第0列开始,填充完page0一整页。效果如图所示,可见在0.96寸的oled屏幕上,一页的内容近似一条粗线:

int i;
/* oled初始化 */
uint8_t data[] = {0xAE,0x00,0x10,0x40,0xB0,0x81,0xFF,0xA1,0xA6,0xA8,0x3F,0xC8,0xD3,0x00,0xD5,0x80,0xD8,0x05,0xD9,0xF1,0xDA,0x12,0xDB,0x30,0x8D,0x14,0xAF};
int len = sizeof(data)/sizeof(data[0]);
for(i=0;i<len;i++){
oled_i2c_write(&oled_st,OLED_CMD,data[i]);
}
/* 填充0x00-息屏 */
fill_screen(0x00);
/* 点亮page0 */
int j;
oled_i2c_write(&oled_st,OLED_CMD,0xb0);
oled_i2c_write(&oled_st,OLED_CMD,0x00);
oled_i2c_write(&oled_st,OLED_CMD,0x10);
for(j=0;j<128;j++){
oled_i2c_write(&oled_st,OLED_DATA,0xff); //点亮一列
msleep(50);
}
(4)显示图片
📌显示图片主要是将图片用取模工具转换成十六进制数据,通过i2c写入;这里用的是购买模块商家提供的51例程代码中的图片,转化后得到的c语言数组如下:
unsigned char picture[]={
0x00,0x00,0x00,0x06,0x0A,0xFE,0x0A,0xC6,0x00,0xE0,0x00,0xF0,0x00,0xF8,0x00,0x00,
0x00,0x00,0x00,0x00,0xFE,0x7D,0xBB,0xC7,0xEF,0xEF,0xEF,0xEF,0xEF,0xEF,0xEF,0xC7,
0xBB,0x7D,0xFE,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x08,0x0C,0xFE,0xFE,0x0C,0x08,0x20,0x60,0xFE,0xFE,0x60,0x20,0x00,0x00,0x00,
0x78,0x48,0xFE,0x82,0xBA,0xBA,0x82,0xBA,0xBA,0x82,0xBA,0xBA,0x82,0xBA,0xBA,0x82,
0xFE,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,0x01,0x01,0x01,0x01,0x01,
0x01,0x01,0x01,0x01,0x01,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,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0xFF,0x03,0x03,0x03,0x03,0x03,0x03,
0x03,0x03,0x03,0xFF,0xFF,0x00,0x00,0xFE,0xFF,0x03,0x03,0x03,0x03,0x03,0x03,0x03,
0x03,0x03,0xFF,0xFE,0x00,0x00,0x00,0x00,0xC0,0xC0,0xC0,0x00,0x00,0x00,0x00,0xFE,
0xFF,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0xFF,0xFE,0x00,0x00,0xFE,0xFF,
0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0xFF,0xFE,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x00,0x00,0xFF,0xFF,0x0C,0x0C,0x0C,
0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0xFF,0xFF,0x00,0x00,0x00,0x00,0xE1,0xE1,0xE1,0x00,
0x00,0x00,0x00,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,
0x00,0x00,0xFF,0xFF,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0xFF,0xFF,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x0F,0x1F,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x1F,0x0F,0x00,0x00,0x0F,
0x1F,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x1F,0x0F,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x1F,0x18,0x18,0x18,0x18,0x18,0x18,0x18,
0x18,0x18,0x1F,0x0F,0x00,0x00,0x0F,0x1F,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,
0x18,0x1F,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE2,0x92,0x8A,0x86,0x00,
0x00,0x7C,0x82,0x82,0x82,0x7C,0x00,0xFE,0x00,0x82,0x92,0xAA,0xC6,0x00,0x00,0xC0,
0xC0,0x00,0x7C,0x82,0x82,0x82,0x7C,0x00,0x00,0x02,0x02,0x02,0xFE,0x00,0x00,0xC0,
0xC0,0x00,0x7C,0x82,0x82,0x82,0x7C,0x00,0x00,0xFE,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x24,0xA4,0x2E,0x24,0xE4,
0x24,0x2E,0xA4,0x24,0x00,0x00,0x00,0xF8,0x4A,0x4C,0x48,0xF8,0x48,0x4C,0x4A,0xF8,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0xC0,0x20,0x10,0x10,0x10,0x10,0x20,0xC0,0x00,0x00,
0xC0,0x20,0x10,0x10,0x10,0x10,0x20,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,
0x0A,0x07,0x02,0x7F,0x02,0x07,0x0A,0x02,0x00,0x00,0x00,0x0B,0x0A,0x0A,0x0A,0x7F,
0x0A,0x0A,0x0A,0x0B,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0x20,0x40,0x40,0x40,0x50,
0x20,0x5F,0x80,0x00,0x1F,0x20,0x40,0x40,0x40,0x50,0x20,0x5F,0x80,0x00,0x00,0x00,
};
📌理解完上面的清屏的流程,显示图片就很容易了,其实也就是把转换得到的数组写入oled即可,主要逻辑代码如下
void fill_screen(uint8_t fill_Data)
{
unsigned char i,j;
for(i=0;i<8;i++)
{
oled_i2c_write(&oled_st,OLED_CMD,0xb0+i);
oled_i2c_write(&oled_st,OLED_CMD,0x00);
oled_i2c_write(&oled_st,OLED_CMD,0x10);
for(j=0;j<132;j++)
{
oled_i2c_write(&oled_st,OLED_DATA,fill_Data);
}
}
}
static void show_picture(void){
unsigned char x,y;
for(y=0;y<8;y++){
oled_i2c_write(&oled_st,OLED_CMD,0xb0+y);
oled_i2c_write(&oled_st,OLED_CMD,0x00);
oled_i2c_write(&oled_st,OLED_CMD,0x10);
for(x=0;x<132;x++){
oled_i2c_write(&oled_st,OLED_DATA,*(picture_data++));
}
}
}
2.3 驱动代码
📌同上一篇,这里为了偷懒没有写应用层的测试程序(甚至连清理函数都没有写哈),简单粗暴在加载驱动后显示图片,所写的驱动也只是简单的框架,不是完善的代码。
(1)oled.c
#include <linux/init.h>
#include <linux/ioctl.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/list.h>
#include <linux/errno.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/property.h>
#include <linux/slab.h>
#include <linux/compat.h>
#include <linux/spi/spi.h>
#include <linux/spi/spidev.h>
#include <linux/uaccess.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include "oled.h"
struct oled oled_st = {
.devname = "oled",
.major = -1,
};
static int oled_i2c_read(struct oled *oled, uint8_t start_address, void *data,int size){
struct i2c_msg msg[2];
struct i2c_client *client = (struct i2c_client *)oled->private_data;
/* msg[0]为发送要读取的首地址 */
msg[0].addr = client->addr;
msg[0].flags = 0;
msg[0].buf = &start_address;
msg[0].len = 1;
/* msg[1]读取数据 */
msg[1].addr = client->addr;
msg[1].flags = I2C_M_RD;
msg[1].buf = data;
msg[1].len = size;
int ret = i2c_transfer(client->adapter, msg, 2);
if(ret == 2) {
return ret;
} else {
printk("i2c rd failed=%d reg=%06x len=%d\n",ret, start_address, size);
return -EREMOTEIO;
}
}
static int oled_i2c_write(struct oled *oled, uint8_t address,uint8_t data){
struct i2c_client *client = oled->private_data;
uint8_t buf[2];
buf[0] = address;
buf[1] = data;
struct i2c_msg msg = {
.addr = client->addr,
.buf = buf,
.flags = 0,
.len = 2
};
int ret = i2c_transfer(client->adapter, &msg, 1);
if(ret < 0){
printk("i2c_transfer() occurred error.\n");
return ret;
}
return 0;
}
static int oled_open(struct inode *inode, struct file *filp)
{
filp->private_data = &oled_st;
return 0;
}
static ssize_t oled_read(struct file *filp, char __user *buf, size_t cnt, loff_t *off)
{
return 0;
}
static int oled_release(struct inode *inode, struct file *filp)
{
return 0;
}
static const struct file_operations oled_fop = {
.owner = THIS_MODULE,
.open = oled_open,
.read = oled_read,
.release = oled_release,
};
void fill_screen(uint8_t fill_Data)
{
unsigned char i,j;
for(i=0;i<8;i++)
{
oled_i2c_write(&oled_st,OLED_CMD,0xb0+i);
oled_i2c_write(&oled_st,OLED_CMD,0x00);
oled_i2c_write(&oled_st,OLED_CMD,0x10);
for(j=0;j<132;j++)
{
oled_i2c_write(&oled_st,OLED_DATA,fill_Data);
}
}
}
static void show_picture(void){
uint8_t x,y;
uint8_t *pdata = picture_data;
for(y=0;y<8;y++){
oled_i2c_write(&oled_st,OLED_CMD,0xb0+y);
oled_i2c_write(&oled_st,OLED_CMD,0x00);
oled_i2c_write(&oled_st,OLED_CMD,0x10);
for(x=0;x<132;x++){
oled_i2c_write(&oled_st,OLED_DATA,*(pdata++));
}
}
}
static int oled_probe(struct i2c_client *client){
oled_st.private_data = client;
int i;
uint8_t data[] ={0xAE,0x00,0x10,0x40,0xB0,0x81,0xFF,0xA1,0xA6,0xA8,0x3F,0xC8,0xD3,0x00,0xD5,0x80,0xD8,0x05,0xD9,0xF1,0xDA,0x12,0xDB,0x30,0x8D,0x14,0xAF};
int len = sizeof(data)/sizeof(data[0]);
for(i=0;i<len;i++){
oled_i2c_write(&oled_st,OLED_CMD,data[i]);
}
fill_screen(0xff);
fill_screen(0x00);
show_picture();
return 0;
}
static void oled_remove(struct i2c_client *client){
}
static const struct i2c_device_id oled_id[] = {
{"wo002582,oled", 0},
{}
};
static const struct of_device_id oled_of_match[] = {
{ .compatible = "wo002582,oled" },
{ /* Sentinel */ }
};
static struct i2c_driver oled_driver = {
.probe = oled_probe,
.remove = oled_remove,
.driver = {
.owner = THIS_MODULE,
.name = "oled",
.of_match_table = oled_of_match,
},
.id_table = oled_id,
};
static int __init oled_init(void)
{
int ERR = 0;
oled_st.major = register_chrdev(0, oled_st.devname, &oled_fop);
if (oled_st.major < 0){
return oled_st.major;
}
oled_st.class = class_create(oled_st.devname);
if (IS_ERR(oled_st.class)) {
unregister_chrdev(oled_st.major, oled_st.devname);
return PTR_ERR(oled_st.class);
}
oled_st.devid = MKDEV(oled_st.major,0);
oled_st.device = device_create(oled_st.class,NULL, oled_st.devid,NULL,oled_st.devname);
if(IS_ERR(oled_st.device)){
ERR = PTR_ERR(oled_st.device);
pr_err("device create failed.(error code:%d)\n",ERR);
class_destroy(oled_st.class);
unregister_chrdev(oled_st.major,oled_st.devname);
return ERR;
}
int status = i2c_add_driver(&oled_driver);
if (status < 0) {
class_destroy(oled_st.class);
unregister_chrdev(oled_st.major, oled_st.devname);
}
return 0;
}
static void __exit oled_exit(void)
{
i2c_del_driver(&oled_driver);
}
module_init(oled_init);
module_exit(oled_exit);
MODULE_LICENSE("GPL");
MODULE_INFO(intree, "Y");
MODULE_AUTHOR("wo002582");
(2)oled.h
#ifndef __OLED_H__
#define __OLED_H_
#define OLED_CMD 0x00
#define OLED_DATA 0x40
struct oled{
dev_t devid; /* 设备号 */
struct class *class; /* 类 */
struct device *device; /* 设备 */
struct device_node *nd; /* 设备节点 */
int major; /* 主设备号 */
char *devname;
void *private_data; /* 私有数据 */
};
typedef unsigned char uint8_t;
/***********************Picture Code**************************/
uint8_t picture_data[] = {
0x00,0x00,0x00,0x06,0x0A,0xFE,0x0A,0xC6,0x00,0xE0,0x00,0xF0,0x00,0xF8,0x00,0x00,
0x00,0x00,0x00,0x00,0xFE,0x7D,0xBB,0xC7,0xEF,0xEF,0xEF,0xEF,0xEF,0xEF,0xEF,0xC7,
0xBB,0x7D,0xFE,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x08,0x0C,0xFE,0xFE,0x0C,0x08,0x20,0x60,0xFE,0xFE,0x60,0x20,0x00,0x00,0x00,
0x78,0x48,0xFE,0x82,0xBA,0xBA,0x82,0xBA,0xBA,0x82,0xBA,0xBA,0x82,0xBA,0xBA,0x82,
0xFE,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,0x01,0x01,0x01,0x01,0x01,
0x01,0x01,0x01,0x01,0x01,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,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0xFF,0x03,0x03,0x03,0x03,0x03,0x03,
0x03,0x03,0x03,0xFF,0xFF,0x00,0x00,0xFE,0xFF,0x03,0x03,0x03,0x03,0x03,0x03,0x03,
0x03,0x03,0xFF,0xFE,0x00,0x00,0x00,0x00,0xC0,0xC0,0xC0,0x00,0x00,0x00,0x00,0xFE,
0xFF,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0xFF,0xFE,0x00,0x00,0xFE,0xFF,
0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0xFF,0xFE,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x00,0x00,0xFF,0xFF,0x0C,0x0C,0x0C,
0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0xFF,0xFF,0x00,0x00,0x00,0x00,0xE1,0xE1,0xE1,0x00,
0x00,0x00,0x00,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,
0x00,0x00,0xFF,0xFF,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0xFF,0xFF,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x0F,0x1F,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x1F,0x0F,0x00,0x00,0x0F,
0x1F,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x1F,0x0F,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x1F,0x18,0x18,0x18,0x18,0x18,0x18,0x18,
0x18,0x18,0x1F,0x0F,0x00,0x00,0x0F,0x1F,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,
0x18,0x1F,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE2,0x92,0x8A,0x86,0x00,
0x00,0x7C,0x82,0x82,0x82,0x7C,0x00,0xFE,0x00,0x82,0x92,0xAA,0xC6,0x00,0x00,0xC0,
0xC0,0x00,0x7C,0x82,0x82,0x82,0x7C,0x00,0x00,0x02,0x02,0x02,0xFE,0x00,0x00,0xC0,
0xC0,0x00,0x7C,0x82,0x82,0x82,0x7C,0x00,0x00,0xFE,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x24,0xA4,0x2E,0x24,0xE4,
0x24,0x2E,0xA4,0x24,0x00,0x00,0x00,0xF8,0x4A,0x4C,0x48,0xF8,0x48,0x4C,0x4A,0xF8,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0xC0,0x20,0x10,0x10,0x10,0x10,0x20,0xC0,0x00,0x00,
0xC0,0x20,0x10,0x10,0x10,0x10,0x20,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,
0x0A,0x07,0x02,0x7F,0x02,0x07,0x0A,0x02,0x00,0x00,0x00,0x0B,0x0A,0x0A,0x0A,0x7F,
0x0A,0x0A,0x0A,0x0B,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0x20,0x40,0x40,0x40,0x50,
0x20,0x5F,0x80,0x00,0x1F,0x20,0x40,0x40,0x40,0x50,0x20,0x5F,0x80,0x00,0x00,0x00,
};
#endif
(3)dts
📌在i2c1节点下追加oled节点即可,0x3c这个地址前面解释过了
&i2c1 {
clock-frequency = <100000>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_i2c1>;
status = "okay";
oled@3c{
compatible = "wo002582,oled";
reg = <0x3c>;
};
};
(4)Makefile
KERNELDIR := /home/wo002582/linux-imx-lf-6.6.y/
CURRENT_PATH := $(shell pwd)
obj-m += oled.o
build: kernel_modules
kernel_modules:
$(MAKE) KBUILD_MODPOST_WARN=1 -C $(KERNELDIR) M=$(CURRENT_PATH) modules
clean:
$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐



所有评论(0)