恒美微站 Logo 恒美微站
  • 首页
  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心
  • 联系我们

Linux 驱动实战:ADXL345 驱动

  • 首页
  • 资讯中心
  • /
  • Linux 驱动实战:ADXL345 驱动

相关资讯

Flutter-Notebook 实战:用 Overlay 优雅实现屏幕叠加与浮动控件 2026/10/15 3:35:11
WorkBuddy_AI Harness架构解析7_上下文管理器 2026/10/15 3:35:11
AI 显存分配器深度解析:从 cudaMalloc 系统开销、PyTorch Caching Allocator 块分裂合并到流序复用与 Expandable Segments 碎片治理 2026/10/15 3:35:11

最新资讯

Redis事务机制详解:MULTI/EXEC/WATCH与回滚误区
AI深度思考过程导出完整指南:从页面操作到API脚本
S7-1200 PUT/GET通信全解析:从组态到避坑的实战指南
M580与ATV930通过Modbus TCP实现以太网通讯实验
R语言算术运算在医学数据分析中的核心应用与实战技巧
闪学it-领跑-AI变现实战课:自媒体+AI漫剧+短视频+智能体全流程创作(国学养生/电商带货/口播/数字人)

今日推荐

AI视频生成的时间一致性难题与时空建模演进
基于bbox的PDF文档解析:多栏重排与水印过滤实战
探矿数据进RAG前的清洗全攻略:TXT、Word、PDF、网页四类文档处理实践

本周热门

UE动画修改实战:从资产编辑到重定向与蒙太奇驱动
统计随机数生成器攻击下的KLJN安全密钥交换协议Matlab仿真
政务API安全治理:资产测绘、低代码编排与行标对标实践

本月精选

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证
2026 大模型集体涨价:用 Python 做企业 Token 成本测算与选型避坑(附配置)

Linux 驱动实战:ADXL345 驱动

