您的位置:首页 > 运维架构 > Linux

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

2016-04-15 14:26 453 查看
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 <sound/tlv320aic32x4.h>
#include <mach/imx_rfkill.h>

+/*Kris, 20160325, add ultrasnoic device.  {*/
+#ifdef CONFIG_ULTRASONIC
+#include <linux/ultrasonic.h>
+#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		= "ecovacs-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<kris.fei@ecovacs.com>
*
* 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 <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/miscdevice.h>
#include <linux/gpio.h>
#include <linux/uaccess.h>
#include <linux/delay.h>
#include <linux/workqueue.h>
#include <linux/platform_device.h>
#include <linux/pwm.h>
#include <linux/ultrasonic.h>
#include <linux/fs.h>
#include <linux/wait.h>
#include <linux/time.h>
#include <linux/sched.h>
#include <linux/kthread.h>

#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	= "ecovacs-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 <kris.fei@ecovacs.com>");
MODULE_DESCRIPTION("Ultrasonic driver");
MODULE_LICENSE("GPL");
ultrasonic.h:

#ifndef __ULTRASONIC_H__
#define __ULTRASONIC_H__

#include <linux/pwm.h>
#include <linux/time.h>
#include <linux/mutex.h>
#include <linux/types.h>

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 <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <utils/Log.h>

#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 <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <utils/Log.h>

#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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: