| 网站首页 | 业界新闻 | 小组 | 威客 | 人才 | 下载频道 | 博客 | 代码贴 | 在线编程 | 编程论坛
欢迎加入我们,一同切磋技术
用户名:   
 
密 码:  
共有 3461 人关注过本帖, 4 人收藏
标题:C时钟与键盘中断混合
取消只看楼主 加入收藏
暗留香
Rank: 2
等 级:论坛游民
帖 子:49
专家分:75
注 册:2009-9-4
结帖率:100%
收藏(4)
 问题点数:0 回复次数:1 
C时钟与键盘中断混合
程序代码:
#include <stdlib.h>
#include <stdio.h>
#include <conio.h>
#include <string.h>
#include <mem.h>
#include <dos.h>
/* 以下是 PC 机的 DMA 控制器端口的常量定义 */
#define DMA8_CMD_PORT      0x08   /* 8位DMA写命令寄存器端口 */
#define DMA8_STATU_PORT    0x08   /* 8位DMA独状态寄存器端口 */
#define DMA8_REQUEST_PORT 0x09   /* 8位DMA写请求寄存器端口 */
#define DMA8_MASK_PORT     0x0A   /* 8位DMA屏蔽寄存器端口(只写)*/
#define DMA8_MODE_PORT     0x0B   /* 8位DMA写模式寄存器端口 */
#define DMA8_CLRPTR_PORT   0x0C   /* 8位DMA清先后状态寄存器端口 */
#define DMA8_RESET_PORT    0x0D   /* 8位DMA写复位命令端口 */

/* 以下是 PC 机 DMA 的 7 个通道的地址寄存器、计数寄存器和页面寄存器的端口常量定义 */
/* 其中通道 0-3 用于 8 位的 DMA,通道 4-7 用于 16 位 DMA */
/* PC机中,规定通道 2 用于进行软盘的 DMA 传输,其余通道可供用户使用 */
#define DMA0_ADDR_PORT     0x00   /* 通道0的地址寄存器 */
#define DMA0_COUNT_PORT    0x01   /* 通道0的计数寄存器 */
#define DMA0_PAGE_PORT     0x87   /* 通道0的页面寄存器 */

#define DMA1_ADDR_PORT     0x02   /* 通道1的地址寄存器 */
#define DMA1_COUNT_PORT    0x03   /* 通道1的计数寄存器 */
#define DMA1_PAGE_PORT     0x83   /* 通道1的页面寄存器 */

#define DMA3_ADDR_PORT     0x06   /* 通道3的地址寄存器 */
#define DMA3_COUNT_PORT    0x07   /* 通道3的计数寄存器 */
#define DMA3_PAGE_PORT     0x82   /* 通道3的页面寄存器 */

#define DMA5_ADDR_PORT     0xC4   /* 通道5的地址寄存器 */
#define DMA5_COUNT_PORT    0xC6   /* 通道5的计数寄存器 */
#define DMA5_PAGE_PORT     0x8B   /* 通道5的页面寄存器 */

#define DMA6_ADDR_PORT     0xC8   /* 通道6的地址寄存器 */
#define DMA6_COUNT_PORT    0xCA   /* 通道6的计数寄存器 */
#define DMA6_PAGE_PORT     0x89   /* 通道6的页面寄存器 */

#define DMA7_ADDR_PORT     0xCC   /* 通道7的地址寄存器 */
#define DMA7_COUNT_PORT    0xCE   /* 通道7的计数寄存器 */
#define DMA7_PAGE_PORT     0x8A   /* 通道7的页面寄存器 */

/* DSP 定义 */
#define DSP_RESET_DELAY   10
#define DSP_READY         0xAA

#define DSP_GET_VERSION   0xE1
#define DSP_SET_BLK_SIZE 0x48
#define DSP_START_DMA8    0x1C
#define DSP_PAUSE_DMA8    0xD0
#define DSP_SET_SAM_RATE 0x40

#define PIC_PORT_21H 0x21
#define PIC_PORT_20H 0x20
#define PIC_EOI       0x20
/*数据块大小必须<51199*/
#define DMA_BUFFER_SIZE 51198

