1、Sensor驱动概述:传感器在嵌入式系统中的作用、Linux驱动框架简介、Sensor子系统概览
大家好,我是你们的嵌入式驱动讲师。今天咱们开始《Sensor驱动移植与适配实战手册》的第一课。
说实话,传感器驱动这块,我做了快十年了。从最早的单片机裸机驱动,到后来在Linux上折腾各种奇葩传感器,踩过的坑真不少。今天这第一讲,咱们先把地基打牢。
1.1 传感器在嵌入式系统中的作用
传感器是什么?说白了,就是嵌入式系统的「五官」。
你想想看,一个嵌入式设备如果没有传感器,它就是个瞎子、聋子。手机不知道你横屏还是竖屏,汽车不知道外面温度多少,扫地机器人不知道前面有堵墙——这些都是传感器的功劳。
我个人习惯把传感器分成几大类:
- 环境传感器:温度、湿度、气压、光照。我在做智能家居项目时,最常打交道的就是这些。
- 运动传感器:加速度计、陀螺仪、磁力计。手机里的计步功能,就是靠加速度计实现的。
- 距离传感器:超声波、红外、激光雷达。自动驾驶的核心传感器。
- 生物传感器:心率、血氧、指纹。可穿戴设备的主力军。
我记得有一次,一个客户说他们的设备在高温下老是死机。排查了半天,发现是温度传感器读取的数据异常,导致系统误判。你看,一个小小的传感器,能影响整个系统的稳定性。
核心观点:传感器是嵌入式系统感知物理世界的桥梁。没有传感器,嵌入式系统就是「离线」的。
1.2 Linux驱动框架简介
好,接下来咱们聊聊Linux驱动框架。很多初学者一听到「驱动」两个字就头大,其实没那么可怕。
Linux驱动框架,本质上就是一套「约定」。它规定了驱动该怎么写、怎么注册、怎么和内核交互。
我刚开始学驱动时,总觉得Linux驱动好复杂。后来发现,其实就三个核心概念:
- 设备模型:总线、设备、驱动这三者怎么匹配。说白了就是「谁管谁」的问题。
- 文件操作接口:open、read、write、ioctl。用户程序通过这些接口和驱动打交道。
- 中断与DMA:怎么高效地处理数据。传感器数据通常需要实时处理,中断是首选。
来看一个最简单的字符设备驱动框架:
#include <linux/module.h>
#include <linux/fs.h>
static int __init sensor_drv_init(void)
{
printk("Sensor driver initialized\n");
return 0;
}
static void __exit sensor_drv_exit(void)
{
printk("Sensor driver exited\n");
}
module_init(sensor_drv_init);
module_exit(sensor_drv_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
嗯,这里要注意:module_init 和 module_exit 是驱动的入口和出口。没有它们,内核根本不知道你的驱动在哪。
个人经验:写驱动时,一定要先确认内核版本。不同版本的API可能有差异。我曾经在4.19内核上写的驱动,移植到5.10内核时,发现好几个函数都改名了。
1.3 Sensor子系统概览
Linux内核里,传感器驱动主要归两个子系统管:Input子系统 和 IIO子系统。
为什么要有子系统?你想想看,如果没有统一的管理,每个传感器驱动都自己搞一套接口,那用户程序得疯掉。子系统的作用就是「标准化」。
1.3.1 Input子系统
Input子系统,顾名思义,处理的是「输入」事件。比如按键、触摸屏、鼠标、还有——传感器。
哪些传感器走Input子系统?主要是那些产生「事件」的传感器:
- 加速度计(检测到运动时上报事件)
- 陀螺仪(旋转事件)
- 磁力计(方向变化事件)
- 接近传感器(物体靠近/远离事件)
Input子系统的核心数据结构是 input_dev。驱动需要注册这个结构体,然后上报事件。
struct input_dev *dev;
dev = input_allocate_device();
dev->name = "my_sensor";
dev->id.bustype = BUS_I2C;
set_bit(EV_ABS, dev->evbit);
set_bit(ABS_X, dev->absbit);
input_register_device(dev);
// 上报事件
input_report_abs(dev, ABS_X, value);
input_sync(dev);
我曾经在调试一个触摸板驱动时,发现事件上报后用户程序收不到。查了半天,原来是忘了调用 input_sync。这个函数的作用是「提交」事件,不调用的话,事件就卡在缓冲区里了。
避坑指南:Input子系统的事件上报,一定要记得调用 input_sync。我曾经因为这个bug,浪费了整整一个下午。
1.3.2 IIO子系统
IIO子系统,全称是 Industrial I/O,工业输入输出。它主要处理那些需要「读取数值」的传感器。
哪些传感器走IIO子系统?
- 温度传感器(读取温度值)
- 湿度传感器(读取湿度值)
- 气压传感器(读取气压值)
- 光照传感器(读取光照强度)
- ADC(模数转换器)
IIO子系统的优势在于:它提供了统一的sysfs接口。用户程序可以通过 /sys/bus/iio/devices/iio:deviceX/ 直接读取传感器数据。
来看一个IIO驱动的核心代码:
struct iio_dev *indio_dev;
indio_dev = iio_device_alloc(sizeof(struct sensor_data));
indio_dev->info = &sensor_info;
indio_dev->channels = sensor_channels;
indio_dev->num_channels = ARRAY_SIZE(sensor_channels);
iio_device_register(indio_dev);
IIO子系统的通道(channel)概念很重要。一个传感器可能有多个测量值,比如一个温湿度传感器,有温度通道和湿度通道。每个通道对应一个 iio_chan_spec 结构体。
| 特性 | Input子系统 | IIO子系统 |
|---|---|---|
| 适用场景 | 事件型传感器 | 数值型传感器 |
| 数据上报方式 | 事件驱动(中断) | 轮询或触发 |
| 用户接口 | /dev/input/eventX | /sys/bus/iio/ 或字符设备 |
| 典型传感器 | 加速度计、陀螺仪 | 温度、湿度、气压 |
选择建议:如果你的传感器需要「实时响应」用户操作,用Input子系统。如果只是「定期读取」数据,用IIO子系统更合适。
1.4 小结
好,第一讲的内容就到这里。咱们回顾一下:
- 传感器是嵌入式系统的「五官」,负责感知物理世界。
- Linux驱动框架提供了标准化的接口,让驱动开发和维护更简单。
- Input子系统适合事件型传感器,IIO子系统适合数值型传感器。
下一讲,咱们会深入Input子系统的具体实现,手把手教你写一个加速度计驱动。到时候我会分享一些我在项目中遇到的真实案例,保证让你少走弯路。
嗯,今天就到这里。有什么问题,欢迎在评论区留言。咱们下节课见!
课后思考:你手头的传感器,属于事件型还是数值型?应该用哪个子系统?想清楚了再动手写代码。