【驱动篇】龙芯LS2K0300之i2c设备驱动

实验背景

由于官方内核i2c的BSP有问题(怀疑是设备树这块),本次实验将不通过设备树来驱动aht20(i2c)模块,大致的操作过程如下:

  1. 模块连接,查看aht20设备地址
  2. 编写device驱动,通过i2c_get_adapter注册i2c_client设备
  3. 编写i2c_driver驱动,需要匹device部分的i2c_device_id
  4. 编写测试用例,读取寄存器地址0x00起7个字节的温湿度数据
  5. 运行测试用例,检查传感器数值是否正常

模块连接

连接aht20温湿度传感器

在这里插入图片描述

使用i2c-tools查看i2c0总线上的从设备地址,可以看到为0x38

在这里插入图片描述

驱动代码

device驱动:大致的流程就是不通过设备树来注册一个i2c_client,i2c_get_adapter(0)表示i2c0,要定义一个DEV_ID_NAME作为id

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/i2c.h>

static struct i2c_board_info    aht20;
static struct i2c_client *      client;

#define DEV_ID_NAME "loongson,aht20"

static const unsigned short addrs[] = {0x38, I2C_CLIENT_END};

static int dev_init(void)
{
    struct i2c_adapter *adapter = NULL;

    memset(&aht20, 0, sizeof(struct i2c_board_info));
    strlcpy(aht20.type, DEV_ID_NAME, I2C_NAME_SIZE);

    adapter = i2c_get_adapter(0);
 
    client = i2c_new_probed_device(adapter, &aht20, addrs, NULL);

    i2c_put_adapter(adapter);

    if (client)
    {
        return 0;
    }
    else
    {
        return -ENODEV;
    }
}

static void dev_exit(void)
{
    i2c_unregister_device(client);
}

module_init(dev_init);
module_exit(dev_exit);
MODULE_LICENSE("GPL");

driver驱动:跟一般的设备驱动没有很大差别,这里match的i2c_device_id要和上面的device驱动保持一致

#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <asm/uaccess.h>
#include <linux/i2c.h>
#include <linux/input.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/errno.h>
#include <linux/cdev.h>
#include <linux/of_gpio.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <asm/io.h>
#include <linux/interrupt.h>
#include <linux/gpio.h>
#include <linux/irq.h>
 
#define DEVICE_CNT		1
#define DEVICE_NAME		"aht20"
#define DEV_ID_NAME 	"loongson,aht20"

struct aht20_dev {
	struct i2c_client *client;	
	dev_t dev_id;			
	struct cdev cdev;	
	struct class *class;	
	struct device *device;
};

static struct i2c_client *my_client;

static int aht20_read_regs(struct aht20_dev *dev, u8 reg, void *val, int len)
{
	int ret;
	struct i2c_msg msg[2];
	struct i2c_client *client = (struct i2c_client *)dev->client;

	msg[0].addr = client->addr;		
	msg[0].flags = 0;				
	msg[0].buf = &reg;			
	msg[0].len = 1;				

	msg[1].addr = client->addr;		
	msg[1].flags = I2C_M_RD;			
	msg[1].buf = val;			
	msg[1].len = len;		

	ret = i2c_transfer(client->adapter, msg, 2);
	if(ret == 2) {
		ret = 0;
	} else {
		printk("i2c rd failed=%d reg=%06x len=%d\n",ret, reg, len);
		ret = -EREMOTEIO;
	}
	return ret;
}

static s32 aht20_write_regs(struct aht20_dev *dev, u8 reg, u8 *buf, u8 len)
{
	u8 b[256];
	struct i2c_msg msg;
	struct i2c_client *client = (struct i2c_client *)dev->client;
	
	b[0] = reg;				
	memcpy(&b[1],buf,len);		
		
	msg.addr = client->addr;	
	msg.flags = 0;			

	msg.buf = b;	
	msg.len = len + 1;		

	return i2c_transfer(client->adapter, &msg, 1);
}

