使用i2c子系统驱动i2c oled模块

1、使用linux内核里面的i2c子系统来驱动i2c oled 屏幕
代码如下:

#include "linux/i2c.h"
#include <linux/module.h>
#include <linux/poll.h>

#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#include <linux/gpio/consumer.h>
#include <linux/platform_device.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/slab.h>
#include <linux/fcntl.h>
#include <linux/timer.h>
#include <linux/delay.h>
#include "font.h"
typedef struct i2cmsg
{
	int row;
	int col;
	int buf_len;
	void *buf;
} i2cmsg_type;
struct i2c_client *g_client;
static int major = 0;
struct class *i2c_cls;
#define OLED_I2C_ADDRESS 0x3c
#define OLED_CMD 0x00
#define OLED_DATA 0x40
static void Oled_Clear(struct i2c_client *client);
static int i2c_transfer_char(struct i2c_client *client, unsigned char data, unsigned char oled_ctrl);
static void Oled_Show_Str(char row, char col, char *str);
static long i2c_oled_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{

	return 0;
}
static ssize_t i2c_oled_write(struct file *file, const char __user *buf, size_t size, loff_t *filp)
{
    int ret;
    void *kernel_buf;
    
    // 分配内存
    i2cmsg_type *msg = kzalloc(sizeof(i2cmsg_type), GFP_KERNEL);
    if (!msg) {
        printk(KERN_ERR "Failed to allocate memory for i2cmsg_type\n");
        return -ENOMEM;
    }

    // 从用户空间复制 i2cmsg_type 结构体
    ret = copy_from_user(msg, buf, sizeof(i2cmsg_type));
    if (ret != 0) {
        printk(KERN_ERR "Failed to copy i2cmsg_type from user space, %d bytes not copied\n", ret);
        kfree(msg);
        return -EFAULT;
    }

    // 检查 buf_len 是否合理
    if (msg->buf_len > 20) {
        printk(KERN_ERR "Buffer length exceeds maximum allowed size\n");
        kfree(msg);
        return -EINVAL;
    }

    // 分配内存给 buf 指向的数据
    kernel_buf = kzalloc(msg->buf_len, GFP_KERNEL);
    if (!kernel_buf) {
        printk(KERN_ERR "Failed to allocate memory for buffer\n");
        kfree(msg);
        return -ENOMEM;
    }

    // 从用户空间复制实际的数据
    ret = copy_from_user(kernel_buf, buf + sizeof(i2cmsg_type), msg->buf_len);
    if (ret != 0) {
        printk(KERN_ERR "Failed to copy data from user space, %d bytes not copied\n", ret);
        kfree(kernel_buf);
        kfree(msg);
        return -EFAULT;
    }

    // 清除 OLED 显示
    Oled_Clear(g_client);

    // 显示字符串
    Oled_Show_Str(msg->row, msg->col, (char *)kernel_buf);

    // 打印调试信息
    printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);

    // 释放内存
    kfree(kernel_buf);
    kfree(msg);
    return size;
}




static struct file_operations i2c_oled_fops =
	{
		.owner = THIS_MODULE,
		.write = i2c_oled_write,
		.unlocked_ioctl = i2c_oled_ioctl,
};

static const struct of_device_id i2c_dt_match[] =
	{
		{.compatible = "first,oled_sc", .data = NULL},
		{/* END OF LIST */},
};
static const struct i2c_device_id ap3216c_ids[] = {
	{"oled", (kernel_ulong_t)NULL},
	{/* END OF LIST */}};

static int i2c_transfer_char(struct i2c_client *client, unsigned char data, unsigned char oled_ctrl)
{
	int ret;
	struct i2c_msg msg;
	unsigned char buf[2];

	// 设置控制字节 (OLED_CMD or OLED_DATA) 和数据字节
	buf[0] = oled_ctrl;
	buf[1] = data;

	// 准备 I2C 消息
	msg.addr = client->addr; // 使用 client->addr 而不是硬编码地址
	msg.flags = 0;			 // 写操作
	msg.len = 2;			 // 传输 2 字节数据
	msg.buf = buf;

	// 进行 I2C 传输
	ret = i2c_transfer(client->adapter, &msg, 1);
	if (ret < 0)
	{
		printk("i2c_transfer error\n");
		return ret;
	}

	return 0;
}

static void Oled_Show_Char(char row, char col, char oledChar)
{ // row*2-2
	unsigned int i;
	i2c_transfer_char(g_client, 0xb0 + (row * 2 - 2), OLED_CMD); // page 0
	i2c_transfer_char(g_client, 0x00 + (col & 0x0f), OLED_CMD);	 // low
	i2c_transfer_char(g_client, 0x10 + (col >> 4), OLED_CMD);	 // high

	for (i = ((oledChar - 32) * 16); i < ((oledChar - 32) * 16 + 8); i++)
	{
		i2c_transfer_char(g_client, F8X16[i], OLED_DATA); // 写数据oledTable1
	}

	i2c_transfer_char(g_client, 0xb0 + (row * 2 - 1), OLED_CMD); // page 1
	i2c_transfer_char(g_client, 0x00 + (col & 0x0f), OLED_CMD);	 // low
	i2c_transfer_char(g_client, 0x10 + (col >> 4), OLED_CMD);	 // high
	for (i = ((oledChar - 32) * 16 + 8); i < ((oledChar - 32) * 16 + 8 + 8); i++)
	{
		i2c_transfer_char(g_client, F8X16[i], OLED_DATA); // 写数据oledTable1
	}
}
static void Oled_Show_Str(char row, char col, char *str)
{
	while (*str != 0)
	{
		Oled_Show_Char(row, col, *str);
		str++;
		col += 8;
	}
}

