I2C总线及其设备驱动分析基于tiny4412

i²c的分析

总线的结构

i2c 从soc里连接处两条线SDA串行数据线SCL串行时钟线 从器件都有唯一的设备地址,和soc构成了主从关系
我们分为5种信号
开始信号 一个S电频(SCL高电频时,SDA从高电平向低电平跳),加上7位设备地址,加上第九位读写(SDA=0读,SDA=1写)
读信号
写信号
结束信号 发一个P电频 SCL为高时,SDA从低向高跳变
ACK信号 每个八位的数据信号后面,跟着的第九位是ACK信号(表示已经收到数据)
基本的数据传送方式 其中开始和 读写都是9个周期
在这里插入图片描述

驱动分析

可以直接写个基本的驱动 用ioremap 来控制寄存器 ,但是为了更好的融入内核,里面已经有想关的代码
i2c 的驱动和设备总线驱动类似,分为了 适配器(之前的dev) drv(设备驱动) 用总线 i2c_bus_type 进行适配
在这里插入图片描述

代码编写

之前的函数已经不提倡继续使用
int (*attach_adapter)(struct i2c_adapter *)
int (*detach_adapter)(struct i2c_adapter *)
我们改为新的probe函数

在这里插入图片描述
驱动程序 clien还有些探究

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/i2c.h>
#include <linux/slab.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/interrupt.h>
#include <linux/input.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/platform_device.h>
 
#include <asm/io.h>
#include <asm/uaccess.h>
 
 
#define DEVICE_STYLE_ONE   TRUE // 注册设备节点风格1
 
struct i2c_client *client;
unsigned short ft5x0x_i2c[]={0x38,I2C_CLIENT_END}; //地址?

struct eeprom_i2c_driver {
    int major;
#ifndef DEVICE_STYLE_ONE
    struct class *cls;
#endif
    struct device *dev;
    struct i2c_client *client; 
};

 
struct eeprom_i2c_driver *at24_drv;
 
#if defined (DEVICE_STYLE_ONE)
struct class at24_class = {
    .name = "at24_cls",
};
 
void at24_dev_release(struct device *dev)
{
    // do nothing
}
#endif
 
 
int 
at24_drv_open(struct inode *inode, struct file *filp)
{
    // 硬件初始化,eeprom属于上电即可工作的器件,不需要初始化
    return 0;
}
 
ssize_t at24_drv_read(struct file *filp, char __user *buf, size_t count, loff_t *fops)
{
    int ret = -1;
 
    if(0 > count || 2048 < count)
        return -EFAULT;
 
    char *temp = kzalloc(count, GFP_KERNEL);
    if(NULL == temp){
        printk("read kzalloc failed !\n");
        return -ENOMEM;
    }
 
    ret = i2c_master_recv(at24_drv->client, temp, count);
    if(0 > ret){
        printk("i2c_master_recv failed !\n");
        kfree(temp);
        return ret;
    }
 
    ret = copy_to_user(buf, temp, count);
    if(0 < ret){
        printk("copy to user failed !\n");
        ret = -EBUSY;
    }
 
    kfree(temp);
 
    return count;
}
 
ssize_t at24_drv_write(struct file *filp, const char __user *buf, size_t count, loff_t *fops)
{
    int ret = -1;
 
    if(0 > count || 2048 < count)
        return -EFAULT;
 
    char *temp = kzalloc(count, GFP_KERNEL);
    if(NULL == temp){
        printk("read kzalloc failed !\n");
        return -ENOMEM;
    }
 
    ret = copy_from_user(temp, buf, count);
    if(0 < ret){
        printk("copy from user failed !\n");
        kfree(temp);
        return -EBUSY;
    }
 
    ret = i2c_master_send(at24_drv->client, temp, count);
    if(0 > ret){
        printk("i2c write failed !\n");
        kfree(temp);
        return ret;
    }
 
    kfree(temp);
 
    return count;
}
 
/**
 * 我们可以通过llseek来指定读取时的偏移地址
 * 为了代码简洁,这里暂不做处理
*/

 
struct file_operations at24_fops = {
    .open   = at24_drv_open,
    .read   = at24_drv_read,
    .write  = at24_drv_write,
};
 
