AHT30是由ASAIR奥松推出的新一代数字温湿度传感器,采用双列扁平无引脚SMD封装,尺寸微小且坚固,广泛应用于智能家居消费类电子产品等领域。
规格书如下:
我们只需要关注这一部分内容就可以了


读取温湿度的流程图如下:


本次实验我们选择i2c2接口与AHT30相连。实物连接图如下:

设备树插件(Device Tree Overlay,简称DTO)是Linux内核(通常在4.4版本及以上引入)中一种强大的动态设备树扩展机制。可以把它理解成能给正在运行的系统“打补丁”,允许在系统运行时动态地添加、修改或删除设备树中的节点和属性,而无需重新编译和烧写整个设备树。
1.设备树源码/dts-v1/;2.编译设备树插件
/plugin/;
#include <dt-bindings/leds/common.h>
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/input/input.h>
/*
* Helper to show loaded overlays under: /proc/device-tree/chosen/overlays/
*/
&{/chosen} {
overlays {
kone-pocketbeagle2.kernel = __TIMESTAMP__;
};
};
&main_i2c2 {
status = "okay";
#address-cells = <1>;
#size-cells = <0>;
mpu6050@68{
status = "okay";
compatible = "kone,i2c_mpu6050";
reg = <0x68>;
};
AHT30@38{
status = "okay";
compatible = "kone,AHT30";
reg=<0x38>;
};
};
设置正确的环境变量
export PATH="/home/mxz/pocketbeagle_2/sdk/gcc-11.5.0-nolibc/aarch64-linux/bin:$PATH"
# 设置架构和交叉编译前缀
export ARCH=arm64
export CROSS_COMPILE=aarch64-linux-
makefile中添加新增的设备树
nano /home/mxz/pocketbeagle_2/sdk/linux/arch/arm64/boot/dts/ti/Makefile
dtb-$(CONFIG_ARCH_K3) += kone-pocketbeagle2.dtbo
编译
# 进入内核源码目录3.加载设备树插件
cd /home/mxz/pocketbeagle_2/sdk/linux
# 编译所有设备树
make dtbs
将生成的 kone-pocketbeagle2.dtbo copy到/boot/firmware/overlays/下
更新引导配置
sudo nano /boot/firmware/extlinux/extlinux.conf
label Linux
kernel /Image.gz
fdtdir /
append console=ttyS2,115200n8 earlycon=ns16550a,mmio32,0x02860000 root=/dev/mmcblk1p2 ro rootfstype=ext4 rootwait net.ifnames=0
fdtoverlays /overlays/kone-pocketbeagle2.dtbo验证
# 重启
sudo reboot确认设备树覆盖层加载成功
# 查看覆盖层时间戳(确认编译时间)
cat /sys/firmware/devicetree/base/chosen/overlays/kone-pocketbeagle2.kernel
# 查看所有加载的覆盖层
ls /sys/firmware/devicetree/base/chosen/overlays/查看 I2C2 总线
ls /sys/bus/i2c2/devices/
驱动源码
#include <linux/init.h> #include <linux/module.h> #include <linux/kdev_t.h> #include <linux/cdev.h> #include <linux/fs.h> #include <linux/i2c.h> #include <linux/types.h> #include <linux/kernel.h> #include <linux/errno.h> #include <linux/of.h> #include <asm/io.h> #include <linux/device.h> #include <linux/delay.h> #include <linux/platform_device.h> //==================================define====================================== /*---- AHT30设备地址与寄存器宏 ----*/ #define SHT30_ADDR 0x38 #define DEV_NAME "AHT30" static dev_t AHT30_dev_num; static struct cdev AHT30_cdev; struct device*AHT30_device; struct class*AHT30_class; struct i2c_client*AHT30_client = NULL; static int i2c_write_AHT30(struct i2c_client*client,uint8_t *data,uint8_t len) { int ret; struct i2c_msg msg; if (!client) { printk(KERN_EMERG "AHT30: NULL client pointer\n"); return -EINVAL; } msg.addr = client->addr; msg.flags = 0; msg.buf = data; msg.len = len; ret = i2c_transfer(client->adapter,&msg,1); if(ret != 1) { printk(KERN_EMERG"i2c_write_AHT30 i2c_transfer is error\n"); return -EIO; } return 0; } static int i2c_read_AHT30(struct i2c_client*client,uint8_t *data,uint8_t len) { int ret; struct i2c_msg msg; msg.addr = client->addr; msg.flags = I2C_M_RD; msg.buf = data; msg.len = len; ret = i2c_transfer(client->adapter,&msg,1); if(ret != 1) { printk(KERN_EMERG "AHT30 read error: ret=%d, addr=0x%02X, len=%d\n", ret, client->addr, len); return -EIO; } return 0; } static int i2c_init(void) { msleep(5); return 0; } static int AHT30_open(struct inode*inode,struct file*file) { i2c_init(); return 0; } static ssize_t AHT30_read(struct file*file,char __user*buf,size_t size, loff_t*off) { uint8_t read_SHT30_data_cmd[3] = {0xAC,0x33,0x00}; uint8_t data[7] = {0}; uint8_t ret; i2c_write_AHT30(AHT30_client,read_SHT30_data_cmd,sizeof(read_SHT30_data_cmd)); msleep(80); i2c_read_AHT30(AHT30_client,data,sizeof(data)); ret = copy_to_user(buf,data,size);//目标add 源add if(ret != 0) { printk(KERN_EMERG "SHT30_read copy_to_user is error\n"); return -EIO; } return sizeof(data); } static int AHT30_release(struct inode*inode,struct file*file) { return 0; } static struct file_operations fops = { .owner = THIS_MODULE, .open = AHT30_open, .read = AHT30_read, .release = AHT30_release, }; static int AHT30_probe(struct i2c_client*client) { int ret; ret = alloc_chrdev_region(&AHT30_dev_num,0,1,DEV_NAME); if(ret < 0) { printk(KERN_EMERG "alloc_chrdev_region is error\n"); return -1; } printk(KERN_EMERG "AHT30_dev_num is %d\n",AHT30_dev_num); AHT30_cdev.owner = THIS_MODULE; cdev_init(&AHT30_cdev,&fops); ret = cdev_add(&AHT30_cdev,AHT30_dev_num,1); if(ret < 0) { printk(KERN_EMERG "cdev_add is error\n"); return -1; } AHT30_class = class_create(DEV_NAME); AHT30_device = device_create(AHT30_class,NULL,AHT30_dev_num,NULL,DEV_NAME); AHT30_client = client; return 0; } static void AHT30_remove(struct i2c_client*client) { device_destroy(AHT30_class,AHT30_dev_num); class_destroy(AHT30_class); cdev_del(&AHT30_cdev); unregister_chrdev_region(AHT30_dev_num,1); } static const struct i2c_device_id AHT30_device_id[] = { {"kone,AHT30"}, {}, }; static const struct of_device_id AHT30_of_match_table[] = { {.compatible = "kone,AHT30"}, {}, }; struct i2c_driver AHT30_driver = { .probe = AHT30_probe, .remove = AHT30_remove, .id_table = AHT30_device_id, .driver = { .name = "kone,AHT30", .owner = THIS_MODULE, .of_match_table = AHT30_of_match_table, }, }; static int __init AHT30_driver_init(void) { i2c_add_driver(&AHT30_driver); printk(KERN_EMERG "AHT30_driver_init\n"); return 0; } static void __exit AHT30_driver_exit(void) { i2c_del_driver(&AHT30_driver); printk(KERN_EMERG "AHT30_driver_exit\n"); } module_init(AHT30_driver_init); module_exit(AHT30_driver_exit); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("kone");Makefile
# 确保系统工具目录在 PATH 的最前面 export PATH="/bin:/usr/bin:/home/mxz/pocketbeagle_2/sdk/gcc-11.5.0-nolibc/aarch64-linux/bin:$PATH" # 设置架构和交叉编译前缀 export ARCH=arm64 export CROSS_COMPILE=aarch64-linux- obj-m:=AHT30.o KDIR:=/home/mxz/pocketbeagle_2/sdk/linux PWD:=$(shell pwd) all: make -C $(KDIR) M=$(PWD) modules clean: make -C $(KDIR) M=$(PWD) clean
测试app
#include <unistd.h> #include <stdio.h> #include <fcntl.h> #include <string.h> #include <stdlib.h> #include <stdint.h> //**********************************************************// //CRC校验类型:CRC8 //多项式:X8+X5+X4+1 //Poly:0011 0001 0x31 unsigned char Calc_CRC8(unsigned char *message,unsigned char Num) { unsigned char i; unsigned char byte; unsigned char crc =0xFF; for (byte = 0;byte<Num;byte++){ crc^=(message[byte]); for(i=8;i>0;--i){ if(crc&0x80) crc=(crc<<1)^0x31; else crc=(crc<<1); } } return crc; } int main(int argc,char *argv[]) { int fd,ret; uint8_t aht30_data[7]; float temp,humi; fd = open(argv[1],O_RDWR); if(fd < 0) { perror("open file is error\n"); return -1; } while(1) { ret = read(fd, aht30_data, sizeof(aht30_data)); if(ret < 0) { perror("read error"); } else if (ret < sizeof(aht30_data)) { printf("read only %d bytes (expected %zu)\n", ret, sizeof(aht30_data)); } else { // 打印原始数据 printf("Raw data: "); for(int i = 0; i < 7; i++) { printf("%02X ", aht30_data[i]); } printf("\n"); uint8_t crc = Calc_CRC8(aht30_data, 6); printf("Calculated CRC: 0x%02X, Received CRC: 0x%02X\n", crc, aht30_data[6]); if (crc == aht30_data[6]) { uint32_t raw_humidity = ((uint32_t)aht30_data[1] << 12) | ((uint32_t)aht30_data[2] << 4) | (aht30_data[3] >> 4); uint32_t raw_temperature = ((uint32_t)(aht30_data[3] & 0x0F) << 16) | ((uint32_t)aht30_data[4] << 8) | aht30_data[5]; temp = ((float)raw_temperature / 1048576.0)*200.0 -50.0; humi = ((float)raw_humidity / 1048576.0)*100.0; printf("temp:.%2f humi:%.2f\n",temp,humi); } else { printf("crc is error\n"); } } sleep(1); } }
将生成的AHT30.ko和app.c copy到开发板上
			
			
			
						
			
 我要赚赏金
