LDD之input子系统--代码分析

深入解析C/C++链接错误:undefined reference的五大根源与系统化排查指南 在C/C++项目构建过程中,链接是将多个编译单元组合成可执行程序的关键阶段。其核心原理是符号解析,即链接器需要在所有提供的目标文件和库中找到每个被引用符号的确切定义。这一机制确保了程序的完整性和正确性,是构建可靠软件的基础。当链接器无法找到某个符号的定义时,就会抛出“undefined reference”错误,这直接关系到项目的可构建性和最终产物的质量。此类问题常见于多文件项目、第三方库集成、混合语言编程等工程实践场景。例如,在嵌入式开发中,遗漏HAL库源文件会导致关键驱动函数缺失;在Linux环境下,动 阅读详情
-----------------------------------------------------------------------
本文系本站原创,欢迎转载!
转载请注明出处:http://blog.youkuaiyun.com/android_huber
交流邮箱:dp.shao@gmail.com
-----------------------------------------------------------------------



                                                   input子系统 ---  代码分析

                                                       input初始化分析
static int __init input_init(void)
{
	int err;

	input_init_abs_bypass();

	err = class_register(&input_class); //注册一个input的类,所有的input设备都是这个类
	if (err) {
		printk(KERN_ERR "input: unable to register input_dev class\n");
		return err;
	}

	err = input_proc_init();
	if (err)
		goto fail1;

	err = register_chrdev(INPUT_MAJOR, "input", &input_fops); 注册字符设备,
input设备主设备号为13
	if (err) {
		printk(KERN_ERR "input: unable to register char major %d", INPUT_MAJOR);
		goto fail2;
	}

	return 0;

 fail2:	input_proc_exit();
 fail1:	class_unregister(&input_class);
	return err;
}

static const struct file_operations input_fops = {
	.owner = THIS_MODULE,
	.open = input_open_file,
};

int input_register_device(struct input_dev *dev)
{
	static atomic_t input_no = ATOMIC_INIT(0);
	struct input_handler *handler;
	const char *path;
	int error;

	/* Every input device generates EV_SYN/SYN_REPORT events. */
	__set_bit(EV_SYN, dev->evbit); //设置支持的事件

	/* KEY_RESERVED is not supposed to be transmitted to userspace. */
	__clear_bit(KEY_RESERVED, dev->keybit);

	/* Make sure that bitmasks not mentioned in dev->evbit are clean. */
	input_cleanse_bitmasks(dev);

	/*
	 * If delay and period are pre-set by the driver, then autorepeating
	 * is handled by the driver itself and we don't do it in input.c.
	 */
	init_timer(&dev->timer); //初始化定时器
	if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD]) {
		dev->timer.data = (long) dev;
		dev->timer.function = input_repeat_key;
		dev->rep[REP_DELAY] = 250;
		dev->rep[REP_PERIOD] = 33;
	}

	if (!dev->getkeycode)
		dev->getkeycode = input_default_getkeycode;

	if (!dev->setkeycode)
		dev->setkeycode = input_default_setkeycode;

	dev_set_name(&dev->dev, "input%ld",
		     (unsigned long) atomic_inc_return(&input_no) - 1);

	error = device_add(&dev->dev); //添加设备进总线
	if (error)
		return error;

	path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL); //创建sys目录
	printk(KERN_INFO "input: %s as %s\n",
		dev->name ? dev->name : "Unspecified device", path ? path : "N/A");
	kfree(path);

	error = mutex_lock_interruptible(&input_mutex);
	if (error) {
		device_del(&dev->dev);
		return error;
	}

	list_add_tail(&dev->node, &input_dev_list); //将该设备加入到input设备链表中

	list_for_each_entry(handler, &input_handler_list, node) 
		input_attach_handler(dev, handler);
对每一个挂在input_handler_list的handler调用input_attach_handler
	input_wakeup_procfs_readers();
input_handler_list链表是在input_register_handler中填充,input_dev_list是在input_register_device中填充添加。

	mutex_unlock(&input_mutex);

	return 0;
}

