IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> vibrate-arch -> 正文阅读

[移动开发]vibrate-arch

1app

import android.os.Vibrator;
import android.widget.ToggleButton;

public class MainActivity extends Activity {
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		private Vibrator vibrator=null;
		vibrator=(Vibrator)this.getSystemService(VIBRATOR_SERVICE);
		toggleButton1=(ToggleButton)findViewById(R.id.toggleButton1);
		/*短震动*/
		toggleButton1.setOnCheckedChangeListener(new ToggleButton.OnCheckedChangeListener() {
			
			@Override
			public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
				if(isChecked){
                    Log.i(TAG,"toggleButton1 enter vibrator.vibrate");
					//设置震动周期,第二个参数为 -1表示只震动一次
					vibrator.vibrate(new long[]{1000, 10, 100, 1000},-1);
				}else{
					//取消震动
                    Log.i(TAG,"toggleButton1 enter vibrator.cancel()");
					vibrator.cancel();
				}
			}
		});
	}
}

2fwk


//    代码路径:frameworks\base\services\core\java\com\android\server\VibratorService.java
@Override // Binder call
public void vibrate(int uid, String opPkg, VibrationEffect effect, int usageHint,
                    IBinder token) {
    Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "vibrate");
    try {
        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.VIBRATE)
            != PackageManager.PERMISSION_GRANTED) {
            throw new SecurityException("Requires VIBRATE permission");
        }
。。。。

        // If our current vibration is longer than the new vibration and is the same amplitude,
        // then just let the current one finish.
        synchronized (mLock) {
。。。
            Vibration vib = new Vibration(token, effect, usageHint, uid, opPkg);
            linkVibration(vib);
            long ident = Binder.clearCallingIdentity();
            try {
                doCancelVibrateLocked();
                startVibrationLocked(vib);
                addToPreviousVibrationsLocked(vib);
            } finally {
                Binder.restoreCallingIdentity(ident);
            }
        }
    } finally {
        Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
    }
}

startVibrationLocked->startVibrationInnerLocked->doVibratorOn->vibrateOn->VibratorThread->playWaveform->doVibratorOn->vibrateOn

3jni

//代码路径:frameworks\base\services\core\jni\com_android_server_VibratorService.cpp
static void vibratorOn(JNIEnv* /* env */, jobject /* clazz */, jlong timeout_ms)
{
    Status retStatus = halCall(&V1_0::IVibrator::on, timeout_ms).withDefault(Status::UNKNOWN_ERROR);
    if (retStatus != Status::OK) {
        ALOGE("vibratorOn command failed (%" PRIu32 ").", static_cast<uint32_t>(retStatus));
    }
}

static void vibratorOff(JNIEnv* /* env */, jobject /* clazz */)
{
    Status retStatus = halCall(&V1_0::IVibrator::off).withDefault(Status::UNKNOWN_ERROR);
    if (retStatus != Status::OK) {
        ALOGE("vibratorOff command failed (%" PRIu32 ").", static_cast<uint32_t>(retStatus));
    }
}

static const JNINativeMethod method_table[] = {
    { "vibratorExists", "()Z", (void*)vibratorExists },
    { "vibratorInit", "()V", (void*)vibratorInit },
    { "vibratorOn", "(J)V", (void*)vibratorOn },
    { "vibratorOff", "()V", (void*)vibratorOff },
    { "vibratorSupportsAmplitudeControl", "()Z", (void*)vibratorSupportsAmplitudeControl},
    { "vibratorSetAmplitude", "(I)V", (void*)vibratorSetAmplitude},
    { "vibratorPerformEffect", "(JJ)J", (void*)vibratorPerformEffect}
};

int register_android_server_VibratorService(JNIEnv *env)
{
    return jniRegisterNativeMethods(env, "com/android/server/VibratorService",
            method_table, NELEM(method_table));
}

4 hidl

//代码路径:hardware\interfaces\vibrator\1.0\default\Vibrator.cpp
Return<Status> Vibrator::on(uint32_t timeout_ms) {
    int32_t ret = mDevice->vibrator_on(mDevice, timeout_ms);
    if (ret != 0) {
        ALOGE("on command failed : %s", strerror(-ret));
        return Status::UNKNOWN_ERROR;
    }
    return Status::OK;
}

