美文网首页
Linux简易键盘式电子琴

Linux简易键盘式电子琴

作者: Neo_Zeng | 来源:发表于2019-12-15 18:56 被阅读0次

要求

  1. 在PC机的Linux系统中完成编写8253的内核模块驱动程序和应用程序;
  2. 利用驱动程序提供的方法,应用程序既可以实现简易的键盘式电子琴的功能,又可以完成一段固定乐曲的播放。
  3. 作业报告中包括能正常编译成设备驱动模块的Makefile、C语言源程序和头文件,及其他文字注释;
  4. 画出包含8253, 8255, 8259三个芯片的基本电路图,以说明如何实现扬声器发声。

背景知识与提示

这部分内容主要帮忙理清头绪和思路

1、PC机中有一片可编程的定时器/计数器 8253 芯片, 8253在系统中占用 40H∼43H端口。具备控制系统的扬声器,产生声音信号(T/C2)的功能。控制端 GATE2扬声器前均接有控制信号,来自可编程 I/O接口芯片 8255的 PB0和 PB1。8255初始化已将B口设为方式0输出。主中断控制器 8259A 占用 20H 和 21H 端口,其IR2供级联从片输入。PC机为可编程并行接口 8255A 分配端口 60H∼63H,初始化后A口作为键盘输入端口B口用于一些控制信号输出
2、 利用T/C2对扬声器的控制功能,8253在方式三工作下输出近似方波,可产生适当频率的方波从而将相关的开关打开,使其作用在扬声器上,便可以听到该频率的声音。可以直接使用汇编指令对扬声器等I/O端口操作
但是这里我们不使用汇编语言,而可以使用内核提供的C语言函数inb p()、outb p()的方法

音符与频率对应关系(单位: Hz): 音符-频率

作业内容

本次作业需要完成的内容和解决的问题

  • 设计电路原理图,包含8253、8255、8259三个芯片;
  • 在电路原理图部分里,对8253使用工作方式三而不是方式二做一个简单说明;
  • 编写字符设备驱动程序keyboard.c应用程序app.c,以及相应的Makefile文件,源代码展示;
  • 运行过程展示。

1. 电路原理图

在背景知识和提示部分里,我们已经对8253、8255、8259芯片的功能进行的解释,8259是主中断控制器,而8253利用工作方式三产生一定频率信号8255控制频率信号的通断

1、8255的B口设置为输出,8255的B口的低两位用来控制扬声器驱动,当输出端口的PB0位为“1”或为“0”时,将使控制驱动器的与门电路接通或关闭,使8253所发出的音频信号能到达驱动器或被阻断
2、通过控制PB0的变化,可使扬声器接通和断开,控制扬声器是否能发出声音
3、通过控制PB0的通断时间,就能发出不同的音长
4、8255的PB1位为“1或为0”时,控制8253定时器产生驱动扬声器发声的音频信号不发信号。8253 T/C2 工作在方式三负责向扬声器发送指定频率的脉冲信号。当8255的PB0和PB1都为1时,8253发出指定频率的声音信号的前提下,声音信号通过与门到达驱动器驱动扬声器发声

通过上面这段说明我们可以绘制包含8253、8255、8259芯片的电路原理图:

  1. 8253芯片CLK接1.19MHz的时钟信号;
  2. 8255的PB0端口接到8253的门控GATE2;PB1与8253的OUT2接入与门 -> 驱动 -> 扬声器,从而控制扬声器;
  3. 8259作为主中断控制器,产生计时中断信号。


    电路原理图
关于8253工作方式的说明

8253 有六种工作方式:

  • 方式0:计数结束中断,GATE 保持高电平,输出N*T 的低电平,后变为高电平并保持不变。计数中如果GATE 突然变成低电平,然后变回高电平,则延长低电平输出时间,延长时间为GATE 变为低电平的时间。
  • 方式1:可编程单个触发信号。与方式0 一样,只是GATE 是上升沿触发。计数期间如果GATE变为低电平再变高电平,则重新开始计数。
  • 方式2:速率发生器。GATE 保持高电平。输出(N-1)*T 的高电平,之后输出一个T 的低电平。
  • 方式3:方波发生器。如果N 为偶数,输出占空比50%,周期NT 的方波,如果N 为奇数,输出的方波中高电平为(N+1)/2T,低电平为(N-1)/2*T。
  • 方式4:软件触发选通。如同方式2,只是只计数一个周期,即输出(N-1)T 的高电平和1T的低电平之后保持高电平不变。
  • 方式5: 硬件触发选通。如同方式4,GATE 改为上升沿触发。
    在 8253提供的六种工作方式中,只有方式二和方式三是不需要硬件触发能产生连续波形的方式。方式二产生信号的占空比随频率改变而改变,而与方式二相比,方式三产生的信号更接近于方波,因此选择方式三。

2、字符设备驱动与应用程序编写

