有时,我们发现芯片提供的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,搞定