rk3506驱动超声波测距

驱动代码

c 复制代码
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/timer.h>
#include <linux/uaccess.h>
#include <linux/delay.h>
#include <linux/device.h>

// 配置 HC-SR04 引脚(根据实际接线修改)
#define HC_SR04_TRIG_GPIO    (1*32 + 3*8 + 2)  // GPIO1_D2: 1×32 + (3×8+2) = 58
#define HC_SR04_ECHO_GPIO    (1*32 + 3*8 + 3)  // GPIO1_D3: 1×32 + (3×8+3) = 59
#define HC_SR04_IRQ          gpio_to_irq(HC_SR04_ECHO_GPIO)

// 设备相关定义
#define DEV_NAME     "hc_sr04"
#define DEV_MAJOR    0   // 动态分配主设备号
#define DEV_MINOR    0

// 全局变量
static int hc_sr04_major = DEV_MAJOR;
static struct cdev hc_sr04_cdev;
static struct class *hc_sr04_class;
static struct device *hc_sr04_device;

// 测距数据
static unsigned long echo_start_time = 0;  // Echo 上升沿时间(us)
static unsigned long echo_end_time = 0;    // Echo 下降沿时间(us)
static unsigned int distance = 0;          // 测距结果(mm)
static bool measuring = false;             // 测距中标记

// 定时器(触发 Trig 信号)
static struct timer_list trig_timer;

/**
 * @brief Echo 引脚中断处理函数(检测上升/下降沿)
 */
static irqreturn_t hc_sr04_echo_irq_handler(int irq, void *dev_id)
{
    unsigned long duration=0;
    if (gpio_get_value(HC_SR04_ECHO_GPIO)) {
        // 上升沿:记录开始时间
        echo_start_time = ktime_get_ns();
    } else {
        // 下降沿:记录结束时间,计算距离
        echo_end_time = ktime_get_ns();
         duration = echo_end_time - echo_start_time;
        // 距离 = (时间(us) × 声速(340m/s)) / 2 → 转换为 cm
        distance = (duration * 34) / 2000000;
        measuring = false;
    }
    // printk(KERN_INFO "HC-SR04: echo interrupt duration=%luus distance=%umm\n", duration / 1000, distance);
    return IRQ_HANDLED;
}

/**
 * @brief 触发 Trig 信号(定时器回调)
 */
static void hc_sr04_trig_timer_func(struct timer_list *t)
{
    if (measuring)
    {
 printk(KERN_INFO "HC-SR04: triging\n");
return;
    } 

    measuring = true;
    // 发送 10us 高电平触发信号
    gpio_set_value(HC_SR04_TRIG_GPIO, 1);
    udelay(10);
    gpio_set_value(HC_SR04_TRIG_GPIO, 0);
    // printk(KERN_INFO "HC-SR04: trig 10us high level\n");
}

/**
 * @brief 字符设备 read 函数(读取测距结果)
 */
static ssize_t hc_sr04_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{
    int ret;
    unsigned int dist;

    // 启动一次测距
    mod_timer(&trig_timer, jiffies + msecs_to_jiffies(10));
    // 等待测距完成(最多 300ms,HC-SR04 最大测距 4m)
    msleep(300);

    dist = distance;
    
    ret = copy_to_user(buf, &dist, sizeof(dist));
    if (ret) return -EFAULT;

    return sizeof(dist);
}

// 文件操作集
static const struct file_operations hc_sr04_fops = {
    .owner   = THIS_MODULE,
    .read    = hc_sr04_read,
};
static ssize_t distance_show(struct device *dev, struct device_attribute *attr, char *buf)
{
    mod_timer(&trig_timer, jiffies + msecs_to_jiffies(10));
    msleep(300);
    return sprintf(buf, "%u\n", distance);
}
// 定义Sysfs属性
static DEVICE_ATTR(distance, S_IRUGO, distance_show, NULL);
/**
 * @brief 驱动初始化
 */
static int __init hc_sr04_init(void)
{
    int ret;
    dev_t devno;

    // 1. 申请 GPIO
    if (gpio_request(HC_SR04_TRIG_GPIO, "hc_sr04_trig")) {
        printk(KERN_ERR "HC-SR04: request trig gpio failed!\n");
        return -EINVAL;
    }
    if (gpio_request(HC_SR04_ECHO_GPIO, "hc_sr04_echo")) {
        printk(KERN_ERR "HC-SR04: request echo gpio failed!\n");
        gpio_free(HC_SR04_TRIG_GPIO);
        return -EINVAL;
    }

    // 2. 配置 GPIO 方向
    gpio_direction_output(HC_SR04_TRIG_GPIO, 0);  // Trig 默认为低
    gpio_direction_input(HC_SR04_ECHO_GPIO);      // Echo 为输入

    // 3. 申请中断(检测 Echo 上升/下降沿)
    ret = request_irq(HC_SR04_IRQ, hc_sr04_echo_irq_handler,
                      IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
                      "hc_sr04_echo_irq", NULL);
    if (ret < 0) {
        printk(KERN_ERR "HC-SR04: request irq failed! ret=%d\n", ret);
        gpio_free(HC_SR04_ECHO_GPIO);
        gpio_free(HC_SR04_TRIG_GPIO);
        return ret;
    }

    // 4. 初始化字符设备
    if (hc_sr04_major) {
        devno = MKDEV(hc_sr04_major, DEV_MINOR);
        ret = register_chrdev_region(devno, 1, DEV_NAME);
    } else {
        ret = alloc_chrdev_region(&devno, DEV_MINOR, 1, DEV_NAME);
        hc_sr04_major = MAJOR(devno);
    }
    if (ret < 0) {
        printk(KERN_ERR "HC-SR04: register chrdev failed!\n");
        free_irq(HC_SR04_IRQ, NULL);
        gpio_free(HC_SR04_ECHO_GPIO);
        gpio_free(HC_SR04_TRIG_GPIO);
        return ret;
    }

    cdev_init(&hc_sr04_cdev, &hc_sr04_fops);
    hc_sr04_cdev.owner = THIS_MODULE;
    ret = cdev_add(&hc_sr04_cdev, devno, 1);
    if (ret < 0) {
        printk(KERN_ERR "HC-SR04: add cdev failed!\n");
        unregister_chrdev_region(devno, 1);
        free_irq(HC_SR04_IRQ, NULL);
        gpio_free(HC_SR04_ECHO_GPIO);
        gpio_free(HC_SR04_TRIG_GPIO);
        return ret;
    }

    // 5. 创建类和设备节点
    hc_sr04_class = class_create(THIS_MODULE, DEV_NAME);
    if (IS_ERR(hc_sr04_class)) {
        printk(KERN_ERR "HC-SR04: create class failed!\n");
        cdev_del(&hc_sr04_cdev);
        unregister_chrdev_region(devno, 1);
        free_irq(HC_SR04_IRQ, NULL);
        gpio_free(HC_SR04_ECHO_GPIO);
        gpio_free(HC_SR04_TRIG_GPIO);
        return PTR_ERR(hc_sr04_class);
    }

    hc_sr04_device = device_create(hc_sr04_class, NULL, devno, NULL, DEV_NAME);
    if (IS_ERR(hc_sr04_device)) {
        printk(KERN_ERR "HC-SR04: create device failed!\n");
        class_destroy(hc_sr04_class);
        cdev_del(&hc_sr04_cdev);
        unregister_chrdev_region(devno, 1);
        free_irq(HC_SR04_IRQ, NULL);
        gpio_free(HC_SR04_ECHO_GPIO);
        gpio_free(HC_SR04_TRIG_GPIO);
        return PTR_ERR(hc_sr04_device);
    }
   if (device_create_file(hc_sr04_device, &dev_attr_distance)) {
        printk(KERN_ERR "HC-SR04: create sysfs file failed!\n");
        device_destroy(hc_sr04_class, devno);
        class_destroy(hc_sr04_class);
        cdev_del(&hc_sr04_cdev);
        unregister_chrdev_region(devno, 1);
        free_irq(HC_SR04_IRQ, NULL);
        gpio_free(HC_SR04_ECHO_GPIO);
        gpio_free(HC_SR04_TRIG_GPIO);
        return -EINVAL;
    }
    // 6. 初始化定时器
    timer_setup(&trig_timer, hc_sr04_trig_timer_func, 0);

    printk(KERN_INFO "HC-SR04 driver init success! dev: /dev/%s\n", DEV_NAME);
    return 0;
}

