今天大体看了下Android的Motor驱动模块,这部分高通都做好了,拿来就用,基本不需要修改,所以一直没有看,有空看了下,特作记录。Vibrator是基于Linux的timed output驱动框架的,基本的结构就是timed_output_dev struct,其中涉及的知识还有定时器和消息队列。只介绍driver这一部分,更详细的内容可以搜索CONFIG_MSM_PMIC_VIBRATOR这个配置来了解。

一、设备结点

设备节点在调试驱动时非常有用,直接adb shell通过读写文件可以验证驱动是否OK。

 
  1. # echo ‘10000’> /sys/class/timed_output/vibrator/enable
  2. # cat /sys/class/timed_output/vibrator/enable
  3. 3290
  4. # echo ‘0’> /sys/class/timed_output/vibrator/enable

向enable文件写入成功,就立即震动,震动的持续时间即是写入的值,单位为ms,可以通过读enable文件来获得震动剩余的时间。

二、相关的文件

1.kernel/arch/arm/mach-msm/msm_vibrator.c
这是底层的驱动文件,编译到boot.img,几个主要的函数如下:

#define PMIC_VIBRATOR_LEVEL     (3000) //设置震动强度,3000mvstatic struct work_struct work_vibrator;
static int vibe_state; //记录motor的状态
static struct hrtimer vibe_timer;static DEFINE_MUTEX(vibe_mtx);
static spinlock_t vibe_lock;static void update_vibrator(struct work_struct *work)
{set_pmic_vibrator(vibe_state);
}
//star timer or not,schedule work.NOT control vibrator voltage!
static void vibrator_enable(struct timed_output_dev *dev, int value)
{unsigned long   flags;spin_lock_irqsave(&vibe_lock, flags);hrtimer_cancel(&vibe_timer);if (value == 0)vibe_state = 0;else {value = (value > 15000 ? 15000 : value);        vibe_state = 1;hrtimer_start(&vibe_timer,ktime_set(value / 1000, (value % 1000) * 1000000),HRTIMER_MODE_REL);}spin_unlock_irqrestore(&vibe_lock, flags);schedule_work(&work_vibrator);//执行这个函数会立即导致work_vibrator这个work_struct中的func函数被调用
}
static int vibrator_get_time(struct timed_output_dev *dev)
{if (hrtimer_active(&vibe_timer)) {ktime_t r = hrtimer_get_remaining(&vibe_timer);return r.tv.sec * 1000 + r.tv.nsec / 1000000;} elsereturn 0;
}
//turn ON/OFF Motor
static void set_pmic_vibrator(int on)
{static struct msm_rpc_endpoint *vib_endpoint;struct set_vib_on_off_req {struct rpc_request_hdr hdr;uint32_t data;} req;if(mutex_lock_interruptible(&vibe_mtx))return;if (!vib_endpoint) {vib_endpoint = msm_rpc_connect(PM_LIBPROG, PM_LIBVERS, 0);if (IS_ERR(vib_endpoint)) {printk(KERN_ERR "init vib rpc failed!\n");vib_endpoint = 0;mutex_unlock(&vibe_mtx);return;}}if (on)req.data = cpu_to_be32(PMIC_VIBRATOR_LEVEL);//bigendian32,设置驱动马达的voltageelsereq.data = cpu_to_be32(0);//停止震动//控制电压,使motor震动或关闭msm_rpc_call(vib_endpoint, HTC_PROCEDURE_SET_VIB_ON_OFF, &req,sizeof(req), 5 * HZ);mutex_unlock(&vibe_mtx);
}
//定时器time out会调用这个函数,修改state,调度work
static enum hrtimer_restart vibrator_timer_func(struct hrtimer *timer)
{vibe_state = 0;schedule_work(&work_vibrator);//return HRTIMER_NORESTART;
}
static struct timed_output_dev pmic_vibrator = { .name = "vibrator",//sysFS下文件夹的名字 /sys/class/timed_output/vibrator.get_time = vibrator_get_time,.enable = vibrator_enable,
};
void __init msm_init_pmic_vibrator(void)
{INIT_WORK(&work_vibrator, update_vibrator);//仅仅是初始化work_vibrator这个结构体spin_lock_init(&vibe_lock);vibe_state = 0;hrtimer_init(&vibe_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);//初始hrtimervibe_timer.function = vibrator_timer_func;//给定时器超时callback函数赋值timed_output_dev_register(&pmic_vibrator);//注册这个vibrator设备
}MODULE_DESCRIPTION("timed output pmic vibrator device");
MODULE_LICENSE("GPL");


