【linux iic子系统】GPIO模拟i2c控制器adapter(九)

9 篇文章 5 订阅

有时,我们发现芯片提供的i2c控制器不够咋办?参考单片机做法,使用GPIO模块注册一个i2c adapter。

注意,GPIO相关寄存器请根据实际来配置~~~

gpio_i2c_adapter_dev.c文件,注册adapter设备。

#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include "gpio_i2c_adapter.h"

/* GPIO模拟i2c控制器设备的私有数据 */
struct gpio_i2c_adapter_data c0_c1_adapter_data = {
    .udelay = 5,
    .clk_reg_phys_addr = C_GROUP_PIN(0),//C组引脚0
    .sda_reg_phys_addr = C_GROUP_PIN(1),//C组引脚1
    .clk_reg = NULL,
    .sda_reg = NULL,
    .clk_old_reg_value = 0,
    .sda_old_reg_value = 0,
};

static void dev_release(struct device *dev)
{
    //空操作,无此函数会警告
}

static struct platform_device gpio_i2c_adapter_dev = {
    .name = "gpio_i2c_adap",
    .id = -1,   //表示目前仅有一个这样的设备
    .dev = {
            .release = dev_release,
            .platform_data = &c0_c1_adapter_data,
        }
};

static int __init gpio_i2c_adapter_dev_init(void)
{
    return platform_device_register(&gpio_i2c_adapter_dev);
}

static void __exit gpio_i2c_adapter_dev_exit(void)
{
    platform_device_unregister(&gpio_i2c_adapter_dev);
}

module_init(gpio_i2c_adapter_dev_init);
module_exit(gpio_i2c_adapter_dev_exit);
MODULE_LICENSE("GPL v2");

gpio_i2c_adapter_drv.h文件和gpio_i2c_adapter_drv.c,注册adapter驱动。

gpio_i2c_adapter_drv.h文件。

#ifndef _HIK_GPIO_I2C_ADAPTER_H_
#define _HIK_GPIO_I2C_ADAPTER_H_

/*
* 该头文件中定义的引脚地址宏、操作方法宏、控制位宏均仅适用于Skylake平台
* 其他平台需要根据相应手册修改
*/
#define SBREG_BAR 0xFD000000                        /*bios provide*/
//#define SZ_64K 0x00010000
#define GPP_COMM0_BASE (SBREG_BAR + (0xAF << 16))    /*GPP_A/B*/
#define GPP_COMM1_BASE (SBREG_BAR + (0xAE << 16))    /*GPP_C/D/E/F/G/H*/ 

#define GPIO_REG_SIZE 8    //一个引脚由两个4字节寄存器控制
/* 以下宏并未全部使用,但为兼容以后使用,提前定义在此头文件 */
#define A_GROUP_PIN(n) (GPP_COMM0_BASE + 0x400 + n*GPIO_REG_SIZE)
#define B_GROUP_PIN(n) (GPP_COMM0_BASE + 0x4C0 + n*GPIO_REG_SIZE)
#define C_GROUP_PIN(n) (GPP_COMM1_BASE + 0x400 + n*GPIO_REG_SIZE)
#define D_GROUP_PIN(n) (GPP_COMM1_BASE + 0x4C0 + n*GPIO_REG_SIZE)
#define E_GROUP_PIN(n) (GPP_COMM1_BASE + 0x580 + n*GPIO_REG_SIZE)
#define F_GROUP_PIN(n) (GPP_COMM1_BASE + 0x5E8 + n*GPIO_REG_SIZE)
#define G_GROUP_PIN(n) (GPP_COMM1_BASE + 0x6A8 + n*GPIO_REG_SIZE)
#define H_GROUP_PIN(n) (GPP_COMM1_BASE + 0x768 + n*GPIO_REG_SIZE)

#define CLEAR_PAD_MODE (~(0b111 << 10))        //10、11、12位控制引脚模式
#define SET_PAD_MODE_IS_GPIO (0b000 << 10)    //0b000表示使用GPIO控制模式
#define CLEAR_RX_TX_MODE (~(0b11 << 8))        //8、9位控制输入输出模式
#define SET_TX_MODE (0b10 << 8)
#define SET_RX_MODE (0b01 << 8)
#define SDA_IN(PIN_REG) (*PIN_REG = (*PIN_REG & CLEAR_RX_TX_MODE) | SET_RX_MODE)
#define SDA_OUT(PIN_REG) (*PIN_REG = (*PIN_REG & CLEAR_RX_TX_MODE) | SET_TX_MODE)