#define   KEY_A   0x1E
#define   KEY_B   0x30
#define TRUE 1
#define FALSE 0

typedef struct
{
    unsigned short wavetype;    /* WAVE的类别   */
    unsigned short channel;     /* 通道数       */
    unsigned short samplerate; /* 采样频率     */
    unsigned short samplebits; /* 采样位数     */
    long int datalen;     /* 数据长度     */
    long int leftdatalen; /* 剩余数据长度 */
    int   loops;       /* 播放次数     */
    FILE *fp;
} WAVE, *PWAVE;

typedef struct
{
    char dspenvstr[128];
    unsigned short dspversion;
    unsigned short dspbaseioport;
    unsigned short resetport;
    unsigned short writedataport;
    unsigned short writestatusport;
    unsigned short readdataport;
    unsigned short readstatusport;
    unsigned short mixerbaseioport;
    unsigned short mpu401baseioport;
    unsigned char dspirqnum;
    unsigned char dspdma8;
    unsigned char dspdma16;
} DSP, *PDSP;

/* WAVE 文件结构定义 */
typedef struct
{
    char RIFF[4];     /* RIFF         */
    unsigned long int filelen;     /* 文件长度     */
    char WAVEfmt[8]; /* WAVEfmt      */
    unsigned long int reserved;    /* 保留         */
    unsigned short wavetype;    /* WAVE的类别   */
    unsigned short channel;     /* 通道数目     */
    unsigned short sampling;    /* 采样频率     */
    unsigned long int transpeed;   /* 数据传输速率 */
    unsigned short blkalign;    /* 调整数据块   */
    unsigned short sampbits;    /* 采样位数     */
    char data[4];     /* data         */
    unsigned long int datalen;     /* 语音数据长度 */
    unsigned char *pdata;       /* 数据区       */
} WAVEFILE, *PWAVEFILE;


int initsound(void);
void closesound(void);
int loadwave(PWAVE pwave, char *file);
void destroywave(PWAVE pwave);
void playwave(PWAVE pwave);
void stopwave(PWAVE pwave);
void pausewave(PWAVE pwave);
int initpic(int irqnum);
void closepic(int irqnum);
void picintdone(void);

int initdsp(PDSP pdsp);

void writedsp(PDSP pdsp, unsigned char byte);
void setdspblocksize(PDSP pdsp, unsigned short blksize);
void dspstartdma8(PDSP pdsp);
void dsppausedma8(PDSP pdsp);
void dspsetsamplerate(PDSP pdsp, unsigned short rate);

int initdma8(int channel, unsigned char far *addr, int size);
void closedma8(int channel);
void interrupt (*old_dsp_int_handle)(void) = NULL;

void interrupt new_zhong_duan(void);
void an_zhuang_zhong_duan(void);
void del_zhong_duan(void);


DSP   cursbdsp     = {0};
PWAVE pcurwave     = NULL;
int dma_buf_DSP_flag = NULL;
unsigned char far *sound_dma_buf = NULL;

int parse_sb_envstr(char *envstr, char id);

int DSP_flag=1;

char   key_state[128],key_pressed[128];
void   interrupt  (*oldInt9Handler)(void)=0;       /*OldInt9Handler将保存原有键盘中断值*/
void   far   interrupt   newInt9();

//可参考JIG大拿的DOS播放WAV音乐的解说,很详细
int initdma8(int channel, unsigned char far *addr, int size)
{
    unsigned long phyaddr = FP_SEG(addr) * 0x10L + FP_OFF(addr);
    unsigned char page    = (unsigned char)(phyaddr >> 16);
    unsigned short   offset = (unsigned short)(phyaddr >> 0);
    outportb(DMA8_MASK_PORT, channel | (1 << 2)); /* 屏蔽该通道 */
    outportb(DMA8_MODE_PORT, channel | (1 << 4) | (2 << 2)); /* 请求方式+自动初始化+读传送 */
    outportb(DMA8_CLRPTR_PORT, 0);

    outportb(DMA1_COUNT_PORT, (size - 1)& 0x00FF);
    outportb(DMA1_COUNT_PORT, (size - 1) >> 8);
    outportb(DMA1_ADDR_PORT, (offset)& 0x00FF);
    outportb(DMA1_ADDR_PORT, (offset) >> 8);
    outportb(DMA1_PAGE_PORT, page);

    outportb(DMA8_MASK_PORT, channel);
    return TRUE;             
}

