RK3568笔记五十二:HC-SR04超声波模块驱动测试

news2024/11/20 13:45:07

若该文为原创文章,转载请注明原文出处。

一、HC-SR04简介

HC-SR04超声波模块是一种常用于距离测量和障碍物检测的模块。它通过发射超声波信号并接收回波来计算所测量物体与传感器之间的距离。

1、基本原理

TRIG引脚负责发送超声波脉冲串。此引脚应设置为高电平10μs,此时HC-SR04将以40 kHZ发出8个周期的声波脉冲。发出声波爆发后,ECHO引脚将变为高电平。 ECHO引脚是数据引脚 - 用于进行距离测量。发送超声波脉冲串后, ECHO引脚将变为高电平,它将保持高电平,直到检测到超声波脉冲串为止,此时它将变为低电平。

2、计算公式

我们知道声速是340m/s
根据x=vt
因为超声波发送出去和回来是测量距离的两倍,所以假设距离是L
2L=344xt
t我们用定时器测出来
一般都是us
所以就是tx172x10的-6次方=L,L单位为cm
最终的出 L= t(us) * 0.0172(cm/us)
0.0172=1/58
所以 L= t(us)/58(cm)

3、程序原理

设置Echo引脚为双边沿触发,在上升沿触发中断时记录此刻时刻T0,在下降沿触发中断时记录时刻T1
高电平时间 = T1 - T0

内核中获取时间的API :
 

ktime_get_ns();          // 获取内核启动到现在的时间,在挂起时会暂停
ktime_get_boottime_ns(); // 获取内核启动到现在的时间,不受挂起影响,是绝对时间
ktime_get_real_ns();     // 获取Unix时间(1970年)到现在的时间,可能涉及闰秒更新,用得比较少
ktime_get_raw_ns();      // 类似ktime_get_ns(),不涉及闰秒更新,用得比较少

三、硬件原理

HC-SR04超声波模块和ATK-DLRK3568的接线如下:

HC-SR04RK3568
VCC        3.3V
TrigGPIO3 PC5
EchoGPIO3 PC4
GndGND

四、设备树

1、设备树节点

修/home/alientek/rk3568_linux_sdk/kernel/arch/arm64/boot/dts/rockchip/目录下的rk3568-atk-evb1-ddr4-v10.dtsi文件,在文中添加代码,在设备树下添加hc_sr04节点。

hc_sr04 {
	      compatible = "hc_sr04";
	      pinctrl-names = "default";
	      status = "okay";
	      trig {
		    compatible = "trig-test";
		    pinctrl-0 = <&pinctrl_trig>;
		    gpios-trig = <&gpio3 RK_PC5 GPIO_ACTIVE_HIGH>;
		    status = "okay";
	    };
	  
	    echo {
	  	  compatible = "echo-test";
		  pinctrl-0 = <&pinctrl_echo>;
		  gpios-echo = <&gpio3 RK_PC4 GPIO_ACTIVE_HIGH>;
		  status = "okay";
	    };
	};

2、创建设备的 pinctrl 节点

修改/home/alientek/rk3568_linux_sdk/kernel/arch/arm64/boot/dts/rockchip/目录下的rk3568-pinctrl.dtsi文件,在最后面增加节点

trig-gpio {
		/omit-if-no-ref/
		pinctrl_trig: trig-gpio-ctrl {
			rockchip,pins = <3 RK_PC5 RK_FUNC_GPIO &pcfg_pull_none>;
		};
	};
	 
	echo-gpio {
               /omit-if-no-ref/
		pinctrl_echo: echo-gpio-ctrl {
			rockchip,pins = <3 RK_PC4 RK_FUNC_GPIO &pcfg_pull_none>;
		};
	};

设备树修改完成以后在 SDK 顶层目录输入如下命令重新编译一下内核:

# 指定 SDK 的板级配置文件
./build.sh lunch
# 编译内核
./build.sh kernel

编译完成以后得到 boot.img, boot.img 就是编译出来的内核+设备树打包在一起的文件

只需要重新烧写boot.img。

烧写完成以后启动开发板。Linux 启动成功以后进入到/proc/device-tree/目录中查看是否有节点

会有一个hc-sr04节点,节点下又有trig和echo两个子节点。

在proc/device-tree/hc_sr04/下

五、驱动编写

1、sr04_drv.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <asm/io.h>
#include <linux/device.h>
#include <linux/timer.h>
#include <linux/jiffies.h>
#include <linux/platform_device.h>
#include <linux/of_irq.h>
#include <linux/wait.h>
#include <linux/sched/signal.h> 
#include <linux/poll.h>
#include <linux/atomic.h>