#define RX_BIT 1                            //1位读入输入
#define TX_BIT 0                            //0位设置输出
#define SET_GPIO(PIN_REG, val) (val?(*PIN_REG = *PIN_REG | (0b1 << TX_BIT)) : \
                                (*PIN_REG = *PIN_REG & (~(0b1 << TX_BIT))))
#define GET_GPIO(PIN_REG) ((*PIN_REG & (1 << RX_BIT))?1:0)

#define W_FLAG 0
#define R_FLAG 1

struct gpio_i2c_adapter_data {
    struct mutex my_lock;
    unsigned int udelay;
    unsigned long clk_reg_phys_addr;
    unsigned long sda_reg_phys_addr;
    volatile unsigned long *clk_reg;//一个引脚由两个4字节寄存器控制,只需使用第一个
    volatile unsigned long *sda_reg;
    unsigned long clk_old_reg_value;
    unsigned long sda_old_reg_value;
};

#endif

gpio_i2c_adapter_drv.c文件。

#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <linux/init.h>
#include<linux/delay.h>
#include <linux/i2c.h>
#include "gpio_i2c_adapter.h"

static int my_debug = 1;
module_param(my_debug, int, 0644);    //当前用户可读写,所有用户可读
MODULE_PARM_DESC(my_debug, "enable debugging informationg");

#define debug_printk(ftm, args...) if(my_debug){printk(ftm, ##args);}

/** @fn     gpio_ioremap
* @brief    Author/Date:    caodongwang/2020-12
* @brief    将GPIO引脚控制寄存器地址ioremap
* @param    [in]data:         gpio模拟i2c控制器的私有数据
* @return    0:                successfully
* @return    -x:             errno on linux
*/
static int gpio_ioremap(struct gpio_i2c_adapter_data *data)
{
    int ret = -EINVAL;
    
    /* 可能非连续引脚,因此映射两次 */
    data->clk_reg = ioremap(data->clk_reg_phys_addr, GPIO_REG_SIZE);
    if (NULL == data->clk_reg)
    {
        printk("clk_reg ioremap failed!\n");
        goto out;
    }
    
    data->sda_reg = ioremap(data->sda_reg_phys_addr, GPIO_REG_SIZE);
    if (NULL == data->sda_reg)
    {
        printk("sda_reg ioremap failed!\n");
        iounmap(data->clk_reg);
        goto out;
    }

    ret = 0;
out:
    return ret;
}

/** @fn     gpio_iounmap
* @brief    Author/Date:    caodongwang/2020-12
* @brief    将GPIO引脚控制寄存器地址iounmap
* @param    [in]data:         gpio模拟i2c控制器的私有数据
*/
static void gpio_iounmap(struct gpio_i2c_adapter_data *data)
{
    if (data->clk_reg)
    {
        iounmap(data->clk_reg);
        data->clk_reg = NULL;
    }

    if (data->sda_reg)
    {
        iounmap(data->sda_reg);
        data->sda_reg = NULL;
    }
}

/** @fn     save_register_value
* @brief    Author/Date:    caodongwang/2020-12
* @brief    保存GPIO引脚控制寄存器原始值
* @param    [in]data:         gpio模拟i2c控制器的私有数据
*/
static void save_register_value(struct gpio_i2c_adapter_data *data)
{
    data->clk_reg_phys_addr = *(data->clk_reg);
    data->sda_reg_phys_addr = *(data->sda_reg);
}

