基于RK3399Pro的AM2321温湿度数据采集-单总线


目录

原理图

AM2321单总线介绍

单总线通信特殊说明

单总线说明

单总线传送数据定义

单总线时序说明

读写的流程图

程序代码编写

添加设备树

驱动编写

扫描二维码关注公众号,回复: 13227141 查看本文章

匹配设备节点

文件探索

杂项设备

文件操作集

读温湿度接口

开始信号

温湿度数据读取一个byte字节

上层测试代码

Makefile编写

测试步骤

编译源码

加载驱动

执行测试程序

实验现象


原理图

AM2321采用的是单总线控制。

AM2321单总线介绍

查看am2321的数据手册

单总线通信特殊说明

  1. 典型应用电路中建议连接线长度短于 30 米时用 5.1K 上拉电阻,大于 30 米时根据实际情况降低上拉电阻的阻值。

  2. 使用 3.3V 电压供电时连接线长度不得大于 30cm。否则线路压降会导致传感器供电不足,造成测量偏差。

  3. 读取传感器最小间隔时间为 2S;读取间隔时间小于 2S,可能导致温湿度不准或通信不成功等情况。

  4. 每次读出的温湿度数值是上一次测量的结果,欲获取实时数据,需连续读取两次,建议连续多次读取传感器, 且每次读取传感器间隔大于 2 秒即可获得准确的数据。

单总线说明

​        AM2321 器件采用简化的单总线通信。单总线即只有一根数据线,系统中的数据交换、控制均由数据线完成。设备(微处理器)通过一个漏极开路或三态端口连至该数据线,以允许设备在不发送数据时能够释放总线,而让其它设备使用总线;单总线通常要求外接一个约 5.1kΩ的上拉电阻,这样,当总线闲置时,其状态为高电平。由于它们是主从结构,只有主机呼叫传感器时,传感器才会应答,因此主机访问传感器都必须严格遵循单总线序列,如果出现序列混乱,传感器将不响应主机。

单总线传送数据定义

​ SDA 用于微处理器与 AM2321 之间的通讯和同步,采用单总线数据格式,一次传送 40 位数据,高位先出。具体通信时序如下图1所示,通信格式 如下表1单总线格式所示:

图1 通信时序

表1 单总线格式

 单总线数据计算示例

示例一:接收到的 40 位数据为:

0000 0010 1001 0010 0000 0001 0000 1101 1010 0010
湿度高 8 位 计算: 湿度低 8 位 温度高 8 位 温度低 8 位 校验位

0000 0010+1001 0010 +0000 0001+0000 1101= 1010 0010(校验位)接收数据正确:

湿度: 0000 0010 1001 0010 = 0292H (十六进制)= 2× 256 + 9× 16 + 2 = 658=> 湿度 = 65.8%RH温度: 0000 0001 0000 1101 = 10DH(十六进制) = 1× 256 + 0× 16 + 13 = 269=> 温度= 26.9℃

单总线时序说明

 

       主机从 AM2321 读取的温湿度数据总是前一次的测量值,如两次测量间隔时间很长,请连续读两次以第二次获得的值为实时温湿度值,同时两次读取间隔时间最小为 2S

读写的流程图

程序代码编写

添加设备树

在设备树

arch/arm64/boot/dts/rockchip/rk3399pro-toybrick-prop-linux.dts 

中添加

        gpio-am2321{
                status = "okay";
                compatible = "gpio-am2321";
                gpio-am2321 = <&gpio1 RK_PB2 GPIO_ACTIVE_LOW>;
        };

驱动编写

匹配设备节点

static const struct of_device_id of_gec_am2321_match[] = {  
    { .compatible = "gpio-am2321", .data = NULL},    //compatible 兼容属性名,需与设备树节点的属性一致
    { /* NULL */},  
}; 
​
static struct platform_driver gec3399_am2321_driver = 
{
    .probe = rk_am_probe,
    .remove = rk_am_remove,
    .driver  = {
          .name ="gpio-am2321",
          .of_match_table = of_gec_am2321_match,     //设备树设备匹配
     }
​
};

文件探索