static void input_repeat_key(unsigned long data)
{
	struct input_dev *dev = (void *) data;
	unsigned long flags;

	spin_lock_irqsave(&dev->event_lock, flags);

	if (test_bit(dev->repeat_key, dev->key) &&
	    is_event_supported(dev->repeat_key, dev->keybit, KEY_MAX)) {

		input_pass_event(dev, EV_KEY, dev->repeat_key, 2);

		if (dev->sync) { //如果更新标志位设置了,上报更新
			/*
			 * Only send SYN_REPORT if we are not in a middle
			 * of driver parsing a new hardware packet.
			 * Otherwise assume that the driver will send
			 * SYN_REPORT once it's done.
			 */
			input_pass_event(dev, EV_SYN, SYN_REPORT, 1);
		}

		if (dev->rep[REP_PERIOD])
			mod_timer(&dev->timer, jiffies +
					msecs_to_jiffies(dev->rep[REP_PERIOD]));
	}

	spin_unlock_irqrestore(&dev->event_lock, flags);
}

int input_register_handler(struct input_handler *handler)
{
	struct input_dev *dev;
	int retval;

	retval = mutex_lock_interruptible(&input_mutex);
	if (retval)
		return retval;

	INIT_LIST_HEAD(&handler->h_list);

	if (handler->fops != NULL) {
		if (input_table[handler->minor >> 5]) {
			retval = -EBUSY;
			goto out;
		}
		input_table[handler->minor >> 5] = handler; //除以32,每32个次设备号对应一个handler,evdev为64~64-1
	}

	list_add_tail(&handler->node, &input_handler_list); //添加handler到input_handler_list链表中

	list_for_each_entry(dev, &input_dev_list, node)
		input_attach_handler(dev, handler);
对每一个挂在input_dev_list链表中的dev调用input_attach_handler.
	input_wakeup_procfs_readers();

 out:
	mutex_unlock(&input_mutex);
	return retval;
}

在evbug.c,evdev.c,joydev.c,mousedev.c中分别调用了一次
input_register_handler,注册对应属性的handler

joydev.c ------Joystick device driver---- 游戏手柄/操纵杆支持
mousedev.c -------ExplorerPS/2 device driver------鼠标支持
evdev.c------Event char devices, giving access to raw input device events.---原始输入设备支持

在这里只分析evdev.c,因为一般都是evdev设备

static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
{
	const struct input_device_id *id;
	int error;

	id = input_match_device(handler, dev); //主要调用了handler的match函数
	if (!id)
		return -ENODEV;

	error = handler->connect(handler, dev, id); //handler的connect函数
	if (error && error != -ENODEV)
		printk(KERN_ERR
			"input: failed to attach handler %s to device %s, "
			"error: %d\n",
			handler->name, kobject_name(&dev->dev.kobj), error);

	return error;
}
从上面程序中可以知道,attach的过程就是调用handler的match函数,match通过,进行connect

static const struct input_device_id *input_match_device(struct input_handler *handler,
							struct input_dev *dev)
{
	const struct input_device_id *id;
	int i;

	for (id = handler->id_table; id->flags || id->driver_info; id++) {

		if (id->flags & INPUT_DEVICE_ID_MATCH_BUS)
			if (id->bustype != dev->id.bustype)
				continue;

		if (id->flags & INPUT_DEVICE_ID_MATCH_VENDOR)
			if (id->vendor != dev->id.vendor)
				continue;

		if (id->flags & INPUT_DEVICE_ID_MATCH_PRODUCT)
			if (id->product != dev->id.product)
				continue;

		if (id->flags & INPUT_DEVICE_ID_MATCH_VERSION)
			if (id->version != dev->id.version)
				continue;

		MATCH_BIT(evbit,  EV_MAX);
		MATCH_BIT(keybit, KEY_MAX);
		MATCH_BIT(relbit, REL_MAX);
		MATCH_BIT(absbit, ABS_MAX);
		MATCH_BIT(mscbit, MSC_MAX);
		MATCH_BIT(ledbit, LED_MAX);
		MATCH_BIT(sndbit, SND_MAX);
		MATCH_BIT(ffbit,  FF_MAX);
		MATCH_BIT(swbit,  SW_MAX);

		if (!handler->match || handler->match(handler, dev)) 
//调用handler的match,如果未定义match则直接返回id
			return id;
	}

	return NULL;
}


                                                            evdev分析
static int __init evdev_init(void)
{
	return input_register_handler(&evdev_handler);
}

static struct input_handler evdev_handler = {
	.event		= evdev_event,
	.connect	= evdev_connect,
	.disconnect	= evdev_disconnect,
	.fops		= &evdev_fops,
	.minor		= EVDEV_MINOR_BASE,
	.name		= "evdev",
	.id_table	= evdev_ids,
};