1)字符设备驱动程序keyboard.c
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/ioport.h>
#define MAJOR_NUM 200
#define DEVICE_NAME "8253_driver"
#define BUF_LEN 2
int init_module(void);
void cleanup_module(void);
static int device_open(struct inode *, struct file *);
static int device_release(struct inode *, struct file *);
static ssize_t device_read(struct file *, char *, size_t, loff_t *);
static ssize_t device_write(struct file *, const char *, size_t, loff_t *);
static int Device_Open = 0;
static char msg[BUF_LEN];
static char *msg_Ptr;

static struct file_operations fops = {
    .read = device_read,
    .write = device_write,
    .open = device_open,
    .release = device_release
};

//初始化函数
int init_module(void){
    int major;
    major = register_chrdev(MAJOR_NUM, DEVICE_NAME, &fops);
    if (major < 0){
        printk (KERN_ALERT "Failed ro register the device!");
        return major;
    }
    printk (KERN_ALERT "Success to register the device! The MAJOR_NUM is %d!",MAJOR_NUM);
    return 0;
}

//清除函数
void cleanup_module(void){
    unregister_chrdev(MAJOR_NUM, DEVICE_NAME);
    printk(KERN_ALERT "The device has been unregistered!\n");
}
static int device_open(struct inode *inode, struct file *file){
    if (Device_Open)
    return -EBUSY;
    Device_Open++;
    printk(KERN_ALERT "Device opened!\n");
    outb_p(0xb6,0x43);
    return 0;
}
static int device_release(struct inode *inode, struct file *file){
    Device_Open=0;
    outb_p(0,0x61);
    return 0;
}
//用户空间写操作
static ssize_t device_write(struct file *file, const char *buffer, size_t length,loff_t *offset){
    outb_p(0,0x61);
    copy_from_user(msg, buffer, length);
    outb_p(msg[0],0x42);
    outb_p(msg[1],0x42);
    outb_p(3,0x61);
    printk(KERN_ALERT "Written\n");
    return 0;
}
//用户空间读操作
static ssize_t device_read( struct file *filp, char *buffer,size_t length,loff_t *offset){
    if (*msg_Ptr == 0)
    return 0;
    copy_to_user(buffer, msg_Ptr, length);
    return length;
}
MODULE_AUTHOR("qyz");
MODULE_LICENSE("ZWMDR");
2)应用程序app.c
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <math.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
int main(){
    int fd;
    int flag=1;
    int run=1;
    char mode;
    char key_input;
    int i,j;
    int fre;
    int key_div;
    const int keys[3][7]={
        {131,147,165,175,196,220,247}, //低⼋度
        {262,294,330,349,392,440,494},
        {523,587,659,698,784,880,987} //⾼⼋度
    };
//小星星
    int Little_Star[48]={7,7,11,11,12,12,11,11, 10,10,9,9,8,8,7,7,11,11,10,10,9,9,8,8,11,11,10,10,9,9,8,8, 7,7,11,11,12,12,11,11,10,10,9,9,8,8,7,7};
//两只老虎
    int Two_Tigers[36]={7,8,9,7, 7,8,9,7, 9,10,11,11, 9,10,11,11, 11,12,11,10,9,7,11,12,11,10,9,7,7,4,7,7, 7,5,7,7};
    fd = open("/dev/keyboard", O_RDWR);
    if (fd == -1){
        printf ("Can't open device file\n");
        return -1;
    }
    while (run){
        printf("[请选择程序运⾏模式:\n");
        printf("1.电⼦琴模式\n");
        printf("2.播放器模式\n");
        printf("q.退出程序\n");
        printf("\n");
        scanf("%c",&mode);
        getchar();
        switch(mode){
            case '1':{
                printf("[Mode 1: 电⼦琴模式\n");
                flag=1;
                while(flag){
                    printf("音调从高到低依次为:\n");
                    printf("[< w e r t y u i >\n");
                    printf("[< a s d f g h j >\n");
                    printf("[< z x c v b n m >\n");
                    printf("输入q退出\n");
                    scanf("%c", &key_input);
                    getchar();
                    switch(key_input){
                        case 'w':fre = keys[2][6];break;
                        case 'e':fre = keys[2][5];break;
                        case 'r':fre = keys[2][4];break;
                        case 't':fre = keys[2][3];break;
                        case 'y':fre = keys[2][2];break;
                        case 'u':fre = keys[2][1];break;
                        case 'i':fre = keys[2][0];break;
                        case 'a':fre = keys[1][6];break;
                        case 's':fre = keys[1][5];break;
                        case 'd':fre = keys[1][4];break;
                        case 'f':fre = keys[1][3];break;
                        case 'g':fre = keys[1][2];break;
                        case 'h':fre = keys[1][1];break;
                        case 'j':fre = keys[1][0];break;
                        case 'z':fre = keys[0][6];break;
                        case 'x':fre = keys[0][5];break;
                        case 'c':fre = keys[0][4];break;
                        case 'v':fre = keys[0][3];break;
                        case 'b':fre = keys[0][2];break;
                        case 'n':fre = keys[0][1];break;
                        case 'm':fre = keys[0][0];break;
                        case 'q':fre=0;flag=0;break;
                    }
                key_div=(1193182.0/(1.0*fre));
                write(fd, &key_div, 2);
                if(flag){
                    usleep(500000);
                    key_div=0;
                    write(fd, &key_div,2);
                }
            }
            break;
        }
        case '2':{
            printf("[Mode 2: 播放器模式\n");
            printf("[播放列表:\n");
            printf("[1:两只老虎\n");
            printf("[2:小星星\n");
            printf("[请选择播放歌曲:\n");
            scanf("%c", &key_input);
            getchar();
            switch(key_input){
                case '1': //两只老虎
                    for(j=0;j<36;j++){
                        key_div=1193182.0/keys[Two_Tigers[j]/7]
                        [Two_Tigers[j]%7];
                        write(fd,&key_div,2);
                        usleep(500000);
                        printf("正在播放中...\n");
                    }
                    break;
                case '2':
                    for(j=0;j<48;j++){
                        key_div=1193182.0/keys[Little_Star[j]/7]
                        [Little_Star[j]%7];
                        write(fd, &key_div,2);
                        usleep(500000);
                        printf("[正在播放中...\n");
                    }
                    break;
                default:
                    printf("[退出播放器模式\n");
            }
            key_div=0;
            write(fd, &key_div,2);
            break;
            printf("In mode 2: keyboard\n");
        }
        case 'q':{
            printf("[退出");
            printf("\n");
            run=0;
            break;
        }
        default:{
            printf("[ERROR\n");
            break;
            }
        }
    }
close(fd);//关闭设备
return 0;
}
 
