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<[email protected]> * * 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 <[email protected]>"); 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)
#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