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

news2025/1/18 20:26:48

实验背景

由于官方内核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.coloradmin.cn/o/1881163.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

力扣931. 下降路径最小和

Problem: 931. 下降路径最小和 文章目录 题目描述思路复杂度Code 题目描述 思路 1.定义状态&#xff1a;我们定义dp[i][j]为从矩阵的第一行到达位置(i, j)的最小下降路径和。 2.初始化状态&#xff1a;对于矩阵的第一行&#xff0c;即i 0时&#xff0c;dp[0][j]就是矩阵的第一…

ESP8266[ 关于-巴发云MQTT/TCP:arduino 设置回调函数 ] 日志2024/6/29

ESP8266 [ 关于-巴发云MQTT/TCP:arduino 设置回调函数 ] 日志2024/6/29 arduino库:#include <PubSubClient.h> 回调函数 是其库设置好的 可以改名字 这里只写上关键代码 设置客户端为 A 关键代码: A.setCallback(回调名) //MQTT 回调处理mqttmsgg(自定义…

光速入门 Tailwind CSS

文章目录 入门安装IDE 设置使用预编译器生产环境优化 基础概念分层指令tailwindlayerapplyconfig 函数theme()screen() 基础案例怎么设置属性任意值&#xff1f;hover 父元素时&#xff0c;怎么选中子元素添加样式&#xff1f;添加 animation 动画 配置主题 Tailwind CSS 中文网…

Unity 数据持久化【PlayerPrefs】

1、数据持久化 文章目录 1、数据持久化PlayerPrefs基本方法1、PlayerPrefs概念2、存储相关3、读取相关4、删除数据思考 信息的存储和读取 PlayerPrefs存储位置1、PlayerPrefs存储的数据在哪个位置2、PlayerPrefs 数据唯一性思考 排行榜功能 2、Playerprefs实践1、必备知识点-反…

【SQLmap】常用命令

文章目录 实际使用案例常用命令基本命令数据库指纹识别用户信息用户权限数据库枚举数据导出密码哈希操作系统命令执行文件操作代理和网络参数指定保存恢复自动搜索注入智能模式等级设置自动注入WAF 绕过杂项帮助和支持 SQLmap 是一款开源的自动化 SQL 注入检测和利用工具&#…

一文读懂Feed流

前言&#xff1a;当前最流行的Feed流产品有微博、微信朋友圈、头条的资讯推荐、快手抖音的视频推荐等&#xff0c;还有一些变种&#xff0c;比如私信、通知等&#xff0c;这些系统都是Feed流系统。Feed流是Feed 流&#xff0c;Feed的本意是饲料&#xff0c;Feed流的本意就是有…

【python】python基于tkinter的学生成绩管理系统(源码+数据文件)【独一无二】

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化【获取源码商业合作】 &#x1f449;荣__誉&#x1f448;&#xff1a;阿里云博客专家博主、5…

AI绘画-Stable Diffusion 原理介绍及使用

引言 好像很多朋友对AI绘图有兴趣&#xff0c;AI绘画背后&#xff0c;依旧是大模型的训练。但绘图类AI对计算机显卡有较高要求。建议先了解基本原理及如何使用&#xff0c;在看看如何实现自己垂直行业的绘图AI逻辑。或者作为使用者&#xff0c;调用已有的server接口。 首先需…

20240701 每日AI必读资讯

&#x1f3eb;AI真炼丹&#xff1a;整整14天&#xff0c;无需人类参与 - 英矽智能推出全球首个AI参与决策的生物学实验室&#xff0c;实现了14天内完成靶点发现和验证的全自动化闭环实验。 - 该实验室由PandaOmics平台驱动&#xff0c;集成多种预测模型和海量数据&#xff0…

springboot基础入门2(profile应用)

Profile应用 一、何为Profile二、profile配置方式1.多profile文件方式2.yml多文档方式 三、加载顺序1. file:./config/: 当前项目下的/config目录下2. file:./ &#xff1a;当前项目的根目录3. classpath:/config/:classpath的/config目录4. classpath:/ : classpath的根目录 四…