#define HC_SR04_DTS_NAME    "hc_sr04"

#define DEV_NAME            "hc-sr04"
  
    
struct hc_sr04 {
    int     irq;                        /* 中断号 */
    int     trig_gpio;    /* trig-gpio */
    int     echo_gpio;    /* echo-gpio */
    dev_t   dev_no;                   /* 设备号 */    
    struct  cdev chrdev;             
    struct  class *class;
    struct device_node *nd[2]; 
    wait_queue_head_t  wq;          /* 等待队列 */
};

static struct hc_sr04  sr04;
static int sr04_data_ns = 0;

int led_flag = 0;


/* 使设备只能被一个进程打开 */
static int sr04_drv_open(struct inode *node, struct file *file)
{
	printk("hc-sr04 open\n");
	
    gpio_direction_input(sr04.echo_gpio);   
    gpio_direction_output(sr04.trig_gpio, 0);
  
    return 0;
}


static ssize_t sr04_drv_read(struct file *filp, char __user *buf, size_t size, loff_t *offset)
{
    int ret;
    int timeout;

    unsigned long flags;
    /* 中断屏蔽 */
    local_irq_save(flags);
    /* 启动触发信号 */
	
    gpio_set_value(sr04.trig_gpio, 1); 
	udelay(30);
    gpio_set_value(sr04.trig_gpio, 0);    

    /* 恢复中断 */
    local_irq_restore(flags);

    timeout = wait_event_interruptible_timeout(sr04.wq, sr04_data_ns, HZ);	  /* wait 1 sec */
    if (!timeout) return -EAGAIN;
    
	if (copy_to_user(buf, &sr04_data_ns, size > 4 ? 4 : size)) 
    {
        ret = -EFAULT;
    } 
    else 
    {
        ret = size;
    }
    sr04_data_ns = 0;
    return ret;
}

static irqreturn_t hc_sr04_isr(int irq_num, void *dev)
{
    if (gpio_get_value(sr04.echo_gpio))
    {
        sr04_data_ns = ktime_get_ns();
    }
    else
    {
        sr04_data_ns = ktime_get_ns() - sr04_data_ns;

        wake_up(&sr04.wq);                             /* 唤醒等待队列中进入休眠的进程 */
    }
	
    
    return IRQ_RETVAL(IRQ_HANDLED);   
}



static int sr04_drv_release(struct inode *node, struct file *file)
{
    printk("hc-sr04 release\n");
    return 0;
}


static struct file_operations sr04_drv_ops = { 
	.owner	= THIS_MODULE,
	.open   = sr04_drv_open,
    .read   = sr04_drv_read,
    .release = sr04_drv_release,
};

/* 设备树的匹配列表 */
static struct of_device_id dts_match_table[] = {
    {.compatible = HC_SR04_DTS_NAME, },                     /* 通过设备树来匹配 */
};


