zoukankan      html  css  js  c++  java
  • Linux 一种调用蜂鸣器的方法

    最近想试试能不能把蜂鸣器精度提高到微秒级,然后学习了一下 ioctl 的代码……

    和系统中断次数有关……也就是说现在很难提高到微秒级。然后使用了较为底层的实现,发现跑 10000 次还是要 0.6s ……

    终究败在了 io 速度上……?(当然我不太懂内核,如果有 dalao 知道具体的原因请教教我)

    不过还是发现了一个较为奇特的写法。

    首先我用的驱动是 pcspkr,然后我去 /drivers/input/misc/pcspkr.c 里找到了这么一段代码(撰写该文的时候版本为 5bfc75d92e):

    static int pcspkr_event(struct input_dev *dev, unsigned int type,
    			unsigned int code, int value)
    {
    	...
    	if (count) {
    		/* set command for counter 2, 2 byte write */
    		outb_p(0xB6, 0x43);
    		/* select desired HZ */
    		outb_p(count & 0xff, 0x42);
    		outb((count >> 8) & 0xff, 0x42);
    		/* enable counter 2 */
    		outb_p(inb_p(0x61) | 3, 0x61);
    	} else {
    		/* disable counter 2 */
    		outb(inb_p(0x61) & 0xFC, 0x61);
    	}
    	...
    	return 0;
    }
    

    然后发现和 dev 无关,这意味着我们只需要有 outb 等函数即可。

    于是我们就可以魔改出一个 beep 函数(当然我没加锁):

    #include <unistd.h> // usleep
    #include <linux/input.h> // SND_BELL, SND_TONE
    #include <sys/io.h> // ioperm, inb_p, outb, outb_p
    #include <iostream>
    #include <vector>
    
    #define PIT_TICK_RATE 1193182
    
    int beep(unsigned int code, int value) {
    	unsigned int count = 0;
    
    	switch (code) {
    		case SND_BELL:
    			if (value) value = 1000;
    			break;
    		case SND_TONE: break;
    		default: return -1;
    	}
    
    	if (value > 20 && value < 32767)
    		count = PIT_TICK_RATE / value;
    
    	if (count) {
    		outb_p(0xB6, 0x43);
    		outb_p(count & 0xff, 0x42);
    		outb((count >> 8) & 0xff, 0x42);
    		outb_p(inb_p(0x61) | 3, 0x61);
    	} else {
    		outb(inb_p(0x61) & 0xFC, 0x61);
    	}
    
    	return 0;
    }
    
    int main() {
    	std::vector<int> init{0x42, 0x43, 0x61, 0x80};
    	for (auto t : init) {
    		int ret = ioperm(t, 1, 1);
    		std::cout << std::hex << t << " : " << ioperm(t, 1, 1) << std::endl;
    	}
    	beep(SND_BELL, 1000); // 开始响
    	beep(SND_TONE, 600); // 调声高
    	usleep(1000000);
    	beep(SND_BELL, 0); // 停止
    	return 0;
    }
    
    

    注意到要使用 outb 等函数之前要 ioperm,而我这边库的 outb_p 实现中还用到了 0x80。这样我们就可以在用户态中使用 outb 这种函数了!

    然后还是要注意这东西需要管理员权限……我也不知道人家 beep 程序是怎么写的,怎么做到不用管理员权限的……当然想学一下辣

    然后就整了这么一个好像没啥用的东西。

  • 相关阅读:
    性能测试之数据准备
    工作笔记
    Ruby on Rails 模型关联(多对多关系)
    oracle 分页查询优化
    mysql命令学习
    mysql 导入导出
    aix 管理网卡
    dataguard没成功创建数据文件
    aix删除网卡
    oracle rac 随笔
  • 原文地址:https://www.cnblogs.com/daklqw/p/14925298.html
Copyright © 2011-2022 走看看