这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 活动中心 » 板卡试用 » 【PocketBeagle2】六,I2C子系统-AHT30驱动

共1条 1/1 1 跳转至

【PocketBeagle2】六,I2C子系统-AHT30驱动

菜鸟
2025-11-03 09:31:41     打赏
【PocketBeagle2】六,I2C子系统-AHT30驱动

AHT30是由ASAIR奥松推出的新一代数字温湿度传感器,采用双列扁平无引脚SMD封装,尺寸微小且坚固,广泛应用于智能家居消费类电子产品等领域。

规格书如下:AHT30 -PDF数据手册-参考资料-立创商城

我们只需要关注这一部分内容就可以了

image.pngimage.png


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

image-20250728093457455image.png


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

image.png

一,AHT30设备树插件

设备树插件(Device Tree Overlay,简称DTO)是Linux内核(通常在4.4版本及以上引入)中一种强大的动态设备树扩展机制。可以把它理解成能给正在运行的系统“打补丁”,允许在系统运行时动态地添加、修改或删除设备树中的节点和属性,而无需重新编译和烧写整个设备树。

1.设备树源码
/dts-v1/;
/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>;
   };
};
2.编译设备树插件

设置正确的环境变量

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

编译

# 进入内核源码目录
cd /home/mxz/pocketbeagle_2/sdk/linux

# 编译所有设备树
make dtbs
3.加载设备树插件
  1. 将生成的 kone-pocketbeagle2.dtbo  copy到/boot/firmware/overlays/下

  2. 更新引导配置

    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
  3. 验证

    # 重启
    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/
二,AHT30驱动
  1. 驱动源码

    #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");
  2. 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


  3. 测试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到开发板上





共1条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]