资讯详情 《嵌入式操作系统》_驱动开发_misc类设备与蜂鸣器_20261008
📅 2026/10/9 5:54:44
misc 类设备杂项设备 / Miscellaneous DeviceLinux 把所有无法归入标准大类、功能简单、零散小众的硬件统一收纳到 misc 框架下共用固定主设备号10MISC_MAJOR靠不同次设备号区分每个独立设备。适用场景蜂鸣器、看门狗、按键、LED、温度传感器、实时时钟、调试串口、虚拟控制节点等简单外设。[rootLXY210 misc]# ls CEC binder log_system s3c-jpg HPD cpu_dma_latency network_latency s3c-mfc adc device-mapper network_throughput sec-g2d alarm log_events pmem_gpu1 apm_bios log_main psaux ashmem log_radio rfkill[rootLXY210 dev]# ls buzzer -l crw-rw---- 1 0 0 10, 61 Jan 1 13:18 buzzer [rootLXY210 dev]# ls adc -l crw-rw---- 1 0 0 10, 131 Jan 1 13:18 adc核心底层原理统一主设备号 10内核提前注册主设备号 10 作为 misc 总入口所有 misc 设备共享该主号只分配独立次设备号省去单独申请主设备号的麻烦节约设备号资源。全局链表管理内核维护一条misc_list链表所有注册成功的struct miscdevice都会挂入链表用户层 open 设备时内核通过次设备号遍历链表匹配对应设备再调用其读写 /ioctl 接口。自动创建设备节点调用misc_register()注册时内核内部自动完成创建设备类/sys/class/misc→ 创建设备对象 → 在/dev/下自动生成设备文件不需要手动mknod。极简封装屏蔽 cdev 复杂操作普通字符设备需要手动申请设备号 →cdev_init→cdev_add misc 框架内部封装了全套 cdev 逻辑开发者只需要填充一个miscdevice结构体一行函数完成注册 / 注销。misc 类实例蜂鸣器驱动文件找到文件你怎么知道当前的源码中有没有蜂鸣器的驱动文件vscode在内核源码中直接搜索buzzer如果文件太多你去menuconfig中找buzzer也是可以的编译问题在menuconfig中都没有被选择这个肯定不会被编译。选择它但系统里还是没有buzzer的设备文件这个驱动的配置文件名和kconfig中的文件名不一致修改makefile中的定义文件为成功添加驱动。使用驱动该文件的open和close函数几乎什么也没干我们使用x210_pwm_ioctl去操控硬件static struct file_operations dev_fops { .owner THIS_MODULE, .open x210_pwm_open, .release x210_pwm_close, .ioctl x210_pwm_ioctl, }; static int x210_pwm_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { switch (cmd) { case PWM_IOCTL_SET_FREQ: printk(PWM_IOCTL_SET_FREQ:\r\n); if (arg 0) return -EINVAL; PWM_Set_Freq(arg); break; case PWM_IOCTL_STOP: default: printk(PWM_IOCTL_STOP:\r\n); PWM_Stop(); break; } return 0; }程序文件#include stdio.h #include stdlib.h #include fcntl.h #include unistd.h #include sys/ioctl.h #include sys/stat.h #define DEVICE_NAME /dev/buzzer #define PWM_IOCTL_SET_FREQ 1 #define PWM_IOCTL_STOP 0 int main() { int fd; int ret; fd open(DEVICE_NAME, O_RDWR); if (fd 0) { perror(Failed to open buzzer device); return -1; } ret ioctl(fd, PWM_IOCTL_SET_FREQ, 10000); // Example ioctl call sleep(3); // Sleep for 3 seconds to let the buzzer sound ret ioctl(fd, PWM_IOCTL_SET_FREQ, 5000); // Example ioctl call sleep(3); // Sleep for 3 seconds to let the buzzer sound ret ioctl(fd, PWM_IOCTL_STOP); // Example ioctl call close(fd); return 0; }misc 类程序关键点内核部分misc类的创建misc_init#ifdef CONFIG_PROC_FS //是否使用proc文件系统 [rootLXY210 ]# ls /proc/ 1 4 cgroups kmsg softirqs 10 41 cmdline kpagecount stat 11 43 cpu kpageflags sys 12 44 cpuinfo loadavg sysrq-trigger 13 45 crypto locks sysvipc 14 48 devices meminfo timer_list 15 5 diskstats misc tty 16 54 driver modules uptime 17 6 execdomains mounts version 18 7 fb mtd vmallocinfo 19 73 filesystems net vmstat 2 8 fs pagetypeinfo wakelocks 20 9 interrupts partitions yaffs 3 apm iomem pvr yaffs_stats 32 asound ioports sched_debug zoneinfo 33 buddyinfo irq scsi 34 bus kallsyms self [rootLXY210 proc]# cat misc 51 network_throughput 52 network_latency 53 cpu_dma_latency 54 log_system 55 log_radio 56 log_events 57 log_main 58 binder 59 device-mapper 242 CEC 243 HPD 240 sec-g2d 254 s3c-jpg 252 s3c-mfc 60 alarm 1 psaux 0 pmem_gpu1 134 apm_bios 61 buzzer 62 ashmem 131 adc 63 rfkillmisc设备创建misc_registerint misc_register(struct miscdevice * misc) { struct miscdevice *c; dev_t dev; int err 0; //使用链表管理misc类设备 INIT_LIST_HEAD(misc-list); mutex_lock(misc_mtx); //查询次设备号是否被占用 list_for_each_entry(c, misc_list, list) { if (c-minor misc-minor) { mutex_unlock(misc_mtx); return -EBUSY; } } //使用位图的方法自动分配次设备号 if (misc-minor MISC_DYNAMIC_MINOR) { int i find_first_zero_bit(misc_minors, DYNAMIC_MINORS); if (i DYNAMIC_MINORS) { mutex_unlock(misc_mtx); return -EBUSY; } misc-minor DYNAMIC_MINORS - i - 1; set_bit(i, misc_minors); } //合成主、次设备号 dev MKDEV(MISC_MAJOR, misc-minor); //创建设备 misc-this_device device_create(misc_class, misc-parent, dev, misc, %s, misc-name); if (IS_ERR(misc-this_device)) { int i DYNAMIC_MINORS - misc-minor - 1; if (i DYNAMIC_MINORS i 0) clear_bit(i, misc_minors); err PTR_ERR(misc-this_device); goto out; } /* * Add it to the front, so that later devices can override * earlier defaults */ //将设备节点添加到内核misc链表中 list_add(misc-list, misc_list); out: mutex_unlock(misc_mtx); return err; }驱动工程师实现部分设备文件的读写接口//自定义设备文件操作结构体 static struct file_operations dev_fops { .owner THIS_MODULE, .open x210_pwm_open, .release x210_pwm_close, .ioctl x210_pwm_ioctl, }; //建立miscdevice结构体 static struct miscdevice misc { //自动分配次设备号 .minor MISC_DYNAMIC_MINOR, //这里的name只是用来记录 .name DEVICE_NAME, //关键自定义文件操作结构体 .fops dev_fops, }; static int __init dev_init(void) { int ret; //将信号量初始化为1也就成了互斥锁 init_MUTEX(lock); //注册misc设备 ret misc_register(misc); //申请引脚 /* GPD0_2 (PWMTOUT2) */ ret gpio_request(S5PV210_GPD0(2), GPD0); if(ret) printk(buzzer-x210: request gpio GPD0(2) fail); //设置为上拉 s3c_gpio_setpull(S5PV210_GPD0(2), S3C_GPIO_PULL_UP); //特殊模式设置为1也就是输出模式 s3c_gpio_cfgpin(S5PV210_GPD0(2), S3C_GPIO_SFN(1)); //输出值为0 gpio_set_value(S5PV210_GPD0(2), 0); printk (x210 DEVICE_NAME initialized\n); return ret; }硬件操作相关代码// TCFG0在Uboot中设置这里不再重复设置 // Timer0输入频率Finputpclk/(prescaler11)/MUX1 // 66M/16/16 // TCFG0 tcnt (pclk/16/16)/freq; // PWM0输出频率Foutput Finput/TCFG0 freq static void PWM_Set_Freq( unsigned long freq ) { unsigned long tcon; unsigned long tcnt; unsigned long tcfg1; struct clk *clk_p; unsigned long pclk; //unsigned tmp; //设置GPD0_2为PWM输出 s3c_gpio_cfgpin(S5PV210_GPD0(2), S3C_GPIO_SFN(2)); //读寄存器 tcon __raw_readl(S3C2410_TCON); tcfg1 __raw_readl(S3C2410_TCFG1); //mux 1/16 tcfg1 ~(0xf8); tcfg1 | (0x48); __raw_writel(tcfg1, S3C2410_TCFG1); //获取时钟频率pclk clk_p clk_get(NULL, pclk); pclk clk_get_rate(clk_p); tcnt (pclk/16/16)/freq; __raw_writel(tcnt, S3C2410_TCNTB(2)); __raw_writel(tcnt/2, S3C2410_TCMPB(2));//占空比为50% tcon ~(0xf12); tcon | (0xb12); //disable deadzone, auto-reload, inv-off, update TCNTB0TCMPB0, start timer 0 __raw_writel(tcon, S3C2410_TCON); tcon ~(212); //clear manual update bit __raw_writel(tcon, S3C2410_TCON); } void PWM_Stop( void ) { //将GPD0_2设置为input s3c_gpio_cfgpin(S5PV210_GPD0(2), S3C_GPIO_SFN(0)); }内核中的互斥锁另一个进程访问到已经上锁的资源时会在此休眠进入等待序列直到锁被释放解决竞争状态mutex_lock(misc_mtx);mutex_unlock(misc_mtx);int misc_register(struct miscdevice * misc) { struct miscdevice *c; dev_t dev; int err 0; INIT_LIST_HEAD(misc-list); //建立互斥锁防止多线程访问时修改同一资源 mutex_lock(misc_mtx); list_for_each_entry(c, misc_list, list) { if (c-minor misc-minor) { mutex_unlock(misc_mtx); return -EBUSY; } }防止竞争的手段原子访问这次操作一下就能完成不可以被打断自旋锁一种忙等锁线程抢锁失败时不放弃 CPU、不进入休眠原地循环自旋反复查询锁状态直到锁被释放。 底层本质原子操作 CAS 实现靠原子指令保证锁状态修改不冲突。互斥锁只能计数一次只能被打开一次信号量通过计数来避免竞争