static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
			 const struct input_device_id *id)
{
	struct evdev *evdev;
	int minor;
	int error;

	for (minor = 0; minor < EVDEV_MINORS; minor++)
		if (!evdev_table[minor])
			break;

	if (minor == EVDEV_MINORS) {
		printk(KERN_ERR "evdev: no more free evdev devices\n");
		return -ENFILE;
	}

	evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
	if (!evdev)
		return -ENOMEM;

	INIT_LIST_HEAD(&evdev->client_list);
	spin_lock_init(&evdev->client_lock);
	mutex_init(&evdev->mutex);
	init_waitqueue_head(&evdev->wait);

	dev_set_name(&evdev->dev, "event%d", minor);
	evdev->exist = 1;
	evdev->minor = minor;

	evdev->handle.dev = input_get_device(dev);
	evdev->handle.name = dev_name(&evdev->dev);
	evdev->handle.handler = handler;
	evdev->handle.private = evdev;
这个结构封装了一个handle结构,这个结构与handler不同,handle可以看成是handler和input device的结合。
	evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor);
	evdev->dev.class = &input_class;
	evdev->dev.parent = &dev->dev;
	evdev->dev.release = evdev_free;
	device_initialize(&evdev->dev);
在这段代码里主要完成evdev封装的device的初始化.注意在这里,使它所属的类指向input_class.这样在/sysfs中创建的设备目录就会在/sys/class/input/下面显示.

	error = input_register_handle(&evdev->handle);
	if (error)
		goto err_free_evdev;

	error = evdev_install_chrdev(evdev);
	if (error)
		goto err_unregister_handle;

	error = device_add(&evdev->dev); //将evdev->device注册到sysfs
	if (error)
		goto err_cleanup_evdev;

	return 0;

 err_cleanup_evdev:
	evdev_cleanup(evdev);
 err_unregister_handle:
	input_unregister_handle(&evdev->handle);
 err_free_evdev:
	put_device(&evdev->dev);
	return error;
}

int input_register_handle(struct input_handle *handle)
{
	struct input_handler *handler = handle->handler;
	struct input_dev *dev = handle->dev;
	int error;

	/*
	 * We take dev->mutex here to prevent race with
	 * input_release_device().
	 */
	error = mutex_lock_interruptible(&dev->mutex);
	if (error)
		return error;

//将handle挂载到h_list结构体
	if (handler->filter)
		list_add_rcu(&handle->d_node, &dev->h_list);
	else
		list_add_tail_rcu(&handle->d_node, &dev->h_list);

	mutex_unlock(&dev->mutex);

//还将handle挂载在handler的h_list结构体
	list_add_tail_rcu(&handle->h_node, &handler->h_list);
//如果handler定义了start,则调用start,这里没有定义
	if (handler->start)
		handler->start(handle);

	return 0;
}

static int evdev_install_chrdev(struct evdev *evdev)
{
	evdev_table[evdev->minor] = evdev; //evdev_table的minor项指向evdev.
	return 0;
}


                                                       event层分析
static inline void input_report_abs(struct input_dev *dev, unsigned int code, int value)
{
	input_event(dev, EV_ABS, code, value);
}

static inline void input_report_key(struct input_dev *dev, unsigned int code, int value)
{
	input_event(dev, EV_KEY, code, !!value);
}

static inline void input_sync(struct input_dev *dev)
{
	input_event(dev, EV_SYN, SYN_REPORT, 0);
}

static inline void input_mt_sync(struct input_dev *dev)
{
	input_event(dev, EV_SYN, SYN_MT_REPORT, 0);
}
void input_event(struct input_dev *dev,
		 unsigned int type, unsigned int code, int value)
{
	unsigned long flags;

	if (is_event_supported(type, dev->evbit, EV_MAX)) {

		spin_lock_irqsave(&dev->event_lock, flags);
		add_input_randomness(type, code, value);
		input_handle_event(dev, type, code, value);
		spin_unlock_irqrestore(&dev->event_lock, flags);
	}
}

#define INPUT_IGNORE_EVENT	0
#define INPUT_PASS_TO_HANDLERS	1
#define INPUT_PASS_TO_DEVICE	2
#define INPUT_PASS_TO_ALL
(INPUT_PASS_TO_HANDLERS | INPUT_PASS_TO_DEVICE)