/** @fn     register_init
* @brief    Author/Date:    caodongwang/2020-12
* @brief    初始化寄存器用于GPIO控制
* @param    [in]data:         gpio模拟i2c控制器的私有数据
* @return    0:                successfully
* @return    -x:             errno on linux
*/
static int register_init(struct gpio_i2c_adapter_data *data)
{
    int ret = 0;
    unsigned int temp1,temp2;

    temp1 = *(data->clk_reg);
    temp1 = ((temp1 & CLEAR_PAD_MODE) & CLEAR_RX_TX_MODE) | \
            SET_PAD_MODE_IS_GPIO | SET_TX_MODE;
    *(data->clk_reg) = temp1;

    temp2 = *(data->sda_reg);
    temp2 = ((temp2 & CLEAR_PAD_MODE) & CLEAR_RX_TX_MODE) | \
            SET_PAD_MODE_IS_GPIO | SET_TX_MODE;
    *(data->sda_reg) = temp2;
    wmb();//写内存屏障,确保能设置成GPIO控制模式

    /* 不比较只读位,若不相等,则可能配置寄存器加锁或引脚被系统功能占用 */
    if ((*data->clk_reg & (~(1 << RX_BIT))) != (temp1 & (~(1 << RX_BIT))) || \
        (*data->sda_reg & (~(1 << RX_BIT))) != (temp2 & (~(1 << RX_BIT))))
    {
        printk("init_reg failed, *clk_reg = 0x%x, *sda_reg = 0x%x\n", \
            *(data->clk_reg), *(data->sda_reg));
        ret = -EAGAIN;
    }

    return ret;
}

/** @fn     restore_register_value
* @brief    Author/Date:    caodongwang/2020-12
* @brief    恢复GPIO引脚控制寄存器原始值
* @param    [in]data:         gpio模拟i2c控制器的私有数据
*/
static void restore_register_value(struct gpio_i2c_adapter_data *data)
{
    *(data->clk_reg) = data->clk_old_reg_value;
    *(data->sda_reg) = data->sda_old_reg_value;
}

/** @fn     set_sda
* @brief    Author/Date:    caodongwang/2020-12
* @brief    设置sda引脚电平
* @param    [in]data:         gpio模拟i2c控制器的私有数据
* @param    [in]val:         0表示低电平,1表示高电平
*/
static void set_sda(struct gpio_i2c_adapter_data *data, int val)
{
    SET_GPIO(data->sda_reg, val);
}

/** @fn     set_clk
* @brief    Author/Date:    caodongwang/2020-12
* @brief    设置clk引脚电平
* @param    [in]data:         gpio模拟i2c控制器的私有数据
* @param    [in]val:         0表示低电平,1表示高电平
*/
static void set_clk(struct gpio_i2c_adapter_data *data, int val)
{
    SET_GPIO(data->clk_reg, val);
}

/** @fn     get_sda
* @brief    Author/Date:    caodongwang/2020-12
* @brief    获取sda引脚状态
* @param    [in]data:         gpio模拟i2c控制器的私有数据
* @return    x:                 0表示低电平,1表示高电平
*/
static int get_sda(struct gpio_i2c_adapter_data *data)
{
    return GET_GPIO(data->sda_reg);
}

/** @fn     set_sda_in
* @brief    Author/Date:    caodongwang/2020-12
* @brief    设置sda引脚为输入状态
* @param    [in]data:         gpio模拟i2c控制器的私有数据
*/
static void set_sda_in(struct gpio_i2c_adapter_data *data)
{
    SDA_IN(data->sda_reg);
}

/** @fn     set_sda_out
* @brief    Author/Date:    caodongwang/2020-12
* @brief    设置sda引脚为输出状态
* @param    [in]data:         gpio模拟i2c控制器的私有数据
*/
static void set_sda_out(struct gpio_i2c_adapter_data *data)
{
    SDA_OUT(data->sda_reg);
}

/** @fn     gpio_i2c_start
* @brief    Author/Date:    caodongwang/2020-12
* @brief    模拟产生i2c开始信号或重复开始信号
* @param    [in]data:         gpio模拟i2c控制器的私有数据
*/
static void gpio_i2c_start(struct gpio_i2c_adapter_data *data)
{
    set_sda_out(data);
    mb();        //配置引脚方向与设置电平之间加入内存屏障,防止指令重排
    set_sda(data, 1);
    ndelay(10);    //少量延时,等待信号稳定
    set_clk(data, 1);
    udelay(data->udelay);//保证重复开始时间间隔

    set_sda(data, 0);
    udelay(data->udelay);
    set_clk(data, 0);
}

/** @fn     gpio_i2c_stop
* @brief    Author/Date:    caodongwang/2020-12
* @brief    模拟产生i2c停止信号
* @param    [in]data:         gpio模拟i2c控制器的私有数据
*/
static void gpio_i2c_stop(struct gpio_i2c_adapter_data *data)
{
    set_sda_out(data);
    set_clk(data, 0);
    ndelay(10);//少量延时,等待信号稳定
    set_sda(data, 0);
    udelay(data->udelay);//保证数据位与停止位时间间隔

    set_clk(data, 1);
    udelay(data->udelay);
    set_sda(data, 1);
}