static unsigned char aht20_read_reg(struct aht20_dev *dev, u8 reg)
{
	u8 data = 0;

	aht20_read_regs(dev, reg, &data, 1);
	return data;
}

void ATH20_Read_CTdata(struct aht20_dev *dev, uint32_t *ct)
{
    uint32_t RetuData = 0;
	uint16_t cnt = 0;
    uint8_t Data[10];
    uint8_t tmp[10];
	uint8_t val = 0;

    tmp[0] = 0x33;
    tmp[1] = 0x00;

    aht20_write_regs(dev, 0xAC, tmp, 2);

	mdelay(75);//等待75ms

	while((((val = aht20_read_reg(dev, 0x00))&0x80) == 0x80))
	{
        mdelay(1);
        if(cnt++ >= 100)
        {
            break;
        }
	}

    aht20_read_regs(dev, 0x00, Data, 7);

	RetuData = 0;
    RetuData = (RetuData|Data[1]) << 8;
	RetuData = (RetuData|Data[2]) << 8;
	RetuData = (RetuData|Data[3]);
	RetuData = RetuData >> 4;
	ct[0] = RetuData;

    RetuData = 0;
	RetuData = (RetuData|Data[3]) << 8;
	RetuData = (RetuData|Data[4]) << 8;
	RetuData = (RetuData|Data[5]);
	RetuData = RetuData&0xfffff;
	ct[1] = RetuData;
}

void aht20_readdata(struct aht20_dev *dev, uint32_t *CT_data)
{
	ATH20_Read_CTdata(dev, CT_data);
}


uint8_t ATH20_Read_Cal_Enable(struct aht20_dev *dev)
{
    uint8_t val = aht20_read_reg(dev, 0x00);
    if((val & 0x68) == 0x08) 
        return 1;
    else
        return 0;
}

static int aht20_open(struct inode *inode, struct file *filp)
{
    uint8_t count;
	struct cdev *cdev = filp->f_path.dentry->d_inode->i_cdev;
	struct aht20_dev *aht20 = container_of(cdev, struct aht20_dev, cdev);

	uint8_t tmp[10];

    mdelay(40);

    tmp[0] = 0x08;
    tmp[1] = 0x00;

    aht20_write_regs(aht20, 0xBE, tmp, 2);

    mdelay(500);
    count = 0;

    while(ATH20_Read_Cal_Enable(aht20) == 0)
    {
        aht20_write_regs(aht20, 0xBA, tmp, 0);
        mdelay(200);

        aht20_write_regs(aht20, 0xBE, tmp, 2);

        count++;
        if(count >= 10)
            return 0;
        mdelay(500);
    }

	return 0;
}

static ssize_t aht20_read(struct file *filp, char __user *buf, size_t cnt, loff_t *off)
{
	uint32_t data[2];
	long err = 0;

	struct cdev *cdev = filp->f_path.dentry->d_inode->i_cdev;
	struct aht20_dev *dev = container_of(cdev, struct aht20_dev, cdev);
	
	aht20_readdata(dev, data);

	err = copy_to_user(buf, data, sizeof(data));
	return err;
}

static int aht20_release(struct inode *inode, struct file *filp)
{
	return 0;
}

static const struct file_operations aht20_ops = {
	.owner = THIS_MODULE,
	.open = aht20_open,
	.read = aht20_read,
	.release = aht20_release,
};
 
static const struct i2c_device_id aht20_dev_id[] = {
	{ DEV_ID_NAME, 0 },
	{ }
};
 