static void Oled_Clear(struct i2c_client *client)
{
	unsigned char i, j; //-128 --- 127

	for (i = 0; i < 8; i++)
	{
		i2c_transfer_char(client, 0xB0 + i, OLED_CMD); // page0--page7
		// 每个page从0列
		i2c_transfer_char(client, 0x00, OLED_CMD);
		i2c_transfer_char(client, 0x10, OLED_CMD);

		// 0到127列,依次写入0,每写入数据,列地址自动偏移
		for (j = 0; j < 128; j++)
		{
			i2c_transfer_char(client, 0, OLED_DATA);
		}
	}
}

static int oled_init(struct i2c_client *i2c_client)
{

	i2c_transfer_char(i2c_client, 0xAE, OLED_CMD);
	i2c_transfer_char(i2c_client, 0x00, OLED_CMD);
	i2c_transfer_char(i2c_client, 0x10, OLED_CMD);
	i2c_transfer_char(i2c_client, 0x40, OLED_CMD);
	i2c_transfer_char(i2c_client, 0xB0, OLED_CMD);
	i2c_transfer_char(i2c_client, 0x81, OLED_CMD);
	i2c_transfer_char(i2c_client, 0xFF, OLED_CMD);
	i2c_transfer_char(i2c_client, 0xA1, OLED_CMD);
	i2c_transfer_char(i2c_client, 0xA6, OLED_CMD);
	i2c_transfer_char(i2c_client, 0xA8, OLED_CMD);
	i2c_transfer_char(i2c_client, 0x3F, OLED_CMD);
	i2c_transfer_char(i2c_client, 0xC8, OLED_CMD);
	i2c_transfer_char(i2c_client, 0xD3, OLED_CMD);
	i2c_transfer_char(i2c_client, 0x00, OLED_CMD);
	i2c_transfer_char(i2c_client, 0xD5, OLED_CMD);
	i2c_transfer_char(i2c_client, 0x80, OLED_CMD);
	i2c_transfer_char(i2c_client, 0xD8, OLED_CMD);
	i2c_transfer_char(i2c_client, 0x05, OLED_CMD);
	i2c_transfer_char(i2c_client, 0xD9, OLED_CMD);
	i2c_transfer_char(i2c_client, 0xF1, OLED_CMD);
	i2c_transfer_char(i2c_client, 0xDA, OLED_CMD);
	i2c_transfer_char(i2c_client, 0x12, OLED_CMD);
	i2c_transfer_char(i2c_client, 0xDB, OLED_CMD);
	i2c_transfer_char(i2c_client, 0x30, OLED_CMD);
	i2c_transfer_char(i2c_client, 0x8D, OLED_CMD);
	i2c_transfer_char(i2c_client, 0x14, OLED_CMD);
	i2c_transfer_char(i2c_client, 0xAF, OLED_CMD);

	i2c_transfer_char(i2c_client, 0x20, OLED_CMD);
	i2c_transfer_char(i2c_client, 0x02, OLED_CMD);

	Oled_Clear(i2c_client);

	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	return 0;
}
static int i2c_oled_probe(struct i2c_client *i2c_client, const struct i2c_device_id *i2c_device_id)
{
	int ret;
	unsigned short slave_addr;
	g_client = i2c_client;
	slave_addr = 0x007f | g_client->addr;
	printk("slave client addr:0x%x\n", g_client->addr);
	major = register_chrdev(0, "oled_i2c", &i2c_oled_fops);

	i2c_cls = class_create(THIS_MODULE, "oled_class");
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	if (IS_ERR(i2c_cls))
	{
		printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
		unregister_chrdev(major, "oled_i2c");
		return PTR_ERR(i2c_cls);
	}

	device_create(i2c_cls, NULL, MKDEV(major, 0), NULL, "oled_i2c");

	ret = oled_init(g_client);
	if (ret < 0)
	{
		printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
		printk("oled_init error\n");
		return ret;
	}
	Oled_Show_Str(2, 2, "welcome");
	printk("oled_init success\n");
	return 0;
}

static int i2c_oled_remove(struct i2c_client *i2c_client)
{

	device_destroy(i2c_cls, MKDEV(major, 0));
	unregister_chrdev(major, "oled_i2c");
	class_destroy(i2c_cls);
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	return 0;
}

static struct i2c_driver i2c_oled = {
	.driver = {
		.name = "oled",
		.of_match_table = i2c_dt_match,

	},
	.probe = i2c_oled_probe,
	.remove = i2c_oled_remove,
	.id_table = ap3216c_ids,
};

static int __init i2c_oled_init(void)
{
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	return i2c_add_driver(&i2c_oled);
}

static void __exit i2c_oled_exit(void)
{
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	i2c_del_driver(&i2c_oled);
}

module_init(i2c_oled_init);
module_exit(i2c_oled_exit);
MODULE_LICENSE("GPL");

然后在对应的设备树里面i2c master下面加上i2c client的信息就可以了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值