3)Makefile文件
KSRC = /lib/modules/$(shell uname -r)/build  
PWD = $(shell pwd)
obj-m = keyboard.o
default:  
        $(MAKE) -C $(KSRC) SUBDIRS=$(PWD) modules  
clean:  
        $(MAKE) -C $(KSRC) SUBDIRS=$(PWD) clean 

3、程序运行展示

1)make编译与加载驱动

~$ make 得到设备驱动文件keyboard.ko

make编译 ~$ sudo insmod keyboard.ko 加载驱动
~$ lsmod 查看驱动加载成功
2)编译运行应用程序

~$ sudo gcc -o app app.c 编译应用程序
~$ sudo ./app 运行应用程序

4、心得体会

本次实验在加载内核模块部分耽误了很多时间。原因是编译用内核版本与主机内核版本不一致,然后更改grub文件切换内核版本时,出现了失误,开机进入Memtest86界面,我一时没有找到方法处理这个问题,就先搁置了。然后重装了一个版本的Linux,继续完成本次作业。

本次实验难点先是在于对电子琴电路设计的理解,背景知识与提示内容已经给出很多但是有点凌乱,需要自己从中整理出思绪。最关键是设备驱动程序编写,因为不需要通过汇编程序来调用扬声器等I/O端口,使用例如inb_p(0x61);outb_p(3|bit,0x61)读8255PB口,置位打开扬声器,函数应用使得这部分方便很多。应用程序编写相对简单点,主要是对接口函数的调用。

相关文章

  • Linux简易键盘式电子琴

    要求 在PC机的Linux系统中完成编写8253的内核模块驱动程序和应用程序;利用驱动程序提供的方法,应用程序既可...

  • 电子琴独奏

    简单介绍一下电子琴,现在有的电子琴一般都为多功能电子琴。分有无脚踏之分。“电子琴是一种电子键盘乐器,属于电子...

  • 电子琴学习1

    【电子琴指法】电子琴指法大全 电子琴8种基本指法练习 键盘乐器,采用半导体集成电路,对乐音信号进行放大,通过扬声器...

  • 学钢琴的这些禁忌,你触碰了吗?

    有的人会以为电子琴比钢琴便宜,所以用电子琴去代替钢琴学习。这种想法是错误的,电子琴的音域、发声、触键以及键盘踏板等...

  • arduino——简易电子琴(笔记)

    我们根据上一篇的例子制作一个简易电子琴。 材料准备 arduino板,按键(由于小道只有4个就用了4个),220欧...

  • 乐队里的键盘手

    键盘手,在乐队里是指操作掌管键盘类乐器的人。键盘里的种类有钢琴、电钢琴、电子琴、电风琴、合成器等等一系列的乐器。 ...

  • 我在瑜伽村第12天

    今天复习了简易舞王式和战士第三式,新学习了手杖式,束角式,简易坐式和花环第一式。坐式的都还挺好学的,把握力量走向,...

  • “集大成”的键盘乐器

    电子琴面世以来一直深受人们的喜爱,它是一种“集大成”的键盘乐器,拥有包括电子琴等多种乐器的音色和自动伴奏和声功能,...

  • 周复盘09 20200224-20200301/新爸五年计划/学

    本周收获好心人借给妞的电子琴,认识的盆友看到我每天发妞用纸键盘练习弹琴的视频,就把自己家闲置的电子琴拿来了!真的是...

  • 如何选择适合你的兴趣爱好(六十),电子琴

    围城网的摇摇今天给大家带来了“如何选择适合你的兴趣爱好”系列专辑的第六十讲 ——电子琴。电子琴是一种键盘乐器,其实...

网友评论

      本文标题:Linux简易键盘式电子琴

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