目录:一. 说明
二. 驱动程序说明及问题
三. 案例一
四. 案例二
一. 说明
mini210开发板上带了at24c08, 看了linux内核自带的at24.c的驱动程序,编译下载到看发板,读写都行;通过增加一些调试信息,对linux i2c驱动其中的编写方法之一有了一定了解,在我的另外一篇博文有详细说明。但同时对在linux下GPIO模拟i2c产生了兴趣,于是就写这篇博文来记录驱动编写过程中遇到的问题。如果想了解了i2c时序,请google或百度一下。
本篇博文通过misc驱动模型来编写模拟i2c设备驱动:比如at24c08, pcf8591(AD/DA)。
本博文是本人原创,如有不足之处,请斧正;
二. 驱动程序说明及议题
1.1 本驱动使用s3c_gpio_cfgpin来配制硬件引脚寄存器,其实可以使用ioremap函数后,来对硬件寄存器地址访问;如果想详细了解,请看linux驱动初级开发第01篇--I/O内存控制硬件buzzer。
1.2 pcf8591(AD/DA)驱动程序,基本上没遇到什么问题;但是编写出来的atc08驱动程序,出现了些现在无法解决的问题;
问题1:只能先读一个字节,再写一个字节;
问题2: 如果先写一个字节,再读一个字节会读出现错误;但是去掉write_set_at24xx函数中i2c_send_byte(dat)就不会出现读出错,但是不会写入数据。
问题3:一次写多个字节,就会出现一些字节写出错。
问题4: 只读就不个会出错,但只写就会出现一些字节写出错
三. 案例一
pcf8591.c:
运行结果:
代码:
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/ioctl.h>
#include <linux/cdev.h>
#include <linux/delay.h>
#include <asm/uaccess.h> //其中copy_to*在其中
#include <mach/gpio.h>
#include <mach/regs-gpio.h>
#include <plat/gpio-cfg.h>
spinlock_t lock; //加不加这都 可以
#define DEVICE_NAME "PCF8591"
#define uchar unsigned char
int askflag;
#define NUM 4 //接收和发送缓存区的深度
uchar receivebuf[NUM]; //数据接收缓冲区
uchar SystemError; //从机错误标志位
void set_conOUT_sda(void);
void set_conOUT_scl(void);
void set_conIN_sda(void);
void set_conIN_scl(void);
void set_data_sda(int i);
void set_data_scl(int i);
//-------------------------------------------------------------------
// 函数名称: i2c_start()
// 函数功能: 启动I2C总线子程序
//-------------------------------------------------------------------
void i2c_start(void)
{ //时钟保持高,数据线从高到低一次跳变,I2C通信开始
set_conOUT_sda();
set_conOUT_scl();
__udelay(1);
set_data_sda(1);
set_data_scl(1);
__udelay(5);
set_data_sda(0);
__udelay(5);
set_data_scl(0);
}
//-------------------------------------------------------------------
// 函数名称: i2c_stop()
// 函数功能: 停止I2C总线数据传送子程序
//-------------------------------------------------------------------
void i2c_stop(void)
{
set_conOUT_sda();
set_conOUT_scl();
__udelay(1);
set_data_sda(0);//时钟保持高,数据线从低到高一次跳变,I2C通信停止
set_data_scl(1);
__udelay(5);
set_data_sda(1);
__udelay(5);
set_data_scl(0);
}
//------------------------------------------------------------------
// 函数名称: i2c_Init()
// 函数功能: 初始化I2C总线子程序
//------------------------------------------------------------------
void i2c_init(void)
{
set_conOUT_scl();
__udelay(1);
set_data_scl(0);
i2c_stop();
}
//-------------------------------------------------------------------
// 函数名称: slave_ack
// 函数功能: 从机发送应答位子程序
//-------------------------------------------------------------------
void slave_ack(void)
{
set_conOUT_sda();
set_conOUT_scl();
__udelay(1);
set_data_sda(0);
set_data_scl(1);
__udelay(5);
set_data_scl(0);
}
//-------------------------------------------------------------------
// 函数名称: slave_noack
// 函数功能: 从机发送非应答位子程序,迫使数据传输过程结束
//-------------------------------------------------------------------
void slave_noack(void)
{
set_conOUT_sda();
set_conOUT_scl();
__udelay(1);
set_data_sda(1);
set_data_scl(1);
__udelay(5);
set_data_sda(0);
set_data_scl(0);
}
//-------------------------------------------------------------------
// 函数名称: check_ack
// 函数功能: 主机应答位检查子程序,迫使数据传输过程结束
//-------------------------------------------------------------------
void check_ack(void)
{
set_conOUT_sda();
set_conOUT_scl();
__udelay(1);
set_data_sda(1); // 将p1.1设置成输入,必须先向端口写1
set_data_scl(1);
askflag = 0;
__udelay(5);
set_conIN_sda();
__udelay(3);
if ( gpio_get_value(S5PV210_GPE0(3)) == 1)
askflag = 1;
// 若SDA=1表明非应答,置位非应答标志askflag
set_data_scl(0);
}
//-------------------------------------------------------------------
// 函数名称: i2c_send_byte
// 入口参数: ch
// 函数功能: 发送一个字节
//-------------------------------------------------------------------
void i2c_send_byte(uchar ch)
{
unsigned char n=8; // 向SDA上发送一位数据字节,共八位
set_conOUT_sda();
set_conOUT_scl();
__udelay(1);
spin_lock(&lock);
while(n--) {
if((ch&0x80) == 0x80) // 若要发送的数据最高位为1则发送位1
{
set_data_sda(1); // 传送位1
set_data_scl(1);
__udelay(5);
set_data_scl(0);
}
else
{
set_data_sda(0); // 否则传送位0
set_data_scl(1);
__udelay(5);
set_data_scl(0);
}
ch = ch<<1; // 数据左移一位
}
spin_unlock(&lock);
}
//-------------------------------------------------------------------
// 函数名称: i2c_receive_byte
// 返回接收的数据
// 函数功能: 接收一字节子程序
//-------------------------------------------------------------------
uchar i2c_receive_byte(void)
{
uchar n=8; // 从SDA线上读取一上数据字节,共八位
uchar tdata=0;
int temp;
spin_lock(&lock);
while(n--) {
set_conOUT_sda();
set_conOUT_scl();
__udelay(1);
set_data_sda(1);
set_data_scl(1);
tdata =tdata<<1; //左移一位
set_conIN_sda();
__udelay(3);
temp = gpio_get_value(S5PV210_GPE0(3));
if( temp == 1)
tdata = tdata|0x01; // 若接收到的位为1,则数据的最后一位置1
else
tdata = tdata&0xfe; // 否则数据的最后一位置0
//printk("read_data_bit: %d", temp); //++
set_data_scl(0);
}
spin_unlock(&lock);
printk("read_data: %d\n", tdata);
return(tdata);
}
//-------------------------------------------------------------------
// 函数名称: adc_pcf8591
// 入口参数: controlbyte控制字
// 函数功能: 连续读入4路通道的A/D转换结果到receivebuf
//-------------------------------------------------------------------
void adc_pcf8591(uchar controlbyte)
{
uchar receive_da,i=0;
i2c_start();
i2c_send_byte(0x90); //控制字
check_ack();
__udelay(5);
if(askflag == 1) //++
{
SystemError = 1;
return;
}
i2c_send_byte(controlbyte); //控制字
check_ack();
__udelay(5);
if(askflag == 1)//++
{
SystemError = 1;
return;
}
i2c_start(); //重新发送开始命令
i2c_send_byte(0x91); //控制字
check_ack();
__udelay(5);
if(askflag == 1) // ++
{
SystemError = 1;
return;
}
i2c_receive_byte(); //空读一次,调整读顺序
slave_ack(); //收到一个字节后发送一个应答位
__udelay(5);
while(i<4)
{
receive_da=i2c_receive_byte();
receivebuf[i++]=receive_da;
slave_ack(); //收到一个字节后发送一个应答位
}
slave_noack(); //收到最后一个字节后发送一个非应答位
i2c_stop();
}
void set_conIN_sda(void)
{
s3c_gpio_cfgpin(S5PV210_GPE0(3), S3C_GPIO_INPUT);
//gpio_set_value(S5PV210_GPE0(3), 1);
}
void set_conIN_scl(void)
{
s3c_gpio_cfgpin(S5PV210_GPE0(4), S3C_GPIO_INPUT);
//gpio_set_value(S5PV210_GPE0(3), 1);
}
void set_conOUT_sda(void)
{
s3c_gpio_cfgpin(S5PV210_GPE0(3), S3C_GPIO_OUTPUT);
}
void set_conOUT_scl(void)
{
s3c_gpio_cfgpin(S5PV210_GPE0(4), S3C_GPIO_OUTPUT);
}
void set_data_sda(int i)
{
if (i == 0) {
gpio_set_value(S5PV210_GPE0(3), 0);
} else if(i == 1) {
gpio_set_value(S5PV210_GPE0(3), 1);
}
}
void set_data_scl(int i)
{
if (i == 0) {
gpio_set_value(S5PV210_GPE0(4), 0);
} else if(i == 1) {
gpio_set_value(S5PV210_GPE0(4), 1);
}
}
static int pcf8591_open(struct inode *inode, struct file *filp)
{
printk (KERN_INFO "Device opened\n");
spin_lock_init(&lock);
return 0;
}
/*读取数据*/
static int pcf8591_read(struct file *filp, char __user *buffer, size_t count, loff_t *ppos)
{
printk("pcf8591_read\n");
int i;
int v[4]; //存实际的电压值
i2c_init(); //I2C总线初始化
adc_pcf8591(0x04);
if(SystemError == 1) //有错误,重新来
{
printk("SystemError: %d\n", SystemError);
i2c_init(); //I2C总线初始化
adc_pcf8591(0x04);
}
for(i=0;i<4;i++)
{
printk("AD_%d: %d\t\n", i, receivebuf[i]); //显示通道0
v[2]=receivebuf[i]/51; //AD值转换为3位BCD码,最大为5.00V。
v[2]=v[2]; //转换为ACSII码
v[3]=receivebuf[i]%51; //余数暂存
v[3]=v[3]*10; //计算小数第一位
v[1]=v[3]/51;
v[1]=v[1]; //转换为ACSII码
v[3]=v[3]%51;
v[3]=v[3]*10; //计算小数第二位
v[0]=v[3]/51; //
v[0]=v[0]; //转换为ACSII码
printk("AD_%d: %d.%d%dV\t\n", i, v[2], v[1], v[0]);
}
copy_to_user(buffer, receivebuf, sizeof(receivebuf));
return 0;
}
/*写命令,在此置空*/
static int pcf8591_write(struct file *file, const char *buffer, size_t count, loff_t * ppos)
{
return 0;
}
static int pcf8591_release(struct inode *inode,struct file *filp)
{
printk (KERN_INFO "device closed\n");
return 0;
}
static long pcf8591_ioctl(struct file *filp, unsigned int cmd,
unsigned long arg)
{
return 0;
}
static struct file_operations pcf8591_fops = {
.owner = THIS_MODULE,
.open = pcf8591_open,
.read = pcf8591_read,
.write = pcf8591_write,
.unlocked_ioctl = pcf8591_ioctl,
.release = pcf8591_release,
};
static struct miscdevice pcf8591_dev = {
.minor = MISC_DYNAMIC_MINOR,
.name = DEVICE_NAME,
.fops = &pcf8591_fops,
};
static int __init pcf8591_dev_init(void) {
int ret;
int i;
for (i = 3; i < 5; i++) {
ret = gpio_request(S5PV210_GPE0(i), "PCF8591");
if (ret) {
printk("%s: request GPIO %d for LED failed, ret = %d\n", DEVICE_NAME,
S5PV210_GPE0(i), ret);
return ret;
s3c_gpio_cfgpin(S5PV210_GPE0(i), S3C_GPIO_OUTPUT);
gpio_set_value(S5PV210_GPE0(i), 1);
}
}
ret = misc_register(&pcf8591_dev);
printk(DEVICE_NAME"\tinitialized\n");
return ret;
}
static void __exit pcf8591_dev_exit(void) {
int i;
for (i = 0; i < 1; i++) {
gpio_free(S5PV210_GPE0(3));
gpio_free(S5PV210_GPE0(4));
}
misc_deregister(&pcf8591_dev);
}
module_init(pcf8591_dev_init);
module_exit(pcf8591_dev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("FriendlyARM Inc.");
四. 案例二
at24c08.c:
运行结果:
代码:
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/ioctl.h>
#include <linux/cdev.h>
#include <linux/delay.h>
#include <asm/uaccess.h> //其中copy_to*在其中
#include <mach/gpio.h>
#include <mach/regs-gpio.h>
#include <plat/gpio-cfg.h>
spinlock_t lock; //加不加这都 可以
#define DEVICE_NAME "at24xx"
#define uchar unsigned char
int askflag;
#define NUM 4 //接收和发送缓存区的深度
uchar receivebuf[NUM]; //数据接收缓冲区
uchar SystemError; //从机错误标志位
void set_conOUT_sda(void);
void set_conOUT_scl(void);
void set_conIN_sda(void);
void set_conIN_scl(void);
void set_data_sda(int i);
void set_data_scl(int i);
//-------------------------------------------------------------------
// 函数名称: i2c_start()
// 函数功能: 启动I2C总线子程序
//-------------------------------------------------------------------
void i2c_start(void)
{ //时钟保持高,数据线从高到低一次跳变,I2C通信开始
set_conOUT_sda();
set_conOUT_scl();
__udelay(1);
set_data_sda(1);
set_data_scl(1);
__udelay(1);
set_data_sda(0);
__udelay(5);
set_data_scl(0);
__udelay(1);
}
//-------------------------------------------------------------------
// 函数名称: i2c_stop()
// 函数功能: 停止I2C总线数据传送子程序
//-------------------------------------------------------------------
void i2c_stop(void)
{
set_conOUT_sda();
set_conOUT_scl();
__udelay(1);
set_data_sda(0);//时钟保持高,数据线从低到高一次跳变,I2C通信停止
set_data_scl(1);
__udelay(5);
set_data_sda(1);
__udelay(5);
set_data_sda(0);
set_data_scl(0);
}
//------------------------------------------------------------------
// 函数名称: i2c_Init()
// 函数功能: 初始化I2C总线子程序
//------------------------------------------------------------------
void i2c_init(void)
{
set_conOUT_scl();
__udelay(1);
set_data_scl(0);
i2c_stop();
}
//-------------------------------------------------------------------
// 函数名称: slave_ack
// 函数功能: 从机发送应答位子程序
//-------------------------------------------------------------------
void slave_ack(void)
{
set_conOUT_sda();
set_conOUT_scl();
__udelay(1);
set_data_sda(0);
set_data_scl(1);
__udelay(5);
set_data_scl(0);
}
//-------------------------------------------------------------------
// 函数名称: slave_noack
// 函数功能: 从机发送非应答位子程序,迫使数据传输过程结束
//-------------------------------------------------------------------
void slave_noack(void)
{
set_conOUT_sda();
set_conOUT_scl();
__udelay(1);
set_data_sda(1);
set_data_scl(1);
__udelay(5);
set_data_sda(0);
set_data_scl(0);
}
//-------------------------------------------------------------------
// 函数名称: check_ack
// 函数功能: 主机应答位检查子程序,迫使数据传输过程结束
//-------------------------------------------------------------------
void check_ack(void)
{
set_conOUT_sda();
set_conOUT_scl();
__udelay(1);
set_data_sda(1); // 将p1.1设置成输入,必须先向端口写1
set_data_scl(1);
askflag = 0;
__udelay(5);
set_conIN_sda();
__udelay(3);
if ( gpio_get_value(S5PV210_GPE0(3)) == 1)
askflag = 1;
// 若SDA=1表明非应答,置位非应答标志askflag
set_data_scl(0);
}
//-------------------------------------------------------------------
// 函数名称: i2c_send_byte
// 入口参数: ch
// 函数功能: 发送一个字节
//-------------------------------------------------------------------
void i2c_send_byte(uchar ch)
{
unsigned char n=8; // 向SDA上发送一位数据字节,共八位
set_conOUT_sda();
set_conOUT_scl();
__udelay(1);
spin_lock(&lock);
while(n--) {
if((ch&0x80) == 0x80) // 若要发送的数据最高位为1则发送位1
{
set_data_sda(1); // 传送位1
set_data_scl(1);
__udelay(5);
set_data_scl(0);
}
else
{
set_data_sda(0); // 否则传送位0
set_data_scl(1);
__udelay(5);
set_data_scl(0);
}
ch = ch<<1; // 数据左移一位
}
spin_unlock(&lock);
}
//-------------------------------------------------------------------
// 函数名称: i2c_receive_byte
// 返回接收的数据
// 函数功能: 接收一字节子程序
//-------------------------------------------------------------------
uchar i2c_receive_byte(void)
{
uchar n=8; // 从SDA线上读取一上数据字节,共八位
uchar tdata=0;
int temp;
spin_lock(&lock);
while(n--) {
set_conOUT_sda();
set_conOUT_scl();
__udelay(1);
set_data_sda(1);
set_data_scl(1);
tdata =tdata<<1; //左移一位
set_conIN_sda();
__udelay(3);
temp = gpio_get_value(S5PV210_GPE0(3));
if( temp == 1)
tdata = tdata|0x01; // 若接收到的位为1,则数据的最后一位置1
else
tdata = tdata&0xfe; // 否则数据的最后一位置0
//printk("read_data_bit: %d", temp); //++
set_data_scl(0);
}
spin_unlock(&lock);
printk("read_data: %d\n", tdata);
return(tdata);
}
//***************************************************
//函数功能:向AT24xx中指定地址写入数据
//入口参数:add(地址), dat(储存待写入的数据)
//***************************************************
void write_set_at24xx(uchar add, uchar dat)
{
i2c_start();
i2c_send_byte(0xA0);
__udelay(1);
check_ack();
if(askflag == 1) //++
{
SystemError = 1;
printk("write_set_01_SystemError: %d\n", SystemError);
return;
} else {
SystemError = 0;
}
i2c_send_byte(add);
__udelay(1);
check_ack();
if(askflag == 1) //++
{
SystemError = 1;
printk("write_set_02_SystemError: %d\n", SystemError);
return;
} else {
SystemError = 0;
}
i2c_send_byte(dat);
__udelay(1);
check_ack();
if(askflag == 1) //++
{
SystemError = 1;
printk("write_set_03_SystemError: %d\n", SystemError);
return;
} else {
SystemError = 0;
}
i2c_stop();
__udelay(5);
}
//***************************************************
//函数功能:从AT24Cxx中的当前地址读取数据
//出口参数:x (储存读出的数据)
//***************************************************
uchar read_current_at24xx()
{
unsigned char x;
i2c_start(); //开始数据传递
i2c_send_byte(0xA1); //选择要操作的AT24Cxx芯片,并告知要读其数据
__udelay(5);
check_ack();
if(askflag == 1) //++
{
SystemError = 1;
printk("read_current_01_SystemError: %d\n", SystemError);
return;
} else {
SystemError = 0;
}
x=i2c_receive_byte(); //将读取的数据存入x
__udelay(3);
i2c_stop(); //停止数据传递
return x; //返回读取的数据
}
//***************************************************
//函数功能:从AT24Cxx中的指定地址读取数据
//入口参数:set_addr
//出口参数:x
//***************************************************
unsigned char read_set_at24xx(unsigned char set_addr)
// 在指定地址读取
{
uchar temp;
i2c_start(); //开始数据传递
i2c_send_byte(0xA0); //选择要操作的AT24Cxx芯片,并告知要对其写入数据
__udelay(5);
check_ack();
if(askflag == 1) //++
{
SystemError = 1;
printk("read_set_01_SystemError: %d\n", SystemError);
return;
} else {
SystemError = 0;
}
i2c_send_byte(set_addr); //写入指定地址
__udelay(1);
check_ack();
temp = read_current_at24xx();
return(temp); //从指定地址读出数据并返回
}
void set_conIN_sda(void)
{
s3c_gpio_cfgpin(S5PV210_GPE0(3), S3C_GPIO_INPUT);
//gpio_set_value(S5PV210_GPE0(3), 1);
}
void set_conIN_scl(void)
{
s3c_gpio_cfgpin(S5PV210_GPE0(4), S3C_GPIO_INPUT);
//gpio_set_value(S5PV210_GPE0(3), 1);
}
void set_conOUT_sda(void)
{
s3c_gpio_cfgpin(S5PV210_GPE0(3), S3C_GPIO_OUTPUT);
}
void set_conOUT_scl(void)
{
s3c_gpio_cfgpin(S5PV210_GPE0(4), S3C_GPIO_OUTPUT);
}
void set_data_sda(int i)
{
if (i == 0) {
gpio_set_value(S5PV210_GPE0(3), 0);
} else if(i == 1) {
gpio_set_value(S5PV210_GPE0(3), 1);
}
}
void set_data_scl(int i)
{
if (i == 0) {
gpio_set_value(S5PV210_GPE0(4), 0);
} else if(i == 1) {
gpio_set_value(S5PV210_GPE0(4), 1);
}
}
static int at24xx_open(struct inode *inode, struct file *filp)
{
printk (KERN_INFO "Device opened\n");
spin_lock_init(&lock);
return 0;
}
/*读取数据*/
static int at24xx_read(struct file *filp, char __user *buffer, size_t count, loff_t *ppos)
{
printk("at24xx_read\n");
int i;
uchar read_dat;
//i2c_init();
//write_set_at24xx(5, ‘A‘);
i2c_init();
read_dat = read_set_at24xx(5);
printk("add_%d: %d\t", 5, read_dat);
//i2c_init();
for (i=‘a‘; i<‘z‘; i++) {
i2c_init();
write_set_at24xx(i, i+1); //在24c08的地址2中写入数据sec
if (SystemError == 1) {
i2c_init();
write_set_at24xx(i, i);
}/*
if (SystemError == 1) {
i2c_init();
write_set_at24xx(i, i);
}
if (SystemError == 1) {
i2c_init();
write_set_at24xx(i, i);
}*/
__udelay(5);
}
__udelay(9);
for (i=‘a‘; i<‘z‘; i++) {
i2c_init();
read_dat = read_set_at24xx(i);
printk("add_%d: %c\t",i, read_dat);
__udelay(5);
}
printk("\n");
for (i=2; i< 10; i++) {
i2c_init();
read_dat = read_set_at24xx(i);
printk("add_%d: %c\t",i, read_dat);
__udelay(5);
}
copy_to_user(buffer, receivebuf, sizeof(receivebuf));
return 0;
/*printk("at24xx_read\n");
int i;
int v[4]; //存实际的电压值
i2c_init(); //I2C总线初始化
adc_at24xx(0x04);
if(SystemError == 1) //有错误,重新来
{
printk("SystemError: %d\n", SystemError);
i2c_init(); //I2C总线初始化
adc_at24xx(0x04);
}
for(i=0;i<4;i++)
{
printk("AD_%d: %d\t\n", i, receivebuf[i]); //显示通道0
v[2]=receivebuf[i]/51; //AD值转换为3位BCD码,最大为5.00V。
v[2]=v[2]; //转换为ACSII码
v[3]=receivebuf[i]%51; //余数暂存
v[3]=v[3]*10; //计算小数第一位
v[1]=v[3]/51;
v[1]=v[1]; //转换为ACSII码
v[3]=v[3]%51;
v[3]=v[3]*10; //计算小数第二位
v[0]=v[3]/51; //
v[0]=v[0]; //转换为ACSII码
printk("AD_%d: %d.%d%dV\t\n", i, v[2], v[1], v[0]);
}
copy_to_user(buffer, receivebuf, sizeof(receivebuf));
return 0;
*/
}
/*写命令,在此置空*/
static int at24xx_write(struct file *file, const char *buffer, size_t count, loff_t * ppos)
{
printk("at24xx_write\n");
char b[10];
char *a = b;//kmalloc(100, GFP_KERNEL);
int i;
int len;
len = sizeof(buffer);
copy_from_user(a,buffer, count);
for (i=0; i < count; i++) {
i2c_init();
write_set_at24xx(i+2, *(a++));
}
printk("len: %s\n", a);
return count;
}
static int at24xx_release(struct inode *inode,struct file *filp)
{
printk (KERN_INFO "device closed\n");
return 0;
}
static long at24xx_ioctl(struct file *filp, unsigned int cmd,
unsigned long arg)
{
return 0;
}
static struct file_operations at24xx_fops = {
.owner = THIS_MODULE,
.open = at24xx_open,
.read = at24xx_read,
.write = at24xx_write,
.unlocked_ioctl = at24xx_ioctl,
.release = at24xx_release,
};
static struct miscdevice at24xx_dev = {
.minor = MISC_DYNAMIC_MINOR,
.name = DEVICE_NAME,
.fops = &at24xx_fops,
};
static int __init at24xx_dev_init(void) {
int ret;
int i;
for (i = 3; i < 5; i++) {
ret = gpio_request(S5PV210_GPE0(i), "PCF8591");
if (ret) {
printk("%s: request GPIO %d for LED failed, ret = %d\n", DEVICE_NAME,
S5PV210_GPE0(i), ret);
return ret;
s3c_gpio_cfgpin(S5PV210_GPE0(i), S3C_GPIO_OUTPUT);
gpio_set_value(S5PV210_GPE0(i), 1);
}
}
ret = misc_register(&at24xx_dev);
printk(DEVICE_NAME"\tinitialized\n");
return ret;
}
static void __exit at24xx_dev_exit(void) {
int i;
for (i = 0; i < 1; i++) {
gpio_free(S5PV210_GPE0(3));
gpio_free(S5PV210_GPE0(4));
}
misc_deregister(&at24xx_dev);
}
module_init(at24xx_dev_init);
module_exit(at24xx_dev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("FriendlyARM Inc.");
S5PV210之GPIO模拟I2c时序之pcf8591与at24xx linux3.0.8驱动
原文:http://www.cnblogs.com/minglicnblogs/p/3967431.html