4. 加速度计HAL驱动封装
任务目标
本案例完成加速度计从Linux内核驱动到Android HAL层的完整封装链路:编写一个可加载的内核驱动模块,提供原始数据读取接口;实现HAL层模块,通过sysfs节点读取驱动输出的原始值,并转换为标准单位(m/s²)。最终通过HAL层测试程序验证数据读取与单位转换的正确性。
环境与依赖
- Linux内核源码树(已配置交叉编译环境),目标平台为ARM64 Android设备
- Android NDK(用于编译HAL测试程序)
- 内核驱动依赖:
linux/i2c.h、linux/miscdevice.h、linux/uaccess.h - HAL层依赖:
hardware/libhardware/include/hardware/(Android系统源码环境)
内核驱动实现
驱动模拟I2C加速度计(如ADXL345),注册misc设备并提供sysfs属性节点。驱动内部维护一个可写的原始值寄存器,便于测试时注入数据。
// accel_drv.c — 加速度计内核驱动(模拟I2C设备)
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/sysfs.h>
#include <linux/platform_device.h>
#define ACCEL_DRV_NAME "accel_dev"
#define ACCEL_RANGE (4 * 9.80665f) /* ±4g 量程 */
static int raw_x = 100, raw_y = -50, raw_z = 900; /* 可注入的原始值 */
static ssize_t raw_show(struct device *dev, struct device_attribute *attr, char *buf)
{
return sprintf(buf, "%d %d %d\n", raw_x, raw_y, raw_z);
}
static ssize_t raw_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
sscanf(buf, "%d %d %d", &raw_x, &raw_y, &raw_z);
return count;
}
static DEVICE_ATTR(raw, 0664, raw_show, raw_store);
static struct attribute *accel_attrs[] = {
&dev_attr_raw.attr,
NULL,
};
ATTRIBUTE_GROUPS(accel_attrs);
static struct miscdevice accel_miscdev = {
.minor = MISC_DYNAMIC_MINOR,
.name = ACCEL_DRV_NAME,
.groups = accel_groups,
};
static int accel_probe(struct platform_device *pdev)
{
return misc_register(&accel_miscdev);
}
static int accel_remove(struct platform_device *pdev)
{
misc_deregister(&accel_miscdev);
return 0;
}
static struct platform_driver accel_driver = {
.probe = accel_probe,
.remove = accel_remove,
.driver = {
.name = ACCEL_DRV_NAME,
},
};
module_platform_driver(accel_driver);
MODULE_LICENSE("GPL");
驱动加载后生成设备节点/dev/accel_dev及sysfs属性/sys/devices/platform/accel_dev/raw。写入"x y z"可注入原始值,读取返回当前原始值。
HAL模块实现
HAL模块实现sensors_module_t标准接口,提供加速度计传感器句柄。核心逻辑为读取sysfs节点、解析原始值、按量程转换为m/s²。
// accel_hal.c — 加速度计HAL模块
#include <hardware/sensors.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#define SYSFS_RAW_PATH "/sys/devices/platform/accel_dev/raw"
#define ACCEL_RANGE (4 * 9.80665f)
#define ACCEL_RESOLUTION 1024.0f /* 假设12位ADC,中心点512 */
static int read_raw_values(int *x, int *y, int *z)
{
FILE *fp = fopen(SYSFS_RAW_PATH, "r");
if (!fp) return -errno;
int ret = fscanf(fp, "%d %d %d", x, y, z);
fclose(fp);
return (ret == 3) ? 0 : -EIO;
}
static float convert_to_si(int raw)
{
/* 原始值偏移中心点,按比例映射到量程 */
return ((raw - 512) / ACCEL_RESOLUTION) * ACCEL_RANGE;
}
static int accel_get_sensors_list(struct sensors_module_t *module,
struct sensor_t const **list)
{
static struct sensor_t accel_sensor = {
.name = "MMA8452Q Accelerometer",
.vendor = "Example Corp",
.version = 1,
.handle = SENSORS_HANDLE_BASE,
.type = SENSOR_TYPE_ACCELEROMETER,
.maxRange = ACCEL_RANGE,
.resolution = ACCEL_RANGE / ACCEL_RESOLUTION,
.power = 0.2f,
.minDelay = 10000,
};
*list = &accel_sensor;
return 1;
}
static int accel_open(const struct hw_module_t *module, const char *id,
struct hw_device_t **device)
{
(void)module; (void)id;
/* 实际工程中在此分配sensors_poll_device_t并实现data_open/data_close */
return 0;
}
static struct hw_module_methods_t accel_methods = {
.open = accel_open,
};
struct sensors_module_t HAL_MODULE_INFO_SYM = {
.common = {
.tag = HARDWARE_MODULE_TAG,
.module_api_version = SENSORS_MODULE_API_VERSION_1_0,
.hal_api_version = HARDWARE_HAL_API_VERSION,
.id = SENSORS_HARDWARE_MODULE_ID,
.name = "Accelerometer HAL Module",
.author = "Example",
.methods = &accel_methods,
},
.get_sensors_list = accel_get_sensors_list,
};
HAL测试程序
// test_accel_hal.c — 验证HAL层读取与转换
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#define SYSFS_RAW_PATH "/sys/devices/platform/accel_dev/raw"
int main(void)
{
int fd = open(SYSFS_RAW_PATH, O_WRONLY);
if (fd < 0) { perror("open sysfs"); return 1; }
/* 注入已知原始值:中心点512 + 1g对应102.4 LSB/g */
write(fd, "614 512 512\n", 11); /* X轴 +1g */
close(fd);
FILE *fp = fopen(SYSFS_RAW_PATH, "r");
int x, y, z;
fscanf(fp, "%d %d %d", &x, &y, &z);
fclose(fp);
float range = 4 * 9.80665f;
float res = 1024.0f;
float ax = (x - 512) / res * range;
float ay = (y - 512) / res * range;
float az = (z - 512) / res * range;
printf("raw: %d %d %d\n", x, y, z);
printf("accel (m/s^2): %.2f %.2f %.2f\n", ax, ay, az);
return 0;
}
编译与运行
# 1. 编译内核驱动(在Android内核源码树中)
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-android- M=$(pwd)/drivers/misc modules
# 2. 加载驱动并注入测试数据
adb root
adb push accel_drv.ko /data/local/tmp/
adb shell insmod /data/local/tmp/accel_drv.ko
adb shell "echo '614 512 512' > /sys/devices/platform/accel_dev/raw"
# 3. 编译HAL测试程序(使用NDK)
aarch64-linux-android-gcc -o test_accel_hal test_accel_hal.c
adb push test_accel_hal /data/local/tmp/
adb shell /data/local/tmp/test_accel_hal
验证方法
测试程序注入原始值614 512 512(X轴偏移102 LSB,对应+1g),预期输出:
raw: 614 512 512
accel (m/s^2): 9.81 0.00 0.00
判定通过条件:X轴加速度值在9.7~9.9 m/s²范围内(考虑浮点精度),Y/Z轴接近0。若输出与预期不符,检查sysfs路径是否正确、驱动是否加载成功。
常见问题
- insmod失败:确认内核版本与驱动编译环境一致,检查
dmesg查看具体错误。 - sysfs节点不存在:确认驱动probe成功,执行
ls /sys/devices/platform/accel_dev/查看属性。 - 数值偏差:检查量程与分辨率宏定义是否与驱动实际配置一致。