写enable文件时调用vibrator_enable;读enable文件时调用vibrator_get_time。
msm_init_pmic_vibrator是模块入口, vibrator_enable是写enable文件的入口。
vibrator_enable中的处理非常有意思,这个函数“正常”的步骤应该是:设置voltage以开震动----->延迟需要震动的时间----->把volt设为0以关闭震动,然而上面的步骤却没有这样处理,而是启动了高精度定时器并在退出前通过schedule_work(&work_vibrator)调度work,调度work即是执行set_pmic_vibrator,这里通过RPC通信操作VIB_DRV_N
pin,控制输出电压。即写enable文件时并没有对motor 电压的任何直接操作,而是"后续"进行的。为何采取这种策略,一种解释是:.enable的调用将形成一中持续时间的效果,但是调用本身不宜阻塞,因此实现就让vibrator_enable函数退出后通过定时器实现效果。我反正不太理解上面这个解释:-(~
白话一些,我的理解:
那种“正常”的步骤,最关键的是有个延时,而在函数中这种长时间的延时(震动可能很长时间如闹钟或来电)是不好的而且是很不好,于是策略改变,采用Timer +work,具体步骤如下:
引入关键的全局变量 vibe_state,只有在向enable文件写入不为0时才将 vibe_state置为1,同时启动定时器,并调度work;定时器超时的callback函数中又将 vibe_state置0并调度work。而调度work时的执行函数set_pmic_vibrator(int on)其传入参数即是vibe_state,会根据这一值来设置motor的pin电压为需要的值(开震动)或者0(关震动)。如此通过迂回的方式就避开了“在函数中延长很长时间”的问题,不会造成阻塞。
通过这个例子可以理解work将“工作推后延迟执行”的特点,TouchPanel中的work是将读点的工作延后到中断服务退出,不同的是TouchPanel中还要创建独立的工作队列(create_singlethread_workqueue),而不是像这个使用内核缺省的队列。没有搞清楚
msm_rpc_call这个函数如何使用的?!
2.hardware/libhardware_legacy/vibrator.c

这是硬件抽象层,其实就是对/sys/class/timed_output/vibrator/enable文件的写操作,提供给上层JNI的两个接口:

这个文件就3个函数,核心是sendit(),全文如下:
#define THE_DEVICE "/sys/class/timed_output/vibrator/enable"static int sendit(int timeout_ms)
{int nwr, ret, fd;char value[20];#ifdef QEMU_HARDWAREif (qemu_check()) {return qemu_control_command( "vibrator:%d", timeout_ms );}
#endiffd = open(THE_DEVICE, O_RDWR);if(fd < 0)return errno;nwr = sprintf(value, "%d\n", timeout_ms);ret = write(fd, value, nwr);close(fd);return (ret == nwr) ? 0 : -1;
}int vibrator_on(int timeout_ms)
{LOGE("============entry %:timeout_ms=%dms",__func__,timeout_ms);/* constant on, up to maximum allowed time */return sendit(timeout_ms);
}int vibrator_off()
{   LOGE("============entry %",__func__);return sendit(0);
}
备注:
1)如何单独编译这个文件?只要make libhardware_legacy。
至于如何知道这个vibrator.c被编译到那个so,可以通过Android.mk查看到LOCAL_MODULE:= libhardware_legacy
或者直接在out文件夹下搜索vibrator.o被放到了哪个目录下,这个目录名去掉“_intermediates"就是so库的名字了,这是确定C文件链到哪个库文件的小技巧:-)。如
$find ./out -name 'vibrator.o' -ok rm {} \;
< rm ... ./out/target/product/msm7627_sku2/obj/SHARED_LIBRARIES/libhardware_legacy_intermediates/vibrator/vibrator.o > ? y

