linux i2c 24c08 tiny6410 一个24c08的设备驱动
来源:互联网 发布:金庸小说 知乎 话题 编辑:程序博客网 时间:2024/05/17 01:59
之前在做i2c测试的时候,都是利用内核源码/driver/i2c/i2c-dev.c这一份已经写好的设备驱动,它提供了我们所要使用的open,read,write,ioctl的接口函数。
这种测试的代码见: http://blog.csdn.net/weiqing1981127/article/details/8010762
在文章的最后,前面对内核代码的分析也推荐大家看看。这种测试的方法就是要在用户程序定义和内核空间一样的i2c_rdwr_ioctl_data(见i2c-dev.c),不能自己随便来,显得很不方便。
以下是我自己写的一份I2C设备驱动
1.添加自己的i2c设备:打开内核源码文件arch/arm/mach-s3c64xx/mach-mini6410.c,添加如下代码,最后一个就是我自己添加的
然后还是在这个文件的mini6410_machine_init函数中增加如下代码
这是newstyle的添加i2c设备的方法,参考这个: http://blog.csdn.net/lanmanck/article/details/7836734
接下来在重新编译下内核,在内核源码根目录下make zImage,会在arch/arm/boot下生成zImage文件,利用SDCARD或者其他方法,烧写该内核到板子上,之所以要进行这一步是因为板子上的内核没有你添加的那个board_info(即24c08_my),至于怎样烧写就不用我再详细说明了吧,能学到i2c的不至于不会吧。。。真不会的留言
2.第二步奉上源码和测试程序
24c08.c:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/kdev_t.h>
#include <asm/uaccess.h>
#define MAJOR_24C08 155
unsigned int major = MAJOR_24C08;
static struct i2c_driver at24c08_driver;
static struct i2c_adapter *at24c08_adapter;
static unsigned short addr;
static struct class *at24c08_cls = NULL;
static unsigned char r_addr = 0;
ssize_t at24c08_read (struct file *filp, char __user *buf, size_t sz, loff_t *off)
{
struct i2c_msg msg[2];
unsigned char args, data[2];
printk("[read]\n");
printk("[addr:0x%x]\n",addr);
// if(copy_from_user((void *)&args, buf, 1))//这部分语句已经没用了,下面我将用别的方法把读地址
// return -EFAULT; 发回用户空间
args = 0x01;
/* 先传读地址 */
msg[0].addr = addr;
msg[0].buf = &r_addr; //原来的语句是这样msg[0].buf = &args;
msg[0].len = 1;
msg[0].flags = 0;
// printk("read_addr=%x,",*buf); //buf被清零了,所以打印出来的read_addr是0
printk("read_addr=%x,",r_addr);
/* 再 读 */
msg[1].addr = addr;
msg[1].buf = &data[0];
msg[1].len = 1;
msg[1].flags = 1; /* 读 */
if (2 == i2c_transfer(at24c08_adapter, msg, 2)) {
/* 读成功 */
printk("read_value=%x\n",data[0]);
data[1] = r_addr;
if(copy_to_user((void *)buf, &data, sz))
return -EFAULT;
return 1;
}
else
return -EIO;
}
ssize_t at24c08_write (struct file *filp, const char __user *buf, size_t sz, loff_t *off)
{
struct i2c_msg msg;
unsigned char args[2];
printk("[write]\n");
printk("[addr:0x%x]\n",addr);
if(copy_from_user((void *)&args, buf, sz))
return -EFAULT;
printk("write_addr=%x,write_value=%x\n",args[0],args[1]);
//以下一步非常重要,它将提供读地址给read函数,根据我的理解,默认情况下,虽然用户空间的read函数
//传进来了读地址buf,不过它会在内核空间的read函数被清零,这就导致读地址一直是0不是我们需要读
//的地址
r_addr = args[0];
/* args[0] = addr, args[1] = val */
msg.addr = addr;
msg.buf = args;
msg.len = sz;
msg.flags = 0; /* 写 */
if(1 == i2c_transfer(at24c08_adapter,&msg, 1))
return 2;
else
return -EIO;
}
static struct file_operations at24c08_fops = {
.owner = THIS_MODULE,
.read = at24c08_read,
.write = at24c08_write,
};
static struct cdev cdev;
static int at24c08_probe(struct i2c_client *client, const struct i2c_device_id *dev_id)
{
dev_t devno = MKDEV(major,0);
printk("[probe]\n");
printk("[client->addr:0x%x]\n",client->addr);
if(major)
register_chrdev_region(devno, 1, "at24c08");
else{
alloc_chrdev_region(&devno,0,1,"at24c08");
major = MAJOR(devno);
}
cdev_init(&cdev, &at24c08_fops);
cdev_add(&cdev, MKDEV(major, 0), 1);
at24c08_cls = class_create(THIS_MODULE,"at24c08_cls");
device_create(at24c08_cls, NULL, MKDEV(major, 0), NULL, "24c08_dev");
at24c08_adapter = client->adapter;
addr = client->addr; //这2步至关重要,read,write的msg.addr要靠这个赋值
return 0;
}
static int at24c08_remove(struct i2c_client *client)
{
printk("[remove]\n");
device_destroy(at24c08_cls, MKDEV(major, 0));
class_destroy(at24c08_cls);
cdev_del(&cdev);
unregister_chrdev_region(MKDEV(major, 0), 1);
return 0;
}
static const struct i2c_device_id at24c08_id[] = {
{"24c08_my", 0},
{}
};
MODULE_DEVICE_TABLE(i2c,at24c08_id);
static struct i2c_driver at24c08_driver= {
.probe = at24c08_probe,
.remove = at24c08_remove,
.driver = {
.name = "24c08_driver",
},
.id_table = at24c08_id,
};
static int at24c08_init(void)
{
printk("[init]\n");
i2c_add_driver(&at24c08_driver);
return 0;
}
static void at24c08_exit(void)
{
printk("[exit]\n");
i2c_del_driver(&at24c08_driver);
return ;
}
module_init(at24c08_init);
module_exit(at24c08_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("ZHYANG");
代码看起来似乎比较长,但其实不是的,大家可以把那些我在调试的时候用到的打印信息删掉看起来就舒服多了,read,write函数中的注释最好看下,比较重要,原因是read函数中从用户程序传过来的buf会被清零掉,这个buf原本是存放我们要读的地址。
makefile:
obj-m = 24c08.o
KDIR := /opt/FriendlyARM/mini6410/linux/linux-2.6.38/
all:
make -C $(KDIR) SUBDIRS=$(shell pwd) modules
clean:
rm -f *.o *.ko *.mod.* *.symvers *.order
测试程序test_24c08_my.c:
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
int fd;
unsigned char dat[2];
fd = open("/dev/24c08_dev", O_RDWR);
if (-1 == fd) {
printf("failed to open device.\n");
return -1;
}
dat[0] = 0x11; //要写入的地址
dat[1] = 0x64; //写入的值
write(fd, dat, 2);
sleep(1);
// dat[0] = 0x11; //这一步其实没意义了,读地址在内核空间会被传递
// dat[1] = dat[0]; //内核空间也会被读地址发过来,用户空间不用再指定读地址
dat[0] = dat[1] = 0; //清零
read(fd, dat,2);
printf("read address 0x%2x: %x\n",dat[1],dat[0]);
return 0;
}
有不懂的问题,或者有什么错误指出,欢迎大家在评论中指教!谢谢!
- linux i2c 24c08 tiny6410 一个24c08的设备驱动
- I2C总线的EEPROM(24C08)Linux驱动
- I2C总线的EEPROM(24C08)Linux驱动
- I2C总线的EEPROM(24C08)Linux驱动(原创)
- s3c2440A操作24C08
- c08
- 原机拷贝康佳SP21SK391原机数据(24C08 OM8373PS/N3/A/1914)
- C08(初见AWT)
- linux i2c设备驱动
- Linux I2C 设备驱动
- linux i2c设备驱动 .
- linux I2C设备驱动
- Linux 设备驱动 - I2C
- linux i2c设备驱动
- linux i2c设备驱动
- linux i2c设备驱动
- linux驱动-I2C设备
- LINUX I2C设备驱动的编写方法
- 对象的序列化与反序列化
- C# 实现Excel转Word、Word转PDF、合并PDF
- Objective-C Programming: The Big Nerd Ranch Guide (2nd Edition) 阅读笔记(Part V)
- GOF23之原型模式Demo
- Linux中各目录含义
- linux i2c 24c08 tiny6410 一个24c08的设备驱动
- 堆和栈的区别
- 优化Linux下的内核TCP参数以提高系统性能
- java中URL和File的相互转化
- struts2.0配置及使用
- 231A Team
- html5移动设备浏览器触屏事件兼容处理
- 【Android 排错第二例】UnsupportedOperationException
- SparkSQL 使用jdbcRDD访问数据库