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的信息就可以了