ffmpeg使用bmp编码器把bgr24编码为bmp图像

version #define LIBAVCODEC_VERSION_MAJOR 60 #define LIBAVCODEC_VERSION_MINOR 15 #define LIBAVCODEC_VERSION_MICRO 100 note 不使用AVOutputFormat code void CFfmpegOps::EncodeBGR24ToBMP(const char* infile, const char* width_str, const char* height_str…

Python数据分析-风湿关节炎生存分析

一、研究背景和意义 类风湿关节炎&#xff08;RA&#xff09;是一种慢性炎症性疾病&#xff0c;主要影响关节&#xff0c;但也可能影响身体的其他部分。RA的病因尚不完全清楚&#xff0c;但已知其涉及免疫系统的异常反应。患者的免疫系统错误地攻击自身的关节组织&#xff0c;…

机器学习中的数学底蕴与设计模式

在说机器学习设计模式之前&#xff0c;想多说几句&#xff0c;在进入软件行业最初的10年&#xff0c;那时候耳熟能详的基本就是多线程编程&#xff0c;互斥同步锁&#xff0c;设计模式&#xff0c;OOA&#xff0c;OOP&#xff0c;常规数组&#xff0c;tree&#xff0c;图的数据…

【图论】200. 岛屿问题

200. 岛屿问题 难度&#xff1a;中等 力扣地址&#xff1a;https://leetcode.cn/studyplan/top-100-liked/ 问题描述 给你一个由 1&#xff08;陆地&#xff09;和 0&#xff08;水&#xff09;组成的的二维网格&#xff0c;请你计算网格中岛屿的数量。 岛屿总是被水包围&…

你真的会udf提权???数据库权限到系统权限 内网学习 mysql的udf提权操作 ??msf你会用了吗???

我们在已经取得了数据库的账号密码过后&#xff0c;我们要进一步进行提取的操作&#xff0c;我们mysql有4钟提权的操作。 udf提权(最常用的)mof提权启动项提权反弹shell提权操作 怎么获取密码操作&#xff1a; 怎么获取密码&#xff0c;通过sql注入获取这个大家都应该知道了&a…

专题四:Spring源码初始化环境与BeanFactory

上文我们通过new ClassPathXmlApplicationContext("applicationContext.xml");这段代码看了下Spring是如何将Xml里面内容注入到Java对象中&#xff0c;并通过context.getBean("jmUser");方式获得了一个对象实例&#xff0c;而避开使用new 来耦合。今天我们…

Houdini速通VOP强化训练上部

Houdini 速通 VOP 强化训练上部是一门针对 Houdini 中 VOP&#xff08;Vector Operation Language&#xff09;的强化训练课程。本课程深入探讨了 Houdini 中 VOP 的核心概念和高级技术&#xff0c;通过实际案例和项目实战&#xff0c;帮助学员快速掌握 VOP 的使用技巧和编程方…

SpringBoot Task 定时任务

springboot中使用Task定时任务非常简单 springboot 中自带的都有注解不需要引入依赖 第一步&#xff1a;在启动类上添加启用定时任务注解 EnableScheduling //开启任务调度 第二步&#xff1a;创建一个springboot组件用于定时任务管理 package cn.lsy.api.Task;import cn.ls…

【Unity实战】在RHEL 9上安装UnityHub

一般来说&#xff0c;Unity编辑器的安装&#xff0c;官方已经给出了安装教程。 但是这个忽略了RHEL 9的一个特性&#xff1a;默认的加解密策略已经不支持SHA1了&#xff0c;你会在执行yum install unityhub那里出现如下报错&#xff1a; [shepherdlocalhost ~]$ sudo yum inst…

[C++][设计模式][备忘录模式]详细讲解

目录 1.动机2.模式定义3.要点总结4.代码感受 1.动机 在软件构建过程中&#xff0c;某些对象的状态转换过程中&#xff0c;可能由于某中需要&#xff0c;要求程序能够回溯到对象之前处于某个点的状态。 如果使用一些公开接口来让其他对象得到对象的状态&#xff0c;便会暴露对象…