static int sr04_driver_probe(struct platform_device *pdev)
{ 
    int err;
	int ret;
    struct device *sr04_dev;
    struct property *proper;
    struct device_node *node = pdev->dev.of_node;

    if (!node) {          
        printk("hc-sr501 dts node can not found!\r\n");    
        return -EINVAL; 
    }
    
    sr04.nd[0] = of_find_node_by_path("/hc_sr04/trig");     
    if (IS_ERR(sr04.nd[0])) {          
        printk("hc-sr04 DTS Node not found!\r\n"); 
        return PTR_ERR(sr04.nd[0]); 
    }

    sr04.trig_gpio = of_get_named_gpio(sr04.nd[0], "gpios-trig", 0);   /* 获取trig-gpio的编号 */
    if (sr04.trig_gpio < 0) {
        printk("trig-gpio not found!\r\n"); 
        return -EINVAL;
    }

    err = gpio_request(sr04.trig_gpio, "gpios-trig");  
	if(err) 
    {
		printk("gpio_request trig-gpios is failed!\n");
        return -EINVAL;
	}
	
	/* 3、设置为输出 */
    ret = gpio_direction_output(sr04.trig_gpio, 1);
    if(ret < 0) {
        printk("can't set sr04.trig_gpio!\r\n");
    }
    /*获取字节点的compatible属性*/
    proper = of_find_property(sr04.nd[0], "compatible", NULL);
    if(proper == NULL) {
        printk("compatible property find failed\r\n");
    } else {
        printk("led compatible = %s\r\n", (char*)proper->value);
    }
 

    sr04.nd[1] = of_find_node_by_path("/hc_sr04/echo");    
	if (IS_ERR(sr04.nd[1])) {          
        printk("hc-sr04 DTS Node not found!\r\n"); 
        return PTR_ERR(sr04.nd[1]); 
    }
	
    sr04.echo_gpio = of_get_named_gpio(sr04.nd[1], "gpios-echo", 0);   /* 获取echo-gpio的编号 */
    if ( sr04.echo_gpio < 0) {
        printk("echo-gpio not found!\r\n"); 
        return -EINVAL;
    }
    err = gpio_request(sr04.echo_gpio, "echo-gpios");  
    if(err) 
    {
        gpio_free(sr04.trig_gpio);
		printk("gpio_request echo-gpios is failed!\n");
        return -EINVAL;
	}

    printk("trig-gpio %d  echo-gpio %d\n", sr04.trig_gpio, sr04.echo_gpio);

    sr04.irq = gpio_to_irq(sr04.echo_gpio);

    err = request_irq(sr04.irq, hc_sr04_isr, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, DEV_NAME, NULL);    /* 申请中断 */
    if (err) {
        printk(KERN_INFO"failed to request irq %d\r\n", sr04.irq);
        gpio_free(sr04.trig_gpio);
        gpio_free(sr04.echo_gpio);
        return err;
    }
    /* 内核自动分配设备号 */
    err = alloc_chrdev_region(&sr04.dev_no, 0, 1, DEV_NAME);        
	if (err < 0) {
		pr_err("Error: failed to register mbochs_dev, err: %d\n", err);
		return err;
	}

	cdev_init(&sr04.chrdev, &sr04_drv_ops);

	cdev_add(&sr04.chrdev, sr04.dev_no, 1);

    sr04.class = class_create(THIS_MODULE, DEV_NAME);
	if (IS_ERR(sr04.class)) { 
        err = PTR_ERR(sr04.class);
        goto failed1;
	}

    /* 创建设备节点 */
    sr04_dev = device_create(sr04.class , NULL, sr04.dev_no, NULL, DEV_NAME); 
    if (IS_ERR(sr04_dev)) {       /* 判断指针是否合法 */
        err = PTR_ERR(sr04_dev);
		goto failed2;
	}

    init_waitqueue_head(&sr04.wq);     /* 初始化等待队列头  */

    printk("hc-sr04 probe success\r\n");
    return 0;
failed2:
    device_destroy(sr04.class, sr04.dev_no);
    class_destroy(sr04.class);
failed1:
    unregister_chrdev_region(sr04.dev_no, 1);
    cdev_del(&sr04.chrdev);


    return err;
}

static int sr04_driver_remove(struct platform_device *pdev)
{
    device_destroy(sr04.class, sr04.dev_no);
	class_destroy(sr04.class);
	unregister_chrdev_region(sr04.dev_no, 1);
    cdev_del(&sr04.chrdev);
    free_irq(sr04.irq, NULL);             /* 释放中断*/
	
	gpio_free(sr04.trig_gpio);
    gpio_free(sr04.echo_gpio);

    printk(KERN_INFO"hc-sr04 drv remove success\n");

    return 0;
}


static struct platform_driver _platform_driver = {
      .probe = sr04_driver_probe,
      .remove = sr04_driver_remove,
      .driver = {
        .name = HC_SR04_DTS_NAME,
        .owner = THIS_MODULE,
        .of_match_table = dts_match_table,         /* 通过设备树匹配 */
      },
};

/* 入口函数 */ 
static int __init _driver_init(void)
{
    int ret;
    printk("hc-sr04 %s\n", __FUNCTION__);
    
    ret = platform_driver_register(&_platform_driver);   //注册platform驱动
    return ret;
}

/*  出口函数 */
static void __exit _driver_exit(void)
{
    printk("hc-sr04  %s\n", __FUNCTION__);
    platform_driver_unregister(&_platform_driver);
}

module_init(_driver_init);
module_exit(_driver_exit);

MODULE_AUTHOR("yifeng");
MODULE_LICENSE("GPL");

2、makefile

KERNELDIR := /home/alientek/rk3568_linux_sdk/kernel
ARCH=arm64
CROSS_COMPILE=/opt/atk-dlrk356x-toolchain/usr/bin/aarch64-buildroot-linux-gnu-

export  ARCH  CROSS_COMPILE

CURRENT_PATH := $(shell pwd)
obj-m := sr04_drv.o

build: kernel_modules

kernel_modules:
	$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules
clean:
	$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean

编译

六、APP应用程序

