[IMX6DL][Android4.4] 超声波模块HC-SR04 Linux驱动源代码

Platform: IMX6DL
OS: Android 4.4
Kernel branch: 3.0.35


说明:

1. 硬件基于HC-SR04模块,可自行查找。

2. 代码PWM模块用于测试中断响应性能, 背光部分注释掉是因为和PWM复用了。

3. 测试中断响应性能时不要使用在中断上半部使用printk()方式,否则延时会到ms级。

4. 代码中注册的字符设备可不需要,创建的内核线程用于控制时序。

3. 由于超声波采用的是普通的GPIO中断,而且精度需要达到us级,当有许多高优先级的中断需要处理时,

驱动响应会延迟导致得到的时间不同,稳定性会大大下降,只可作为参考,如果要商用最好使用专用单片机模块处理测距。


源代码:

配置:

diff --git a/arch/arm/configs/imx6_tek_android_defconfig b/arch/arm/configs/imx6_tek_android_defconfig
index d26fe73..18125d0 100644
--- a/arch/arm/configs/imx6_tek_android_defconfig
+++ b/arch/arm/configs/imx6_tek_android_defconfig
@@ -12,7 +12,7 @@ CONFIG_GENERIC_CLOCKEVENTS=y
 CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y
 CONFIG_KTIME_SCALAR=y
 CONFIG_HAVE_PROC_CPU=y
-CONFIG_STACKTRACE_SUPPORT=y
+CONFIG_STACKTRACE_SUPPOR100T=y
 CONFIG_LOCKDEP_SUPPORT=y
 CONFIG_TRACE_IRQFLAGS_SUPPORT=y
 CONFIG_HARDIRQS_SW_RESEND=y
@@ -1189,7 +1189,8 @@ CONFIG_MXS_PERFMON=m
 # CONFIG_C2PORT is not set
 #Kris,20160226,Add MIC driver.
 CONFIG_XMF10411=y
-
+#Kris, 20160325, add ultrasnoic device.
+CONFIG_ULTRASONIC=y
 #
 # EEPROM support
 #
diff --git a/arch/arm/mach-mx6/board-mx6-tek.c b/arch/arm/mach-mx6/board-mx6-tek.c
index bea16af..8b0327f 100644
--- a/arch/arm/mach-mx6/board-mx6-tek.c
+++ b/arch/arm/mach-mx6/board-mx6-tek.c
@@ -84,6 +84,12 @@
 #include 
 #include 
 
+/*Kris, 20160325, add ultrasnoic device.  {*/
+#ifdef CONFIG_ULTRASONIC
+#include 
+#endif
+/*Kris, 20160325, add ultrasnoic device.  }*/
+
 #define KD() printk("[DEBUG]%d\n", __LINE__);
 /**
 #define TEK_ANDROID_POWER	IMX_GPIO_NR(1, 4)
@@ -110,7 +116,7 @@
 /*Kris, 20160302, Add head key. }*/
 
 /*Kris, 20160317, Add human sense key. {*/
-#define TEK_HUMEM_SENSE	IMX_GPIO_NR(2, 7)
+#define TEK_HUMAM_SENSE	IMX_GPIO_NR(2, 7)
 /*Kris, 20160317, Add human sense key. }*/
 
 /*Kris, 20150604, add touchscreen driver. {*/
@@ -152,6 +158,16 @@
 #define TEK_HEADPHONE_DET		IMX_GPIO_NR(7, 8)
 #define TEK_PFUZE_INT			IMX_GPIO_NR(7, 13)
 
+
+/*Kris, 20160325, add ultrasnoic device.  {*/
+#ifdef CONFIG_ULTRASONIC
+#define 	ULTRASONIC_DET 			IMX_GPIO_NR(2, 6)
+#define 	ULTRASONIC_CTRL_GPIO  	IMX_GPIO_NR(2, 3)
+#endif
+/*Kris, 20160325, add ultrasnoic device.  }*/
+
+
 
