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");