GXT310/GX75等温度传感器的linux驱动程式
本文介绍了为中科银河芯系列I2C温度传感器开发的通用Linux驱动实现。该驱动支持GXT310和GX21M15等型号,采用模块化设计处理不同传感器的唤醒机制差异:GXT310通过配置转换模式寄存器唤醒,GX21M15通过睡眠标识位控制。驱动核心流程包括配置唤醒、数据采集和睡眠模式切换,温度数据读取统一采用0x00寄存器双字节读取方案。文章详细解析了传感器特有的温度转换公式(如GXT310的0.00
中科银河芯陆续推出了一系列的i2c温度传感器用于满足不同应用的需求:
针对以上i2c传感器我编写了一个通用版本linux驱动供大家参考指导;
温度读取流程:
配置唤醒参数——>等待唤醒——>采集数据——> 进入睡眠
不同的传感器唤醒命令是不一样的,例如GXT310的唤醒命令是利用单次转换还是连续转换来进入低功耗或唤醒,如下图配置寄存器表:
GX21M15是通过配置寄存器第0位的睡眠标识来进行唤醒与睡眠操作:
因此在代码端的wakeup函数内,我们只需要配置不同的命令即可:
//*返回值,成功,返回0。失败,返回 -1
static int gxt_wakeup(void)
{
int ret = 0;
#ifdef SENSOR_GXT310
uint8_t dat310[3] = {0x01,0x02,0x9E};//GXT310
//报警 过温 连续转换 16位 不超时 CRC 8HZ 非单次模式
// 1 0 1 0 0 1 1 1 1 0
//ALT - - FQ1 FQ0 - TM SD EM - - TO EC CR1 CR0 OS
//
// 正常模式
ret = gxt_write_cmd(gxt_i2c_client,dat310,3);
#endif
#ifdef SENSOR_GXT21M15
uint8_t dat21m15[2] = {0x01,0x00};
// B[7:5]保留 B[4:3]故障队列 OSPOL OS_COMP_INIT SLEEP
// 000 00 0 0 0
//
ret = gxt_write_cmd(gxt_i2c_client,dat21m15,2);
#endif
return ret;
}
读取温度数据:
中科银河的温度传感器温度数据寄存器基本上都是在00h,我们只需要读取00h 两个字节即可(有CRC也可以读3个字节把CRC读取出来进行校验)

读取代码如下:
static ssize_t gxt_read(struct file *filp, char __user *buf, size_t cnt, loff_t *off)
{
short gxt_result[2];
uint8_t temp[8];
int error;
error = gxt_read_data(gxt_i2c_client,0,temp,3);
gxt_result[0] = temp[0]*256+temp[1];
//gxt_result[1] = temp[3]*256+temp[4];
if(error>=0)
{
error = copy_to_user(buf, gxt_result, cnt);
printk("temp = %d\n",gxt_result[0]);
}
else
{
printk("%s() i2c_read_data Failed!\n", __func__);
return -1;
}
return 0;
}
温度转化
驱动程式内不做温度转化,我们只需要把16Bit的温度值传给应用层,让应用层进行浮点运算即可,例如GXT310是16位AD数据,将值转化成温度的公式为:temp = AD数据值0.0078125
GX75/GX21M15等是12位AD数据,我们需要将值右移4位即除16,再成温度LSB值,公式为:temp = AD数据值/16.00.
应用层转换代码如下:
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <sys/time.h>
int main(int argc, char *argv[])
{
unsigned short data[2];
float T,RH;
int ret = -1 ;
while(1)
{
/*打开文件*/
int fd = open("/dev/GXT_SENSOR", O_RDWR);
if(fd < 0)
{
printf("open file : %s failed !\n", argv[0]);
return -1;
}
/*读取数据*/
ret = read(fd,&data,2);
if(ret >= 0)//读取到数据
{
float temp;
printf("ret data = %d\n",data[0]);
//计算温度数据
//temp = data[0]/16.0*0.0625;//GXT21M15温度计算公式
temp = data[0]*0.0078125;//GXT310计算公式
printf("temp = %f\n\n",temp);
}
else
{
printf("read failed\n");
}
usleep(1000000);
if(close(fd)<0)
{
printf("close file error!\n");
}
}
return 0;
}
以下是真实测试结果:
linugx源码分享
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/i2c.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <asm/io.h>
#include <linux/device.h>
#include <linux/platform_device.h>
//以下型号选一个
#define SENSOR_GXT310
//#define SENSOR_GXT21M15
/*------------------字符设备内容----------------------*/
#define DEV_NAME "GXT_SENSOR"
#define DEV_CNT (1)
#define DRIVER_VERSION "1.0.0"
struct semaphore sem; //互斥锁,只能有一个打开
static dev_t gxt_devno; //定义字符设备的设备号
static struct cdev gxt_chr_dev; //定义字符设备结构体chr_dev
struct class *class_gxt; //保存创建的类
struct device *device_gxt; // 保存创建的设备
struct device_node *gxt_device_node; //设备树节点结构体
/*------------------IIC设备内容----------------------*/
struct i2c_client *gxt_i2c_client = NULL; //保存gxt设备对应的i2c_client结构体,匹配成功后由.prob函数带回。
/*
//CRC校验
static u8 CRC_Check(unsigned char *check_data, unsigned char num, unsigned
char check_crc)
{
uint8_t bit; // bit mask
uint8_t crc = 0xFF; // calculated checksum
uint8_t byteCtr; // byte counter
// calculates 8-Bit checksum with given polynomial x8+x5+x4+1
for(byteCtr = 0; byteCtr < num; byteCtr++)
{
crc ^= (*(check_data+byteCtr));
//crc校验,最高位是1就^0x31
for(bit = 8; bit > 0; --bit)
{
if(crc & 0x80)
crc = (crc << 1) ^ 0x31;
else
crc = (crc << 1);
}
}
if(crc==check_crc)
return 1;
else
return 0;
}
*/
static int gxt_write_cmd(struct i2c_client *client, uint8_t *dat ,uint8_t datlen)
{
int error = 0;
int i;
uint8_t write_data[8];
struct i2c_msg send_msg; //要发送的数据结构体
if(datlen>8)
{
printk(KERN_DEBUG "\n i2c write_cmd datlen error \n");
return -1;
}
for(i=0;i<datlen;i++)
write_data[i] = *(dat+i);
send_msg.addr = client->addr; //iic 总线上的地址
send_msg.flags = 0; //标记为发送数据
send_msg.buf = write_data; //写入的首地址
send_msg.len = datlen; //reg长度
error = i2c_transfer(client->adapter, &send_msg, 1);
if (error != 1)
{
printk(KERN_DEBUG "\n i2c_transfer error \n");
return -1;
}
return 0;
}
static int gxt_read_data(struct i2c_client *client, u8 addr, uint8_t *dat, u32 length)
{
int error = 0;
uint8_t address_data = addr;
struct i2c_msg msg[2];
msg[0].addr = client->addr;
msg[0].flags = 0;
msg[0].buf = &address_data;
msg[0].len = 1;
msg[1].addr = client->addr;
msg[1].flags = I2C_M_RD;//读数据
msg[1].buf = dat;
msg[1].len = length;
error = i2c_transfer(client->adapter, msg, 2);
if (error != 2)
{
printk(KERN_DEBUG "\n i2c_read error \n");
return -1;
}
return 0;
}
//*返回值,成功,返回0。失败,返回 -1
static int gxt_wakeup(void)
{
int ret = 0;
#ifdef SENSOR_GXT310
uint8_t dat310[3] = {0x01,0x02,0x9E};//GXT310
//报警 过温 连续转换 16位 不超时 CRC 8HZ 非单次模式
// 1 0 1 0 0 1 1 1 1 0
//ALT - - FQ1 FQ0 - TM SD EM - - TO EC CR1 CR0 OS
//
// 正常模式
ret = gxt_write_cmd(gxt_i2c_client,dat310,3);
#endif
#ifdef SENSOR_GXT21M15
uint8_t dat21m15[2] = {0x01,0x00};
// B[7:5]保留 B[4:3]故障队列 OSPOL OS_COMP_INIT SLEEP
// 000 00 0 0 0
//
ret = gxt_write_cmd(gxt_i2c_client,dat21m15,2);
#endif
return ret;
}
static int gxt_sleep(void)
{
int ret = 0;
#ifdef SENSOR_GXT310
uint8_t dat310[3] = {0x01,0x02,0x99};//单次模式
ret = gxt_write_cmd(gxt_i2c_client,dat310,3);
#endif
#ifdef SENSOR_GXT21M15
uint8_t dat21m15[2] = {0x01,0x01};//睡眠模式
ret = gxt_write_cmd(gxt_i2c_client,dat21m15,2);
#endif
return ret;
}
static int gxt_reset(void)
{
int ret = 0;
//部分sensor不支持复位指令
//
return ret;
}
/*字符设备操作函数集,open函数实现*/
static int gxt_open(struct inode *inode, struct file *filp)
{
int ret;
/*上锁*/
down(&sem);
printk("\n gxt device open \n");
ret = gxt_wakeup();//唤醒传感器
mdelay(1);
return ret;
}
static ssize_t gxt_read(struct file *filp, char __user *buf, size_t cnt, loff_t *off)
{
short gxt_result[2];
uint8_t temp[8];
int error;
error = gxt_read_data(gxt_i2c_client,0,temp,3);
gxt_result[0] = temp[0]*256+temp[1];
//gxt_result[1] = temp[3]*256+temp[4];
if(error>=0)
{
error = copy_to_user(buf, gxt_result, cnt);
printk("temp = %d\n",gxt_result[0]);
}
else
{
printk("%s() i2c_read_data Failed!\n", __func__);
return -1;
}
return 0;
}
/*字符设备操作函数集,.release函数实现*/
static int gxt_release(struct inode *inode, struct file *filp)
{
printk("\n gxt release \n");
/*向gxt sensor 发送睡眠命令,进入关机状态*/
gxt_sleep();
/*释放锁*/
up(&sem);
return 0;
}
/*字符设备操作函数集*/
static struct file_operations gxt_chr_dev_fops =
{
.owner = THIS_MODULE,
.open = gxt_open,
.read = gxt_read,
.release = gxt_release,
};
/*----------------平台驱动函数集-----------------*/
static int gxt_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
int ret = -1; //保存错误状态码
sema_init(&sem,1);
printk(KERN_EMERG "\t match successed \n");
/*---------------------注册 字符设备部分-----------------*/
//采用动态分配的方式,获取设备编号,次设备号为0,
//设备名称为GXT_SENSOR,可通过命令cat /proc/devices查看
//DEV_CNT为1,当前只申请一个设备编号
ret = alloc_chrdev_region(&gxt_devno, 0, DEV_CNT, DEV_NAME);
if (ret < 0)
{
printk("fail to alloc gxt_devno\n");
goto alloc_err;
}
//关联字符设备结构体cdev与文件操作结构体file_operations
gxt_chr_dev.owner = THIS_MODULE;
cdev_init(&gxt_chr_dev, &gxt_chr_dev_fops);
// 添加设备至cdev_map散列表中
ret = cdev_add(&gxt_chr_dev, gxt_devno, DEV_CNT);
if (ret < 0)
{
printk("fail to add cdev\n");
goto add_err;
}
/*创建类 */
class_gxt = class_create(THIS_MODULE, DEV_NAME);
/*创建设备 DEV_NAME 指定设备名,*/
device_gxt = device_create(class_gxt, NULL, gxt_devno, NULL, DEV_NAME);
gxt_i2c_client = client;
return 0;
add_err:
// 添加设备失败时,需要注销设备号
unregister_chrdev_region(gxt_devno, DEV_CNT);
printk("\n error! \n");
alloc_err:
return -1;
}
static int gxt_remove(struct i2c_client *client)
{
/*删除设备*/
device_destroy(class_gxt, gxt_devno); //清除设备
class_destroy(class_gxt); //清除类
cdev_del(&gxt_chr_dev); //清除设备号
unregister_chrdev_region(gxt_devno, DEV_CNT); //取消注册字符设备
return 0;
}
/*定义ID 匹配表*/
static const struct i2c_device_id gxt_device_id[] = {
{"gxcas,gxt_sensor", 0},
{}};
/*定义设备树匹配表*/
static const struct of_device_id gxt_of_match_table[] = {
{.compatible = "gxcas,gxt_sensor"},
{/* sentinel */}};
/*定义i2c总线设备结构体*/
struct i2c_driver gxt_driver = {
.probe = gxt_probe,
.remove = gxt_remove,
.id_table = gxt_device_id,
.driver = {
.name = "gxcas,gxt",
.owner = THIS_MODULE,
.of_match_table = gxt_of_match_table,
},
};
/*
*驱动初始化函数
*/
static int __init gxt_driver_init(void)
{
int ret;
pr_info("gxt_driver_init\n");
ret = i2c_add_driver(&gxt_driver);
gxt_reset();
return ret;
}
/*
*驱动注销函数
*/
static void __exit gxt_driver_exit(void)
{
pr_info("gxt_driver_exit\n");
i2c_del_driver(&gxt_driver);
}
module_init(gxt_driver_init);
module_exit(gxt_driver_exit);
MODULE_LICENSE("GPL");
魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐


所有评论(0)