@@ -664,6 +680,27 @@ static struct platform_device tek_battery_device = {
 #endif
 /*Kris, 20150611, add pseudo battery device. }*/
 
+/*Kris, 20160325, add ultrasnoic device.  {*/
+#ifdef CONFIG_ULTRASONIC
+struct ultrasonic_platform_data ultrasonic_data = {
+	.pwm_id = 0,
+	.pwm_duty_ns = 250000,
+	.pwm_period_ns = 500000,
+	.irq = gpio_to_irq(ULTRASONIC_DET),
+	.ctrl_gpio = ULTRASONIC_CTRL_GPIO,
+};
+
+static struct platform_device ultrasonic_device = {
+	.name		= "e-ultrasonic",
+	.id		= -1,
+	.dev	= {
+		.platform_data = &ultrasonic_data,
+	}
+};
+#endif
+/*Kris, 20160325, add ultrasnoic device.  }*/
+
+
 
 struct imx_vout_mem {
 	resource_size_t res_mbase;
@@ -719,12 +756,16 @@ static void daogou_power_on(void)
 	/*Kris, 20150609, add touchscreen driver. }*/
 }
 
+/*Kris, 20160325, add ultrasnoic device.  {*/
+#ifndef CONFIG_ULTRASONIC
 static struct platform_pwm_backlight_data mx6_tek_pwm_backlight_data = {
 	.pwm_id = 0,
 	.max_brightness = 248,
 	.dft_brightness = 128,
 	.pwm_period_ns = 50000,
 };
+#endif
+/*Kris, 20160325, add ultrasnoic device.  }*/
 
 static struct mxc_dvfs_platform_data tek_dvfscore_data = {
 	.reg_id = "VDDCORE",
@@ -986,7 +1027,12 @@ static void __init mx6_tek_board_init(void)
 	imx6q_add_mxc_pwm(1);
 	imx6q_add_mxc_pwm(2);
 	imx6q_add_mxc_pwm(3);
+
+/*Kris, 20160325, add ultrasnoic device.  {*/
+#ifndef CONFIG_ULTRASONIC
 	imx6q_add_mxc_pwm_backlight(0, &mx6_tek_pwm_backlight_data);
+#endif
+/*Kris, 20160325, add ultrasnoic device.  }*/
 
 	imx6q_add_otp();
 	imx6q_add_viim();
@@ -1041,6 +1087,12 @@ static void __init mx6_tek_board_init(void)
 #endif
 /*Kris, 20150611, add pseudo battery device. }*/
 
+
+/*Kris, 20160325, add ultrasnoic device.  {*/
+#ifdef CONFIG_ULTRASONIC
+	platform_device_register(&ultrasonic_device);
+#endif
+/*Kris, 20160325, add ultrasnoic device.  }*/
 }
 
 extern void __iomem *twd_base;
diff --git a/arch/arm/mach-mx6/board-mx6dl-tek.h b/arch/arm/mach-mx6/board-mx6dl-tek.h
index 184a0e3..be7725f 100644
--- a/arch/arm/mach-mx6/board-mx6dl-tek.h
+++ b/arch/arm/mach-mx6/board-mx6dl-tek.h
@@ -149,6 +149,13 @@ static iomux_v3_cfg_t mx6dl_tek_pads[] = {
 #endif
 /*Kris, 20160317, Add human sense key. }*/
 
+/*Kris, 20160325, add ultrasnoic device.  {*/
+#ifdef CONFIG_ULTRASONIC
+	MX6DL_PAD_NANDF_D3__GPIO_2_3,
+	MX6DL_PAD_NANDF_D6__GPIO_2_6,
+#endif
+/*Kris, 20160325, add ultrasnoic device.  }*/
+
 	/* USDHC3 */
 	MX6DL_PAD_SD3_CLK__USDHC3_CLK_50MHZ,
 	MX6DL_PAD_SD3_CMD__USDHC3_CMD_50MHZ,
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 6e660e9..910d286 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -543,6 +543,11 @@ config XMF10411
 	tristate "XMF10411 sound support"
 	depends on I2C
 
+#Kris, 20160325, add ultrasnoic device.
+config ULTRASONIC
+	bool "Ultrasonic driver"
+	default n
+
 source "drivers/misc/c2port/Kconfig"
 source "drivers/misc/eeprom/Kconfig"
 source "drivers/misc/cb710/Kconfig"
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index fbe4f1f..4099a15 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -55,3 +55,6 @@ obj-$(CONFIG_SENSORS_AK8975)	+= akm8975.o
 
 #Kris,20160226,Add MIC driver.
 obj-$(CONFIG_XMF10411) += xmf10411.o
+
+#Kris, 20160325, add ultrasnoic device.
+obj-$(CONFIG_ULTRASONIC) += ultrasonic.o

驱动:

ultrasonic.c

/* drivers/misc/ultrasonic.c - ultrasonic  driver
 *
 * Copyright (C) 2007-2008 HTC Corporation.
 * Author: Kris Fei
 *
 * This software is licensed under the terms of the GNU General Public
 * License version 2, as published by the Free Software Foundation, and
 * may be copied, distributed, and modified under those terms.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 */

 #define DEBUG 1

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 


#define US_STATUS_OPEN		0
#define US_STATUS_START		1

//#define ULTRASONIC_USE_PWM

static struct platform_device *us_dev;
static struct ultrasonic_platform_data *us_pd;

//static DECLARE_WAIT_QUEUE_HEAD(us_waitq);


static int ultrasonic_open(struct inode *inode, struct file *file)
{
	if (test_and_set_bit(US_STATUS_OPEN, &us_pd->status))
		return -EBUSY;
	return 0;
}

static int ultrasonic_release(struct inode *inode, struct file *file)
{
	clear_bit(US_STATUS_OPEN, &us_pd->status);
	return 0;
}

static long ultrasonic_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
	void __user *argp = (void __user *) arg;
	struct ultrasonic_platform_data *pdata = file->private_data;

	switch (cmd) {
	case ULTRASONIC_CMD_START:
		if (!pdata->start) {
			do_gettimeofday(&pdata->l_time);
#ifdef ULTRASONIC_USE_PWM
			pwm_enable(pdata->pwm);
#endif
			pdata->start = 1;
		}
		break;
	case ULTRASONIC_CMD_STOP:
		if (pdata->start) {
			//disable_irq(pdata->irq);
#ifdef ULTRASONIC_USE_PWM
			pwm_disable(pdata->pwm);
#endif
			pdata->start = 0;
		}
		break;
#ifdef ULTRASONIC_USE_PWM
	case ULTRASONIC_CMD_SET_DUTY:
		if (pdata->start)
			return -EFAULT;
		if(copy_from_user(&pdata->pwm_duty_ns, argp, sizeof(pdata->pwm_duty_ns)))
			return -EFAULT;
		pwm_config(pdata->pwm, pdata->pwm_duty_ns, pdata->pwm_period_ns);
		break;
	case ULTRASONIC_CMD_GET_DUTY:
		if(copy_to_user(argp, &pdata->pwm_duty_ns, sizeof(pdata->pwm_duty_ns)))
			return -EFAULT;
		break;
#endif
	case ULTRASONIC_CMD_GET_DIS:
		mutex_lock(&pdata->u_mutex);
		if(copy_to_user(argp, &pdata->dis_mm, sizeof(pdata->dis_mm))) {
			mutex_unlock(&pdata->u_mutex);
			return -EFAULT;
		}
		mutex_unlock(&pdata->u_mutex);
		break;
	default:
		printk("Unknown command!\n");
		return -EINVAL;
	}

	return 0;
}


static irqreturn_t ultrasonic_irq_handler(int irq, void *data)
{
	struct ultrasonic_platform_data *pdata = data;

	//Don't add printk(), or it will increase the time of the handler!!!
	if (test_bit(US_STATUS_START, &pdata->status)) {
		if (atomic_read(&pdata->count) == 0) {
			//Rising edge, record the start time.
			do_gettimeofday(&pdata->l_time);
			atomic_inc(&pdata->count);
		} else if (atomic_read(&pdata->count) == 1) {
			//Falling edge, record the stop time.
			do_gettimeofday(&pdata->c_time);
			//Following two filter other unuseful interrupts.
			atomic_inc(&pdata->count);
			clear_bit(US_STATUS_START, &pdata->status);
		}
	}

	return IRQ_HANDLED;
}


static int ultrasonic_thread(void *arg)
{
	struct ultrasonic_platform_data *pdata = arg;

	//enable interrupt before start test.
	enable_irq(pdata->irq);

	while(1) {
		//dev_dbg(&us_dev->dev, "test start!\n");
		atomic_set(&pdata->count, 0);
		set_bit(US_STATUS_START, &pdata->status);

		/*Following is the timing of starting ultrasonic.*/
		//Low active.
		gpio_set_value(pdata->ctrl_gpio, 0);
		//follow the spec, at least 10us.
		udelay(30);
		gpio_set_value(pdata->ctrl_gpio, 1);
		//Control test peroid.
		msleep(900);
		
		/*Calculate distance from time interval.*/
		//Max is 1000000
		if (pdata->c_time.tv_usec < pdata->l_time.tv_usec) 
			pdata->interval = 1000000 - pdata->l_time.tv_usec + pdata->c_time.tv_usec;
		else 
			pdata->interval =pdata->c_time.tv_usec - pdata->l_time.tv_usec;
		dev_dbg(&us_dev->dev,"c:%ld l:%ld\n",pdata->c_time.tv_usec, pdata->l_time.tv_usec);
		//dev_dbg(&us_dev->dev, "interval:%ld \n", pdata->interval );
		mutex_lock(&pdata->u_mutex);
		pdata->dis_mm =  (170 * pdata->interval)/1000;
		mutex_unlock(&pdata->u_mutex);
		dev_dbg(&us_dev->dev, "distance is :%ld mm\n", pdata->dis_mm);
	}

	return 0;
}

static const struct file_operations ultrasonic_fops = {
	.owner = THIS_MODULE,
	.open = ultrasonic_open,
	.release = ultrasonic_release,
	.unlocked_ioctl = ultrasonic_ioctl,
};


static struct miscdevice ultrasonic_dev = {
	.minor = MISC_DYNAMIC_MINOR,
	.name = "ultrasonic",
	.fops = &ultrasonic_fops,
};


static int __devinit ultrasonic_probe(struct platform_device *pdev)
{
	struct ultrasonic_platform_data *pdata = pdev->dev.platform_data;

	int ret;
	unsigned long irq_flags;
	struct task_struct *p;
	
	if (pdata == NULL) {
		dev_err(&pdev->dev, "missing platform data\n");
		return -ENODEV;
	}

#ifdef ULTRASONIC_USE_PWM
	pdata->pwm = pwm_request(pdata->pwm_id, "ultrasonic-pwm");
	if (IS_ERR(pdata->pwm)) {
		dev_err(&pdev->dev, "unable to request PWM for ultrasonic\n");
		ret = PTR_ERR(pdata->pwm);
		return -ENODEV;
	} else
		dev_dbg(&pdev->dev, "got pwm for ultrasonic\n");
	//default config.
	pwm_config(pdata->pwm, pdata->pwm_duty_ns, pdata->pwm_period_ns);
	pwm_enable(pdata->pwm);
#endif

	//protect data to be read in user space.
	mutex_init(&pdata->u_mutex);

	//gpio init, control it to start and stop.
	gpio_request(pdata->ctrl_gpio, "ultrasonic control gpio");
	gpio_direction_output(pdata->ctrl_gpio, 1);
	gpio_set_value(pdata->ctrl_gpio, 1);

	//request irq.
	irq_flags = IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING;
	ret = devm_request_threaded_irq(&pdev->dev, pdata->irq,
					  ultrasonic_irq_handler,NULL,
					  irq_flags, "ultrasonic-irq", pdata);
	if (ret) {
		dev_err(&pdev->dev, "request ultrasnoic-irq failed: %d\n", ret);
		goto exit_pwm_free;
	}
	//enable later.
	disable_irq(pdata->irq);

	pdata->status = 0;
	pdata->start = 0;
	us_dev = pdev;
	us_pd = pdata;

	//core thread to run and caculate.
	p = kthread_run(ultrasonic_thread, pdata, "us kthread");
	if (IS_ERR(p)) {
		ret = PTR_ERR(p);
		dev_err(&pdev->dev, "create ultrasnoic core thread failed: %d\n", ret);
		goto exit_pwm_free;
	}

	//Used in user space.
	ret = misc_register(&ultrasonic_dev);
	if (ret) {
		dev_err(&pdev->dev, "ultrasonic_dev register failed\n");
		goto exit_stop_thread;
	}

	platform_set_drvdata(pdev, pdata);

	printk("#########%s\n", __func__);
	return 0;

exit_stop_thread:
	kthread_stop(p);
exit_pwm_free:
#ifdef ULTRASONIC_USE_PWM
	pwm_free(pdata->pwm);
#endif
	return ret;
}


static int __devexit ultrasonic_remove(struct platform_device *pdev)
{
	struct ultrasonic_platform_data *pdata = pdev->dev.platform_data;
	
	misc_deregister(&ultrasonic_dev);
	pwm_free(pdata->pwm);

	return 0;
}



static struct platform_driver ultrasonic_driver = {
	.driver	= {
		.name	= "e-ultrasonic",
		.owner	= THIS_MODULE,
	},
	.probe		= ultrasonic_probe,
	.remove		= __devexit_p(ultrasonic_remove),
};


static int __init ultrasonic_init(void)
{
	return platform_driver_register(&ultrasonic_driver);
}

static void __exit ultrasonic_exit(void)
{
	platform_driver_unregister(&ultrasonic_driver);
}

module_init(ultrasonic_init);
module_exit(ultrasonic_exit);

MODULE_AUTHOR("Kris Fei");
MODULE_DESCRIPTION("Ultrasonic driver");
MODULE_LICENSE("GPL");
ultrasonic.h:

#ifndef __ULTRASONIC_H__
#define __ULTRASONIC_H__

#include 
#include 
#include 
#include 

struct ultrasonic_platform_data {
	struct pwm_device	*pwm;
	u32 irq;
	u8 pwm_id;
	u32 pwm_period_ns;
	u32 pwm_duty_ns;
	long dis_mm;
	struct timeval l_time;	//last time
	struct timeval c_time;	//current time
	struct mutex u_mutex;
	u8 start;
	u8 done;
	u32 ctrl_gpio;	//control start and stop.
	unsigned long status;
	atomic_t count;
	long interval; 
	struct timespec now;
	struct timespec old;
	long t;
};


#define ULTRASONIC_CMD_START			_IOW(0, 1, int)
#define ULTRASONIC_CMD_STOP			_IOW(0, 2, int)
#define ULTRASONIC_CMD_SET_DUTY		_IOW(0, 3, int)
#define ULTRASONIC_CMD_GET_DUTY		_IOW(0, 4, int)
#define ULTRASONIC_CMD_GET_DIS		_IOW(0, 5, int)

#endif


测试程序:

Android.mk

# Copyright 2006 The Android Open Source Project 

LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)

