打印

软件实现简单音乐发生器

[复制链接]
1846|7
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
对于单片机而言,产生不同频率的脉冲非常方便,可以利用它的定时/计数器来实现方波率信号,因此要弄清楚音乐中的音符和对应的频率,以及单片机定时计数器的关系。
如果单片机工作在12MHz时钟,使用其定时/计数器T0,工作模式为1,改变计数值TH0和TL0可以产生不同频率的脉冲信号。
除了音符以外,节拍也是音乐的关键组面部分。
节拍实际上就是音持续时间的长短,在单片机系统中可以用延时来实现。
简单音乐厅发生器和硬件电路较为简单,主要包括单片机及键盘电路和LM386芯片来实现的音频功放电路原理图如下;

C语言程序如下
*******************************************************************************
程序功能为51单片机定时查询键盘状态,根据键盘的按键输入演奏相应的单个乐音,多个按键的组合构成一段简单的音乐演奏,程序主要包括:键盘键位的按下与释放判别子程序、根据按键选择定时器的定时参数,实现对应音频信号的输出
*******************************************************************************
#include <reg51.h>
#define uchar unsigned char
#define uint unsigned int
/* 16个音符与计数器计数值对应表 */
uint code tab[]={64021,64103,64260,64400,
                 64524,64580,64684,64777,
                 64820,64898,64968,65030,
                 65058,65110,65157,65178};
sbit MUSIC = P0^6;  // 此引脚输出脉冲
uchar DATA2;
uchar DATA1;

//主程序
void main()
{
uchar key,k;
TMOD = 0x01;       // T0,工作方式1
ET0 = 1;
EA = 1;
  while(1)
    {
  P1 = 0xf0;         // 发全0行扫描码
  if ((P1&0xf0)!=0xf0)        // 若有键按下
  {
   delay();       // 延时去抖动
   if ((P1&0xf0)!=0xf0)    // 延时后再判断一次,去除抖动影响
   {
    key = getkey();     // 调用键盘扫描函数
    // 根据获取的按键位置得到k值
    switch(key)
       {
          case 0x11:      // 1行1列
      k = 0;
      break;
     case 0x21:      // 1行2列
      k = 1;
      break;   
     case 0x41:      // 1行3列
      k = 2;
      break;
         case 0x81:      // 1行4列
      k = 3;
      break;
     case 0x12:      // 2行1列
      k = 4;
      break;
     case 0x22:      // 2行2列
      k = 5;
      break;
     case 0x42:      // 2行3列
      k = 6;
      break;
     case 0x82:      // 2行4列
      k = 7;
      break;
     case 0x14:      // 3行1列
      k = 8;
      break;
     case 0x24:      // 3行2列
      k = 9;
      break;
     case 0x44:      // 3行3列
      k = 10;
      break;
     case 0x84:      // 3行4列
      k = 11;
      break;
     case 0x18:      // 3行4列
      k = 12;
      break;
     case 0x28:      // 3行4列
      k = 13;
      break;
     case 0x48:      // 3行4列
      k = 14;
      break;
     case 0x88:      // 3行4列
      k = 15;
      break;
     default:
      break;
    }
    MUSIC = ~MUSIC;       // 反相
    /* 根据所得的k值设定计数器1的计数初值 */
            DATA2 = tab[k]/256;
             DATA1 = tab[k]%256;
           TR0 = 1;       // 开始计数
    P1 = 0xf0;        // 发全0行扫描码
    while ((P1&0xf0)!=0xf0)    // 若没有松开按键
    {
     P1 = 0xf0;
    }
    TR1 = 0; // 若按键松开,则停止计数,不产生脉冲输出
   }
  }
}   
}
// 键消抖延时子程序
void delay(void)
{
uchar i;
for (i=300;i>0;i--);                              //延时20ms
}
//键扫描子程序
uchar getkey(void)
{
uchar scancode,tmpcode;
if ((P1&0xf0)==0xf0)   
  return(0);
scancode = 0xfe;
while((scancode&0x10)!=0)           // 逐行扫描
{
  P1 = scancode;         // 输出行扫描码
  if ((P1&0xf0)!=0xf0)           // 本行有键按下
  {
   tmpcode = (P1&0xf0)|0x0f;
   /* 返回特征字节码,为1的位即对应于行和列 */
   return((~scancode)+(~tmpcode));
  }
  else scancode = (scancode<<1)|0x01;  // 行扫描码左移一位
}
}
///定时器0中断服务子程序
void time0_int(void) interrupt 1 using 0
{
   TH0 = DATA2;                               //定时值重装
   TL0 = DATA1;
    MUSIC=~MUSIC;       //反相,产生输出脉冲
}

相关帖子

沙发
jxmzzr| | 2012-4-6 20:20 | 只看该作者
喜欢这种基础的东西,正好刚刚学习,非常适合初学者,谢谢了。
希望LZ多多分享。

使用特权

评论回复
板凳
zheng522| | 2012-4-6 21:00 | 只看该作者
Mark一下。

使用特权

评论回复
地板
wewo| | 2012-4-6 21:44 | 只看该作者
过来看下

使用特权

评论回复
5
wewo| | 2012-4-6 21:45 | 只看该作者
谢谢楼主分享

使用特权

评论回复
6
czdo2008| | 2012-4-6 21:50 | 只看该作者
用pwm做

使用特权

评论回复
7
拿起书本| | 2012-4-7 17:56 | 只看该作者
本帖最后由 拿起书本 于 2012-4-7 17:58 编辑

PWM经济、节约空间、抗噪性能强,是一种值得广大工程师在许多设计应用中使用的有效技术。

使用特权

评论回复
8
tianyu01| | 2012-4-7 21:18 | 只看该作者
过来了解下 技术还是很成熟的嘛

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

482

主题

2214

帖子

11

粉丝