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)
#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