LOCAL_SRC_FILES:= ultrasonic_test.c

LOCAL_SHARED_LIBRARIES := \
    libcutils \
    liblog \

LOCAL_MODULE:= ultrasonic_test

include $(BUILD_EXECUTABLE)

ultrasonic_test.c

#if 1
#define LOG_TAG "Ultrasonic"

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
 

 
#define ULTRASONIC_CMD_START		_IOW(0, 1, int)
#define ULTRASONIC_CMD_STOP			_IOW(0, 2, int)
#define ULTRASONIC_CMD_GET_DIS		_IOW(0, 5, int)


 int main(int argc, const char *argv[])
{
    int fd ;
    int ret;
    long dis;

    fd = open("/dev/ultrasonic",O_RDWR);
    if(fd < 0){
        ALOGE("Fail ot open");
        return -1;
    }

    ALOGI("open successful ,fd = %d\n",fd);
    
    ret = ioctl(fd,ULTRASONIC_CMD_START,NULL);
    if(ret < 0){
        ALOGE("Fail to ioctl");
        return -1;
    }


	while(1) {
		ret = ioctl(fd,ULTRASONIC_CMD_GET_DIS,&dis);
		if(ret < 0){
			ALOGE("Fail to ioctl");
			return -1;
		}

		ALOGI("dis:%ld\n",dis);
	}

    return 0;
} 




