Kernel 6.1 中 MS5607 温度传感器驱动深度解析
嵌入式系统中,温度传感器是硬件监控的核心组件之一,MS5607作为一款高精度的压力/温度传感器,被广泛应用在工业控制、消费电子等场景。本文基于Linux Kernel 6.1版本,深入解析drivers/input/sensors/temperature/tmp_ms5607.c驱动代码的实现逻辑,带你搞懂传感器驱动的注册、初始化、数据采集与上报全流程,以及如何在系统中获取传感器的状态和数据。
一、MS5607传感器基础
MS5607是MEAS(TE Connectivity)推出的高精度数字压力传感器,集成温度传感功能,采用I2C接口通信,内置128位校准数据(PROM)。驱动通过读取校准数据补偿温度和压力的测量误差,最终输出-40~85℃范围内的高精度温度值,以及对应的压力值。
二、驱动代码核心结构解析
驱动代码基于LinuxI2C子系统和Input子系统实现,整体结构分为「驱动注册」「核心操作函数」「数据采集与上报」三大模块,下面逐一拆解。
2.1驱动注册:I2C驱动框架
Linux下I2C设备驱动遵循标准的I2C驱动框架,核心是i2c_driver结构体和设备ID表,这是驱动被内核识别的基础:
// 设备ID表:匹配I2C设备staticconststructi2c_device_id temperature_ms5607_id[] = { {"tmp_ms5607", TEMPERATURE_ID_MS5607}, {}};// I2C驱动核心结构体staticstructi2c_driver temperature_ms5607_driver = { .probe = temperature_ms5607_probe, // 设备匹配成功后执行 .remove= (void*)temperature_ms5607_remove,// 设备卸载时执行 .shutdown = sensor_shutdown, .id_table = temperature_ms5607_id, // 设备ID匹配表 .driver = { .name ="temperature_ms5607", #ifdef CONFIG_PM .pm = &sensor_pm_ops, // 电源管理 #endif },};// 简化I2C驱动注册/注销的宏module_i2c_driver(temperature_ms5607_driver);
•module_i2c_driver:底层调用i2c_add_driver完成驱动注册,无需手动写init/exit函数;
•probe函数:I2C设备匹配成功后触发,调用sensor_register_device完成传感器设备注册;
•remove函数:设备卸载时注销传感器设备,释放资源。
2.2核心操作函数:初始化与激活
驱动通过sensor_operate结构体封装传感器的核心操作(初始化、激活、数据上报),核心函数如下:
(1)sensor_init:传感器初始化
初始化的核心目标是将传感器置为「关闭状态」,为后续激活做准备:
staticintsensor_init(structi2c_client *client){ structsensor_private_data *sensor = (structsensor_private_data *) i2c_get_clientdata(client); intresult =0; // 先禁用传感器,确保初始状态为OFF result = sensor->ops->active(client,0,0); if(result) { printk("%s:line=%d,errorn",__func__,__LINE__); returnresult; } sensor->status_cur = SENSOR_OFF; // 标记当前状态为OFF g_ms5607_temp_status = sensor->status_cur;// 同步到全局状态变量 returnresult;}
(2)sensor_active:传感器激活与校准数据读取
激活是传感器从「OFF→ON」的关键步骤,核心是读取校准数据(PROM),这是温度补偿的基础:
staticintsensor_active(structi2c_client *client,intenable,intrate){ intresult =0; inti =0; charprom[16]; // 仅当「启用传感器」且「当前为OFF」时执行激活逻辑 if((enable)&&(g_ms5607_pr_status == SENSOR_OFF)) { // 1. 发送复位指令,重置传感器 result =sensor_write_reg_normal(client, CMD_RESET); if(result) printk("%s:line=%d,errorn",__func__,__LINE__); // 2. 读取128位校准数据(8组,每组2字节) memset(prom,0,16); for(i=0; i<8; i++) { prom[i*2]= CMD_PROM_RD + i*2; // 校准数据读取指令 result = sensor_rx_data(client, &prom[i*2], 2); if(result) return result; } // 3. 校准数据转存到全局数组C,供后续温度计算使用 for (i=0;i<8;i++) { C[i] = prom[2*i] << 8 | prom[2*i + 1]; } } g_ms5607_temp_status = enable; // 更新全局激活状态 return result;}
关键说明:MS5607的校准数据(PROM)是出厂时写入的,包含8组补偿参数,决定了温度测量的精度,必须在激活阶段读取并保存。
2.3数据采集与上报:温度计算+Input子系统
sensor_report_value是驱动的核心数据处理函数,负责「触发AD转换→读取数据→温度补偿→上报数据」全流程:
staticintsensor_report_value(structi2c_client *client){ // 省略变量定义... if(g_ms5607_pr_status == SENSOR_OFF) { // 1. 触发压力(D1)AD转换(4096倍过采样,平衡精度/速度) sensor_write_reg_normal(client, CMD_ADC_CONV+CMD_ADC_D1+CMD_ADC_4096); msleep(10);// 等待转换完成 result = sensor_rx_data(client, &buffer[0],3); D1 = (buffer[0] << 16) | (buffer[1] << 8) | buffer[2]; // 2. 触发温度(D2)AD转换 sensor_write_reg_normal(client, CMD_ADC_CONV + CMD_ADC_D2 + CMD_ADC_4096); msleep(10); result = sensor_rx_data(client, &buffer[0], 3); D2 = (buffer[0] << 16) | (buffer[1] << 8) | buffer[2]; // 3. 基础温度计算(补偿算法) dT = D2 - ((unsigned int)C[5] << 8); g_ms5607_temp = (int)(2000 + ((long long)dT * C[6] >>23)); // 4. 二阶补偿(低温场景<20℃,提升精度) if (g_ms5607_temp < 2000) { int tmp = (g_ms5607_temp - 2000) * (g_ms5607_temp - 2000); T2 = (int)((long long)(dT * dT) >>31); OFF2 = (((longlong)tmp *61)*((longlong)tmp *61)) >>4; SENS2 = (longlong)((tmp*tmp) << 1);
if (g_ms5607_temp < -1500) { // 超低温额外补偿 tmp = (g_ms5607_temp + 1500) * (g_ms5607_temp + 1500); OFF2 += 15 * tmp; SENS2 += 8 * tmp; } g_ms5607_temp -= T2; // 最终温度值 } // 5. 通过Input子系统上报温度数据 temperature_report_value(sensor->input_dev, g_ms5607_temp); } // 省略其他逻辑... returnresult;}
核心细节:
•AD过采样配置:代码中使用CMD_ADC_4096(4096倍过采样),也可切换为256/512/1024倍,过采样率越高,精度越高但功耗/耗时越大;
•温度单位:g_ms5607_temp的单位是0.01℃(如2500代表25.00℃);
•Input上报:通过input_report_abs上报ABS_THROTTLE类型数据,input_sync同步数据,用户态可通过Input节点读取。
三、驱动在系统中的体现
MS5607驱动依托Linux内核子系统实现,在系统中的体现可分为「驱动加载」「设备注册」「数据流转」三个层面,以下是核心流程可视化:
3.1驱动加载流程图

3.2系统层面的关键体现
1.驱动加载状态:
○查看已加载的I2C驱动:
cat /sys/bus/i2c/drivers/temperature_ms5607/;
○查看驱动日志:dmesg | grep tmp_ms5607,可排查初始化/激活错误。
2.Input子系统节点:
驱动通过Input子系统上报数据,系统会生成/dev/input/eventX节点(X为设备号),可通过以下命令查看设备信息:
cat/proc/bus/input/devices | grep -A 5 tmp_ms5607
3.全局状态变量:
驱动通过g_ms5607_temp_status(激活状态:0=OFF/1=ON)、g_ms5607_temp(温度值)维护核心状态,内核态可直接访问。
四、如何获取传感器状态与数据
4.1内核态获取
1.调试打印:开启CONFIG_PR_MS5607后,驱动会打印温度/压力值,通过dmesg查看:
dmesg| grep sensor_report_value# 输出示例:sensor_report_value:pressure=101325,temperature=2500
2.全局变量引用:其他内核模块可通过extern引用全局变量:
externintg_ms5607_temp; // 温度值(0.01℃)externintg_ms5607_temp_status;// 激活状态printk("MS5607 Temp: %.2f℃, Status: %dn", g_ms5607_temp/100.0, g_ms5607_temp_status);
4.2用户态获取
方式1:读取Input事件(原生方式)
编写简单的C程序读取/dev/input/eventX节点:
#include#include #include intmain(){ intfd =open("/dev/input/eventX", O_RDONLY);// 替换为实际的eventX structinput_eventev; while(read(fd, &ev,sizeof(ev)) >0) { // 过滤温度事件(ABS_THROTTLE类型) if(ev.type == EV_ABS && ev.code == ABS_THROTTLE) { printf("MS5607 Temperature: %.2f℃n", ev.value /100.0); } } close(fd); return0;}
方式2:扩展sysfs节点
原驱动未暴露sysfs节点,可扩展代码添加,简化用户态读取:
// 1. 定义sysfs属性读取函数staticssize_ttemp_show(structdevice *dev,structdevice_attribute *attr,char*buf){ returnsprintf(buf,"%.2fn", g_ms5607_temp /100.0);}staticDEVICE_ATTR(temp, S_IRUGO, temp_show,NULL);// 只读权限// 2. 在probe函数中创建sysfs节点staticinttemperature_ms5607_probe(structi2c_client *client,conststructi2c_device_id *devid){ intret =sensor_register_device(client,NULL, devid, &temperature_ms5607_ops); if(!ret) { device_create_file(&client->dev, &dev_attr_temp);// 创建temp节点 } returnret;}
添加后,用户态可直接读取:
cat/sys/bus/i2c/devices/0-0048/temp# 替换为实际的I2C设备地址# 输出示例:25.00
五、核心流程脑图
六、总结与拓展
MS5607驱动是典型的「I2C设备+Input子系统」传感器驱动实现,核心设计思路可总结为:
1.遵循内核框架:基于I2C驱动框架完成设备匹配,基于Input子系统完成数据上报;
2.校准是核心:必须读取PROM校准数据,才能通过补偿算法得到高精度温度;
3.分层设计:将初始化、激活、上报封装为独立函数,符合Linux驱动的模块化思想。
拓展优化方向
•内核版本适配:Kernel 6.1后I2C/Input子系统接口可能微调,需验证兼容性;
•功耗优化:结合PM子系统,在休眠时关闭传感器AD转换,降低功耗;
•功能扩展:添加阈值中断、sysfs节点配置采样率等功能;
•精度调优:根据场景切换AD过采样率(如低功耗场景用256倍)。
审核编辑 黄宇