void closedma8(int channel)
{
    if (channel > 3 || channel == 2) return;
    outportb(DMA8_MASK_PORT, channel | (1 << 2)); /* 屏蔽该通道 */
}

//这里是应用函数getenv()读取环境变量进行的初始化.
//可以:printf("%s",getenv("BLASTER"));测试.可以得到 A220 I7 D1 H6 P330 T6等.关于此数据说明的文档放在附件里.
int parse_sb_envstr(char *envstr, char id)
{
    char buf[32] = "0x";
    int i;
    int j;

    for (i = 0; envstr[i] != id && envstr[i] != '\0' && i < 128; i++);
    if (envstr[i] == '\0' || i == 128) return 0;
    else i++;
    for (j = 2; j < 32 && envstr[i] != ' '; j++) buf[j] = envstr[i++];
    return (int)strtoul(buf, NULL, 0);
}

int initdsp(PDSP pdsp)
{
    if (!getenv("BLASTER")) return FALSE;
    strupr(strcpy(pdsp->dspenvstr, getenv("BLASTER")));

    pdsp->dspbaseioport    = parse_sb_envstr(pdsp->dspenvstr, 'A');
    pdsp->resetport        = pdsp->dspbaseioport + 0x06;   /*复位端口*/
    pdsp->writedataport    = pdsp->dspbaseioport + 0x0C;    /*写数据端口*/
    pdsp->writestatusport = pdsp->dspbaseioport + 0x0C;     /*写状态端口*/
    pdsp->readdataport     = pdsp->dspbaseioport + 0x0A;    /*读数据端口*/
    pdsp->readstatusport   = pdsp->dspbaseioport + 0x0E;    /*读状态端口*/
    pdsp->dspirqnum        = parse_sb_envstr(pdsp->dspenvstr, 'I');
    pdsp->dspdma8          = parse_sb_envstr(pdsp->dspenvstr, 'D');
    pdsp->dspdma16         = parse_sb_envstr(pdsp->dspenvstr, 'H');
    pdsp->mixerbaseioport = parse_sb_envstr(pdsp->dspenvstr, 'M');
    pdsp->mpu401baseioport = parse_sb_envstr(pdsp->dspenvstr, 'P');
    writedsp(pdsp, DSP_GET_VERSION);
    return TRUE;
}

void writedsp(PDSP pdsp, unsigned char byte)
{

    outportb(pdsp->writedataport, byte);
}

void setdspblocksize(PDSP pdsp, unsigned short blksize)
{
    writedsp(pdsp, DSP_SET_BLK_SIZE);
    writedsp(pdsp, (blksize - 1) &0x00FF);
    writedsp(pdsp, (blksize - 1) >> 8);
}


void dspstartdma8(PDSP pdsp)
{
    writedsp(pdsp, DSP_START_DMA8);
}

void dsppausedma8(PDSP pdsp)
{
    writedsp(pdsp, DSP_PAUSE_DMA8);
}

void dspsetsamplerate(PDSP pdsp, unsigned short rate)
{
    unsigned short timeconst = (unsigned short)(65536L - (256000000L / rate));
    writedsp(pdsp, DSP_SET_SAM_RATE);
    writedsp(pdsp, timeconst >> 8);
}
//中断的屏蔽与打开.可以使某位=0或1即可.
int initpic(int irqnum)
{
    unsigned char mask;
    mask = inportb(PIC_PORT_21H);
    mask &= ~(1 << irqnum);
    outportb(PIC_PORT_21H, mask);
}

void closepic(int irqnum)
{
    unsigned char mask;
    mask = inportb(PIC_PORT_21H);
    mask |= (1 << irqnum);
    outportb(PIC_PORT_21H, mask);
}