/**
 * @brief 驱动卸载
 */
static void __exit hc_sr04_exit(void)
{
    // 释放定时器
    del_timer(&trig_timer);

    // 释放字符设备
    dev_t devno = MKDEV(hc_sr04_major, DEV_MINOR);
    device_remove_file(hc_sr04_device, &dev_attr_distance);
    device_destroy(hc_sr04_class, devno);
    class_destroy(hc_sr04_class);
    cdev_del(&hc_sr04_cdev);
    unregister_chrdev_region(devno, 1);

    // 释放中断和 GPIO
    free_irq(HC_SR04_IRQ, NULL);
    gpio_free(HC_SR04_ECHO_GPIO);
    gpio_free(HC_SR04_TRIG_GPIO);

    printk(KERN_INFO "HC-SR04 driver exit success!\n");
}

module_init(hc_sr04_init);
module_exit(hc_sr04_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("RK3506G2 HC-SR04 Driver");
MODULE_DESCRIPTION("HC-SR04 Ultrasonic Sensor Driver for RK3506G2 (Kernel 6.1)");
MODULE_VERSION("1.0");

驱动Makefile

bash 复制代码
# 交叉编译工具链(适配 RK3506G2,根据 SDK 调整)
# RK3506 Kernel 6.1 源码路径
# root@luckfox:~# insmod helloworld.ko
# root@luckfox:~# rmmod helloworld.ko
SDK=/home/book/work/rk3506/sdk
KERNELDIR ?= $(SDK)/kernel-6.1
CROSS_COMPILE = $(SDK)/prebuilts/gcc/linux-x86/arm/gcc-arm-10.3-2021.07-x86_64-arm-none-linux-gnueabihf/bin/arm-none-linux-gnueabihf-
MAKE = make
 
PWD := $(shell pwd)

obj-m := hc_sr04.o

all:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) ARCH=arm CROSS_COMPILE=$(CROSS_COMPILE) modules

clean:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) clean
	rm -rf *.ko *.o *.mod.o *.mod.c *.symvers *.order

测试代码

c 复制代码
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>

#define HC_SR04_DEV "/dev/hc_sr04"

int main(void)
{
    int fd;
    unsigned int distance;

    // 打开设备
    fd = open(HC_SR04_DEV, O_RDONLY);
    if (fd < 0) {
        perror("open hc_sr04 dev failed");
        return -1;
    }

    // 循环读取测距结果
    while (1) {
        read(fd, &distance, sizeof(distance));
        printf("HC-SR04 Distance: %d cm\n", distance);
        sleep(1);
    }

    close(fd);
    return 0;
}

测试代码Makefile

bash 复制代码
# 交叉编译工具链(适配 RK3506G2,根据 SDK 调整)
# RK3506 Kernel 6.1 源码路径
# root@luckfox:~# insmod helloworld.ko
# root@luckfox:~# rmmod helloworld.ko
SDK=/home/book/work/rk3506/sdk
KERNELDIR ?= $(SDK)/kernel-6.1
CROSS_COMPILE = $(SDK)/prebuilts/gcc/linux-x86/arm/gcc-arm-10.3-2021.07-x86_64-arm-none-linux-gnueabihf/bin/arm-none-linux-gnueabihf-
 
PWD := $(shell pwd)


all:
	$(CROSS_COMPILE)gcc *.c -o test_hc_sr04
	chmod +x test_hc_sr04

clean:
	rm -rf test_hc_sr04