一、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

在这里插入图片描述

Logo

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

更多推荐