static int i2c_drv_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
	int ret;
	struct aht20_dev *aht20;

    my_client = client;    
 
	aht20 = devm_kzalloc(&client->dev, sizeof(*aht20), GFP_KERNEL);
	if(!aht20)
		return -ENOMEM;
		
	ret = alloc_chrdev_region(&aht20->dev_id, 0, DEVICE_CNT, DEVICE_NAME);
	if(ret < 0) {
		pr_err("%s Couldn't alloc_chrdev_region, ret=%d\r\n", DEVICE_NAME, ret);
		return -ENOMEM;
	}

	aht20->cdev.owner = THIS_MODULE;
	cdev_init(&aht20->cdev, &aht20_ops);
	
	ret = cdev_add(&aht20->cdev, aht20->dev_id, DEVICE_CNT);
	if(ret < 0) {
		goto del_unregister;
	}
	
	aht20->class = class_create(THIS_MODULE, DEVICE_NAME);
	if (IS_ERR(aht20->class)) {
		goto del_cdev;
	}

	aht20->device = device_create(aht20->class, NULL, aht20->dev_id, NULL, DEVICE_NAME);
	if (IS_ERR(aht20->device)) {
		goto destroy_class;
	}

	aht20->client = client;
	i2c_set_clientdata(client,aht20);

	return 0;

destroy_class:
	device_destroy(aht20->class, aht20->dev_id);
del_cdev:
	cdev_del(&aht20->cdev);
del_unregister:
	unregister_chrdev_region(aht20->dev_id, DEVICE_CNT);

	return -EIO;
}
 
static int i2c_drv_remove(struct i2c_client *c)
{
    struct aht20_dev *aht20 = i2c_get_clientdata(c);
	cdev_del(&aht20->cdev);
	unregister_chrdev_region(aht20->dev_id, DEVICE_CNT); 
	device_destroy(aht20->class, aht20->dev_id);
	class_destroy(aht20->class);  
    return 0;
}
 
static struct i2c_driver aht20_drv = {
	.driver = {
		.name	= "aht20_drv",
        .owner = THIS_MODULE,
	},
	.probe		= i2c_drv_probe,
	.remove		= i2c_drv_remove,
	.id_table	= aht20_dev_id,
};
 
static int __init i2c_drv_init(void)
{
	i2c_add_driver(&aht20_drv);
	return 0;
}
 
static void __exit i2c_drv_exit(void)
{
	i2c_del_driver(&aht20_drv);
}
 
module_init(i2c_drv_init);
module_exit(i2c_drv_exit);
MODULE_LICENSE("GPL");

Makefile文件

obj-m += aht20_dev.o aht20_drv.o 
KDIR:=/home/asensing/loongson/linux-4.19
ARCH=loongarch 
CROSS_COMPILE=loongarch64-linux-gnu-
PWD?=$(shell pwd) 
all:
	make -C $(KDIR) M=$(PWD) modules 

测试用例

#include "stdio.h"
#include "unistd.h"
#include "fcntl.h"

#define DEV_NAME "/dev/aht20"

int main()
{
    int fd, temp, humi;
    unsigned int data[2];

    fd = open(DEV_NAME, 0);
    if(fd < 0)
    {
        printf("Open %s failed\n", DEV_NAME);
        return 1;
    }
    else
	{
		printf("Open %s success!\n", DEV_NAME);
	}

    while(1)
    {
        read(fd, &data, sizeof(data)); 

		humi = data[0] * 1000.0 / 1024 / 1024;  			
        temp = data[1] * 2000.0 / 1024 / 1024 - 500;

		printf("temp : %d.%d℃, humi : %d.%d%%\n", (temp/10), (temp%10), (humi/10),(humi%10));

		sleep(1);
    }

	close(fd);
	return 0;
}

构建脚本

export PATH=$PATH:/home/asensing/loongson/loongson-gnu-toolchain-8.3-x86_64-loongarch64-linux-gnu-rc1.3-1/bin
make -j8
loongarch64-linux-gnu-gcc test.c -o test
FILE=$PWD/$(basename $PWD).ko
scp aht20_dev.ko aht20_drv.ko test root@192.168.137.148:/home/root

实验结果

insmod相关驱动、运行测试用例即可查看环境中的温湿度数值

在这里插入图片描述

参考

介绍:AHT20集成式温湿度传感器-温湿度传感器-温湿度传感器 温湿度芯片 温湿度变送器模块 气体传感器 流量传感器 广州奥松电子股份有限公司 (aosong.com)

例程:http://www.aosong.com/userfiles/files/file/20240119/20240119105503_8338.zip

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

下里巴人hywing

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值