static void input_handle_event(struct input_dev *dev,
			       unsigned int type, unsigned int code, int value)
{
	int disposition = INPUT_IGNORE_EVENT;

	switch (type) {

	case EV_SYN:
		switch (code) {
		case SYN_CONFIG:
			disposition = INPUT_PASS_TO_ALL;
			break;

		case SYN_REPORT:
			if (!dev->sync) {
				dev->sync = 1; //更新
				disposition = INPUT_PASS_TO_HANDLERS; //控制权交给handler
			}
			break;
		case SYN_MT_REPORT:
			dev->sync = 0; //等待更新
			disposition = INPUT_PASS_TO_HANDLERS; //控制权交给handler
			break;
		}
		break;

	case EV_KEY:
		if (is_event_supported(code, dev->keybit, KEY_MAX) && //判断事件是否有效
		    !!test_bit(code, dev->key) != value) {

			if (value != 2) {
				__change_bit(code, dev->key);
				if (value)
					input_start_autorepeat(dev, code); //如果有按键按下,则循环
				else
					input_stop_autorepeat(dev);
			}

			disposition = INPUT_PASS_TO_HANDLERS;
		}
		break;

	case EV_SW:
		if (is_event_supported(code, dev->swbit, SW_MAX) &&
		    !!test_bit(code, dev->sw) != value) {

			__change_bit(code, dev->sw);
			disposition = INPUT_PASS_TO_HANDLERS;
		}
		break;

	case EV_ABS:
		if (is_event_supported(code, dev->absbit, ABS_MAX)) {

			if (test_bit(code, input_abs_bypass)) {
				disposition = INPUT_PASS_TO_HANDLERS;
				break;
			}

			value = input_defuzz_abs_event(value,
					dev->abs[code], dev->absfuzz[code]); //处理坐标,什么原理啊??

			if (dev->abs[code] != value) {
				dev->abs[code] = value; //保存value到dev->abs[code]
				disposition = INPUT_PASS_TO_HANDLERS;
			}
		}
		break;

	case EV_REL:
		if (is_event_supported(code, dev->relbit, REL_MAX) && value)
			disposition = INPUT_PASS_TO_HANDLERS;
		break;

	case EV_MSC:
		if (is_event_supported(code, dev->mscbit, MSC_MAX))
			disposition = INPUT_PASS_TO_ALL;
		break;

	case EV_LED:
		if (is_event_supported(code, dev->ledbit, LED_MAX) &&
		    !!test_bit(code, dev->led) != value) {

			__change_bit(code, dev->led);
			disposition = INPUT_PASS_TO_ALL;
		}
		break;

	case EV_SND:
		if (is_event_supported(code, dev->sndbit, SND_MAX)) {

			if (!!test_bit(code, dev->snd) != !!value)
				__change_bit(code, dev->snd);
			disposition = INPUT_PASS_TO_ALL;
		}
		break;

	case EV_REP:
		if (code <= REP_MAX && value >= 0 && dev->rep[code] != value) {
			dev->rep[code] = value;
			disposition = INPUT_PASS_TO_ALL;
		}
		break;

	case EV_FF:
		if (value >= 0)
			disposition = INPUT_PASS_TO_ALL;
		break;

	case EV_PWR:
		disposition = INPUT_PASS_TO_ALL;
		break;
	}

	if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
		dev->sync = 0;

	if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event) //调用device的event函数,如果定义了dev->event的话
		dev->event(dev, type, code, value);

	if (disposition & INPUT_PASS_TO_HANDLERS) //调用handler的event函数
		input_pass_event(dev, type, code, value);
}

static int input_defuzz_abs_event(int value, int old_val, int fuzz)
{    什么原理啊???
	if (fuzz) {
		if (value > old_val - fuzz / 2 && value < old_val + fuzz / 2)
			return old_val;

		if (value > old_val - fuzz && value < old_val + fuzz)
			return (old_val * 3 + value) / 4;

		if (value > old_val - fuzz * 2 && value < old_val + fuzz * 2)
			return (old_val + value) / 2;
	}

	return value;
}

