参考: https://blog.csdn.net/zlsh007/article/details/21600759
1 需求
在imx6ul上完成EEROM驱动
2 修改设备树
查询数据手册得地址为0xa0
linux设备树里面需要全部右移一位,于是地址是0x50
&i2c1 {
clock-frequency = <100000>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_i2c1>;
status = "okay";
/* EEPROM */
at24c512c@50 {
compatible = "at24c512c";
reg = <0x50>;
status = "ok";
};
};
3 驱动
#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>
#define eerom_CNT 1
#define eerom_NAME "eerom"
#define MAX_SIZE 512
struct oled_dev {
dev_t devid; /* 设备号 */
struct cdev cdev; /* cdev */
struct class *class; /* 类 */
struct device *device; /* 设备 */
struct device_node *nd; /* 设备节点 */
int major; /* 主设备号 */
void *private_data; /* 私有数据 */
unsigned short ir, als, ps; /* 三个光传感器数据 */
};
static struct oled_dev oled_dev;
// read reg
static int eerom_read_regs(u16 reg, void *val, int len)
{
int ret;
struct i2c_msg msg[2];
struct i2c_client *client = (struct i2c_client *)oled_dev.private_data;
uint8_t addr[2];
addr[0]=reg>>8;
addr[1]=reg&0xff;
/* msg[0]为发送要读取的首地址 */
msg[0].addr = client->addr; /* eerom地址 */
msg[0].flags = 0; /* 标记为发送数据 */
msg[0].buf = &addr; /* 读取的首地址 */
msg[0].len = 2; /* reg长度*/
/* msg[1]读取数据 */
msg[1].addr = client->addr; /* eerom地址 */
msg[1].flags = I2C_M_RD; /* 标记为读取数据*/
msg[1].buf = val; /* 读取数据缓冲区 */
msg[1].len = len; /* 要读取的数据长度*/
ret = i2c_transfer(client->adapter, msg, 2);
if(ret == 2) {
ret = 0;
} else {
printk("i2c rd failed=%d reg=%06x len=%d\n",ret, reg, len);
ret = -EREMOTEIO;
}
return ret;
}
// read buff
static int eerom_read_buff(void *val, int len)
{
int ret;
struct i2c_msg msg[2];
struct i2c_client *client = (struct i2c_client *)oled_dev.private_data;
/* msg[0]为发送要读取的首地址 */
msg[0].addr = client->addr; /* eerom地址 */
msg[0].flags = 0; /* 标记为发送数据 */
msg[0].buf = &val[0]; /* 读取的首地址 */
msg[0].len = 2; /* reg长度*/
/* msg[1]读取数据 */
msg[1].addr = client->addr; /* eerom地址 */
msg[1].flags = I2C_M_RD; /* 标记为读取数据*/
msg[1].buf = val; /* 读取数据缓冲区 */
msg[1].len = 1; /* 要读取的数据长度*/
ret = i2c_transfer(client->adapter, msg, 2);
if(ret == 2) {
ret = 0;
} else {
ret = -EREMOTEIO;
}
return ret;
}
// 写寄存器
static s32 eerom_write_regs(u16 reg, u8 *buf, u8 len)
{
unsigned char dev_addr[MAX_SIZE];
struct i2c_msg msg;
struct i2c_client *client = (struct i2c_client *)oled_dev.private_data;
dev_addr[0]=reg>>8;
dev_addr[1]=reg&0xff;
memcpy(&dev_addr[2],buf,len);
msg.addr = client->addr; /* eerom地址 */
msg.flags = 0; /* 标记为写数据 */
msg.buf = &dev_addr[0];; /* 要写入的数据缓冲区 */
msg.len = 2+len; /* 要写入的数据长度 */
return i2c_transfer(client->adapter, &msg, 1);
}
// 写缓冲
static s32 eerom_write_buff(u8 *buf, u8 len)
{
struct i2c_msg msg;
struct i2c_client *client = (struct i2c_client *)oled_dev.private_data;
msg.addr = client->addr; /* eerom地址 */
msg.flags = 0; /* 标记为写数据 */
msg.buf = &buf[0];; /* 要写入的数据缓冲区 */
msg.len = len; /* 要写入的数据长度 */
return i2c_transfer(client->adapter, &msg, 1);
}
static int oled_open(struct inode *inode, struct file *filp)
{
return 0;
}
static ssize_t oled_read(struct file *filp, char __user *buf, size_t size, loff_t *offset)
{
if(size >= MAX_SIZE){
return -1;
}
uint8_t mbuff[MAX_SIZE];
copy_from_user(mbuff,buf,2);
eerom_read_buff(mbuff,1);
copy_to_user(buf,&mbuff,size);
return 0;
}
static ssize_t oled_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *off)
{
if(cnt >= MAX_SIZE){
return -1;
}
uint8_t mbuff[cnt];
copy_from_user(mbuff,buf,cnt);
eerom_write_buff(mbuff,cnt);
return 0;
}
// IO控制
static long oled_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
}
static int oled_release(struct inode *inode, struct file *filp)
{
return 0;
}
/* eerom操作函数 */
static const struct file_operations oled_ops = {
.owner = THIS_MODULE,
.open = oled_open,
.write=oled_write,
.read = oled_read,
.unlocked_ioctl =oled_ioctl,
.release = oled_release,
};
static int oled_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
/* 1、构建设备号 */
if (oled_dev.major) {
oled_dev.devid = MKDEV(oled_dev.major, 0);
register_chrdev_region(oled_dev.devid, eerom_CNT, eerom_NAME);
} else {
alloc_chrdev_region(&oled_dev.devid, 0, eerom_CNT, eerom_NAME);
oled_dev.major = MAJOR(oled_dev.devid);
}
/* 2、注册设备 */
cdev_init(&oled_dev.cdev, &oled_ops);
cdev_add(&oled_dev.cdev, oled_dev.devid, eerom_CNT);
/* 3、创建类 */
oled_dev.class = class_create(THIS_MODULE, eerom_NAME);
if (IS_ERR(oled_dev.class)) {
return PTR_ERR(oled_dev.class);
}
/* 4、创建设备 */
oled_dev.device = device_create(oled_dev.class, NULL, oled_dev.devid, NULL, eerom_NAME);
if (IS_ERR(oled_dev.device)) {
return PTR_ERR(oled_dev.device);
}
oled_dev.private_data = client;
printk("hello match:%x\r\n",client->addr);
return 0;
}
static oled_remove(struct i2c_client *client)
{
/* 删除设备 */
cdev_del(&oled_dev.cdev);
unregister_chrdev_region(oled_dev.devid, eerom_CNT);
/* 注销掉类和设备 */
device_destroy(oled_dev.class, oled_dev.devid);
class_destroy(oled_dev.class);
return 0;
}
/* 传统匹配方式ID列表 */
static const struct i2c_device_id oled_id[] = {
{"at24c512c", 0},
{}
};
/* 设备树匹配列表 */
static const struct of_device_id oled_of_match[] = {
{ .compatible = "at24c512c" },
{ /* Sentinel */ }
};
/* i2c驱动结构体 */
static struct i2c_driver oled_driver = {
.probe = oled_probe,
.remove = oled_remove,
.driver = {
.owner = THIS_MODULE,
.name = "at24c512c",
.of_match_table = oled_of_match,
},
.id_table = oled_id,
};
static int __init oled_init(void)
{
int ret = 0;
ret = i2c_add_driver(&oled_driver);
return ret;
}
static void __exit oled_exit(void)
{
i2c_del_driver(&oled_driver);
}
/* module_i2c_driver(eerom_driver) */
module_init(oled_init);
module_exit(oled_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("kirito");
4 应用
#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"
#include "stdint.h"
#define LEDOFF 0
#define LEDON 1
struct gpio_ctl
{
uint8_t io;
uint8_t state;
};
/*
* @description : main主程序
* @param - argc : argv数组元素个数
* @param - argv : 具体参数
* @return : 0 成功;其他 失败
*/
int main(int argc, char *argv[])
{
int fd, retvalue;
char *filename;
if(argc != 2){
printf("Error Usage!\r\n");
return -1;
}
filename = argv[1];
/* 打开led驱动 */
fd = open(filename, O_RDWR);
if(fd < 0){
printf("file %s open failed!\r\n", argv[1]);
return -1;
}
struct gpio_ctl ctl;
int state = 0;
int i =0;
char buf[20];
uint16_t addr = 1;
while (1)
{
sleep(1);
if(state){
state = 0;
buf[0]=addr>>8;
buf[1]=addr&0xff;
read(fd,buf, 1);
printf("data:%x\r\n",buf[0]);
}else{
state =1;
buf[0]=addr>>8;
buf[1]=addr&0xff;
buf[2]=i;
write(fd, buf, 3);
i++;
}
}
return 0;
}
5 测试