static int rk_am_probe(struct platform_device *pdev)
{
    int ret;
    struct device_node *buz_node = pdev->dev.of_node;
    printk(KERN_INFO "%s\n", __func__);
    
    printk(KERN_INFO"gpio-am2321 math succee\n");
    
    ret =  misc_register(&gec3399_am_misc);   //注册字符设备
    if(ret < 0){
        printk(KERN_INFO"misc register error\n");
        goto err_misc_register; 
     }  
    
​
    gpio_am = of_get_named_gpio(buz_node,"gpio-am2321", 0); //从设备树获取GPIO号
    if (!gpio_is_valid(gpio_am)) {
        printk("gpio-am2321: %d is invalid\n",gpio_am);
        ret = -ENODEV;
        goto err_get_gpio;
    } 
    printk(KERN_INFO"gpio-am2321 = %d\n",gpio_am);
    gpio_free(gpio_am);
    ret = gpio_request(gpio_am,"ak8963c_DYDR");    //申请gpio_am引脚为GPIO模式
    if(ret < 0){
        printk("gpio_request  gpio = ak8963c_DYDR error\n");
        goto err_get_gpio;
    }
    
    ret = gpio_direction_output(gpio_am,0);      //初始划蜂鸣器为关闭状态
    if(ret < 0){
        printk("gpio direction input  gpio = ak8963c_DYDR error\n");
        goto err_gpio_direction;
    }
    
    printk( KERN_ALERT "am2321 dirve install succee\n");
    return 0;
err_gpio_direction:
    gpio_free(gpio_am);
err_get_gpio:
    misc_deregister(&gec3399_am_misc);
err_misc_register:
    return ret;
}

杂项设备

static struct miscdevice gec3399_am_misc = {
    .minor = MISC_DYNAMIC_MINOR,
    .fops = &gec3399_am_fops,
    .name   = "am2321_drv", 
};   //混杂设备结构体定义和初始化

文件操作集

static const struct file_operations gec3399_am_fops = {
    .owner = THIS_MODULE,
    .read     = am2321_read,
    .release = gec3399_am_release,
};   //文件操作集结构体

读温湿度接口

static ssize_t am2321_read (struct file *file, char __user *buffer, size_t size, loff_t *offset)
{
    int ret; 
    unsigned long flags;
    unsigned short verify_sum = 0;
    //int i;
    debug("<0>""enter am2321_read functionn\n"); 
    debug("<0>""am2321_PIN level = %d\n",read_pin_level());
    
    /* 因为am2321的时序要求很高,所以在读温湿度的时候要让代码进入临界区,防止内核调度和抢占 */
    local_irq_save(flags);  
    ret = am2321_start_signal();
    
    if(ret ==1)  //如果收到应答
        am2321_read_data();
    else
        return -1;
    
    local_irq_restore(flags); 
    
    debug("<0>""%x %x %x %x %x\n",data_buf[0],data_buf[1],data_buf[2],data_buf[3],data_buf[4]);
    
​
    verify_sum = data_buf[0]+data_buf[1]+data_buf[2]+data_buf[3];
    
    if(verify_sum == data_buf[4])
    {
        /* 将读取的温湿度数据拷贝到用户空间 */
        ret = copy_to_user(buffer, data_buf, 4);  
        if(ret < 0)
        {  
            debug("<0>""copy to user err\n");  
            return -EAGAIN;  
        }  
    }
    else{
        printk("data is error\r\n");
    }
    
    return 0;
}

开始信号

static bool am2321_start_signal(void)  
{  
​
    int i = 0;  
    set_pin_level(1);
    mdelay(50); 
    //第一步:拉低10ms
    set_pin_level(0);  
    mdelay(1);  
    //第二步:拉高30us
    set_pin_level(1);  
    udelay(30); 
    //第三步:等待am2321主动拉低
    while(read_pin_level()==1)
    {
       i++;
       udelay(1); 
       if(i>500){
           debug("<0>""wait pin for low level  out time! %s %d\n",__FUNCTION__,__LINE__);
           goto err0;
       }
    }
    //debug("<0>""(1)wait pin for low level  success! i=%d\n",i);
    
    i = 0;
    //第四步:等待am2321拉高,不超过85us
    while(read_pin_level()==0)
    {
       i++;
       udelay(1); 
       if(i>90){
           debug("<0>""wait pin for hight level  out time! %s %d\n",__FUNCTION__,__LINE__);
           goto err0;
       }
    }
    //debug("<0>""(2)wait pin for hight level  success! i=%d\n",i);
    
    i = 0;
    //第五步:等待am2321拉低,不超过85us
    while(read_pin_level()==1)
    {
       i++;
       udelay(1); 
       if(i>500){
           debug("<0>""wait pin for data input  out time! %s %d\n",__FUNCTION__,__LINE__);
           goto err0;
       }
    }
    //debug("<0>""(3)wait pin for data input  success! i=%d\n",i);
​
    return 1;
err0:
    set_pin_level(1);
    return 0;
}  