#else

#define LOG_TAG "Ultrasonic"

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define ULTRASONIC_CMD_START		_IOW(0, 1, int)
#define ULTRASONIC_CMD_STOP			_IOW(0, 2, int)
#define ULTRASONIC_CMD_GET_DIS		_IOW(0, 5, int)


class Ultrasonic
{
public:
	Ultrasonic();
	~Ultrasonic();
	
	int init();
	long getDistance();

private:
	int fd;
	int ret;
	long distance;
};


Ultrasonic::Ultrasonic()
:fd(-1), ret(-1), distance(-1)
{

}

int Ultrasonic::init()
{
	fd = open("/dev/ultrasonic",O_RDWR);
    if(fd < 0){
        ALOGE("Fail ot open");
        return -1;
    }

    ALOGI("open /dev/ultrasonic successful ,fd = %d\n",fd);
    
    ret = ioctl(fd,ULTRASONIC_CMD_START,NULL);
    if(ret < 0){
        ALOGE("Fail to ioctl");
        return -1;
    }
    
    return 0;
}

long Ultrasonic::getDistance()
{
	ret = ioctl(fd,ULTRASONIC_CMD_GET_DIS,&distance);
	if(ret < 0){
		ALOGE("Fail to ioctl");
		return -1;
	}

	ALOGI("dis:%ld\n",distance);
	
	return distance;
}


Ultrasonic::~Ultrasonic()
{
	if (fd > 0) {
		ret = ioctl(fd,ULTRASONIC_CMD_STOP,NULL);
		if(ret < 0){
			ALOGE("Fail to ioctl");
		}
		close(fd);
	}
}

#endif


你可能感兴趣的:(IMX6_Android4.4)