/** @fn     gpio_i2c_wait_ack
* @brief    Author/Date:    caodongwang/2020-12
* @brief    等待i2c从机ACK应答信号
* @param    [in]data:         gpio模拟i2c控制器的私有数据
* @return    0:                从机有应答
* @return    -x:             errno on linux
*/
static int gpio_i2c_wait_ack(struct gpio_i2c_adapter_data *data)
{
    unsigned char cut = 0;
    set_sda_in(data);
    udelay(data->udelay);
    set_clk(data, 1);

    while (get_sda(data))
    {
        cut++;
        if (cut > 250)    //循环等待250次,最多等待50us
        {
            set_clk(data, 0);
            return -EIO;
        }
        ndelay(200);
    }
    udelay(data->udelay);
    set_clk(data, 0);

    return 0;
}

/** @fn     gpio_i2c_ack
* @brief    Author/Date:    caodongwang/2020-12
* @brief    模拟主机产生i2c ACK应答信号
* @param    [in]data:         gpio模拟i2c控制器的私有数据
*/
static void gpio_i2c_ack(struct gpio_i2c_adapter_data *data)
{
    set_clk(data, 0);
    set_sda_out(data);
    udelay(data->udelay);

    set_sda(data, 0);//sda_reg为0表示ACK
    udelay(data->udelay);

    set_clk(data, 1);
    udelay(data->udelay);

    set_clk(data, 0);
}

/** @fn     gpio_i2c_nack
* @brief    Author/Date:    caodongwang/2020-12
* @brief    模拟主机产生i2c无ACK应答信号
* @param    [in]data:         gpio模拟i2c控制器的私有数据
*/
static void gpio_i2c_nack(struct gpio_i2c_adapter_data *data)
{
    set_clk(data, 0);
    set_sda_out(data);
    udelay(data->udelay);

    set_sda(data, 1);//sda_reg为1表示NACK
    udelay(data->udelay);

    set_clk(data, 1);
    udelay(data->udelay);

    set_clk(data, 0);
}

/** @fn     gpio_i2c_send_bety
* @brief    Author/Date:    caodongwang/2020-12
* @brief    模拟i2c时序发送一字节数据
* @param    [in]data:         gpio模拟i2c控制器的私有数据
* @param    [in]send_data:     需要发送的数据
*/
static void gpio_i2c_send_byte(struct gpio_i2c_adapter_data *data, unsigned char send_data)
{
    unsigned char i;
    set_sda_out(data);
    set_clk(data, 0);
    ndelay(100);

    for (i = 0; i < 8; i++)    //发送8位数据
    {
        if (send_data & 0x80)   //发送bit7
        {
            set_sda(data, 1);
        }
        else
        {
            set_sda(data, 0);
        }
        send_data = send_data << 1;
        udelay(data->udelay);

        set_clk(data, 1);
        udelay(data->udelay);

        set_clk(data, 0);
    }
}

/** @fn     gpio_i2c_read_bety
* @brief    Author/Date:    caodongwang/2020-12
* @brief    模拟i2c时序接受一字节数据
* @param    [in]data:         gpio模拟i2c控制器的私有数据
* @return    x:                 接受到的数据
*/
static unsigned char gpio_i2c_read_byte(struct gpio_i2c_adapter_data *data)
{
    unsigned char read_data = 0;
    unsigned char i;

    set_sda_in(data);

    for (i = 0; i < 8; i++)    //接收8位数据
    {
        udelay(data->udelay);
        set_clk(data, 1);

        read_data = read_data << 1;//接收1 bit数据
        if (get_sda(data))
        {
            read_data |= 1;
        }

        udelay(data->udelay);
        set_clk(data, 0);
    }

    return read_data;
}