Return<Status> Vibrator::off()  {
    int32_t ret = mDevice->vibrator_off(mDevice);
    if (ret != 0) {
        ALOGE("off command failed : %s", strerror(-ret));
        return Status::UNKNOWN_ERROR;
    }
    return Status::OK;
}

5hal

//代码路径:hardware\libhardware\modules\vibrator\vibrator.c
static const char THE_DEVICE[] = "/sys/class/timed_output/vibrator/enable";

static int sendit(unsigned int timeout_ms)
{
    char value[TIMEOUT_STR_LEN]; /* large enough for millions of years */

    snprintf(value, sizeof(value), "%u", timeout_ms);
    return write_value(THE_DEVICE, value);
}

static int vibra_on(vibrator_device_t* vibradev __unused, unsigned int timeout_ms)
{
    /* constant on, up to maximum allowed time */
    return sendit(timeout_ms);
}

static int vibra_off(vibrator_device_t* vibradev __unused)
{
    return sendit(0);
}

static int vibra_open(const hw_module_t* module, const char* id __unused,
                      hw_device_t** device __unused) {
    bool use_led;

    if (vibra_exists()) {
        ALOGD("Vibrator using timed_output");
        use_led = false;
    } else if (vibra_led_exists()) {
        ALOGD("Vibrator using LED trigger");
        use_led = true;
    } else {
        ALOGE("Vibrator device does not exist. Cannot start vibrator");
        return -ENODEV;
    }

    vibrator_device_t *vibradev = calloc(1, sizeof(vibrator_device_t));

    if (!vibradev) {
        ALOGE("Can not allocate memory for the vibrator device");
        return -ENOMEM;
    }

    vibradev->common.tag = HARDWARE_DEVICE_TAG;
    vibradev->common.module = (hw_module_t *) module;
    vibradev->common.version = HARDWARE_DEVICE_API_VERSION(1,0);
    vibradev->common.close = vibra_close;

    if (use_led) {
        vibradev->vibrator_on = vibra_led_on;
        vibradev->vibrator_off = vibra_led_off;
    } else {
        vibradev->vibrator_on = vibra_on;
        vibradev->vibrator_off = vibra_off;
    }

    *device = (hw_device_t *) vibradev;

    return 0;
}

其实开启和关闭马达的工作很简单,就是往节点"/sys/class/timed_output/vibrator/enable"写入震动时间,所以可以想得到驱动层只需要提供一个节点供上层操作就好

4 driver

马达的驱动是基于kernel提供的timed_output框架完成的:

代码比较简单,提供接口给驱动在"/sys/class/timed_output/"路径下面建立自己的节点,并提供节点的device attribute的操作接口,当我们写节点的时候就会调用到enable_store函数,并调用注册驱动的enable函数。

static struct class *timed_output_class;

static ssize_t enable_store(struct device *dev, struct device_attribute *attr,
			    const char *buf, size_t size)
{
	struct timed_output_dev *tdev = dev_get_drvdata(dev);
	int value;
	int rc;

	rc = kstrtoint(buf, 0, &value);
	if (rc != 0)
		return -EINVAL;

	tdev->enable(tdev, value);

	return size;
}
static DEVICE_ATTR_RW(enable);

static struct attribute *timed_output_attrs[] = {
	&dev_attr_enable.attr,
	NULL,
};
ATTRIBUTE_GROUPS(timed_output);

static int create_timed_output_class(void)
{
	if (!timed_output_class) {
		timed_output_class = class_create(THIS_MODULE, "timed_output");
		if (IS_ERR(timed_output_class))
			return PTR_ERR(timed_output_class);
		atomic_set(&device_count, 0);
		timed_output_class->dev_groups = timed_output_groups;
	}

	return 0;
}

int timed_output_dev_register(struct timed_output_dev *tdev)
{
	int ret;

	if (!tdev || !tdev->name || !tdev->enable || !tdev->get_time)
		return -EINVAL;

	ret = create_timed_output_class();
	if (ret < 0)
		return ret;

	tdev->index = atomic_inc_return(&device_count);
	tdev->dev = device_create(timed_output_class, NULL,
		MKDEV(0, tdev->index), NULL, "%s", tdev->name);
	if (IS_ERR(tdev->dev))
		return PTR_ERR(tdev->dev);

	dev_set_drvdata(tdev->dev, tdev);
	tdev->state = 0;
	return 0;
}

