美文网首页
PC蜂鸣器音乐

PC蜂鸣器音乐

作者: 网路元素 | 来源:发表于2019-10-30 07:26 被阅读0次

    有了《使用procfs》、《I/O映射之I/O端口》、《内核读写磁盘文件》这三篇文章的基础后,我们将其结合,实现如下功能的实例: 

    1.打开传入的音乐谱文件(通过procfs接口);
    2.读取音乐谱文件(以“频率”、“延时”、“频率”、“延时”、……这样的格式保存);
    3.解析读取的文件;
    4.将解析的数据传送去操作8254,让PC蜂鸣器弹奏文件对应的音乐。 

    好了,基础的内容看开头提及的文章,接下来上代码:

    #include <linux/init.h>
    #include <linux/module.h>
    #include <linux/fs.h>
    #include <linux/proc_fs.h>
    #include <asm/uaccess.h>
    #include <linux/timex.h>

    #ifndef MAX_PATH
    #define MAX_PATH 256
    #endif

    #define PACKAGE_SIZE 512

    extern void msleep(unsigned int msecs);

    struct proc_dir_entry * slam_dir = NULL;
    struct proc_dir_entry * slam_entry = NULL;

    static char * dir_name = "slam_song";
    static char * entry_name = "path";

    static void beep(unsigned int freq, unsigned int delay)
    {
            unsigned int count;

            if (freq)
                    count = PIT_TICK_RATE / freq;
            else
                    count = 20;

            outb_p(0xB6, 0x43);
            outb_p(count & 0xff, 0x42);
            outb((count >> 8) & 0xff, 0x42);
            outb_p(inb_p(0x61) | 0x03, 0x61);
            msleep(delay);
            outb(inb_p(0x61) & 0xFC, 0x61);
    }

    static int play(char * filename)
    {
            struct file * fp;
            mm_segment_t cur_mm_seg;
            loff_t fpos = 0;
            char buf;
            int data[PACKAGE_SIZE] = {0};
            int i = 0, j = 0; //i:data count,j:just temp value

            fp = filp_open(filename, O_RDONLY, 0644);
            if (IS_ERR(fp)) {
                    printk("<xinu>filp_open error\n");
                    return -1;
            }

            cur_mm_seg = get_fs();
            set_fs(KERNEL_DS);

            while(vfs_read(fp, &buf, sizeof(buf), &fpos)>0)
            {
                    if(buf == ','){
                            i++;
                            continue;
                    }

                    if(buf == '\n' || buf == '\r')
                            continue;

                    data[i] = data[i] * 10 + (buf - '0');
            }

            set_fs(cur_mm_seg);
            filp_close(fp, NULL);

            for (j = 0; j < i; j += 2) {
                    beep(data[j], data[j+1]);
            }

            return 0;
    }

    static ssize_t song_path_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
    {
            char filename[MAX_PATH];
            if(count > MAX_PATH){
                    printk("The filename is too long,the length must < %d\n", MAX_PATH);
                    return -EFAULT;
            }

            if(copy_from_user(filename, buf, count)){
                    printk("copy_from_user error!\n");
                    return -EFAULT;
            }

            filename[count-1] = '\0';
            play(filename);

            return count;
    }

    static const struct file_operations slam_fops =
    {
            .owner = THIS_MODULE,
            .write = song_path_write,
    };

    static int beep_songs_init(void)
    {
    #ifdef CONFIG_PROC_FS
            slam_dir = proc_mkdir(dir_name, NULL);
            if (!slam_dir){
                    printk("Create directory \"%s\" failed.\n", dir_name);
                    return -1;
            }

            slam_entry = proc_create(entry_name, 0666, slam_dir, &slam_fops);
            if (!slam_entry){
                    printk("Create file \"%s\" failed.\n", entry_name);
                    return -1;
            }
    #else
            printk("This module requests PROCFS support in linux kernel,need set CONFIG_PROC_FS configure Y\n");
    #endif
            return 0;
    }

    static void beep_songs_exit(void)
    {
    #ifdef CONFIG_PROC_FS
            proc_remove(slam_entry);
            proc_remove(slam_dir);
    #endif
            printk("Bye %s!\n", __func__);
    }

    module_init(beep_songs_init);
    module_exit(beep_songs_exit);

    MODULE_LICENSE("GPL");

    相应的Makefile文件内容如下:

    obj-m += beep_songs.o

    CUR_PATH:=$(shell pwd)
    LINUX_KERNEL_PATH:=/home/xinu/linux-3.13.6

    all:
            make -C $(LINUX_KERNEL_PATH) M=$(CUR_PATH) modules

    clean:
            make -C $(LINUX_KERNEL_PATH) M=$(CUR_PATH) clean

    对应的源码文件目录树如下: 

    /home/xinu/xinu/linux_kernel_driver_l1/beep_songs/
    ├── beep_songs.c
    └── Makefile 

    下面有两个实现好的音乐文件: 

    1.中文版“生日快乐歌”(happy_birthday_chinese.txt):

    392,375,392,125,440,500,392,500,523,500,494,1000,392,375,392,125,440,500,392,500,587,500,523,1000,392,375,392,125,784,500,659,500,523,500,494,500,440,1000,698,375,698,125,659,500,523,500,587,500,523,1000

    2.周华健“朋友”(pengyou.txt):

    0,900,587,450,659,450,523,900,587,450,659,450,587,900,659,450,784,450,880,900,784,450,659,450,659,900,587,450,659,450,523,900,587,450,659,450,392,900,587,450,659,450,587,900,440,450,523,4950,587,450,659,450,523,900,587,450,459,450,523,900,587,450,659,450,784,900,784,450,880,450,523,900,440,450,523,450,587,900,587,450,523,450,440,900,440,450,523,450,587,450,659,450,587,450,523,225,587,225,587,900,659,450,587,225,523,225,523,900,587,450,587,450,659,900,523,450,587,450,659,900,784,450,784,225,784,225,880,900,523,450,440,450,523,900,587,450,587,225,523,225,440,900,440,450,392,450,440,1800,523,900,0,450,659,225,659,225,784,450,784,450,784,450,784,225,880,1125,784,450,880,450,988,450,1046,450,880,450,880,225,784,1125,659,450,659,225,587,1125,523,450,523,225,880,1125,784,450,659,225,587,1125,523,225,523,225,440,1350,587,450,659,225,659,225,784,450,784,450,784,450,784,225,880,11,784,25,880,450,988,450,1046,450,880,450,880,450,784,225,659,1125,659,450,587,225,523,1125,523,450,880,225,784,1125,659,450,587,225,523,1125,440,450,440,225,523,675,523,1350

    当我们将上面的模块编译加载后,会在/proc目录下有/proc/slam_song/path文件生成,然后我们将上面的音乐文件路径写入该文件后即可开始演奏,如下: 

    echo “/home/xinu/happy_birthday_chinese.txt” > /proc/slam_song/path
    echo “/home/xinu/pengyou.txt” > /proc/slam_song/path 

    本例解决了之前相关例子里beep函数对freq为0时未做除数0的检查,还有本例有如下优化建议: 

    1.在写入文件路径时需等待演奏结束才返回,此时可采用多线程方式解决;
    2.采用多线程方式处理后,还需要使用同步机制对演奏过程中再次写入路径情况进行处理。 

    关于这两条建议,我们将在后面的实例中再展开应用,你可先在本例基础上做些尝试。 

    参考资料:

    http://blog.csdn.net/sky_j123/article/details/19574955

    相关文章

      网友评论

          本文标题:PC蜂鸣器音乐

          本文链接:https://www.haomeiwen.com/subject/btqryctx.html