首页 > 其他分享 >【驱动篇】龙芯LS2K0300之i2c设备驱动

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

时间:2024-06-30 18:59:45浏览次数:20  
标签:include struct 龙芯 dev aht20 client 驱动 i2c

实验背景

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

  1. 模块连接,查看aht20设备地址
  2. 编写device驱动,通过i2c_get_adapter注册i2c_client设备
  3. 编写i2c_driver驱动,需要匹device部分的i2c_device_id
  4. 编写测试用例,读取两个寄存器地址的温湿度数值
  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 [email protected]:/home/root

实验结果

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

在这里插入图片描述

参考

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

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

标签:include,struct,龙芯,dev,aht20,client,驱动,i2c
From: https://blog.csdn.net/HeavenMo/article/details/140084690

相关文章

  • IIC驱动-基于EEPROM存储芯片AT24C02模块和三合一环境传感器AP3216C
    本文将基于IIC协议编写EEPROM芯片AT24C02存储芯片的IIC驱动程序,本文内容将分为三个部分:imx6ull的IIC控制器介绍,AT24C02存储芯片介绍,IIC的Linux驱动程序编写。关于IIC协议的内容与介绍这里不展开,相关资料很多,可以自行去查阅,但是这里需要注意的是,IIC协议本身就是一个协议,只是一些基......
  • IMX6ULL开发板spi OLED驱动
    本文是IMX6ULL开发板spiOLED驱动学习笔记,方便后面查看时快速的回顾,而不需要一点点的看视频视频地址:https://www.bilibili.com/video/BV1Yb4y1t7Uj?p=144&spm_id_from=pageDriver&vd_source=1d93d6a5e22d4b223c6c3ac4f5727eb8视频选集:P141-P1501、将文件上传到虚拟机共享目......
  • 通过ESP32读取I2C温湿度传感器项目:协议与代码实例
    简介在本项目中,我们将使用ESP32开发板读取I2C温湿度传感器的数据。我们将详细介绍I2C协议,并提供图文并茂的代码实例,帮助你快速上手。项目流程选择硬件:ESP32开发板、I2C温湿度传感器(如DHT12、HTU21D、SHT30等)、连接线和面包板。了解I2C协议:I2C(Inter-IntegratedCircuit)是......
  • 阿里巴巴关键字搜索商品API返回值探索:数据驱动的电商产品创新策略
    阿里巴巴关键字搜索商品API返回值探索与数据驱动的电商产品创新策略密切相关。以下是对API返回值结构的解析以及如何利用这些数据驱动电商产品创新策略的详细阐述:一、阿里巴巴关键字搜索商品API返回值探索阿里巴巴关键字搜索商品API的返回值结构通常是一个JSON格式的数据包,包......
  • 英伟达的驱动跟CUDA的关系,我可以类比为python解释器和python吗?
    问题:英伟达的驱动跟CUDA的关系,我可以类比为python解释器和python吗?AI答案:是的,你可以把英伟达的驱动和CUDA的关系类比为Python解释器和Python语言。具体来说:英伟达驱动(NVIDIADriver):这个类似于Python解释器。它是底层的软件,负责与硬件(即GPU)进行通信。英伟达驱动提供了基本的......
  • 【测试文章搬运】用selenium并从excel里面读取测试数据——采用数据驱动测试自动化框
    SeleniumFrameworkCreationAndAccessingTestDataFromExcel–SeleniumTutorial在本系列的当前教程中,我们将为您提供一个示例框架,用于存储测试数据及其Excel操作的Excel。同样,我们将继续前进并引入新的策略和资源来完善我们的框架。让我们学习一下:使用示例项目的......
  • 用ChatGPT革新SEO:搜索引擎优化的AI驱动策略
    用ChatGPT革新SEO:搜索引擎优化的AI驱动策略引言搜索引擎优化(SEO)是数字营销中不可或缺的一部分,它影响着网站在搜索引擎结果页(SERP)上的表现。随着人工智能(AI)技术的兴起,ChatGPT等AI工具为SEO带来了新的机遇。本文将深入探讨如何利用ChatGPT辅助SEO,提供一系列创新策略和实践指......
  • 【HDC 2024】华为云开发者联盟驱动应用创新,赋能开发者成长
    本文分享自华为云社区《【HDC2025】华为云开发者联盟驱动应用创新,赋能开发者成长》,作者:华为云社区精选。6月21日到23日,华为开发者大会(HDC2024)于东莞松山湖举行,这里有丰富多样的主题演讲、峰会、专题论坛和互动体验,数百场面向开发者的特色活动,汇聚璀璨星光、激发创新灵感……6......
  • 鸿蒙生态伙伴SDK市场正式发布,驱动千行百业鸿蒙原生应用开发
    6月21-23日,华为开发者大会(HDC2024)在东莞举办。在22日举办的【鸿蒙生态伙伴SDK】论坛中,正式发布了【鸿蒙生态伙伴SDK市场】(以下简称:伙伴SDK市场),伙伴SDK市场是为开发者提供各类优质、安全SDK的聚合平台,帮助开发者便捷搜索、选取和使用能力丰富的SDK,轻松、高效地打造焕然一新的鸿蒙......
  • Linux平台I2C多字节数据、按页连续读写E2PROM Demo(AT24C16)
    1)Linux平台I2C多字节数据按页连续读写E2PROM之AT24C16小知识,分享给将要学习或者正在学习Linux平台I2C多字节按页连续读写E2PROM开发的同学。2)内容属于原创,若转载,请说明出处。3)提供相关问题有偿答疑和支持。我测试使用的是 E2PROM(AT24C16)芯片,其中内部地址是1010 外部的地......