2)这是HAL层的东东,已经不能printk:-(,要打log,加入以下
//by yue
//#define LOG_NDEBUG 0
//#define LOG_NIDEBUG 0
#define LOG_TAG "vibrator"
#include <utils/Log.h>
//end
然后使用LOGE,可以在DDMS中查看输出log。

三、硬件控制
      马达震动控制是很简单的,只要供给电压就震动,关建是如何控制震动强度及持续时间。我的平台上PM7540有专门的马达电压控制 pin VIB_DRV_N,只要控制这个pin脚的电压输出就可控制马达,电压的大小控制震动的强弱,电压大小可编程的范围[1200,3100],100steps,提供的API有三个:
pm_vib_mot_set_volt()//设置输出电压voltage的大小,单位mv
关闭震动就调用pm_vib_mot_set_volt(0),震动持续的时间是调用pm_vib_mot_set_volt(volt_lvl)和调用pm_vib_mot_set_volt(0)之间的间隔。

pm_vib_mot_set_polarity()
pm_vib_mot_set_mode()
详细解释可以参考80-VB857-1_C_PM7500_PM7540_IC_ISOD.pdf

PS:开机震动的函数在AMSS端 products\76XX\secboot\oemsbl\oemsbl_gbc.c
void oemsbl_vibrator_power_on_procedure(void)
{pm_err_flag_type pm_err=0;pm_err=pm_vib_mot_set_volt(VIBRATE_VOLT);//开始震动
/*if(PM_ERR_FLAG__SUCCESS==pm_err)sprintf(dbgMsg,"[vibrator:oemsbl_qrd.c] Turn on vibrator successfully.\n");elsesprintf(dbgMsg,"[vibrator:oemsbl_qrd.c] Fail to turn on vibrator. Error Code=%X\n",pm_err);OEM_DBG_MSG(dbgMsg);sprintf(dbgMsg,"[vibrator:oemsbl_qrd.c] Wait for %d micro-seconds.\n",VIBRATE_TIME);OEM_DBG_MSG(dbgMsg);
*/clk_busy_wait(VIBRATE_TIME);//震动持续时间pm_err=pm_vib_mot_set_volt(0);//结束震动/*if(PM_ERR_FLAG__SUCCESS==pm_err)sprintf(dbgMsg,"[vibrator:oemsbl_qrd.c] Turn off vibrator successfully.\n");elsesprintf(dbgMsg,"[vibrator:oemsbl_qrd.c] Fail to turn off vibrator. Error Code=%X\n",pm_err);OEM_DBG_MSG(dbgMsg);
*/
}
四、参考

80-VD691-1 PM7540 POWER MANAGEMENT IC DEVICE SPECIFICATION.pdf

80-VB857-1_C_PM7500_PM7540_IC_ISOD.pdf

2.6 内核中的计时器和列表

Anroid振动器系统
Android开发平台振动器系统详解
《Android系统级深入开发--移植与调试》第23章 振动器系统





























  1. int vibrator_on(int timeout_ms);            // 开始振动  
  2. int vibrator_off();                         // 关闭振动

Vibrator motor驱动相关推荐

  1. Developing native extensions for Adobe AIR

    原文:http://www.adobe.com/devnet/air/articles/developing-native-extensions-air.html 以下为摘译 Note: Adobe ...

  2. PhysX3.4文档(9) -- Joints

    Joints Joint Basics 关节(Joints)限制了两个Actor相对于彼此移动的方式.关节的典型用途是模拟门铰链或character的肩膀.关节在 PhysX extensions l ...

  3. Android震动vibrator(马达)--系统到驱动的流程【转】

    本文转载自:https://blog.csdn.net/tianshiyalin/article/details/17136723 一.前言 本人刚学习安卓驱动开发,水平不能说菜,是根本没有水平,在这 ...

  4. MTK 驱动开发(33)---Vibrator

    Vibrator 驱动开发相对比较简单 1.配置功能及参数 vibrator0:vibrator@0 {compatible = "mediatek,vibrator";vib_t ...

  5. Android之 震动(Vibrator)如何贯通Android系统 (从硬件设计 -- 驱动 -- HAL -- JNI -- Framework -- Application)

        在Android 2.3(Gingerbread) 系统的时候,我写过一篇关于"Android 震动马达系统"的文章,当时的Linux内核还是2.6版本的.写那篇文章的目的 ...

  6. Android硬件服务框架实例之Vibrator(驱动到应用)

    转载于:http://www.cnblogs.com/skywang12345/p/3404808.html,内容有所修改,这篇vibrator源码是基于安卓7.1分析.      系列:安卓硬件访问 ...

  7. Android震动vibrator系统开发全过程

    一.前言 本人刚学习安卓驱动开发,水平不能说菜,是根本没有水平,在这里把学习过程贴出来,跟大家一起学习交流,还望大家多多指正,转载的请标明出处. 二.android驱动介绍 安卓总体架构是在 Linu ...

  8. android MTK平台驱动设置

    1. LCD 1.1怎样新建一个LCD驱动 LCD模组主要包括LCD显示屏和驱动IC.比如LF040DNYB16a模组的驱动IC型号为NT35510.要在MT577平台上新建这个lcd的驱动,步骤如下 ...

  9. MTK_android项目驱动设置

    1. LCD 1.1怎样新建一个LCD驱动 LCD模组主要包括LCD显示屏和驱动IC.比如LF040DNYB16a模组的驱动IC型号为NT35510.要在MT577平台上新建这个lcd的驱动,步骤如下 ...

最新文章

  1. 百面机器学习之特征工程
  2. 基于OpenCV的表格文本内容提取
  3. 独家 | 一文了解强化学习的商业应用
  4. 【转载】关于 Google Chrome 中的全屏模式和 APP 模式
  5. 2021年Q2母婴行业季度洞察报告
  6. [转载]让Windows Server 2008 + IIS 7+ ASP.NET 支持10万并发请求
  7. 中标2020中国联通数据设备集采项目,中兴通讯备受鼓舞
  8. TMS320F28335之外部中断和自定义中断
  9. fastdfs 内网 公网 双ip问题
  10. SD卡与TF卡基础知识
  11. HTTP请求的TCP瓶颈分析
  12. 朋友圈爱心拼图php源码_微信朋友圈九宫格爱心拼图怎么弄 拼图教程
  13. 从狂热的苹果粉丝们来看中国式“杯具”
  14. 小程序如何通过云开发获取手机号
  15. item_password-获得淘口令真实url接口,淘宝app短链接商品接口,1688商品淘口令url接口
  16. php 查看文件信息,文件信息查看
  17. C#List子类转List父类或者Obj对象转List
  18. 修改db_create_online_log_dest_1
  19. 盘点国产青春电影十宗罪
  20. 微信赚钱越来越难?该如何转型?

热门文章

  1. C#怎么选中datagridview中的一行,并获得它的数据?
  2. 高等数学笔记-乐经良老师-第四章-微分中值定理和导数的应用-第二节-洛必达法则
  3. axio请求数据如何等待异步完成接着执行
  4. 闽江学院计算机系微博,我校计控学子在第13届中国大学生计算机设计大赛中勇创佳绩...
  5. es查询-统计总数以及深度分页
  6. 带阻滤波器对周期性噪声消除matlab实现
  7. Ubuntu内核版本的降级
  8. java中不用Math.sqrt,实现开平方根的算法
  9. 关于数据治理的读书笔记 - 企业数据治理的“道、法、术、器”
  10. 天融信虚拟企业防火墙学习