sr04App.c

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
#include <poll.h>

#define DEV_NAME   "/dev/hc-sr04"

void sleep_ms(unsigned int ms)
{
    struct timeval delay;
	delay.tv_sec = 0;
	delay.tv_usec = ms * 1000; 
	select(0, NULL, NULL, NULL, &delay);
}

int main(int argc, char **argv)
{
	int fd;
    int ret;
  
    struct pollfd fds[1];
	
	/* 2. 打开文件 */
	fd = open(DEV_NAME, O_RDWR);   // | O_NONBLOCK

	if (fd < 0)
	{
		printf("can not open file %s, %d\n", DEV_NAME, fd);
		return -1;
	}

    int time_ns;
    while (1)
    {
        if ((ret = read(fd, &time_ns, 4)) == 4)
        {
            printf("time %d ns %d ms, distance %d mm %d cm\r\n", time_ns, time_ns/1000000, time_ns*340/2/1000000, time_ns*340/2/1000000/10);
        }
        else
        {
            printf("not get time, err %d\r\n", ret);
        }
        sleep_ms(2000);
    }
	
	close(fd);
	
	return 0;
}

编译

 /opt/atk-dlrk356x-toolchain/bin/aarch64-buildroot-linux-gnu-gcc sr04App.c -o sr04App

七、测试

把生成的ko文件和sr04App文件拷贝到开发板。

加载驱动

 insmod sr04_drv.ko 

加载完后,会在dev下查到hc-sr04

测试

 ./sr04App 

驱动采用的是中断方式,比较常用的方法。

也可以用等待方式,但不建议,自行测试。

如有侵权,或需要完整代码,请及时联系博主。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1978959.html

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

相关文章

4.指令系统

4.指令系统 指令系统概述—复习指导 一个好的框架是学习掌握知识的捷径&#xff0c;能够帮助我们更快的掌握所学知识&#xff0c;下面从王道计算机组成原理书本第四章-指令系统出发&#xff0c;讲解一下第四章的知识框架组成&#xff0c;下面是指令系统这一章节的思维导图和详…

【doghead】h264测试文件、读取、模拟采集时间戳及packtizer

使用原生代码但是原生仓库没有264文件使用绝对路径但是在wsl2的ubuntu22.04中构建添加264文件路径 clion IDE : 来自RtspServer 的测试264文件 PHZ76 提供的264文件读取264成功:按照帧读取 这里是模拟采集视频帧? 定时器:

【Rust练习】7.引用与借用

练习题来自&#xff1a;https://practice-zh.course.rs/ownership/borrowing.html 1 fn main() {let x 5;// 填写空白处let p __;println!("x 的内存地址是 {:p}", p); // output: 0x16fa3ac84 }其实Rust的借用&#xff0c;就类似C的指针和引用&#xff0c;如果你…

面对挫折和恶语 良好心态非常重要

你等年纪轻轻一遇挫折,便松散懈怠,日后怎成大器? 虽称满腹经纶,却是鸡肠鼠肚,连几句恶语都容它不下,你等要记住,为人者,有大度成大器也!夫处世之道,即应变之术, 岂可偏执一端? 【迷茫时&#xff0c;不妨听听司马仲达的人生格言】https://www.bilibili.com/video/BV1JF411i7…

React--》掌握styled-components重塑React样式管理

想象一下&#xff0c;如果你的React组件不仅能自描述其逻辑&#xff0c;还能直接声明自己的样式&#xff0c;这种“所见即所得”的编程体验是不是让人心动不已&#xff1f;styled-components正是这样一把钥匙&#xff0c;它彻底颠覆了我们对React样式管理的传统认知&#xff0c…

Python 中单例模式实现的几种方式

在设计模式中&#xff0c;单例模式是经常被提及和使用的一种模式。它保证一个类只有一个实例&#xff0c;并提供全局访问点。在Python中&#xff0c;有多种实现单例模式的方法。那么&#xff0c;如何选择合适的方法来实现单例模式呢&#xff1f; 单例模式在Python中的几种实现方…

软件设计师笔记-网络基础知识

计算机网络的发展 计算机网络&#xff08;计算机技术通信技术&#xff09;的发展是一个逐步演进的过程&#xff0c;从简单的具有通信功能的单机系统&#xff0c;到复杂的以局域网及因特网为支撑环境的分布式计算机系统&#xff0c;这一历程经历了多个关键阶段&#xff1a; #me…

大模型是否潜在地进行多跳推理?

