当前位置: 首页 > news >正文

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

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

一、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_COMPILECURRENT_PATH := $(shell pwd)
obj-m := sr04_drv.obuild: kernel_moduleskernel_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_NONBLOCKif (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 

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

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

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

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • modbus控制传感器
  • PHP单例模式详解及应用
  • 使用Python库开发Markdown编辑器并将内容导出为图片
  • 学习笔记-优化问题
  • 正点原子imx6ull-mini-Linux驱动之Linux SPI 驱动实验(22)
  • Netty二
  • 【从零开始一步步学习VSOA开发】搭建VSOA运行环境
  • rust和c传递字符串的七种方法--翻译
  • 【HBZ分享】spring启动时自动装配的位置
  • 基于FPGA的数字信号处理(20)--半减器和全减器
  • PySide6/PyQT学习笔记(很杂)
  • 如何实现element UI循环表单?
  • 神奇的TypeScript -- 进阶篇之实用工具类型
  • 原神自定义倒计时
  • Codeforces Round 960 (Div. 2)-补题
  • CentOS 7 修改主机名
  • Flex布局到底解决了什么问题
  • JavaScript新鲜事·第5期
  • Java知识点总结(JavaIO-打印流)
  • orm2 中文文档 3.1 模型属性
  • PAT A1050
  • php面试题 汇集2
  • RxJS: 简单入门
  • use Google search engine
  • zookeeper系列(七)实战分布式命名服务
  • 阿里云购买磁盘后挂载
  • - 概述 - 《设计模式(极简c++版)》
  • 时间复杂度与空间复杂度分析
  • 在GitHub多个账号上使用不同的SSH的配置方法
  • 在Unity中实现一个简单的消息管理器
  • LevelDB 入门 —— 全面了解 LevelDB 的功能特性
  • 宾利慕尚创始人典藏版国内首秀,2025年前实现全系车型电动化 | 2019上海车展 ...
  • 没有任何编程基础可以直接学习python语言吗?学会后能够做什么? ...
  • ​Benvista PhotoZoom Pro 9.0.4新功能介绍
  • # Python csv、xlsx、json、二进制(MP3) 文件读写基本使用
  • # 计算机视觉入门
  • #ubuntu# #git# repository git config --global --add safe.directory
  • (2024,Flag-DiT,文本引导的多模态生成,SR,统一的标记化,RoPE、RMSNorm 和流匹配)Lumina-T2X
  • (6)【Python/机器学习/深度学习】Machine-Learning模型与算法应用—使用Adaboost建模及工作环境下的数据分析整理
  • (8)STL算法之替换
  • (BFS)hdoj2377-Bus Pass
  • (附源码)ssm本科教学合格评估管理系统 毕业设计 180916
  • (一)VirtualBox安装增强功能
  • (原創) 博客園正式支援VHDL語法著色功能 (SOC) (VHDL)
  • (转)ABI是什么
  • (转)Oracle 9i 数据库设计指引全集(1)
  • (转)scrum常见工具列表
  • (最新)华为 2024 届秋招-硬件技术工程师-单板硬件开发—机试题—(共12套)(每套四十题)
  • .bat批处理(五):遍历指定目录下资源文件并更新
  • .htaccess配置常用技巧
  • .MSSQLSERVER 导入导出 命令集--堪称经典,值得借鉴!
  • .NET delegate 委托 、 Event 事件,接口回调
  • .NET MVC、 WebAPI、 WebService【ws】、NVVM、WCF、Remoting
  • .NET/C# 解压 Zip 文件时出现异常:System.IO.InvalidDataException: 找不到中央目录结尾记录。
  • .net开发时的诡异问题,button的onclick事件无效