瑞芯微Rockchip开发者社区
直播中

李华

7年用户 1340经验值
私信 关注
[问答]

怎样读取基于RK3399Pro的AM2321单总线温湿度数据呢

AM2321单总线有哪些应用呢?
怎样读取基于RK3399Pro的AM2321单总线温湿度数据呢?



回帖(1)

罗标雄

2022-2-14 11:09:07


AM2321采用的是单总线控制。


AM2321单总线介绍

查看am2321的数据手册
单总线通信特殊说明



  • 典型应用电路中建议连接线长度短于 30 米时用 5.1K 上拉电阻,大于 30 米时根据实际情况降低上拉电阻的阻值。
  • 使用 3.3V 电压供电时连接线长度不得大于 30cm。否则线路压降会导致传感器供电不足,造成测量偏差。
  • 读取传感器最小间隔时间为 2S;读取间隔时间小于 2S,可能导致温湿度不准或通信不成功等情况。
  • 每次读出的温湿度数值是上一次测量的结果,欲获取实时数据,需连续读取两次,建议连续多次读取传感器, 且每次读取传感器间隔大于 2 秒即可获得准确的数据。
单总线说明

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

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

图1 通信时序

表1 单总线格式
单总线数据计算示例
示例一:接收到的 40 位数据为:
[tr]0000 00101001 00100000 00010000 11011010 0010[/tr]
湿度高 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 "%sn", __func__);
   
    printk(KERN_INFO"gpio-am2321 math succeen");
   
    ret =  misc_register(&gec3399_am_misc);   //注册字符设备
    if(ret < 0){
        printk(KERN_INFO"misc register errorn");
        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 invalidn",gpio_am);
        ret = -ENODEV;
        goto err_get_gpio;
    }
    printk(KERN_INFO"gpio-am2321 = %dn",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 errorn");
        goto err_get_gpio;
    }
   
    ret = gpio_direction_output(gpio_am,0);      //初始划蜂鸣器为关闭状态
    if(ret < 0){
        printk("gpio direction input  gpio = ak8963c_DYDR errorn");
        goto err_gpio_direction;
    }
   
    printk( KERN_ALERT "am2321 dirve install succeen");
    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 functionnn");
    debug("<0>""am2321_PIN level = %dn",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 %xn",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 errn");  
            return -EAGAIN;  
        }  
    }
    else{
        printk("data is errorrn");
    }
   
    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 %dn",__FUNCTION__,__LINE__);
           goto err0;
       }
    }
    //debug("<0>""(1)wait pin for low level  success! i=%dn",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 %dn",__FUNCTION__,__LINE__);
           goto err0;
       }
    }
    //debug("<0>""(2)wait pin for hight level  success! i=%dn",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 %dn",__FUNCTION__,__LINE__);
           goto err0;
       }
    }
    //debug("<0>""(3)wait pin for data input  success! i=%dn",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 %dn",__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 %dn",__FUNCTION__,__LINE__);
                   return 0;
               }
        }
    }
    return 1;
}
上层测试代码


#include
#include
#include
#include
#include

/* 程序的入口函数 */
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 ==> %.1fn", (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
举报

更多回帖

发帖
×
20
完善资料,
赚取积分