int initsound(void)
{
    sound_dma_buf = (unsigned char far *)malloc(DMA_BUFFER_SIZE); //分配内存空间存放WAV音乐数据
    if (!sound_dma_buf) return FALSE;
    initdsp(&cursbdsp);    //初始化DSP
    setdspblocksize(&cursbdsp, DMA_BUFFER_SIZE / 2); //设置时钟频率
    initdma8(cursbdsp.dspdma8, sound_dma_buf, DMA_BUFFER_SIZE); //初始化DMA
    initpic(cursbdsp.dspirqnum); //开中断
    an_zhuang_zhong_duan();  //安装中断
    return TRUE;
}

void closesound(void)
{
    del_zhong_duan();
    closepic(cursbdsp.dspirqnum);
    closedma8(cursbdsp.dspdma8);

    if (sound_dma_buf) free((void*)sound_dma_buf);
}

/* 装载WAVE 文件到 WAVE 对象 */
int loadwave(PWAVE pw, char *file)
{
    WAVEFILE wf;

    pw->fp = fopen(file,"rb"); //打开WAV文件
    if (!pw->fp) return FALSE;

    fread(&wf, sizeof(wf), 1, pw->fp); //读取WAV文件头,不知道的可以分析WAV文件结构
    pw->wavetype    = wf.wavetype;
    pw->channel     = wf.channel;
    pw->samplerate = wf.sampling;
    pw->samplebits = wf.sampbits;
    pw->datalen     = wf.filelen - sizeof(wf) - 14;
    pw->leftdatalen = wf.filelen - sizeof(wf) - 14;
    fseek(pw->fp, 14, SEEK_CUR);//pw->fp指向的是WAV声音保存的地方
    return TRUE;
}

/* 销毁 WAVE 对象 */
void destroywave(PWAVE pw)
{
    fclose(pw->fp);
    pw->fp = NULL;
}

void playwave(PWAVE pwave)
{
    pcurwave     = pwave;
    dma_buf_DSP_flag = FALSE;
    fread((void*)sound_dma_buf, DMA_BUFFER_SIZE, 1, pcurwave->fp);//读取数据
    initdma8(cursbdsp.dspdma8, sound_dma_buf, DMA_BUFFER_SIZE); //初始化DMA
    dspsetsamplerate(&cursbdsp, pcurwave->samplerate*pcurwave->channel); //设置DSP播放频率
    dspstartdma8(&cursbdsp);//DMA开始传送数据
}

void stopwave(PWAVE pwave)
{
    dsppausedma8(&cursbdsp);
    fseek(pcurwave->fp, sizeof(WAVEFILE) + 14, SEEK_SET);
    pwave->leftdatalen = pwave->datalen;
}

void pausewave()
{
    dsppausedma8(&cursbdsp);
}

/* 内部函数实现 */
void interrupt new_zhong_duan(void)
{    DSP_flag=0;  
    pcurwave->leftdatalen -= DMA_BUFFER_SIZE / 2;
//这句代码意思是每回只读取一数据块大小的一半.实现,播放前面一半的时候中断COPY后面一半进内存.播放后面一半的时候中断COPY
//前面一半进内存
    fread((void*)(sound_dma_buf + dma_buf_DSP_flag * DMA_BUFFER_SIZE / 2),1, DMA_BUFFER_SIZE / 2, pcurwave->fp);
    dma_buf_DSP_flag = ! dma_buf_DSP_flag;
    outportb(0x20,0x20); /*复位中断控制器8259,向端口20h写一个20h*/
    DSP_flag=1;
}

void an_zhuang_zhong_duan(void)
{
    old_dsp_int_handle = getvect(cursbdsp.dspirqnum + 8);
    setvect(cursbdsp.dspirqnum + 8, new_zhong_duan);
}

void del_zhong_duan(void)
{
    setvect(cursbdsp.dspirqnum + 8, old_dsp_int_handle);
    old_dsp_int_handle = NULL;
}