/** @fn     gpio_i2c_send_msg
* @brief    Author/Date:    caodongwang/2020-12
* @brief    发送一个msg
* @param    [in]data:         gpio模拟i2c控制器的私有数据
* @param    [in]msg:         需要发送的msg
* @return    0:                successfully
* @return    -x:             errno on linux
*/
static int gpio_i2c_send_msg(struct gpio_i2c_adapter_data *data, struct i2c_msg *msg)
{
    int ret = 0;
    int i =0;
    int len = msg->len;

    gpio_i2c_start(data);
    gpio_i2c_send_byte(data, (msg->addr << 1) | W_FLAG);
    ret = gpio_i2c_wait_ack(data);
    if (ret)
    {
        goto out;
    }
    
    for (i = 0; i < len; i++)
    {
        gpio_i2c_send_byte(data, msg->buf[i]);
        ret = gpio_i2c_wait_ack(data);
        if (ret)
        {
            goto out;
        }
    }

out:
    gpio_i2c_stop(data);
    return ret;
}

/** @fn     gpio_i2c_read_msg
* @brief    Author/Date:    caodongwang/2020-12
* @brief    接收一个msg
* @param    [in]data:         gpio模拟i2c控制器的私有数据
* @param    [in]msg:         接收到的数据存放至msg
* @return    0:                successfully
* @return    -x:             errno on linux
*/
static int gpio_i2c_read_msg(struct gpio_i2c_adapter_data *data, struct i2c_msg *msg)
{
    int ret = 0;
    int i =0;
    int len = msg->len;
    
    gpio_i2c_start(data);
    gpio_i2c_send_byte(data, (msg->addr << 1) | R_FLAG);
    ret = gpio_i2c_wait_ack(data);
    if (ret)
    {
        goto out;
    }
    
    for(i = 0; i < len; i++)
    {
        msg->buf[i] = gpio_i2c_read_byte(data);

        if (i == len - 1)
        {
            gpio_i2c_nack(data);
        }
        else
        {
            gpio_i2c_ack(data);
        }
    }

out:
    gpio_i2c_stop(data);
    return ret;    
}

/** @fn     gpio_i2c_adapter_xfer
* @brief    Author/Date:    caodongwang/2020-12
* @brief    收发函数,对接i2c核心提供的传输函数
* @param    [in]adap:         i2c适配器
* @param    [in]msgs:         需要发送数据或接收数据的msg集合
* @param    [in]num:         msg集合中有多少个msg
* @return    num:            成功传输num个msg
* @return    -x:             errno on linux
*/
static int gpio_i2c_adapter_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)    
{
    int ret = 0;
    int i = 0;
    struct gpio_i2c_adapter_data *data = adap->algo_data;

    if (NULL == data)
    {
        printk("DATA is a null pointer!\n");
        return -EINVAL;
    }

    if (msgs[0].addr < 0x08 || msgs[0].addr > 0x77)//检查是否为7位有效地址
    {
        printk("is not a valid 7-bit address!\n");
        return -EINVAL;
    }

    mutex_lock(&data->my_lock);            //加锁
    save_register_value(data);  //保存寄存器值
    ret = register_init(data);  //初始化寄存器
    if (ret)
    {
        printk("please check if the configuration register is locked!\n");
        goto out;    //可能其中一个引脚寄存器设置成功,因此也需要恢复寄存器
    }
    
    for (i = 0; i < num; i++)
    {
        if (msgs[i].flags & I2C_M_RD)    //读数据
        {
            ret = gpio_i2c_read_msg(data, &msgs[i]);
            if (ret)
            {
                printk("%s:read_msg fail, i2c addr = 0x%x\n", adap->name, msgs[i].addr);
                ret = -EAGAIN;
                goto out;
            }
        }
        else                            //写数据
        {
            ret = gpio_i2c_send_msg(data, &msgs[i]);
            if (ret)
            {
                printk("%s:send_msg fail, i2c addr = 0x%x\n", adap->name, msgs[i].addr);
                ret = -EAGAIN;
                goto out;
            }
        }
    }

    ret = num;
out:
    restore_register_value(data);        //恢复寄存器值
    mutex_unlock(&data->my_lock);        //释放锁
    return ret;
}

/** @fn     gpio_i2c_adapter_func
* @brief    Author/Date:    caodongwang/2020-12
* @brief    返回i2c适配器所能支持的传输类型
* @param    [in]adap:         i2c适配器
* @return    x:                所支持的传输类型标志集合
*/
static uint32_t gpio_i2c_adapter_func(struct i2c_adapter *adap)
{
    return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & (~I2C_FUNC_SMBUS_QUICK));
}

