一、实验平台:开发板fs2410,采用三星s3c2410的CPU,linux操作系统。
二、实现功能:向E2PROM里面写数据,再读出来。
三、实验原理:
E2PROM属于I2C设备,所以这里用到了I2C的数据传输协议,一根专用串行数据线 SDA和一根串行时钟线SCL在总线主机和连到 I2C总线上的外设之间传输数据,SDA和SCL都是双向的。
当 I2C总线空闲时,SDA和 SCL线都处于高电平状态,当 SCL保持高电平时,一个SDA下降沿可以初始化一个起始条件;当 SCL保持高电平时,SDA的一个上升沿可以初始化一个停止条件。
发送到 SDA上的每个数据必须是8位的,数据总是从MSB开始传输,所有字节后都必须跟1个ACK应答位。
四、实验现象:
应用程序执行时,若输入./e2prom_test w 1则向E2PROM写入数据1,如输入./e2prom_test r则从E2PROM读取数据,并打印出来。由于应用程序一次最多只能写一个字节,所以0~255的数据可以正确读写,超出范围后,数据会失真。
五、实验总结:
在内核中有I2C驱动的体系架构,将其划分为三个层次,从上往下依次为:设备驱动层,核心层以及控制器驱动层。这里主要实现设备驱动层,下面两层使用内核的代码。
驱动的体系架构很重要,不清楚体系架构就没有写代码的思路,所以要多跟内核的代码。同时要体会分层的思想。
六、示例代码:
/*设备驱动代码e2prom.c*/
#include<linux/kernel.h>
#include<linux/module.h>
#include<linux/init.h>
#include<linux/device.h>
#include<linux/fs.h>
#include <linux/i2c.h>
#include<asm/uaccess.h>
static unsigned int e2prom_major = 0;
static struct class *e2prom_class = NULL;
static unsigned short ignore[] = { I2C_CLIENT_END };
static unsigned short normal_addr[] = {0x50, I2C_CLIENT_END };
static unsigned char msgs_data[1024];
static struct i2c_client at24c02_client;
static struct i2c_client_address_data at24c02_addr_data = {
.normal_i2c = normal_addr,
.probe = ignore,
.ignore = ignore,
};
static int at24c02_detect(struct i2c_adapter *adapter, int address, int kind);
static int at24c02_attach_adapter(struct i2c_adapter *adapter);
static int at24c02_detach_client(struct i2c_client *client);
static struct i2c_driver at24c02_driver = {
.driver = {
.name = "at24c02",
},
.attach_adapter = at24c02_attach_adapter,
.detach_client = at24c02_detach_client,
};
static int e2prom_open(struct inode *inode, struct file *file)
{
return 0;
}
static ssize_t e2prom_read(struct file *file, char __user *buf, size_t count, loff_t *opps)
{
int ret;
unsigned char addr = 0;
struct i2c_msg msgs[1];
msgs[0].addr = at24c02_client.addr;
msgs[0].flags = 0; //读之前要写入器件地址和偏移量
msgs[0].len = 1;
msgs[0].buf = &addr; //偏移地址
msgs[1].addr = at24c02_client.addr;
msgs[1].flags = 1; //读命令
msgs[1].len = 1;
msgs[1].buf = msgs_data; //存放读取的数据
i2c_transfer(at24c02_client.adapter, msgs, 2);
ret = copy_to_user(buf, msgs_data, count);
return 0;
}
static ssize_t e2prom_write(struct file *file, const char __user *buf, size_t count, loff_t *opps)
{
int ret;
struct i2c_msg msgs;
ret = copy_from_user(&msgs_data[1], buf, count);
msgs_data[0] = 0;
msgs.addr = at24c02_client.addr;
msgs.flags = 0; //写命令
msgs.buf = msgs_data; //要写入的数据
msgs.len = count+1;
i2c_transfer(at24c02_client.adapter, &msgs, 1);
return 0;
}
static struct file_operations e2prom_fops = {
.owner = THIS_MODULE,
.open = e2prom_open,
.read = e2prom_read,
.write = e2prom_write,
};
/*构建struct i2c_client来描述检测到的I2C设备*/
static int at24c02_detect(struct i2c_adapter *adapter, int address, int kind)
{
/*初始化i2c_client结构体*/
at24c02_client.adapter = adapter;
at24c02_client.addr = address;
at24c02_client.driver = &at24c02_driver;
at24c02_client.flags = 0;
strcpy(at24c02_client.name,"at24c02");
/*加载到I2C子系统中*/
i2c_attach_client(&at24c02_client);
/*注册到内核*/
e2prom_major = register_chrdev(0, "e2prom", &e2prom_fops);
e2prom_class = class_create(THIS_MODULE, "e2prom_class");
class_device_create(e2prom_class, NULL, MKDEV(e2prom_major,0), NULL, "e2prom");
return 0;
}
/*检测I2C设备是否存在,存在就调用at24c02_detect函数*/
static int at24c02_attach_adapter(struct i2c_adapter *adapter)
{
return i2c_probe(adapter, &at24c02_addr_data, at24c02_detect);
}
static int at24c02_detach_client(struct i2c_client *client)
{
i2c_detach_client(client);
unregister_chrdev(e2prom_major, "e2prom");
class_destroy(e2prom_class);
class_device_destroy(e2prom_class, MKDEV(e2prom_major,0));
return 0;
}
int __init e2prom_init(void)
{
i2c_add_driver(&at24c02_driver);
return 0;
}
void __exit e2prom_exit(void)
{
i2c_del_driver(&at24c02_driver);
}
module_init(e2prom_init);
module_exit(e2prom_exit);
MODULE_LICENSE("GPL");
/*测试代码e2prom_test.c*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
void print_usage(char *str)
{
printf("%s r : read at24c02 addresss 0\n", str);
printf("%s w val : write at24c02 addresss 0\n", str);
}
int main(int argc, char **argv)
{
int fd;
int ret;
unsigned char val;
if (argc < 2)
{
print_usage(argv[0]);
return -1;
}
if((fd = open("/dev/e2prom", O_RDWR)) == -1){
perror("open /dev/e2prom failed");
exit(-1);
}
if (strcmp(argv[1], "r") == 0){
read(fd, &val, 1);
printf("get data:%d\n", val);
}
else if ((strcmp(argv[1], "w") == 0) && (argc == 3)){
val = strtoul(argv[2], NULL, 0);
write(fd, &val, 1);
}
return 0;
}