int 
eeprom_i2c_probe(struct i2c_client *client, const struct i2c_device_id *devid)
{
    int ret = -1;
 
    printk("-----%s active------\n", __func__);
    
    // 0, 申请设备对象空间
    at24_drv = kzalloc(sizeof(struct eeprom_i2c_driver), GFP_KERNEL); //驱动里面内存申请 GFP_KERNEL无内存进行休眠
    if(NULL == at24_drv){
        printk("kzalloc failed !\n");
        return -ENOMEM;
    }
 
    // 1, 申请设备号
    at24_drv->major = register_chrdev(0, "at24_chrdev", &at24_fops);  //字符设备注册
    if(at24_drv->major < 0){
        printk("register char device failed !\n");
        ret = -ENODEV;
        goto err1;
    }
 
#if defined (DEVICE_STYLE_ONE)  // 代码走这里
    // 2, 创建设备类
    ret = class_register(&at24_class);  //寄存器
    if(ret){
        printk("class register failed !\n");
        goto err2;
    }

    // 3, 创建设备节点
    dev_set_name(&at24_drv->dev, "at24_dev"); //设置drv
    at24_drv->dev->devt = MKDEV(at24_drv->major, 0);
    at24_drv->dev->class = &at24_class,
    at24_drv->dev->parent = NULL,
    at24_drv->dev->release = at24_dev_release,
    device_initialize(&at24_drv->dev);
    device_add(&at24_drv->dev);
#else
    // 2, 创建设备类
    at24_drv->cls = class_create(THIS_MODULE, "at24_cls");
    if(IS_ERR(at24_drv->cls)){
        printk("class create failed !\n");
        ret = PTR_ERR(at24_drv->cls);
        goto err2;
    }
 
    // 3, 创建设备节点
    at24_drv->dev = device_create(at24_drv->cls, NULL, MKDEV(at24_drv->major, 0), NULL, "at24_dev");
    if(IS_ERR(at24_drv->dev)){
        printk("device create failed !\n");
        ret = PTR_ERR(at24_drv->dev);
        class_destroy(at24_drv->cls);
        goto err2;
    }
#endif
 
    // 4, 记录当前设备对象
    at24_drv->client = client;
 
    return 0;
 
err2:
    unregister_chrdev(at24_drv->major, "at24_chrdev");
err1:
    kfree(at24_drv);
    
    return ret;
}
 
int 
eeprom_i2c_remove(struct i2c_client *client)
{
#if defined(DEVICE_STYLE_ONE)
    device_del(&at24_drv->dev);
    class_unregister(&at24_class);
#else
    device_destroy(at24_drv->cls, MKDEV(at24_drv->major, 0));
    class_destroy(at24_drv->cls);
#endif
    unregister_chrdev(at24_drv->major, "at24_chrdev");
    kfree(at24_drv);
 
    return 0;
}
 
const struct i2c_device_id eeprom_table[] = {  
    {"at24c02a", 0x2},//用来实现i2c_client与i2c_driver匹配绑定,
    {"at24c04a", 0x4},//当i2c_client中的name成员和i2c_driver中id_table中name成员相同的时候,就匹配上了
};
 
 
struct i2c_driver eeprom_driver = {
    .probe  = eeprom_i2c_probe,
    .remove = eeprom_i2c_remove,
    .driver = {
        .name = "at24_drv",  //平台驱动用这个匹配,这里好像没啥用
    },
    .id_table = eeprom_table,  //用于匹配的表
};
 
static void __exit
eeprom_i2c_exit(void)
{

    i2c_del_driver(&eeprom_driver);
}
 
static int __init
eeprom_i2c_init(void)
{
	printk("i2c_init\n");
    //return i2c_add_driver(&eeprom_driver);
   	return i2c_add_driver(i2c_register_driver(THIS_MODULE,&at24_drv));
}
 
module_init(eeprom_i2c_init);
module_exit(eeprom_i2c_exit);
 
MODULE_LICENSE("GPL");

应用程序

#include <linux/i2c-dev.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
 
#define I2C_SLAVE       0x0703
 
 
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;
	unsigned char val;//字节
 
	char register_addr = 0x08; /* Device register to access 片内地址*/
	int  res;
	char wbuf[10];
	char rbuf[10];
 
	if (argc < 2){
		print_usage(argv[0]);
		exit(1);
	}
 
	/*打开设备文件*/
	fd = open("/dev/at24_dev", O_RDWR);
	if (fd < 0) 
	{
		perror("open failed");
		exit(1);
	}
	if (strcmp(argv[1], "r") == 0){
		if (write(fd, &register_addr, 1)!=1) {
			perror("write failed");
			exit(1);
		}
 
		if (read(fd, rbuf, 1) != 1) {
			perror("read failed");
			exit(1);
		} else {
			printf("rbuf =0x%x\n",rbuf[0]);
 
		}	
 
	}
	else if ((strcmp(argv[1], "w") == 0) && (argc == 3))
	{
		//  ./test  w  0x99
		val = strtoul(argv[2], NULL, 0);		
 
		wbuf[0] = register_addr; // 片内地址0x08
		wbuf[1] = val;
 
		if (write(fd, wbuf, 2)!=2) {
			perror("write failed");
			exit(1);
		}		      
		printf("write data ok!\n");
 
	}
 
	close(fd);
	return 0;
}

上机调试
更改内核

先把之前的驱动编译为 可装载(M)

Device Drivers —>
<> I2C support —>//i2c-core.c
<
> I2C device interface//通用i2c从设备驱动–主要用于调试(可选)
I2C Hardware Bus support —>
<*> S3C2410 I2C Driver
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值