//i2c通信算法结构体
static struct i2c_algorithm gpio_i2c_adapter_algo = {
    .master_xfer = gpio_i2c_adapter_xfer,
    .functionality = gpio_i2c_adapter_func,
};

/** @fn     check_parameter
* @brief    Author/Date:    caodongwang/2020-12
* @brief    检查结构体中数据是否正确
* @param    [in]data:         gpio模拟i2c控制器的私有数据
* @return    0:                successfully
* @return    -x:             errno on linux
*/
static int check_parameter(struct gpio_i2c_adapter_data *data)
{
    int ret = -EINVAL;

    if (data)
    {
        if (data->clk_reg_phys_addr && data->sda_reg_phys_addr)
        {
            ret = 0;
        }

        if (0 == data->udelay)
        {
            data->udelay = 5;   //100 kHz
            debug_printk("The initial value of the UDELAY variable is not set," \
                "and the default value of 5us is currently used!\n");
        }
        else if (data->udelay > 50)
        {
            data->udelay = 50;  //10 kHz
            debug_printk("The value of the UDELAY variable is too large " \
                "and has been modified to 50us!\n");
        }
    }

    return ret;
}

static int gpio_i2c_adapter_drv_probe(struct platform_device *pdev)
{
    int ret = 0;
    struct i2c_adapter *adap;
    struct gpio_i2c_adapter_data *data = pdev->dev.platform_data;

    ret = check_parameter(data);    //检查参数
    if (ret)
    {
        printk("parameter fail!\n");
        goto check_parameter_fail;
    }

    mutex_init(&data->my_lock);

    ret = gpio_ioremap(data);
    if (ret)
    {
        printk("gpio_ioremap fail!\n");
        goto gpio_ioremap_fail;
    }
    
    adap = devm_kzalloc(&pdev->dev, sizeof(*adap), GFP_KERNEL);
    if (!adap)
    {
        ret = -ENOMEM;
        printk("devm_kzalloc fail!\n");
        goto devm_kzalloc_fail;
    }

    platform_set_drvdata(pdev, adap);
    
    adap->algo_data = data;
    adap->owner = THIS_MODULE;
    adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD;;
    adap->algo = &gpio_i2c_adapter_algo;
    adap->dev.parent = &pdev->dev;
    adap->retries = 3;  //重试次数
    snprintf(adap->name, sizeof(adap->name),"my gpio i2c adapter");

    ret = i2c_add_adapter(adap);
    if (ret)
    {
        printk("i2c_add_adapter fail!\n");
        goto i2c_add_adapter_fail;
    }

    debug_printk("add %d adapter successed!\n", adap->nr);
    
    return 0;

i2c_add_adapter_fail:
    devm_kfree(&pdev->dev, adap);
    adap = NULL;
devm_kzalloc_fail:
    gpio_iounmap(data);
gpio_ioremap_fail:
check_parameter_fail:
    return ret;
}

static int gpio_i2c_adapter_drv_remove(struct platform_device *pdev)
{
    struct i2c_adapter *adap =platform_get_drvdata(pdev);
    struct gpio_i2c_adapter_data *data = adap->algo_data;

    gpio_iounmap(data);
    
    i2c_del_adapter(adap);
    
    debug_printk("removed %d adapter successed!\n", adap->nr);

    return 0;
}

static struct platform_driver gpio_i2c_adapter_drv = {
    .probe = gpio_i2c_adapter_drv_probe,
    .remove = gpio_i2c_adapter_drv_remove,
    .driver = {
        .name = "gpio_i2c_adap",
    }
};

static int __init gpio_i2c_adapter_drv_init(void)
{
    return platform_driver_register(&gpio_i2c_adapter_drv);
}

static void __exit gpio_i2c_adapter_drv_exit(void)
{
    platform_driver_unregister(&gpio_i2c_adapter_drv);
}

module_init(gpio_i2c_adapter_drv_init);
module_exit(gpio_i2c_adapter_drv_exit);

MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("caodongwang");
MODULE_DESCRIPTION("this is a my adapter driver!");

注册一个tmp75设备来使用gpio模拟的i2c adapter。

#include <linux/module.h>
#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/platform_device.h>

static struct i2c_board_info my_tmp75_info = {
		I2C_BOARD_INFO("my_tmp75", 0x48),
};

static struct i2c_client *my_tmp75_client;

