【驱动篇】龙芯LS2K0300之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 root@192.168.137.148:/home/root

实验结果

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

在这里插入图片描述

参考

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/759323.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

K8S之网络深度剖析(一)(持续更新ing)

K8S之网络深度剖析 一 、关于K8S的网络模型 在K8s的世界上,IP是以Pod为单位进行分配的。一个Pod内部的所有容器共享一个网络堆栈(相当于一个网络命名空间,它们的IP地址、网络设备、配置等都是共享的)。按照这个网络原则抽象出来的为每个Pod都设置一个IP地址的模型也被称作为I…

忍法:声音克隆之术

前言&#xff1a; 最近因为一直在给肚子里面的宝宝做故事胎教&#xff0c;每天&#xff08;其实是看自己心情抽空讲下故事&#xff09;都要给宝宝讲故事&#xff0c;心想反正宝宝也看不见我&#xff0c;只听我的声音&#xff0c;干脆偷个懒&#xff0c;克隆自己的声音&#xf…

信息学奥赛初赛天天练-40-CSP-J2021基础题-组合数学-缩倍法、平均分组、2进制转10进制、面向过程/面向对象语言应用

PDF文档公众号回复关键字:20240630 2021 CSP-J 选择题 单项选择题&#xff08;共15题&#xff0c;每题2分&#xff0c;共计30分&#xff1a;每题有且仅有一个正确选项&#xff09; 1.以下不属于面向对象程序设计语言是( ) A. C B. Python C. Java D. C 2.以下奖项与计…

R包的4种安装方式及常见问题解决方法

R包的4种安装方式及常见问题解决方法 R包的四种安装方式1. install.packages()2. 从Bioconductor安装3. 从本地源码安装4. 从github安装 常见问题的解决1. 版本问题2. 网络/镜像问题3.缺少Rtools R包的四种安装方式 1. install.packages() 对于R自带的包的安装一般都可以通过…

HarmonyOS--路由管理--组件导航 (Navigation)

文档中心 什么是组件导航 (Navigation) &#xff1f; 1、Navigation是路由容器组件&#xff0c;一般作为首页的根容器&#xff0c;包括单栏(Stack)、分栏(Split)和自适应(Auto)三种显示模式 2、Navigation组件适用于模块内和跨模块的路由切换&#xff0c;一次开发&#xff0…

实现点击按钮导出页面pdf

在Vue 3 Vite项目中&#xff0c;你可以使用html2canvas和jspdf库来实现将页面某部分导出为PDF文档的功能。以下是一个简单的实现方式&#xff1a; 1.安装html2canvas和jspdf&#xff1a; pnpm install html2canvas jspdf 2.在Vue组件中使用这些库来实现导出功能&#xff1a;…

网线直连电脑可以上网,网线连tplink路由器上不了网

家里wifi网络连不上好几天了&#xff0c;用网线直连电脑可以上网&#xff0c;但网线连tplink路由器wan口上不了网&#xff0c;无Internet连接&#xff0c;网线连lan口可以电脑上网&#xff0c;手机上不了。 后来发现网线的主路由用的192.168.0.1&#xff0c;我的路由器wan口自…

在node环境使用MySQL

什么是Sequelize? Sequelize是一个基于Promise的NodeJS ORM模块 什么是ORM? ORM(Object-Relational-Mapping)是对象关系映射 对象关系映射可以把JS中的类和对象&#xff0c;和数据库中的表和数据进行关系映射。映射之后我们就可以直接通过类和对象来操作数据表和数据了, 就…

【大数据导论】大数据序言

各位大佬好 &#xff0c;这里是阿川的博客&#xff0c;祝您变得更强 个人主页&#xff1a;在线OJ的阿川 大佬的支持和鼓励&#xff0c;将是我成长路上最大的动力 阿川水平有限&#xff0c;如有错误&#xff0c;欢迎大佬指正 目录 数据概念及类型及可用及组织形式数据概念数据…

golang项目基于gorm框架从postgre数据库迁移到达梦数据库的实践

一、安装达梦数据库 1、登录达梦数据库官网&#xff0c;下载对应系统版本的安装包。 2、下载地址为&#xff1a;https://www.dameng.com/list_103.html 3、达梦数据库对大小写敏感&#xff0c;在安装初始化数据库实例时建议忽略大小写&#xff1b;具体安装教程可参考以下博客: …