static void input_pass_event(struct input_dev *dev,
			     unsigned int type, unsigned int code, int value)
{
	struct input_handler *handler;
	struct input_handle *handle;

	rcu_read_lock();

	handle = rcu_dereference(dev->grab);
	if (handle)
		handle->handler->event(handle, type, code, value); //evdev的event函数
	else {
		bool filtered = false;

		list_for_each_entry_rcu(handle, &dev->h_list, d_node) {
			if (!handle->open)
				continue;

			handler = handle->handler; //找到handle
			if (!handler->filter) {
				if (filtered)
					break;

				handler->event(handle, type, code, value);

			} else if (handler->filter(handle, type, code, value))
				filtered = true;
		}
	}

	rcu_read_unlock();
}

evdev中还有一个比较重要的函数是evdev_event
static void evdev_event(struct input_handle *handle,
			unsigned int type, unsigned int code, int value)
{
	struct evdev *evdev = handle->private;
	struct evdev_client *client;
	struct input_event event; //input_event结构体,即为上层上报的事件
struct input_event {
	struct timeval time; //事件上报事件
	__u16 type;	//事件类型码,如EV_ABS/EV_KEY/EV_REL/EV_SYN
	__u16 code;	//按键时为扫描码,触摸屏时为数据类型
	__s32 value;	//属性值,按键时代表按下弹起,触摸屏为数据
};
	struct timespec ts;

	ktime_get_ts(&ts);
	event.time.tv_sec = ts.tv_sec; //填充时间,秒
	event.time.tv_usec = ts.tv_nsec / NSEC_PER_USEC; //纳秒

	event.type = type; //填充类型码
	event.code = code; //填充按键扫描码或touch数据类型
	event.value = value; //填充数据类型值

	rcu_read_lock();

	client = rcu_dereference(evdev->grab);
	if (client)
		evdev_pass_event(client, &event);
	else
		list_for_each_entry_rcu(client, &evdev->client_list, node)
			evdev_pass_event(client, &event);

	rcu_read_unlock();

	wake_up_interruptible(&evdev->wait); //唤醒睡眠在evdev->wait等待队列等待输入信息的进程,通知evdev_read读取。
}

static void evdev_pass_event(struct evdev_client *client,
			     struct input_event *event)
{
	/*
	 * Interrupts are disabled, just acquire the lock
	 */
	spin_lock(&client->buffer_lock);
	client->buffer[client->head++] = *event; //加入client的信息缓冲
	client->head &= EVDEV_BUFFER_SIZE - 1; //环形缓存区处理
	spin_unlock(&client->buffer_lock);

	if (event->type == EV_SYN)
		kill_fasync(&client->fasync, SIGIO, POLL_IN); //发送SIGIO信号,告知事件
}
//将event保存到client->buf中,client->head是当前数据的位置。注意这里是一个环形缓存区.写数据是从client->head写.而读数据则是从client->tail中读.


                                                           信息读取
//设备的open函数
static int input_open_file(struct inode *inode, struct file *file)
{
	struct input_handler *handler;
	const struct file_operations *old_fops, *new_fops = NULL;
	int err;

	err = mutex_lock_interruptible(&input_mutex);
	if (err)
		return err;

	/* No load-on-demand here? */
	handler = input_table[iminor(inode) >> 5]; //根据次设备号来得到handler,次设备号右移5位,这个就是input子系统次设备号的分配了,evdev的次设备号是64~64+32-1,对应着同一个handler
	if (handler)
		new_fops = fops_get(handler->fops); //得到handler中的fops

	mutex_unlock(&input_mutex);

	/*
	 * That's _really_ odd. Usually NULL ->open means "nothing special",
	 * not "no device". Oh, well...
	 */
	if (!new_fops || !new_fops->open) {
		fops_put(new_fops);
		err = -ENODEV;
		goto out;
	}

	old_fops = file->f_op; //保存老的fops
	file->f_op = new_fops; //然后将handler中的fops替换掉当前的fops.这样只要设备open以后,调用其它的函数时(如read,write,poll,ioctl就可以调用)

	err = new_fops->open(inode, file); //调用handler的fops中的open
	if (err) {
		fops_put(file->f_op);
		file->f_op = fops_get(old_fops);
	}
	fops_put(old_fops);
out:
	return err;
}
//event层中的read函数
static ssize_t evdev_read(struct file *file, char __user *buffer,
			  size_t count, loff_t *ppos)
{
	struct evdev_client *client = file->private_data;
	struct evdev *evdev = client->evdev;
	struct input_event event;
	int retval;

	if (count < input_event_size())
		return -EINVAL;

	if (client->head == client->tail && evdev->exist && //缓存中没有数据,且设备存在
	    (file->f_flags & O_NONBLOCK))
		return -EAGAIN;

	retval = wait_event_interruptible(evdev->wait,
		client->head != client->tail || !evdev->exist); //等待evdev_event函数去唤醒
	if (retval)
		return retval;

	if (!evdev->exist)
		return -ENODEV;

	while (retval + input_event_size() <= count &&
	       evdev_fetch_next_event(client, &event)) { //下面开始读取数据

		if (input_event_to_user(buffer + retval, &event)) //将event结构copy_to_user,给用户层
			return -EFAULT;

		retval += input_event_size();
	}

	return retval;
}

