当前位置:首页 > 工业控制 > 工业控制
[导读]自定义传感器接在板端寄存器或I2C/SPI上,最简洁的给用户层“直接访问”方式就是写成字符设备:/dev下出节点,app用open/read拿数据,用ioctl切通道、设采样率。相比sysfs只适合小参数,字符设备可带内核缓冲、阻塞等待、批量帧和高频mmap,更适合工业采样。


自定义传感器接在板端寄存器或I2C/SPI上,最简洁的给用户层“直接访问”方式就是写成字符设备:/dev下出节点,app用open/read拿数据,用ioctl切通道、设采样率。相比sysfs只适合小参数,字符设备可带内核缓冲、阻塞等待、批量帧和高频mmap,更适合工业采样。

一、驱动框架与设备号

用cdev注册,动态申请主设备号,自动建节点:

#include <linux/module.h>

#include <linux/cdev.h>

#include <linux/fs.h>

#include <linux/uaccess.h>

#include <linux/slab.h>


#define SENS_MAX  16

struct sens_dev {

   struct cdev cdev;

   dev_t devno;

   struct class *cls;

   u8  chan;            /* 当前通道 */

   u8  en;              /* 采样使能 */

   u16 raw[SENS_MAX];   /* 各通道最新值 */

   wait_queue_head_t wq;

   atomic_t dirty;

};

static struct sens_dev *g;


static ssize_t sens_read(struct file *fp, char __user *buf, size_t len, loff_t *off)

{

   struct sens_dev *s = fp->private_data;

   u16 frame[SENS_MAX+1];

   if (len < sizeof(frame)) return -EINVAL;

   wait_event_interruptible(s->wq, atomic_read(&s->dirty) || s->en==0);

   if (signal_pending(current)) return -ERESTARTSYS;

   frame[0] = s->chan;

   memcpy(&frame[1], s->raw, sizeof(s->raw));

   if (copy_to_user(buf, frame, sizeof(frame))) return -EFAULT;

   atomic_set(&s->dirty, 0);

   return sizeof(frame);

}


static long sens_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)

{

   struct sens_dev *s = fp->private_data;

   switch (cmd) {

   case 0x10: /* 设通道 */ if (arg>=SENS_MAX) return -EINVAL; s->chan=arg; break;

   case 0x11: /* 使能/停止 */ s->en=!!arg; break;

   default: return -ENOTTY;

   }

   return 0;

}


static int sens_open(struct inode *in, struct file *fp)

{

   fp->private_data = container_of(in->i_cdev, struct sens_dev, cdev);

   return 0;

}

static const struct file_operations fops = {

   .owner = THIS_MODULE, .open = sens_open,

   .read = sens_read, .unlocked_ioctl = sens_ioctl,

};

init里申请号、挂cdev、建class/device:

static int __init sens_init(void)

{

   g = kzalloc(sizeof(*g), GFP_KERNEL);

   init_waitqueue_head(&g->wq);

   atomic_set(&g->dirty, 0);

   alloc_chrdev_region(&g->devno, 0, 1, "my_sens");

   cdev_init(&g->cdev, &fops);

   cdev_add(&g->cdev, g->devno, 1);

   g->cls = class_create("my_sens_cls");

   device_create(g->cls, NULL, g->devno, NULL, "my_sens");

   return 0;

}

static void __exit sens_exit(void)

{

   device_destroy(g->cls, g->devno);

   class_destroy(g->cls);

   cdev_del(&g->cdev);

   unregister_chrdev_region(g->devno, 1);

   kfree(g);

}

module_init(sens_init); module_exit(sens_exit);

MODULE_LICENSE("GPL");

真实硬件把raw[]更新放在定时器或IRQ里:定时器每周期读ADC/I2C,写完atomic_set(&g->dirty,1); wake_up(&g->wq);。寄存器型传感器用ioremap映射基址,read/write寄存器替代kzalloc变量;I2C传感器用i2c_transfer或在probe走i2c_client,但字符设备接口不变。

二、设备树对接(平台传感器)

若传感器是本地IP或挂在平台总线,dts加节点:

my_sens@40001000 {

   compatible = "myco,my-sens";

   reg = <0x40001000 0x1000>;

   clocks = <&clk_peri>;

   status = "okay";

};

驱动用platform_driver,probe里platform_get_resource+devm_ioremap_resource取基址,of_match_table填"myco,my-sens";字符设备部分同上,只是把cdev挂到platform设备。这样硬件地址不写死在C里,换板只改dts。

三、用户层直接读

#include <fcntl.h>

#include <unistd.h>

#include <stdio.h>

#include <sys/ioctl.h>

int main(void){

   int fd=open("/dev/my_sens",O_RDWR);

   unsigned short buf[17];

   ioctl(fd,0x10,0);   /* 选通道0 */

   ioctl(fd,0x11,1);   /* 开始采样 */

   while(1){

       read(fd,buf,sizeof(buf));

       printf("chan=%u raw0=%u raw1=%u\n",buf[0],buf[1],buf[2]);

       usleep(100000);

   }

   close(fd);

}

read返回“通道号+全通道最新值”,用户态零解析硬件;只关心单通道时驱动可改成只拷贝raw[chan]。

四、高频场景补mmap

每秒万次级不想反复系统调用,可加mmap把内核环形采样区映射到用户空间:

static int sens_mmap(struct file *fp, struct vm_area_struct *vma)

{

   unsigned long pfn = virt_to_phys(g->raw) >> PAGE_SHIFT;

   return remap_pfn_range(vma, vma->vm_start, pfn,

                          vma->vm_end-vma->vm_start, vma->vm_page_prot);

}

fops增加.mmap=sens_mmap,用户mmap后直接读共享区,驱动只负责更新raw和写序号。注意raw区要按页对齐、不swap,实时系统配合mlockall效果更好。

五、调试与避坑

加载后cat /proc/devices看主设备号、ls /dev/my_sens确认节点;没有节点先查class_create/device_create返回值。

read卡住查waitqueue:硬件更新没wake_up,或en没置1,app会一直睡。

-



本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除( 邮箱:macysun@21ic.com )。
关闭