void   installKeyboard(void)     /*安装新键盘中断程序的函数*/  
  {  
          int   i;  
          for(i=0;i<128;i++)  
                  key_state[i]=key_pressed[i]=0;       /*初始化键盘,无任何键被按下*/  
          oldInt9Handler=getvect(9);                       /*保存原09h中断*/  
          setvect(9,newInt9);                                     /*设置新的中断*/  
  }  
   
  void   shutDownKeyboard(void)         /*恢复原键盘中断值*/  
  {  
          setvect(9,oldInt9Handler);  
  }  
   
  void   far   interrupt   newInt9(void)         /*新的键盘中断程序*/  
  {
          unsigned   char   ScanCode,temp;  
          ScanCode=inportb(0x60);       /*从键盘I/O端口读入一个按键码*/  
          temp=inportb(0x61);               /*读取控制寄存器61H*/  
          outportb(0x61,temp   |   0x80);     /*并用82h完成一个OR操作*/    
          /*在控制寄存器上用7fh完成一个AND操作,以便复位键盘触发器,告诉硬件一个按键已被处理,可以读下一个键了*/  
          outportb(0x61,temp   &   0x7f);  
          if(ScanCode&0x80)       /*闭合状态*/  
          {  
                  ScanCode&=0x7f;  
                  key_state[ScanCode]=0;
          }  
          else     /*断开状态*/  
          {  
                  key_state[ScanCode]=1;  
                  key_pressed[ScanCode]=1;  
          }  
         outportb(0x20,0x20);    /*复位中断控制器8259,向端口20h写一个20h*/


  }  
   
  int   getKey(int   ScanCode)     /*读取按键*/  
  {  
          int   res;  
          res=key_state[ScanCode]|key_pressed[ScanCode];       /*求出所有按键的键码和*/
          while(key_state[ScanCode])return 0; /* 如果按键未松开 */
          key_pressed[ScanCode]=0;         /*读取完后,恢复未按状态*/
          return   res;  
  }

main()
{
    WAVE mywave = {0};
    initsound();
    loadwave(&mywave, "test.wav");
    mywave.loops = -1;
    playwave(&mywave);
    installKeyboard();
     while(1)
     {
     if(DSP_flag!=0&&getKey(KEY_A))
             fseek(pcurwave->fp, sizeof(WAVEFILE) + 14, SEEK_SET);

     if(DSP_flag!=0&&getKey(KEY_B)) break;
     }
    destroywave(&mywave);
    closesound();
    shutDownKeyboard();
}

说明:参考了本论坛某牛人代码.名字忘了= =!心中感激.他的代码也贴上供参考学习!!
按字母A键从头重新播放.B键退出.
声霸卡环境变量.rar (1.07 KB)

SRC.rar (6.4 KB)

讨论:1.某些地方不需要读取是否可写也是可以的.如:
readdsp(PDSP pdsp)
{
    while(!(inportb(pdsp->readstatusport) & (1 << 7)));
    return(inportb(pdsp->readdataport));
}
实际上while(!(inportb(pdsp->readstatusport) & (1 << 7)));删掉完全没关系.存在有啥必要的呢?
 2.如何同时播放两首音乐.如一个朗诵与一段背景音乐同时播放?
   我的思考是声音的混合,这东西,一点接触都没- -!有没说明更好的思路?别说多线程.DOS还真木有这玩意.
PS:自己都没分了= =!大家友情探讨下... ...
PS:他的名字:RockCarry!!~!膜拜!!~!
下载它的代码学习~


[ 本帖最后由 暗留香 于 2010-6-2 02:58 编辑 ]
搜索更多相关主题的帖子: 键盘 时钟 
2010-05-10 02:20
暗留香
Rank: 2
等 级:论坛游民
帖 子:49
专家分:75
注 册:2009-9-4
收藏
得分:0 
都没说,我说两点好了。
关于第一点。需要循环等待的问题,我猜测是以往的计算机存储速度没现在快,所以要等待- -!
第二。混音,我参考了一些资料。无论混音公式是什么,都是将两个曲显波(假设两个曲线)合成后乘以某个系数。又设置上限去除杂音。定义分配空间大小为 1024*8 (int为8位) 现在要考虑的问题是就是如何快速的将两个声波合成。公式:(a+b)/2
2010-05-19 16:04
快速回复:C时钟与键盘中断混合
数据加载中...
 
   



关于我们 | 广告合作 | 编程中国 | 清除Cookies | TOP | 手机版

编程中国 版权所有,并保留所有权利。
Powered by Discuz, Processed in 0.022891 second(s), 9 queries.
Copyright©2004-2024, BCCN.NET, All Rights Reserved