1、Sensor驱动概述:传感器在嵌入式系统中的作用、Linux驱动框架简介、Sensor子系统概览

大家好,我是你们的嵌入式驱动讲师。今天咱们开始《Sensor驱动移植与适配实战手册》的第一课。

说实话,传感器驱动这块,我做了快十年了。从最早的单片机裸机驱动,到后来在Linux上折腾各种奇葩传感器,踩过的坑真不少。今天这第一讲,咱们先把地基打牢。

1.1 传感器在嵌入式系统中的作用

传感器是什么?说白了,就是嵌入式系统的「五官」。

你想想看,一个嵌入式设备如果没有传感器,它就是个瞎子、聋子。手机不知道你横屏还是竖屏,汽车不知道外面温度多少,扫地机器人不知道前面有堵墙——这些都是传感器的功劳。

我个人习惯把传感器分成几大类:

  • 环境传感器:温度、湿度、气压、光照。我在做智能家居项目时,最常打交道的就是这些。
  • 运动传感器:加速度计、陀螺仪、磁力计。手机里的计步功能,就是靠加速度计实现的。
  • 距离传感器:超声波、红外、激光雷达。自动驾驶的核心传感器。
  • 生物传感器:心率、血氧、指纹。可穿戴设备的主力军。

我记得有一次,一个客户说他们的设备在高温下老是死机。排查了半天,发现是温度传感器读取的数据异常,导致系统误判。你看,一个小小的传感器,能影响整个系统的稳定性。

核心观点:传感器是嵌入式系统感知物理世界的桥梁。没有传感器,嵌入式系统就是「离线」的。

1.2 Linux驱动框架简介

好,接下来咱们聊聊Linux驱动框架。很多初学者一听到「驱动」两个字就头大,其实没那么可怕。

Linux驱动框架,本质上就是一套「约定」。它规定了驱动该怎么写、怎么注册、怎么和内核交互。

我刚开始学驱动时,总觉得Linux驱动好复杂。后来发现,其实就三个核心概念:

  1. 设备模型:总线、设备、驱动这三者怎么匹配。说白了就是「谁管谁」的问题。
  2. 文件操作接口:open、read、write、ioctl。用户程序通过这些接口和驱动打交道。
  3. 中断与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_initmodule_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子系统的具体实现,手把手教你写一个加速度计驱动。到时候我会分享一些我在项目中遇到的真实案例,保证让你少走弯路。

嗯,今天就到这里。有什么问题,欢迎在评论区留言。咱们下节课见!

课后思考:你手头的传感器,属于事件型还是数值型?应该用哪个子系统?想清楚了再动手写代码。