static int evdev_fetch_next_event(struct evdev_client *client,
				  struct input_event *event)
{
	int have_event;

	spin_lock_irq(&client->buffer_lock);

	have_event = client->head != client->tail;
	if (have_event) {
		*event = client->buffer[client->tail++];
		client->tail &= EVDEV_BUFFER_SIZE - 1;
	}

	spin_unlock_irq(&client->buffer_lock);

	return have_event;
}

int input_event_to_user(char __user *buffer,
			const struct input_event *event)
{
	if (copy_to_user(buffer, event, sizeof(struct input_event)))
		return -EFAULT;

	return 0;
}


这篇文章只是分析了一下input子系统的代码,可能看了这么多代码后大家的心里对input的整个框架并不了解,我将在稍后的一篇文章中,放入我刚画好的一张input子系统框架流程图,同时还有对input子系统的一些简单总结,到时大家可以参照一下,会对理解整个input子系统会有帮助。


 结合下一篇带流程图的文章一起看。

http://blog.youkuaiyun.com/android_huber/article/details/7407217

图比较大,看不全保存在本地方便放大看。



【9】user & wdt 通过对libpam模块打patch的方式生成了自己的用户自定义pam_usrnum.so,并在shadow模块中进行逻辑配置,实现最大创建用户数量限制的功能。bitbake -s |grep libpam,devtool modify libpam,configure.ac:modules/pam_umask/Makefile modules/pam_usrnum/Makefile \,新建pam_usrnum/Makefile.am。如下还在/etc/pam.d/system-auth中,实现D。 阅读详情

相关推荐

《供应链金融风控模型设计:多维度数据整合与动态风险预警机制》

注:方案突出供应链金融「三流合一」(资金流、物流、信息流)的风控逻辑,通过多维度数据整合与动态预警,解决传统风控中中小企业信用评估难、链条风险传导监测滞后的问题。◦ 构建供应链拓扑图,计算「核心企业违约对上下游企业的影响系数」(如某电子厂商违约时,其前五大供应商的风险权重自动提升40%)• 区块链应用:将核心企业应付账款上链存证,防止「一票多融」欺诈(如某供应商用同一应收账款在多家银行融资)◦ 财务数据:核心企业审计报告、上下游企业纳税数据(增值税发票频次/金额)

2501_91651383的博客 1548

input子系统四 input事件处理