温湿度数据读取一个byte字节

static bool am2321_read_byte(char *byte)
{
    int time=0;
    int i = 0;
    for(i=0; i<8; i++)
    {
        *byte = *byte << 1;
        //udelay(30);
        time=0;
        //等待高电平,开始读取数据
        while(read_pin_level()==0)
        {
           time++;
           udelay(1); 
           if(time > 500)
           {
               debug("<0>""wait pin for hight level  out time! %s %d\n",__FUNCTION__,__LINE__);
               return 0;
           }
        }   
        //如果超过了30us后还是高电平,就是逻辑高电平,稳妥采取55us后读取。
        udelay(40);
        *byte |= read_pin_level();  
        //等待一次数据读取结束
           time = 0;
           while(read_pin_level()==1)
           {
               time++;
               udelay(1); 
               if(time > 500)
               {   
                   debug("<0>""wait pin for low level  out time! %s %d\n",__FUNCTION__,__LINE__);
                   return 0;
               }
        }
    }
    return 1;
}

上层测试代码

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
 
/* 程序的入口函数 */
int main(int argc, char *argv[])
{
    int fd;
    char buf[4];    /* 定义存放数据的数组 */
    int length;
    float tem,hum;
 
    /* 以只读方式打开设备节点 */
    fd = open("/dev/am2321_drv", O_RDONLY);
    if(fd < 0)
    {
        printf("open failed!\n");
        return -1;
    }
 
    while(1)
    {
        sleep(3);
        length = read(fd, buf, 4);  /* 读取温湿度数据 */
​
        /* 将数据从终端打印出来 
        * buf[0] 湿度高8位部分
        * buf[1] 湿度低8位部分
        * buf[2] 温度高8位部分
        * buf[3] 温度低8位部分
        */ 
        hum = (float)((buf[0]<<8)|buf[1])/10;
        tem = (float)((buf[2]<<8)|buf[3])/10;
        
        printf("Temp: %xH ==> %.1f   Humi: %xH ==> %.1f\n", (buf[2]<<8)|buf[3],tem,(buf[0]<<8)|buf[1],hum);  
        
    }
 
    /* 关闭DHT11设备节点 */
    close(fd);
    return 0;
}

Makefile编写

obj-m += am2321.o
KERNELDIR:=/file/RK3399Pro/rk3399pro_git_repo/kernel
PWD:=$(shell pwd)
​
default:
    $(MAKE)  -C $(KERNELDIR) M=$(PWD) modules
​
test:
    aarch64-linux-gnu-gcc am2321_test.c -o am2321_test  
​
clean:
    rm -rf *.o *.order .*.cmd *.ko *.mod.c *.symvers *.tmp_versions

测试步骤

编译源码

在ubuntu中输入:

make

得到驱动目标文件am2321.ko

输入:

make test

得到测试目标文件:am2321_test

加载驱动

在开发板命令终端输入:

insmod am2321.ko

执行测试程序

在开发板命令终端输入:

chmod 777 am2321_test
./am2321_test

实验现象

读取温湿度数据

root@linaro-alip:~# ./test/am2321_test 
[  686.323038] <0>enter am2321_read functionn
[  686.323482] <0>am2321_PIN level = 1
[  686.379139] <0>1 b5 1 19 d0
Temp: 119H ==> 28.1   Humi: 1b5H ==> 43.7

猜你喜欢

转载自blog.csdn.net/ZOROE123/article/details/121340432