python办公自动化之pandas

用到的库&#xff1a;pandas 实现效果&#xff1a;创建一张空白的表同时往里面插入准备好的数据 代码&#xff1a; import pandas # 准备好要写入的数据&#xff0c;字典格式 data{日期:[7.2,7.3],产品型号:[ca,ce],成交量:[500,600]} dfpandas.DataFrame(data) # 把数据写入…

Java代码基础算法练习-计算被 3 或 5 整除数之和-2024.06.29

任务描述&#xff1a; 计算 1 到 n 之间能够被 3 或者 5 整除的数之和。 解决思路&#xff1a; 输入的数字为 for 循环总次数&#xff0c;每次循环就以当前的 i 进行 3、5 的取余操作&#xff0c;都成立计入总数sum中&#xff0c;循环结束&#xff0c;输出 sum 的值 代码示例&…

QT加载安装外围依赖库的翻译文件后翻译失败的现象分析:依赖库以饿汉式的形式暴露单例接口导致该现象的产生

1、前提说明 QtClassLibaryDll是动态库,QtWidgetsApplication4是应用程序。 首先明确:动态库以饿汉式的形式进行单例接口暴露; 然后,应用程序加载动态库的翻译文件并进行全局安装; // ...QTranslator* trans = new QTranslator();//qDebug() << trans->load(&quo…

大模型系列:提示词管理

既然大模型应用的编程范式是面向提示词的编程&#xff0c;需要建立一个全面且结构化的提示词库&#xff0c; 对提示词进行持续优化也是必不可少的&#xff0c;那么如何在大模型应用中更好的管理提示词呢&#xff1f; 1. 提示词回顾 提示词在本质上是向大型语言模型&#xff08…

​Chrome插件:React Developer Tools为React开发调试而生

React Developer Tools 是什么? 它是允许在Chrome和Firefox开发者工具中检查React组件层次结构的扩展插件。 插件源码下载 源码下载地址:GitHub - facebook/react-devtools at v3 下载完成以后执行红框中的代码,下载react-devtools 源码,源码如下图所示: 插件打包 当前n…

【C++】 ——【模板初阶】——基础详解

目录 1. 泛型编程 1.1 泛型编程的概念 1.2 泛型编程的历史与发展 1.3 泛型编程的优势 1.4 泛型编程的挑战 2. 函数模板 2.1 函数模板概念 2.2 函数模板格式 2.3 函数模板的原理 2.4 函数模板的实例化 2.5 模板参数的匹配原则 2.6 函数模板的特化 2.7 函数模板的使…

Linux CentOS 宝塔中禁用php8.2的eval函数详细图文教程

PHP_diseval_extension 这个方法是支持PHP8的, Suhosin禁用eval函数&#xff0c;不支持PHP8 一、安装 cd / git clone https://github.com/mk-j/PHP_diseval_extension.gitcd /PHP_diseval_extension/source/www/server/php/82/bin/phpize ./configure --with-php-config/ww…

美团校招机试 - 小美的平衡矩阵(20240309-T1)

题目来源 美团校招笔试真题_小美的平衡矩阵 题目描述 小美拿到了一个 n * n 的矩阵&#xff0c;其中每个元素是 0 或者 1。 小美认为一个矩形区域是完美的&#xff0c;当且仅当该区域内 0 的数量恰好等于 1 的数量。 现在&#xff0c;小美希望你回答有多少个 i * i 的完美…

C++操作系列(二):VSCode安装和配置C++开发环境

1. VSCode下载 进入VSCode的官网网页&#xff1a;Download Visual Studio Code - Mac, Linux, Windows 下载相应的版本&#xff1a; 2. 安装VSCode 安装到指定位置&#xff1a; 一路下一步&#xff0c;直至安装完成&#xff1a; 3. 安装C插件 3.1. 安装C/C 点击扩展图标&…

linux上git的使用

目录 1.测试是否安装有git 2.下载项目到本地 3.三板斧 1.将代码放在创建的目录中 2.提交改动到本地 3.提交代码到远端 4.注意点 以及补充内容 1.测试是否安装有git 如果输入git --help 会显示下面一大串那么就是已经安装&#xff0c;否则需要自行手动安装 yum install g…