input事件处理流程 input driver -> input core ->event handler -> userspace 给应用程序。 一 事件分发跟踪 核心层留给驱动层的上报接口是input_report_abs(),最终会调用input_event()。 void input_event(struct input_dev *dev, unsigned in

coldsnow33的专栏 7139

STM32低功耗模式下ADC采样抖动的5个隐藏陷阱及解决方案(实测避坑)

本文深入探讨了STM32低功耗模式下ADC采样抖动的5个常见陷阱及解决方案,包括多通道采样电压跃迁、外设开关信号稳定、低功耗模式与ADC时钟同步、输入阻抗失配以及基准电压稳定性问题。通过实测数据和优化代码示例,帮助工程师有效规避ADC采样数据抖动问题,提升物联网设备的测量精度和稳定性。

weixin_30316097的博客 381

android 模拟触摸

package android.car.server.input; import android.car.utils.InputIoctl; import android.car.utils.touch.TouchWriter; import android.os.SystemClock; import android.util.Log; import android.view.InputDev

Grindstone_Fos的博客 1283

embedding计算过程

文章目录embedding计算过程(错了跟我讲,我会改)参考示例个人理解过程图示总结(基于pytorch) embedding计算过程(错了跟我讲,我会改) 参考示例 https://www.jianshu.com/p/63e7acc5e890 个人理解过程图示 总结(基于pytorch) 总的来说,就是将独热向量的长度(对应嵌入层的num_embeddings)转变为嵌入层的emdedding_size(而独热向量的维数就是你的数据中一共产生了多少个不一样的词,至少是这么多)。 数据的变化,将二维

纸上得来终觉浅,绝知此事要躬行! 1631

RK3588s Camera ISP调优详解(一)ISP调试工具及环境配置(附下载途径)

本文主要介绍了rkisp tuner工具的连接与使用,并附上了工具及依赖库的下载地址

zongheng_raobo的博客 6269

keypad 按键响应流程解析

一、keypad驱动,接收按键事件并将按键值转换为Linux code上发。 二、如何一层层上传到Android系统的控件中。

The more effort ,the more lucky 2531

C# 调用halcon中visualize_object_model_3d显示点云

3.1 判断线程是否为空,如果为空,则新创建,如果不为空,则中断线程,清空窗口后再次执行创建操作3.2 创建:调用show()方法实例化显示类的对象,传入显示参数,将线程testvisual 绑定 在 类对象的action方法上(此处action方法内部,只调用了visualize_object_model_3d算子)3.3 启动线程。

weixin_42362219的博客 1402

android多指控制协议详细解答

类型部分: 1.EV_SYN 同步事件完,在事件开始或完成会有 对应的code: 0004:代表一个事件开始(不必要) 0005:代表一个事件开始(不必要) SYN_REPORT:代表一个事件的结束 (必要) 2.EV_ABS 事件的一种绝对坐标类型 对应code: 2.0 ABS_MT_SLOT 本质代表者不同手指,它的value代表手指id 2.1 ABS_MT_TRACKING_ID 类型B特有的,实际上,每个slot会和一个ID相对应,一个非负数的表示一次接触,-1表示这是一个无用的slot(或者理

liaosongmao1的专栏 1000

Nestjs框架: 集成 Prisma

NestJS官方文档提供了两种数据库相关的内容:Techniques下的数据库板块和Recipes下的具体数据库集成方案。本文以Prisma为例,演示了如何集成Prisma到NestJS项目中。首先创建NestJS项目并安装Prisma,配置MySQL数据库连接。通过Prisma CLI初始化项目、定义数据模型并生成迁移文件。接着创建Prisma模块和服务,配置Prisma客户端连接数据库。最后在控制器中注入PrismaService实现数据查询。整个过程包含了Docker启动MySQL、Prisma模型定

Wang的专栏 1293

PostgreSQL性能优化:如何搭建监控体系?

本文系统讲解如何构建企业级PostgreSQL监控体系,覆盖四大核心维度:基础设施层、数据库实例层、会话与查询层、对象与存储层。通过Prometheus+Grafana+ELK等工具链实现指标采集、可视化与告警闭环管理,详细解析关键监控指标、部署方法及告警策略设计,帮助DBA快速定位慢查询、连接泄漏、存储膨胀等典型故障,构建"看得见、管得住、防得早"的数据库运维能力。

数据知道的博客 2232

Lua流程控制

Lua流程控制 if-else 单个if分支 x = 10 if x > 0 then print("TRUE") end 多个分支if-elseif-else score = 90 if score == 100 then print("Very good!Your score is 100") elseif score >= 60 then print("Congratulations, you have passed it,your score greater o

Weirdo_ 293

2025年数学建模国赛高教社杯A题、B题、C题、D题、E题详细思路和代码 模型||插值与拟合

核心思想:构造一个精确通过所有已知离散数据点(xi, yi)的函数y = f(x),并以此函数来估算或预测未知点的值。基本假设:我们认为已知的数据点是100%精确、无误差的(或者误差可忽略不计),比如理论计算值、高精度仪器测量值等。我们的目标是“还原”或“重建”出点与点之间的真实函数关系。关键特征精确性:曲线或曲面必须穿过每一个样本点。局部性:插值结果严重依赖于局部数据点的分布和数值。用途:用于“补全”数据、填充缺失值、进行函数近似等。常见方法多项式插值:拉格朗日插值、牛顿插值。

2201_76125261的博客 802

LDD之input子系统 总结与流程图

----------------------------------------------------------------------- 本文系本站原创,欢迎转载! 转载请注明出处:http://blog.youkuaiyun.com/android_huber 交流邮箱:dp.shao@gmail.com --------------------------------------------

android_huber的专栏 2395

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

linux 中国-新手村,从新手村开始,手把手带你入门梳理内核代码

原标题:从新手村开始,手把手带你入门梳理内核代码在上一期内容中,Java离Linux内核有多远? 我们介绍了从 JVM 到内核的编译原理,告诉大家应用和系统工程师如何接触到内核。本文将 从一个简单的底层硬件模块入手,一步步教大家如何梳理内核代码。适合精力集中在内核,不太需要关心用户空间的工程师,比如驱动工程师、嵌入式工程师等,以及想往这方面学习发展的朋友。1初探内核版本信息与往期一致: 在往期的访...

weixin_32791013的博客 228

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

内核 入门_从新手村开始,手把手带你入门梳理内核代码

喜欢就关注我们吧!在上一期内容中,Java离Linux内核有多远?我们介绍了从 JVM 到内核的编译原理,告诉大家应用和系统工程师如何接触到内核。本文将从一个简单的底层硬件模块入手,一步步教大家如何梳理内核代码。适合精力集中在内核,不太需要关心用户空间的工程师,比如驱动工程师、嵌入式工程师等,以及想往这方面学习发展的朋友。1初探内核版本信息与往期一致:在往期的访谈中,我们讨论过如何阅读...

weixin_39566773的博客 318

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

驱动该如何入门 关于file_operations和Linux设备模型

laoshizhuce laoshizhuce 等级: 结帖率:96.15% 楼主发表于: 2011-03-17 21:06:49 本帖最后由 laoshizhuce 于 2011-03-17 21:25:41 编辑   Linux驱动开发入门时,基本上市面上的书都会举这么一个例子:LED驱动 书上说编写驱动尤其是字符驱动就是实现struct file_op

linuxheik的专栏 1097

android键盘映射

转:http://blog.chinaunix.net/u3/90973/showart_2207472.html Android底层和中间层共同学习系列之android键盘映射 介绍 功能 键布局映射         键映射的选择         文件格式         键布局映射文件示例 键字符映射         键字符映射的选择         文件格式  ...

weixin_33936401的博客 196

【驱动】第6课、USB驱动之学习笔记

主 机:VMWare--Ubuntu-16.04.2-x64-100ask 开发板:Mini2440--256M NandFlash,   2M NorFlash,   64M SDRAM,   LCD-TD35;    bootlorder:u-boot1.16,      Kernel:2.6.22.6;编译器:arm-linux-gcc-3.4.5 目录: 编程 一、搭建...

weixin_30918633的博客 235

Android底层和中间层共同学习系列之android键盘映射

介绍 功能 键布局映射         键映射的选择         文件格式         键布局映射文件示例 键字符映射         键字符映射的选择         文件格式         资源二进制文件格式 完善您自己的驱动 示列 介绍:      本文主要描述了键输入怎么转换成Android应用层的行为信息和怎样自定义键的布局和键的字符映射表

B_zhang的专栏 3624

cc-haha 桌面端技能与技能市场:从挑选、安装到跨端共享一次讲清

cc-haha 是本地优先的 Claude Code / AI Agent 桌面工作台,它的技能市场聚合了 ClawHub 与 SkillHub 两个来源。本文带你走完完整动线:技能与 Agent 怎么分工、市场里怎么浏览筛选、安全徽标怎么读、安装前怎么审查、装完去哪里管理、如何用 `.agents/skills` 目录让多个客户端共用一套技能,最后附上 SKILL.md 的最小写法和加载机制,外

gitblog_01020的博客 447

i.MX535嵌入式SoC架构解析:Cortex-A8与硬件加速的黄金组合

片上系统(SoC)是现代嵌入式设备的核心,其设计精髓在于通过异构计算架构实现性能与功耗的平衡。其基本原理是在通用处理器核心之外,集成多个专用硬件加速单元,如视频处理单元(VPU)和图形处理单元(GPU),将高负载的计算任务从CPU卸载,从而在整体上实现更高的能效比。这种软硬协同的设计哲学,对于资源受限的移动和嵌入式设备具有极高的技术价值,它使得流畅的高清视频播放、复杂的图形界面渲染成为可能,广泛应用于平板电脑、智能显示器和工业控制等场景。本文以经典的飞思卡尔i.MX535应用处理器为例,深入剖析其基于ARM

weixin_33860553的博客 349
上一篇: Android中Linux睡眠唤醒流程
下一篇: LDD之input子系统 总结与流程图
huber
博客等级 码龄15年 79粉丝 24原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值