人工智能咨询培训老师叶梓 转载标明出处 以往的研究表明&#xff0c;基于Transformer的LLMs能够在参数中存储和检索事实信息&#xff0c;以完成简单提示&#xff0c;例如“Stevie Wonder的母亲是谁”。此外&#xff0c;当必要信息明确给出时&#xff0c;LLMs表现出了显著的上下…

JS优化了4个自定义倒计时

<!DOCTYPE html> <html lang"zh-CN"><head><meta charset"UTF-8"><title>4个自定义倒计时</title><style>* {margin: 0;padding: 0;box-sizing: border-box;user-select: none;body {background: #0b1b2c;}}hea…

(javaweb)maven--Java项目的构建工具

目录 一.Maven概述 二.Idea导入maven项目 三.maven核心功能--依赖管理 四.依赖管理--依赖传递 五.依赖管理--依赖范围 六.依赖管理--生命周期 一.Maven概述 1.管理和构建Java项目的工具 2.Apache开源&#xff08;源代码开放&#xff09; 3. 4. Maven核心配置文件&#xff…

解题 - 左旋字符串的三种解法(思路)

文章目录 前言&#xff1a; 题干&#xff1a; 解题思路&#xff1a; 思路一&#xff1a; 思路二&#xff1a; 思路三&#xff1a; 小结 前言&#xff1a; 路漫漫其修远兮&#xff0c;吾将上下而求索。 题干&#xff1a; 解题思路&#xff1a; 创建变量k &#xff0c;用…

【知识专栏丨python数分实战】关于电商零售客户细分数据分析及可视化

今天这篇文章将给大家介绍关于电商零售客户细分数据分析及可视化的案例分析。 01 数据整理 导入数据 import pandas as pdimport numpy as npfrom pyecharts.charts import *import pyecharts.options as opts import warningswarnings.filterwarnings(ignore) 数据读取及预…

shell的基础介绍

文章目录 shell数组读取数组关联数组获取数组的长度 Shell运算符算术运算符关系运算符布尔运算符逻辑运算符字符串运算符文件测试运算符 Shell echo命令1.显示普通字符串2.显示转义字符3.显示变量4.显示换行5.显示不换行6.显示结果定向至文件7.原样输出字符串&#xff0c;不进行…

Macbook上运行Windows系统工具Parallels Desktop19

亲爱的笔记本小能手们&#xff0c;你们是否也有这样的困扰&#xff1a;在Macbook上运行Windows系统&#xff0c;却发现虚拟机软件要么卡顿&#xff0c;要么操作复杂&#xff1f;别急&#xff0c;今天就给你们种草一款神器——Parallels Desktop 19。 Parallels Desktop最新绿色…

无人机工程师技术高级证书详解

随着无人机技术的飞速发展&#xff0c;其在航拍、农业、测绘、救援、物流等多个领域的应用日益广泛&#xff0c;对无人机工程师的专业技能与综合素质提出了更高要求。无人机工程师技术高级证书&#xff0c;作为对无人机领域高级工程师专业技能的权威认证&#xff0c;不仅是对个…

QT网络编程

Qt 给用户提供了网络编程的接口&#xff0c;包括TCP、UDP、HTTP三种协议的API以及各种类&#xff0c;可以了解一下。 而在 QT 中想要使用网络编程&#xff0c;必须在pro文件中添加 network 模块&#xff0c;否则无法包含网络编程所需的头文件。 UDP UDP是传输层的协议&#…

Animate软件基础:在时间轴中标识动画

FlashASer&#xff1a;AdobeAnimate2021软件零基础入门教程https://zhuanlan.zhihu.com/p/633230084 FlashASer&#xff1a;实用的各种Adobe Animate软件教程https://zhuanlan.zhihu.com/p/675680471 FlashASer&#xff1a;Animate教程及作品源文件https://zhuanlan.zhihu.co…

实战项目导航

目录 1.AI毕设生成器&#xff08;完善中&#xff09;2.计算机设计大赛案例3.c迷宫游戏4.python学生管理系统教学关注持续更新哦 1.AI毕设生成器&#xff08;完善中&#xff09; 文章链接 &#x1f680; 一键启航&#xff0c;编码从未如此简单&#xff01; 探索Python毕业设计生…

vulnhub之serial

这次我们来做这个靶场 项目地址https://download.vulnhub.com/serial/serial.zip 使用vm新建虚拟机 以下为注意事项 第一步&#xff0c;收集资产 扫描靶场ip netdiscover -i eth0 -r 192.168.177.0/24 抓个包 扫描目录 看到了cookie中有一个user Tzo0OiJVc2VyIjoyOntzOj…