看一下基于上面框架书写的马达驱动:

static void vibrator_off(void)
{
	gpio_direction_output(gpio, !en_value);       
	wake_unlock(&vibdata.wklock); //震动关闭就可以释放 wake_lock锁        
}

void motor_enable(struct timed_output_dev *sdev,int value)
{
	mutex_lock(&vibdata.lock); //关键代码段,同一时间只允许一个线程执行

	/* cancelprevious timer and set GPIO according to value */
	hrtimer_cancel(&vibdata.timer); //当先前定时器完成后 关闭这个定时器
	cancel_work_sync(&vibdata.work); //当上次震动完成后 关闭这次动作
	if(value)
	{
		wake_lock(&vibdata.wklock); //开始震动打开wake lock锁不允许休眠
		gpio_direction_output(gpio, en_value);

		if(value > 0)
		{
			if(value > MAX_TIMEOUT)
				value= MAX_TIMEOUT;
			hrtimer_start(&vibdata.timer,
			         ktime_set(value / 1000, (value % 1000) * 1000000),
			         HRTIMER_MODE_REL);
		}
	}
	else
		vibrator_off();

	mutex_unlock(&vibdata.lock);
}

struct timed_output_dev motot_driver = {
	.name ="vibrator", //注意这个名字,由于HAL层里面的设备为//"/sys/class/timed_output/vibrator/enable"
	                   //因此这个名字必须为"vibrator"
	.enable= motor_enable,
	.get_time= get_time,
};

static enum hrtimer_restart vibrator_timer_func(struct hrtimer *timer) //定时器结束时候的回调函数
{
	schedule_work(&vibdata.work); //定时器完成了 执行work队列回调函数来关闭电机
	return HRTIMER_NORESTART;
}
static void vibrator_work(struct work_struct *work)
{
	vibrator_off();
}

static int motor_probe(struct platform_device *pdev)
{
	struct device_node *node = pdev->dev.of_node;
	enum of_gpio_flags flags;
	int ret =0;
	
	hrtimer_init(&vibdata.timer,CLOCK_MONOTONIC, HRTIMER_MODE_REL);
	vibdata.timer.function= vibrator_timer_func;
	INIT_WORK(&vibdata.work,vibrator_work);

	...
	
	ret=timed_output_dev_register(&motot_driver);
	if (ret< 0)
		goto err_to_dev_reg;
	return 0;

err_to_dev_reg:
	mutex_destroy(&vibdata.lock);
	wake_lock_destroy(&vibdata.wklock);

	printk("vibrator   err!:%d\n",ret);
	return ret;

}
  1. 驱动接收上层传递过来的是震动时长,单位为毫秒。在驱动里注册一个定时器,定时器倒计时到期后会唤醒注册的工作队列,最终会执行vibrator_work()函数去关闭马达震动。
  2. 调用timed_output框架提供的timed_output_dev_register()接口将我们的马达驱动注册进系统,这里的关键就是我们需要自定义struct timed_output_dev结构体,填充enable和get_time函数。enable函数用来开启马达的震动:
void motor_enable(struct timed_output_dev *sdev,int value)
{
	mutex_lock(&vibdata.lock); //关键代码段,同一时间只允许一个线程执行

	/* cancelprevious timer and set GPIO according to value */
	hrtimer_cancel(&vibdata.timer); //当先前定时器完成后 关闭这个定时器
	cancel_work_sync(&vibdata.work); //当上次震动完成后 关闭这次动作
	if(value)
	{
		wake_lock(&vibdata.wklock); //开始震动打开wake lock锁不允许休眠
		gpio_direction_output(gpio, en_value);

		if(value > 0)
		{
			if(value > MAX_TIMEOUT)
				value= MAX_TIMEOUT;
			hrtimer_start(&vibdata.timer,
			         ktime_set(value / 1000, (value % 1000) * 1000000),
			         HRTIMER_MODE_REL);
		}
	}
	else
		vibrator_off();

	mutex_unlock(&vibdata.lock);
}

开启震动的操作也很简单,只是写一下GPIO,然后重新开启定时器,倒计时的时间就是写入节点的值,到时间再把马达关闭就好。

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-04-18 17:54:07  更:2022-04-18 17:57:15 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 21:45:25-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码