发布时间:2026/10/15 3:35:11
Linux 驱动实战:ADXL345 驱动 Linux 驱动实战从 SPI 到 ioctl、mmap完整 ADXL345 驱动最近整理嵌入式 Linux 驱动时把SPI 驱动、内核模块、字符设备、ioctl、mmap串成了一个完整例子用 SPI 读取 ADXL345 三轴加速度数据并通过字符设备提供read、ioctl和mmap接口。一、ADXL345 与 SPI 接口ADXL345 是一款三轴加速度传感器支持 SPI 和 I2C 接口量程可配置为 ±2g / ±4g / ±8g / ±16g。SPI 通信时常用两种方式spi_write_then_read适合简单的寄存器读写spi_messagespi_transfer适合多字节、复杂传输。读取三轴数据时通常从DATAX0寄存器0x32开始连续读取 6 字节tx[0] 0x32 | 0x80 | 0x40; /* 读操作 多字节 */其中0x80读标志0x40多字节连续读0x32起始寄存器地址。二、内核模块与驱动注册Linux 驱动通常以内核模块形式存在。最基础写法是module_init(hello_init); module_exit(hello_exit); MODULE_LICENSE(GPL);如果驱动是平台设备驱动可以用module_platform_driver(my_driver);如果是 SPI 设备驱动更常见的是module_spi_driver(my_spi_driver);module_platform_driver和module_spi_driver都是封装宏内部仍然基于module_init/module_exit只是自动完成了platform_driver_register或spi_driver注册。三、字符设备接口字符设备驱动通过file_operations向用户空间暴露接口。常见映射关系用户空间内核回调open().openread().readioctl().unlocked_ioctlmmap().mmapclose().releaseread适合返回数据流比如三轴加速度原始数据ioctl适合发送控制命令比如设置量程、复位、读取器件 ID。四、ioctl 控制命令ioctl 命令码通常使用linux/ioctl.h中的宏定义#define ADXL345_IOC_MAGIC A #define ADXL345_IOC_RESET _IO(ADXL345_IOC_MAGIC, 0) #define ADXL345_IOC_SET_RANGE _IOW(ADXL345_IOC_MAGIC, 1, unsigned char) #define ADXL345_IOC_GET_ID _IOR(ADXL345_IOC_MAGIC, 2, unsigned char)方向从用户空间视角看_IO无参数_IOW用户向内核写_IOR内核向用户读_IOWR双向。驱动实现示例static long adxl345_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { struct adxl345_dev *dev filp-private_data; switch (cmd) { case ADXL345_IOC_GET_ID: if (copy_to_user((unsigned char __user *)arg, dev-id, sizeof(dev-id))) return -EFAULT; break; case ADXL345_IOC_SET_RANGE: /* 从用户空间获取量程并写入寄存器 */ break; case ADXL345_IOC_RESET: /* 复位或重新初始化传感器 */ break; default: return -ENOTTY; } return 0; }五、mmap 内存映射mmap可以把内核物理内存映射到用户空间避免每次读写都走copy_to_user/copy_from_user。驱动中实现.mmap时通常步骤是分配物理连续内存计算物理页帧号 PFN设置 VMA 属性调用remap_pfn_range建立映射。示例static int adxl345_mmap(struct file *filp, struct vm_area_struct *vma) { struct adxl345_dev *dev filp-private_data; unsigned long size vma-vm_end - vma-vm_start; unsigned long pfn; if (size dev-buf_size) return -EINVAL; pfn virt_to_phys(dev-sample_buf) PAGE_SHIFT; vma-vm_flags | VM_IO | VM_DONTEXPAND | VM_DONTDUMP; vma-vm_page_prot pgprot_writecombine(vma-vm_page_prot); return remap_pfn_range(vma, vma-vm_start, pfn, size, vma-vm_page_prot); }如果是 DMA 缓冲区PFN 计算要改为使用 DMA 物理地址不能简单依赖virt_to_phys。六、完整驱动骨架#include linux/module.h #include linux/init.h #include linux/spi/spi.h #include linux/cdev.h #include linux/fs.h #include linux/uaccess.h #include linux/slab.h #include linux/mutex.h #include linux/delay.h #include linux/ioctl.h #include linux/platform_device.h #include linux/mm.h #define ADXL345_REG_DEVID 0x00 #define ADXL345_REG_POWER_CTL 0x2D #define ADXL345_REG_DATAX0 0x32 #define ADXL345_IOC_MAGIC A #define ADXL345_IOC_RESET _IO(ADXL345_IOC_MAGIC, 0) #define ADXL345_IOC_SET_RANGE _IOW(ADXL345_IOC_MAGIC, 1, unsigned char) #define ADXL345_IOC_GET_ID _IOR(ADXL345_IOC_MAGIC, 2, unsigned char) #define ADXL345_SAMPLE_NUM 64 struct adxl345_dev { struct spi_device *spi; struct cdev cdev; dev_t devno; struct mutex lock; unsigned char id; unsigned char range; void *sample_buf; unsigned int buf_size; }; static struct adxl345_dev *g_adxl; static int adxl345_spi_read_regs(struct spi_device *spi, unsigned char reg, unsigned char *buf, unsigned int len) { unsigned char tx[7] {0}; unsigned char rx[7] {0}; struct spi_message msg; struct spi_transfer xfer; if (len 6) return -EINVAL; tx[0] reg | 0x80 | 0x40; memset(xfer, 0, sizeof(xfer)); xfer.tx_buf tx; xfer.rx_buf rx; xfer.len len 1; xfer.delay_usecs 20; spi_message_init(msg); spi_message_add_tail(xfer, msg); return spi_sync(spi, msg) ? : (memcpy(buf, rx 1, len), 0); } static int adxl345_spi_write_reg(struct spi_device *spi, unsigned char reg, unsigned char val) { unsigned char tx[2] { reg, val }; return spi_write(spi, tx, sizeof(tx)); } static int adxl345_read_xyz(struct adxl345_dev *dev, short *x, short *y, short *z) { unsigned char buf[6]; int ret; ret adxl345_spi_read_regs(dev-spi, ADXL345_REG_DATAX0, buf, 6); if (ret) return ret; *x (short)((buf[1] 8) | buf[0]); *y (short)((buf[3] 8) | buf[2]); *z (short)((buf[5] 8) | buf[4]); return 0; } static int adxl345_open(struct inode *inode, struct file *filp) { filp-private_data g_adxl; return 0; } static ssize_t adxl345_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos) { struct adxl345_dev *dev filp-private_data; short xyz[3]; int ret; if (count sizeof(xyz)) return -EINVAL; mutex_lock(dev-lock); ret adxl345_read_xyz(dev, xyz[0], xyz[1], xyz[2]); mutex_unlock(dev-lock); if (ret) return ret; if (copy_to_user(buf, xyz, sizeof(xyz))) return -EFAULT; return sizeof(xyz); } static long adxl345_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { struct adxl345_dev *dev filp-private_data; switch (cmd) { case ADXL345_IOC_RESET: mutex_lock(dev-lock); adxl345_spi_write_reg(dev-spi, ADXL345_REG_POWER_CTL, 0x00); msleep(10); adxl345_spi_write_reg(dev-spi, ADXL345_REG_POWER_CTL, 0x08); mutex_unlock(dev-lock); break; case ADXL345_IOC_SET_RANGE: { unsigned char range; if (copy_from_user(range, (unsigned char __user *)arg, sizeof(range))) return -EFAULT; mutex_lock(dev-lock); dev-range range; adxl345_spi_write_reg(dev-spi, 0x31, range); mutex_unlock(dev-lock); break; } case ADXL345_IOC_GET_ID: mutex_lock(dev-lock); if (copy_to_user((unsigned char __user *)arg, dev-id, sizeof(dev-id))) return -EFAULT; mutex_unlock(dev-lock); break; default: return -ENOTTY; } return 0; } static int adxl345_mmap(struct file *filp, struct vm_area_struct *vma) { struct adxl345_dev *dev filp-private_data; unsigned long size vma-vm_end - vma-vm_start; unsigned long pfn; if (size dev-buf_size) return -EINVAL; pfn virt_to_phys(dev-sample_buf) PAGE_SHIFT; vma-vm_flags | VM_IO | VM_DONTEXPAND | VM_DONTDUMP; vma-vm_page_prot pgprot_writecombine(vma-vm_page_prot); return remap_pfn_range(vma, vma-vm_start, pfn, size, vma-vm_page_prot); } static const struct file_operations adxl345_fops { .owner THIS_MODULE, .open adxl345_open, .read adxl345_read, .unlocked_ioctl adxl345_ioctl, .mmap adxl345_mmap, }; static int adxl345_probe(struct platform_device *pdev) { struct spi_device *spi; struct adxl345_dev *dev; int ret; spi spi_alloc_dummy(); if (!spi) return -ENOMEM; dev kzalloc(sizeof(*dev), GFP_KERNEL); if (!dev) { spi_unregister_device(spi); return -ENOMEM; } dev-spi spi; mutex_init(dev-lock); dev-buf_size ADXL345_SAMPLE_NUM * sizeof(short) * 3; dev-sample_buf kzalloc(dev-buf_size, GFP_KERNEL | GFP_DMA); if (!dev-sample_buf) { ret -ENOMEM; goto err_free_dev; } ret alloc_chrdev_region(dev-devno, 0, 1, adxl345); if (ret) goto err_free_buf; cdev_init(dev-cdev, adxl345_fops); dev-cdev.owner THIS_MODULE; ret cdev_add(dev-cdev, dev-devno, 1); if (ret) goto err_unregister_chrdev; adxl345_spi_write_reg(spi, ADXL345_REG_POWER_CTL, 0x08); adxl345_spi_read_regs(spi, ADXL345_REG_DEVID, dev-id, 1); pr_info(adxl345 id 0x%02x\n, dev-id); g_adxl dev; platform_set_drvdata(pdev, dev); return 0; err_unregister_chrdev: unregister_chrdev_region(dev-devno, 1); err_free_buf: kfree(dev-sample_buf); err_free_dev: kfree(dev); spi_unregister_device(spi); return ret; } static int adxl345_remove(struct platform_device *pdev) { struct adxl345_dev *dev platform_get_drvdata(pdev); if (!dev) return 0; cdev_del(dev-cdev); unregister_chrdev_region(dev-devno, 1); kfree(dev-sample_buf); kfree(dev); g_adxl NULL; return 0; } static const struct of_device_id adxl345_of_match[] { { .compatible adi,adxl345-spi }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, adxl345_of_match); static struct platform_driver adxl345_driver { .probe adxl345_probe, .remove adxl345_remove, .driver { .name adxl345-spi, .of_match_table adxl345_of_match, }, }; module_platform_driver(adxl345_driver); MODULE_LICENSE(GPL);七、用户空间测试#include stdio.h #include fcntl.h #include unistd.h #include sys/ioctl.h #include sys/mman.h #define ADXL345_IOC_MAGIC A #define ADXL345_IOC_RESET _IO(ADXL345_IOC_MAGIC, 0) #define ADXL345_IOC_SET_RANGE _IOW(ADXL345_IOC_MAGIC, 1, unsigned char) #define ADXL345_IOC_GET_ID _IOR(ADXL345_IOC_MAGIC, 2, unsigned char) #define SAMPLE_NUM 64 int main(void) { int fd open(/dev/adxl345, O_RDWR); if (fd 0) { perror(open); return -1; } unsigned char id; ioctl(fd, ADXL345_IOC_GET_ID, id); printf(ADXL345 ID: 0x%02x\n, id); unsigned char range 0x0B; ioctl(fd, ADXL345_IOC_SET_RANGE, range); short xyz[3]; if (read(fd, xyz, sizeof(xyz)) sizeof(xyz)) printf(x%d y%d z%d\n, xyz[0], xyz[1], xyz[2]); size_t size SAMPLE_NUM * sizeof(short) * 3; short *buf mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); if (buf ! MAP_FAILED) { printf(mmap addr %p\n, buf); munmap(buf, size); } close(fd); return 0; }八、注意事项上板时spi_device通常来自设备树和 SPI 控制器匹配示例里的spi_alloc_dummy只是骨架用法。mmap如果后续接 DMA 缓冲区PFN 需要用 DMA 物理地址计算。read接口目前只演示单次读取实际采样可以放到工作队列、hrtimer 或内核线程里周期性填充缓冲区。设备节点可以通过class_create/device_create自动生成也可以手动mknod。寄存器地址、量程配置、SPI 模式需要以实际 ADXL345 规格书和硬件连接为准。

关于恒美微站

恒美微站专注于为个体商户、工作室提供极简自助建站服务,让每个人都能轻松拥有专业网站。

快速链接

  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心

服务项目

  • 可视化建站
  • 拖拽编辑
  • 主题定制
  • SEO 优化
  • 网站托管

联系方式

  • 📍 地址:北京市朝阳区建国路 88 号
  • 📞 电话:400-888-8888
  • ✉️ 邮箱:info@hmyw.cn
  • 🕐 时间:周一至周日 9:00-18:00

© 2024 恒美微站 hmyw.cn 版权所有 | 京 ICP 备 12345678 号