static int my_tmp75_init(void)
{
	struct i2c_adapter *i2c_adapt;
	int ret = 0;

	i2c_adapt = i2c_get_adapter(7);//注册gpio模拟i2c adapter时的总线号
	if (i2c_adapt == NULL)
	{
		printk("get adapter fail!\n");
		ret = -ENODEV;
	}
	
	my_tmp75_client = i2c_new_device(i2c_adapt, &my_tmp75_info);
	if (my_tmp75_client == NULL)
	{
		printk("i2c new fail!\n");
		ret = -ENODEV;
	}

	i2c_put_adapter(i2c_adapt);

	return ret;
}

static void my_tmp75_exit(void)
{
	i2c_unregister_device(my_tmp75_client);
}


module_init(my_tmp75_init);
module_exit(my_tmp75_exit);

MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("caodongwang");
MODULE_DESCRIPTION("This my i2c device for tmp75");

tmp75的驱动使用之前文章提供的就好~~~

上层使用的测试程序简单参考以下

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <unistd.h>

#define MY_I2C_MAGIC_NUMBER 'k'
#define GET_TEMP _IO(MY_I2C_MAGIC_NUMBER, 0)
#define GET_CONFIG _IO(MY_I2C_MAGIC_NUMBER, 1)
#define GET_TLOW_REG _IO(MY_I2C_MAGIC_NUMBER, 2)
#define GET_THIGH_REG _IO(MY_I2C_MAGIC_NUMBER, 3)

#define SET_CONFIG _IO(MY_I2C_MAGIC_NUMBER, 4)
#define SET_TLOW_REG _IO(MY_I2C_MAGIC_NUMBER, 5)
#define SET_THIGH_REG _IO(MY_I2C_MAGIC_NUMBER, 6)

#define SAMP_ACC 13 //采样精度控制位为13、14位

static float temperature_conversion(unsigned long data, unsigned long sampling_accuracy)
{
	float ret = 0;
	
	switch ((sampling_accuracy >> SAMP_ACC) & 0b11)
	{
		case 0://9位精度
		{
			ret = (data >> 7) * 0.5;
			break;
		}
		case 1://10位精度
		{
			ret = (data >> 6) * 0.25;
			break;
		}
		case 2://11位精度
		{
			ret = (data >> 5) * 0.125;
			break;
		}
		case 3://12位精度
		{
			ret = (data >> 4) * 0.0625;
			break;
		}
		default://可以不需要
			break;
	}
	
	return ret;
}

int main(int argc, char *agrv[])
{
	unsigned long data;
	unsigned long sampling_accuracy;
	int fd;
	
	fd = open("/dev/my_tmp75-7-0x48",O_RDWR);
	if(fd<0)
	{
		perror("open fail \n");
		return -1;
	}

	printf("修改前寄存器值!\n");
	ioctl(fd, GET_TEMP, &data);
	printf("TEMP = 0x%04lx\n", data);
	ioctl(fd, GET_CONFIG, &data);
	printf("CONFIG = 0x%04lx\n", data);
	ioctl(fd, GET_TLOW_REG, &data);
	printf("TLOW_REG = 0x%04lx\n", data);
	ioctl(fd, GET_THIGH_REG, &data);
	printf("THIGH_REG = 0x%04lx\n", data);

	data = 0x60ff;
	ioctl(fd, SET_CONFIG, &data);
	data = 0x5000;
	ioctl(fd, SET_TLOW_REG, &data);
	data = 0x6000;
	ioctl(fd, SET_THIGH_REG, &data);
	sleep(1);

	printf("\n修改后寄存器值!\n");
	ioctl(fd, GET_TEMP, &data);
	printf("TEMP = 0x%04lx\n", data);
	ioctl(fd, GET_CONFIG, &data);
	printf("CONFIG = 0x%04lx\n", data);
	ioctl(fd, GET_TLOW_REG, &data);
	printf("TLOW_REG = 0x%04lx\n", data);
	ioctl(fd, GET_THIGH_REG, &data);
	printf("THIGH_REG = 0x%04lx\n", data);

	while (1)
	{
		ioctl(fd, GET_CONFIG, &sampling_accuracy);
		ioctl(fd, GET_TEMP, &data);
		printf("TMP75: %6.2f\n", temperature_conversion(data, sampling_accuracy));
		sleep(1);
	}

	close(fd);

	return 0;
}

OK,搞定

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值