基于51单片机的16键电子琴音乐盒设计proteus仿真原理图程序

基于51单片机的模拟刹车灯疲劳提示报警红外检测控制系统 刹车灯 疲劳提示 51单片机 阅读详情

功能:
0.本项目采用STC89C52作为单片机系统的控制MCU
1.LCD1602液晶实时显示时间和当前工作模式
2.支持电子琴模式和歌曲模式(内置8首歌曲)
3.短触’设置/切换’键可切换工作模式
4.长按’设置/切换‘键可进入时间设置模式
5.采用DC002作为电源接口可直接输入5V给整个系统供电

原理图:
在这里插入图片描述

PCB :
在这里插入图片描述

主程序:

//////////////////////////////////////////////
//51单片机音乐播放器程序
//STC89C52+11.0592MHz+无源蜂鸣器
//共三个按键:上一曲、一下曲、播放\暂停
//注意:按键必须按下约1S才有相应动作,播放完一首歌曲无按键按下时顺序播放

//////////////////////////////////////////////
#include <REG52.H>
#include <stdio.h>
#include "delay.h"
#include "lcd1602.h"
#include "ds1302.h"

#define MUSIC_AMOUNT 8 //歌曲的数量
sbit SPEAKER = P3^2;     //无源蜂鸣器
sbit KEY_PAUSE = P1^5;
sbit KEY_SET = P2^7;
sbit KEY_PREVIOUS = P1^2;
sbit KEY_NEXT = P1^3;

//////////////////////////////////////////////
unsigned char beatTime = 0;    //T0RH,T0RL为定时器T0的高低位初值,g_beatTime为对应的节拍时间
char musicNum = 0;              //g_musicNum为歌曲编号,musicNum=0表示刚开机时的状态,g_noteCnt是查找歌曲数据表的地址
unsigned int noteCnt;
signed int g_fre;                      //对应频率数据表的地址
bit playEnFlag = 1;                          //歌曲播放的使能标志位,用于播放\暂停
unsigned char keyValue;
unsigned char dispRow0[] = "  /  /  ";
unsigned char dispRow1[] = "  :  ";
bit dispSongFlag = 1;
bit dispTimeFlag = 1;
bit setTimeFlag = 0;
unsigned char setLocation = 0;

void SetTime();
void DispTime();
void KeyScan();	                           //按键处理
void DelayOfBeat(unsigned char t);	   //延时子函数,控制发音的时间长度
void PlayOneNote(void);		           //演奏一个音符
void PlayMusic(void);	               //播放歌曲
void DispMusic(void);
void Timer0_Init();
void Timer1_Init();
//////////////////////////////////////////////
//每三个数字,代表一个音符
//第一个数字是音符的数值1234567之一(第几个音),代表哆来咪发...
//第二个数字是0123之一,代表低音\中音\高音\超高音(第几个八度)
//第三个数字是时间长度,以半拍为单位,乐曲数据表的结尾是三个0
//////////////////////////////////////////////



//《烟花易冷》
unsigned char code song1[]={
    5,2,1, 3,2,1, 2,2,2, 2,2,4, 3,2,1, 1,2,1, 2,2,1, 3,2,4, 
    5,2,1, 3,2,1, 2,2,2, 2,2,2, 5,1,1, 3,2,1, 4,2,1, 3,2,4, 
    3,2,1, 3,2,1, 7,2,1, 3,2,1, 2,2,2, 1,2,1, 7,1,1, 1,2,1, 
    2,2,1, 3,2,1, 6,2,3, 6,1,1, 1,2,1, 3,2,1, 2,2,1, 6,1,1, 
    1,2,1, 7,1,1, 5,1,1, 6,1,6, 5,2,1, 3,2,1, 2,2,2, 2,2,1, 
    2,2,1, 3,2,1, 1,2,1, 2,2,1, 3,2,4, 5,2,1, 3,2,1, 2,2,2, 
    2,2,1, 2,2,1, 5,1,1, 3,2,1, 4,2,1, 3,2,4, 3,2,1, 3,2,1, 
    7,2,3, 3,2,1, 2,2,2, 1,2,1, 7,1,1, 1,2,1, 2,2,1, 3,2,1, 
    6,2,3, 6,1,1, 1,2,1, 3,2,1, 2,2,1, 6,1,1, 1,2,1, 7,1,2, 
    5,1,2, 6,1,6, 0,0,0 };

//《发如雪》
unsigned char code song2[]={
    5,1,1, 2,2,1, 3,2,2, 2,2,1, 3,2,1, 5,2,1, 6,2,1, 5,2,3, 1,2,1, 2,2,1, 
    3,2,1, 6,2,1, 5,2,1, 3,2,1, 5,2,3, 5,2,1, 6,2,1, 1,3,2, 6,2,1, 5,2,1, 
    3,2,1, 5,2,1, 3,2,2, 1,2,1, 2,2,1, 3,2,1, 1,2,1, 6,1,1, 3,2,1, 2,2,1, 
    5,1,1, 2,2,1, 3,2,2, 2,2,1, 3,2,1, 5,2,1, 6,2,1, 5,2,3, 1,2,1, 2,2,1, 
    3,2,1, 6,2,1, 5,2,3, 5,2,1, 6,2,1, 1,3,2, 6,2,1, 
    5,2,1, 3,2,1, 5,2,1, 3,2,2, 1,2,1, 6,1,1, 3,2,1, 2,2,1, 1,2,1, 6,1,1, 
    1,2,1, 2,2,1, 2,2,1, 1,2,4, 0,0,0 };

//《简单爱》
unsigned char code song3[]={
    5,1,1, 1,2,1, 2,2,1, 3,2,1, 2,2,1, 3,2,1, 4,2,1, 5,2,1, 5,2,1, 5,2,1, 
    4,2,1, 3,2,1, 2,2,3, 5,1,1, 1,2,1, 2,2,1, 3,2,1, 4,2,1, 5,2,1, 5,2,1, 
    5,2,1, 6,2,1, 5,2,2, 2,2,1, 3,2,1, 1,2,2, 1,2,1, 6,1,1, 2,2,1, 2,2,1, 
    3,2,1, 3,2,1, 1,2,1, 5,2,1, 1,2,1, 5,2,1, 1,2,1, 7,1,1, 1,2,1, 1,2,1, 6,1,1, 
    2,2,1, 2,2,1, 3,2,1, 3,2,1, 5,2,1, 5,2,1, 
    5,2,1, 6,2,1, 5,2,2, 2,2,1, 3,2,1, 1,2,2,  
    5,1,1, 1,2,1, 2,1,2, 3,2,1, 2,2,1, 3,2,1, 4,2,1, 5,2,1, 5,2,1, 5,2,1, 
    4,2,1, 3,2,1, 2,2,3, 5,1,1, 1,2,1, 2,2,1, 3,2,1, 2,2,1, 3,2,1, 4,2,1, 
    5,2,1, 5,2,1, 5,2,2, 2,2,1, 3,2,1, 1,2,2, 1,2,1, 6,1,1, 2,2,1, 2,2,1, 
    3,2,1, 3,2,1, 1,2,2, 5,2,1, 1,2,1, 5,2,1, 5,2,1, 7,1,1, 1,2,1, 0,0,0 };

//《世上只有妈妈好》
unsigned char code song4[]={
    6,2,3, 5,2,1, 3,2,2, 5,2,2, 1,3,2, 6,2,1,
     //6,2,3代表6,中音,3个半拍;
     //5,2,1代表5,中音,1个半拍;
     //3,2,2代表3,中音,2个半拍;
     //5,2,2代表5,中音,2个半拍;
     //1,3,2代表1,高音,2个半拍;
     //...
    5,2,1,6,2,4, 3,2,2, 5,2,1, 6,2,1, 5,2,2, 3,2,2, 1,2,1,6,1,1, 5,2,1, 
    3,2,1, 2,2,4, 2,2,3, 3,2,1, 5,2,2, 5,2,1, 6,2,1, 3,2,2, 2,2,2, 
    1,2,4, 5,2,3, 3,2,1,2,2,1, 1,2,1, 6,1,1, 1,2,1, 5,1,6, 0,0,0};

//《当你孤单你会想起谁》
unsigned char code song5[]={
    3,2,2, 3,2,1, 4,2,1, 3,2,2, 2,2,1, 1,2,1,  2,2,2, 5,2,2, 2,2,2, 2,2,3,
    1,2,2, 1,2,1, 2,2,1, 1,2,2, 7,1,1, 6,1,1, 7,1,2, 3,2,2, 7,1,2, 7,1,3, 
    6,1,2, 2,2,1, 3,2,1, 2,2,1, 1,2,1, 6,1,2, 5,1,2, 2,2,1, 3,2,1, 2,2,1, 
    1,2,1, 6,1,2, 6,1,2, 2,2,1, 3,2,1, 2,2,1, 1,2,1, 6,1,1, 7,1,1, 1,2,6, 0,0,0 };

//《乡间小路》
unsigned char code song6[]={
    3,1,2, 3,1,1, 3,1,1, 6,0,1, 6,0,1, 1,1,2,
    6,0,1, 5,0,1, 6,0,4, 6,0,2, 6,0,1 ,6,0,1, 6,0,2, 6,0,1, 1,1,1, 
    2,1,2, 2,1,1, 3,1,1, 2,1,4, 3,1,1, 3,1,1, 3,1,1, 2,1,1, 4,1,2,
    4,1,2, 3,1,2, 2,1,1, 1,1,1, 2,1,4, 7,0,1, 7,0,1, 7,0,1, 6,0,1, 
    5,0,1, 5,0,1, 6,0,1, 7,0,1, 7,0,1, 6,0,1, 5,0,1, 6,0,4, 6,1,2,
    3,1,1, 6,1,1, 7,1,1, 6,1,1, 5,1,1, 5,1,2, 5,1,1, 2,1,1, 5,1,1, 
    6,1,1, 5,1,1, 4,1,1, 4,1,2, 4,1,1, 3,1,1, 2,1,2, 1,1,1, 2,1,1, 
    3,1,1, 2,1,1, 1,1,1, 2,1,1, 3,1,4, 6,1,2, 3,1,1, 6,1,1, 7,1,1, 
    6,1,1, 5,1,1, 5,1,2, 2,1,1, 5,1,1, 6,1,1, 5,1,1, 4,1,1, 0,0,0};     

//《送别》
unsigned char code song7[]={
    5,1,2, 3,1,3, 5,1,1, 1,2,3, 6,1,2, 1,2,2, 
    5,1,4, 5,1,2, 1,1,1, 2,1,1, 3,1,2, 2,1,1, 1,1,1, 2,1,4, 5,1,2, 
    3,1,1, 5,1,1, 1,2,2, 7,1,1, 6,1,2, 1,2,2, 5,1,4, 5,1,2, 2,1,1, 
    3,1,1, 4,1,2, 7,0,1, 1,1,4, 6,1,2, 1,2,2, 1,2,4, 7,1,2, 6,1,1,
    7,1,1, 1,2,4, 6,1,1, 7,1,1, 1,2,1, 6,1,1, 6,1,1, 5,1,1, 3,1,1, 
    1,1,1, 2,1,8, 5,1,2, 3,1,1, 5,1,1, 1,2,2, 7,1,1, 6,1,2, 1,2,2, 
    5,1,4, 5,1,2, 2,1,1, 3,1,1, 4,1,2, 7,0,2, 1,1,4, 0,0,0};

//《最浪漫的事》
unsigned char code song8[]={
    5,1,1, 6,1,1, 1,2,1, 6,1,2, 6,1,1, 5,1,1, 6,1,1, 5,1,1, 3,1,1, 5,1,5, 
    5,1,1, 6,1,1, 1,2,1, 6,1,2, 6,1,1, 5,1,1, 6,1,1, 5,1,1, 6,1,1, 1,1,5, 
    1,1,1, 2,1,1, 3,1,1, 2,1,1, 2,1,1, 2,1,1, 1,1,1, 2,1,1, 1,1,1, 6,1,1, 
    3,1,2, 2,1,3, 5,1,1, 6,1,1, 1,2,1, 6,1,2, 6,1,1, 5,1,1, 6,1,1, 5,1,1, 
    6,1,1, 1,1,5, 1,1,1, 2,1,1, 3,1,1, 4,1,2, 4,1,1, 5,1,1, 6,1,1, 6,1,1, 
    5,1,1, 6,1,2, 1,2,1, 6,1,3, 1,2,1, 6,1,1, 5,1,1, 5,1,4, 1,1,1, 6,1,1, 
    5,1,5, 5,1,1, 6,1,1, 1,2,1, 3,1,1, 2,1,1, 3,1,1, 1,1,6, 0,0,0}; 

// 频率-半周期数据表 高八位  共保存了四个八度的28个频率数据
unsigned char code FREQH[]={
    0xF2, 0xF3, 0xF5, 0xF5, 0xF6, 0xF7, 0xF8,    //低音1234567
    0xF9, 0xF9, 0xFA, 0xFA, 0xFB, 0xFB, 0xFC, 0xFC,//1,2,3,4,5,6,7,i
    0xFC, 0xFD, 0xFD, 0xFD, 0xFD, 0xFE,            //高音 234567
    0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFF};   //超高音 1234567

// 频率-半周期数据表 低八位
unsigned char code FREQL[]={
    0x42, 0xC1, 0x17, 0xB6, 0xD0, 0xD1, 0xB6,    //低音1234567
    0x21, 0xE1, 0x8C, 0xD8, 0x68, 0xE9, 0x5B, 0x8F, //1,2,3,4,5,6,7,i
    0xEE, 0x44, 0x6B, 0xB4, 0xF4, 0x2D,             //高音 234567
    0x47, 0x77, 0xA2, 0xB6, 0xDA, 0xFA, 0x16};   //超高音 1234567

void main(void)
{

    Timer0_Init();
    Timer1_Init();
    DS1302_Init();
    // DS1302_Write_Time();
    LCD_Init();
    DelayMs(10);
    LCD_Clear();
    LCD_DispStr(0, 0, "    MusicBox    ");
    DelayS(2);
    LCD_Clear();

    while (1)
    {

        if (setTimeFlag == 1)
        {
            SetTime();
        }
        else
        {
            DS1302_Read_Time();
            PlayMusic(); //根据当前状态播放相应歌曲的某个音符
            if (dispSongFlag == 1)
            {
                DispMusic();
                dispSongFlag = 0;
            }
        }

        if (dispTimeFlag == 1) //每200ms刷新一次屏幕
        {
            DispTime();
            dispTimeFlag = 0;
        }

        KeyScan();
    }
}

void DelayOfBeat(unsigned char t) //延时子函数,控制发音的时间长度,每个节拍0.4S
{
    unsigned char t1;
    unsigned long t2;
    for (t1 = 0; t1 < t; t1++) //嵌套循环, 共延时t个半拍
    {
        for (t2 = 0; t2 < 8000; t2++) //延时期间, 可进入T0中断去发音
        {
            ;
        }
    }
    TR0 = 0; //关闭T0, 停止发音
}

void PlayOneNote(void) //演奏一个音符
{
    TH0 = FREQH[g_fre];      //下次的中断时间,这个时间控制音调高低
    TL0 = FREQL[g_fre];
    TR0 = 1;     //启动T0, 由T0输出方波发音
    DelayOfBeat(beatTime); //每个音符的演奏时间
}
////////////////////////////////////////////////////////////////////////////////////
void PlayMusic(void) //播放相应歌曲的某个音符
{
    if (musicNum == 0)
    {
        beatTime = 0;
        PlayOneNote(); //发出一个音符
    }

    if ((musicNum == 1) && (playEnFlag == 1))
    {
        g_fre = song1[noteCnt] + 7 * song1[noteCnt + 1] - 1; //第i个是音符,第i+1个是第几个八度
        beatTime = song1[noteCnt + 2]; //读出时间长度数值
        noteCnt += 3;
        if (g_fre < 0) //判断歌曲的结束位,结束后转到下一首
        {
            noteCnt = 0; //下一首从头播放
            musicNum = 2;
        }
        PlayOneNote(); //发出一个音符
        
    }
    ////////////////////////////////////////////////////////////////////////////////////
    if ((musicNum == 2) && (playEnFlag == 1))
    {
        g_fre = song2[noteCnt] + 7 * song2[noteCnt + 1] - 1;
        beatTime = song2[noteCnt + 2];
        noteCnt += 3;
        if (g_fre < 0)
        {
            noteCnt = 0;
            musicNum = 3;
        }
        PlayOneNote();
    }
    ////////////////////////////////////////////////////////////////////////////////////
    if ((musicNum == 3) && (playEnFlag == 1))
    {
        g_fre = song3[noteCnt] + 7 * song3[noteCnt + 1] - 1;
        beatTime = song3[noteCnt + 2];
        noteCnt += 3;
        if (g_fre < 0)
        {
            noteCnt = 0;
            musicNum = 4;
        }
        PlayOneNote();
    }
    ////////////////////////////////////////////////////////////////////////////////////
    if ((musicNum == 4) && (playEnFlag == 1))
    {
        g_fre = song4[noteCnt] + 7 * song4[noteCnt + 1] - 1;
        beatTime = song4[noteCnt + 2];
        noteCnt += 3;
        if (g_fre < 0)
        {
            noteCnt = 0;
            musicNum = 5;
        }
        PlayOneNote();
    }
    ////////////////////////////////////////////////////////////////////////////////////
    if ((musicNum == 5) && (playEnFlag == 1))
    {
        g_fre = song5[noteCnt] + 7 * song5[noteCnt + 1] - 1;
        beatTime = song5[noteCnt + 2];
        noteCnt += 3;
        if (g_fre < 0)
        {
            noteCnt = 0;
            musicNum = 6;
        }
        PlayOneNote();
    }
    ////////////////////////////////////////////////////////////////////////////////////
    if ((musicNum == 6) && (playEnFlag == 1))
    {
        g_fre = song6[noteCnt] + 7 * song6[noteCnt + 1] - 1;
        beatTime = song6[noteCnt + 2];
        noteCnt += 3;
        if (g_fre < 0)
        {
            noteCnt = 0;
            musicNum = 7;
        }
        PlayOneNote();
    }
    ////////////////////////////////////////////////////////////////////////////////////
    if ((musicNum == 7) && (playEnFlag == 1))
    {
        g_fre = song7[noteCnt] + 7 * song7[noteCnt + 1] - 1;
        beatTime = song7[noteCnt + 2];
        noteCnt += 3;
        if (g_fre < 0)
        {
            noteCnt = 0;
            musicNum = 8;
        }
        PlayOneNote();
    }
    ////////////////////////////////////////////////////////////////////////////////////
    if ((musicNum == 8) && (playEnFlag == 1))
    {
        g_fre = song8[noteCnt] + 7 * song8[noteCnt + 1] - 1;
        beatTime = song8[noteCnt + 2];
        noteCnt += 3;
        if (g_fre < 0)
        {
            noteCnt = 0;
            musicNum = 1;
        }
        PlayOneNote();
    }
    ////////////////////////////////////////////////////////////////////////////////////
}

void KeyScan() //矩阵按键程序
{
    static unsigned char keyIdle = 0;
    static unsigned char keyBuf;
    static unsigned char cnt = 0;

    keyValue = 20; //按键值还原
    P2 = 0x0f;
    if ((P2 & 0x0f) != 0x0f) //按键按下
    {
        DelayMs(1); //按键消抖动
        if (((P2 & 0x0f) != 0x0f) && (keyIdle == 1))
        { //确认是按键按下
            keyIdle = 0;
            keyBuf = (P2 | 0xf0); //矩阵键盘扫描
            P2 = keyBuf;
            if (setTimeFlag == 1)
            {
                switch (P2)
                {
                case 0xee:
                    switch (setLocation)
                    {
                    case 1:
                    {
                        if (timeBufDec[1] == 0)
                        {
                            timeBufDec[1] = 100;
                        }
                        timeBufDec[1]--;
                        break;
                    }
                    case 2:
                    {
                        timeBufDec[2]--;
                        if (timeBufDec[2] < 1)
                        {
                            timeBufDec[2] = 12;
                        }
                        break;
                    }
                    case 3:
                    {
                        timeBufDec[3]--;
                        if (timeBufDec[3] < 1)
                        {
                            timeBufDec[3] = YDay(timeBufDec[1], timeBufDec[2]);
                        }
                        break;
                    }
                    case 4:
                    {
                        if (timeBufDec[4] == 0)
                        {
                            timeBufDec[4] = 24;
                        }
                        timeBufDec[4]--;
                        break;
                    }
                    case 5:
                    {
                        if (timeBufDec[5] == 0)
                        {
                            timeBufDec[5] = 60;
                        }
                        timeBufDec[5]--;
                        break;
                    }
                    case 6:
                    {
                        timeBufDec[7]--;
                        if (timeBufDec[7] < 1)
                        {
                            timeBufDec[7] = 7;
                        }
                        break;
                    }
                    default:;
                    }
                    break;
                case 0xde:
                    switch (setLocation)
                    {
                    case 1:
                    {
                        timeBufDec[1]++;
                        if (timeBufDec[1] >= 100)
                        {
                            timeBufDec[1] = 0;
                        }
                        break;
                    }
                    case 2:
                    {
                        timeBufDec[2]++;
                        if (timeBufDec[2] >= 13)
                        {
                            timeBufDec[2] = 1;
                        }
                        break;
                    }
                    case 3:
                    {
                        timeBufDec[3]++;
                        if (timeBufDec[3] >= YDay(timeBufDec[1], timeBufDec[2]) + 1)
                        {
                            timeBufDec[3] = 1;
                        }
                        break;
                    }
                    case 4:
                    {
                        timeBufDec[4]++;
                        if (timeBufDec[4] >= 24)
                        {
                            timeBufDec[4] = 0;
                        }
                        break;
                    }
                    case 5:
                    {
                        timeBufDec[5]++;
                        if (timeBufDec[5] >= 60)
                        {
                            timeBufDec[5] = 0;
                        }
                        break;
                    }
                    case 6:
                    {
                        timeBufDec[7]++;
                        if (timeBufDec[7] > 7)
                        {
                            timeBufDec[7] = 1;
                        }
                        break;
                    }
                    default:;
                    }
                    break;
                case 0xbe: 
                        setLocation = 7; //确定键
                        break;
                default:;
                }
            }
            else
            {
                if (musicNum == 0)
                {
                    dispTimeFlag = 0;
                    switch (P2)
                    {
                    case 0xee:
                        keyValue = 0;
                        g_fre = 8;
                        TR0 = 1;
                        break; //得到按键值  //打开定时器
                    case 0xde:
                        keyValue = 1;
                        g_fre = 9;
                        TR0 = 1;
                        break; //得到按键值
                    case 0xbe:
                        keyValue = 2;
                        g_fre = 10;
                        TR0 = 1;
                        break; //得到按键值
                    case 0x7e:
                        keyValue = 3;
                        g_fre = 11;
                        TR0 = 1;
                        break; //得到按键值

                    case 0xed:
                        keyValue = 4;
                        g_fre = 12;
                        TR0 = 1;
                        break; //得到按键值
                    case 0xdd:
                        keyValue = 5;
                        g_fre = 13;
                        TR0 = 1;
                        break; //得到按键值
                    case 0xbd:
                        keyValue = 6;
                        g_fre = 14;
                        TR0 = 1;
                        break; //得到按键值
                    case 0x7d:
                        keyValue = 7;
                        g_fre = 15;
                        TR0 = 1;
                        break; //得到按键值

                    case 0xeb:
                        keyValue = 8;
                        g_fre = 16;
                        TR0 = 1;
                        break; //得到按键值
                    case 0xdb:
                        keyValue = 9;
                        g_fre = 17;
                        TR0 = 1;
                        break; //得到按键值
                    case 0xbb:
                        keyValue = 10;
                        g_fre = 18;
                        TR0 = 1;
                        break; //得到按键值
                    case 0x7b:
                        keyValue = 11;
                        g_fre = 19;
                        TR0 = 1;
                        break; //得到按键值

                    case 0xe7:
                        keyValue = 12;
                        g_fre = 20;
                        TR0 = 1;
                        break; //得到按键值
                    case 0xd7:
                        keyValue = 13;
                        g_fre = 21;
                        TR0 = 1;
                        break; //得到按键值
                    case 0xb7:
                        keyValue = 14;
                        g_fre = 22;
                        TR0 = 1;
                        break; //得到按键值
                    case 0x77:
                        keyValue = 15;
                        g_fre = 23;
                        TR0 = 1;
                        break; //得到按键值
                    }
                }
                else
                {
                    switch (P2)
                    {
                    case 0xee:
                        dispSongFlag = 1;
                        noteCnt = 0;
                        if (musicNum <= 1) //上一曲
                        {
                            musicNum = MUSIC_AMOUNT;
                        }
                        else
                        {
                            musicNum--;
                        }
                        break; 
                    case 0xde:
                        dispSongFlag = 1;
                        noteCnt = 0;
                        if (musicNum >= MUSIC_AMOUNT) //下一曲
                        {
                            musicNum = 1;
                        }
                        else
                        {
                            musicNum++;
                        }
                        break; 
                    case 0xbe: 
                        playEnFlag = 1; //播放
                        break;
                    case 0x7e:
                        playEnFlag = 0; //暂停
                        TR0 = 0;
                        break; 
                    }
                }
            }
            while (P2 != 0x0f)
                P2 = 0x0f;
            TR0 = 0; //关闭定时器
        }
    }
    else //按键松开
    {
        keyIdle = 1;
    }

    /*********************歌曲播放*********************/
    KEY_SET = 1;
    if (!KEY_SET)
    {
        DelayMs(10); //按键去抖动
        while (!KEY_SET)
        {
            DelayMs(20);
            cnt++;
            if (cnt >= 75)
            {
                break;
            }
        }

        if (cnt >= 50) //长按键处理
        {
            TR0 = 0;
            setTimeFlag = 1;
            setLocation++;
        }
        else 
        {
            if (setTimeFlag)
            {
                setLocation++;
            }
            else
            {
                if (musicNum != 0)
                {
                    dispSongFlag = 1;
                    musicNum = 0;
                    noteCnt = 0;
                    TR0 = 0;
                }
                else
                {
                    dispSongFlag = 1;
                    musicNum = 1;
                    noteCnt = 0;
                }
            }
        }
        
        cnt = 0;
        while (!KEY_SET);
    }
}

void SetTime()
{
    TR1 = 0;
    LCD_WriteCommand(0x0F, 0); //打开闪烁
    // LCD_WriteCommand(0x0C, 0);
    switch (setLocation)
    {
        case 1: LCD_WriteCommand(0x80 + 1, 0); break;
        case 2: LCD_WriteCommand(0x80 + 4, 0); break;
        case 3: LCD_WriteCommand(0x80 + 7, 0); break;
        case 4: LCD_WriteCommand(0x80 + 0x40 + 1, 0); break;
        case 5: LCD_WriteCommand(0x80 + 0x40 + 4, 0); break;
        case 6: LCD_WriteCommand(0x80 + 14, 0); break;
        case 7: 
        {
            LCD_WriteCommand(0x0C, 0);
            LCD_WriteCommand(0x0C, 0);
            DS1302_Write_Time();
            setLocation = 0;
            setTimeFlag = 0;
            TR0 = 1;
            break;
        }
        default: ;
    }
    TR1 = 1;
}

void DispTime()
{

    dispRow0[0] = timeBufDec[1] / 10 +'0';
    dispRow0[1] = timeBufDec[1] % 10 +'0';
    dispRow0[3] = timeBufDec[2] / 10 +'0';
    dispRow0[4] = timeBufDec[2] % 10 +'0';
    dispRow0[6] = timeBufDec[3] / 10 +'0';
    dispRow0[7] = timeBufDec[3] % 10 +'0';

    dispRow1[0] = timeBufDec[4] / 10 +'0';
    dispRow1[1] = timeBufDec[4] % 10 +'0';
    dispRow1[3] = timeBufDec[5] / 10 +'0';
    dispRow1[4] = timeBufDec[5] % 10 +'0';
    // dispRow1[6] = timeBufDec[6] / 10 +'0';
    // dispRow1[7] = timeBufDec[6] % 10 +'0';

    LCD_DispStr(0, 0, dispRow0);
    LCD_DispStr(0, 1, dispRow1);

    switch (timeBufDec[7])
    {
        case 1: LCD_DispStr(10, 0, " Sun."); break;
        case 2: LCD_DispStr(10, 0, " Mon."); break;
        case 3: LCD_DispStr(10, 0, " Tue."); break;
        case 4: LCD_DispStr(10, 0, " Wed."); break;
        case 5: LCD_DispStr(10, 0, "Thur."); break;
        case 6: LCD_DispStr(10, 0, " Fri."); break;
        case 7: LCD_DispStr(10, 0, " Sat."); break;
        
        default: LCD_DispStr(10, 0, " Sun.");;
    }
}

void DispMusic()
{
    unsigned char table[] = "Music   ";

    if (musicNum == 0)
    {
        LCD_DispStr(7, 1, "KeyBoard");
    }
    else
    {
        table[6] = musicNum / 10 + '0';
        table[7] = musicNum % 10 + '0';
        LCD_DispStr(7, 1, table);
    }
}

void Timer0_Init()
{
    TMOD &= 0xF0;
    TMOD |= 0x01; //T0 T1 均在工作方式1
    TR0 = 0;
    ET0 = 1;     //T0开中断
    EA = 1;      //CPU开中断
}

void Timer1_Init()
{
    TMOD &= 0x0F;
    TMOD |= 0x10; //T0 T1 均在工作方式1
    TH1 = (65536 - 18432) / 256; //定时20ms
    TL1 = (65536 - 18432) % 256;
    TR1 = 1;
    ET1 = 1;     //T0开中断
    EA = 1;      //CPU开中断
}

void Timer0(void) interrupt 1 //T0中断程序,控制发音的音调
{
    SPEAKER = !SPEAKER; //输出方波, 发音
    TH0 = FREQH[g_fre];      //下次的中断时间,这个时间控制音调高低
    TL0 = FREQL[g_fre];
}

void Timer1(void) interrupt 3 //T1中断程序
{
    static unsigned char cnt = 0;

    TH1 = (65536 - 18432) / 256; //定时20ms
    TL1 = (65536 - 18432) % 256;

    cnt++;

    if (cnt >= 10)
    {
        dispTimeFlag = 1;
        cnt = 0;
    }
}
//程序结束

仿真演示视频:
https://www.bilibili.com/video/BV1Be4y1p7w4/

实物演示视频:
https://www.bilibili.com/video/BV17P411u7MA/

Proteus实现555计时器模拟简易电子琴 D触发器实现时钟信号分频一. 元器件介绍二. 原理分析三. 仿真实验 一. 元器件介绍 这里用到的元器件有: RESISTOR 电阻器 CAP 电容器 BUTTON 按钮 555 555计时器 BUZZER (ACTIVE) 有源蜂鸣器 SPEAKER (ACTIVE) 有源扬声器 PROWER (VCC) 接入电路电压 GROUND (GND) 接地 555计时器输出特定频率信号 滤波电路滤除未达到电子琴最低音高频率的方波信号(以避免未弹 阅读详情

相关推荐

基于51单片机8音电子琴仿真设计原理图

*单片机设计介绍, 基于51单片机8音电子琴仿真设计原理图

2201_75940997的博客 2470

基于51单片机16电子琴音乐盒仿真原理图方案设计

系统硬件电路设计 (末尾附文件) 原理图仿真图 主控模块 主控模块模块在整个系统中起着统筹的作用,需要检测盘,温度传感器等各种参数,同时驱动液晶显示相关参数,在这里我们选用了51系列单片机中的STC89C52单片机作为系统的主控芯片。 51系列单片机最初是由Intel 公司开发设计的,但后来Intel 公司把51 核的设计方案卖给了几家大的电子设计生产商,譬如 SST、Philip、Atmel 等大公司。因此市面上出现了各式各样的均以51 为内核的单片机。这些各大电子生产商推出的单片机都兼容51

ajing666的博客 1万+

基于51单片机的多功能电子琴音乐盒设计Proteus仿真实现

本文详细介绍了基于51单片机的多功能电子琴音乐盒设计Proteus仿真实现。项目融合电子琴演奏、音乐播放和实时时钟功能,采用STC89C52主控,通过矩阵盘和LCD1602实现交互。文章深入解析硬件电路设计、软件架构及Proteus仿真技巧,提供实用优化方案,适合电子专业实训和DIY爱好者参考。

weixin_29189381的博客 572

基于51单片机设计的简易电子琴

基于51单片机设计的简易电子琴

zy010101博客 4万+

基于51单片机的7电子琴音乐播放器proteus

设计仅供参考 基于51单片机的7电子琴音乐播放器proteus仿真+源码+原理图原理图:Altium Designer 仿真proteus 7.8 程序编译器:keil 4/keil 5 编程语言:C语言 编号C0036有需要学习可以点击下方链接下载 ...

weixin_52733843的博客 1万+

基于AT89C52与Proteus电子琴音乐盒仿真设计实战

单片机是嵌入式系统的核心,通过编程控制外围硬件实现特定功能是其基本原理。在嵌入式开发学习中,仿真技术能有效降低硬件门槛,加速原理验证。Proteus作为一款主流的电路仿真软件,结合Keil开发环境,为学习者提供了从设计到验证的完整虚拟平台。本项目以经典的AT89C52单片机为核心,通过C51编程驱动矩阵盘和无源蜂鸣器,实现了电子琴弹奏与音乐盒播放两大功能。该案例将单片机定时器、I/O口控制、盘扫描等基础技术融入一个完整的仿真项目中,为初学者提供了一个低成本、高效率的实践路径,特别适合课程设计、毕业设计

weixin_30596023的博客 303

286、基于51单片机电子琴音乐播放系统Proteus仿真设计(程序+Proteus仿真+原理图+PCB源文件+元器件清单+参考论文+系统框图+配套资料等)

方案一:STM32系列单片机控制,该型号单片机为LQFP44封装,内部资源足够用于本次设计。STM32F103系列芯片最高工作频率可达72MHZ,在存储器的01等等待周期仿真时可达到1.25Mip/MHZ(Dhrystone2.1)。内部128k字节的闪存程序存储器,也就是说代码量可以写到128k字节,本次设计足够,内部高达20K字节的SRAM。

ENGLISH_HHZ的专栏 1153

932、基于51单片机电子琴(数码管,LED,24C02)(程序+Proteus仿真+原理图+流程图+元器件清单+配套资料等)

(2)STC单片机执行指令的速度很快,大约是AT的3-30倍,尽管快是好事,但这样一来,你在AT上好使的程序在STC上不一定好用,最典型的例子就是那些对时序有严格要求的模块,用STC时注意得加长延时,大约是AT的10—30倍就差不多,这一点自己调试就知道了。(3)STC单片机对工作环境的要求比较低,电压低于5伏时仍然正常工作,甚至3伏到4伏之间都还可以工作,然而这样的环境下AT肯定不行了,所以当一个系统用STC单片机好用,但用AT的单片机不工作时,直接查最小系统,看单片机的供电是否正常。

ENGLISH_HHZ的专栏 722

基于AT89C52单片机电子琴设计:定时器中断与矩阵盘扫描实战

在嵌入式系统与单片机应用开发中,定时器和中断是核心的底层硬件控制机制。定时器通过精确计时产生周期性事件,中断则允许CPU及时响应外部或内部事件,二者结合是实现实时控制与信号生成的基础。其技术价值在于能高效、可靠地处理时序关型任务,例如在工业控制、智能仪表和消费电子中广泛应用的音频生成与用户输入检测。本文聚焦于一个经典的应用场景——电子琴/音乐盒设计,具体阐述了如何利用AT89C52单片机的定时器中断来产生不同频率的方波以模拟音符,并配合矩阵盘扫描技术实现按识别。通过Proteus仿真与Keil开发的

weixin_34080571的博客 297

基于51单片机的音乐播放器毕业设计:从原理到实现的完整指南

单片机作为嵌入式系统的核心,通过执行存储的程序指令来控制外围设备,实现特定功能。其工作原理基于冯·诺依曼或哈佛体系结构,通过定时器、中断和GPIO等外设与外部世界交互。在嵌入式开发中,掌握单片机编程和硬件设计是构建智能设备的基础,具有极高的技术价值。这类技术广泛应用于智能家居、工业控制、消费电子和教育实验等领域。本文聚焦于一个经典的教学与实践项目——基于单片机的音乐播放器,详细阐述了使用STC89C52RC等51单片机,结合定时器中断和乐谱编码技术,驱动蜂鸣器播放音乐的实现方案。内容涵盖了从系统设计、硬件电

conghuang7343的博客 800

从简谱到蜂鸣器:基于STC89C52的音乐编码与播放系统设计

本文详细介绍了基于STC89C52单片机的音乐编码与播放系统设计,涵盖音调生成原理、节拍控制算法和简谱编码方法。通过无源蜂鸣器和LCD显示,实现高效音乐播放,适用于音乐盒等嵌入式应用。文章还分享了硬件设计精要和软件优化策略,帮助开发者快速构建稳定可靠的音乐播放系统。

weixin_34021089的博客 345

基于51单片机简易电子琴设计(含Keil程序Proteus文件)

1.使用数码管显示按下的音符。⒉通过矩阵盘输入音符,蜂鸣器就会发出对应的音调。3.按下弹奏/停止按后,点击歌曲选择,并选择矩阵盘上的任一按就能播放指定的音调。

XCHardware的博客 3963

Proteus仿真】【51单片机音乐盒&电子琴设计

主要功能如下: 1、能播放/切换音乐,播放速度调节 2、能当作播放器&电子琴 3、电子琴支持低音区1-7,中音区1-7和高音区1-2

Hello_Muyi的博客 9528

29、基于51单片机简易电子琴(有存储功能)系统设计程序+原理图+仿真+参考论文+元件清单+开题报告)

(1)有优异的性能价格比。(2)集成度高、体积小、有很高的可靠性。单片机把各功能部件集成在一块芯片上,内部采用总线结构,减少了各芯片之间的连线,大大提高了单片机的可靠性和抗干扰能力。另外,其体积小,对于强磁场环境易于采取屏蔽措施,适合在恶劣环境下工作。(3)控制功能强。为了满足工业控制的要求,一般单片机的指令系统中均有极丰富的转移指令、I/O口的逻辑操作以及位处理功能。单片机的逻辑控制功能及运行速度均高于同一档次的微机。(4)低功耗、低电压,便于生产便携式产品。

qq_46030545的博客 5192

单片机】利用独立盘弹琴

利用51单片机的独立盘弹琴

请赐予我力量 1255

基于51单片机电子琴Protues仿真设计

设计51单片机为控制核心,设计一种基于51单片机电子琴。整个系统包括MCU、晶振电路、时钟电路、功能按、琴盘、扬声器以及数码管显示。该设计能够实现自然大调音阶do re mi fa sol la si的低、中、高音阶的演奏,能够实现“两只老虎”、“粉刷匠”、“祝你生日快乐”三首经典曲目的播放,并通过按切换播放的顺序,利用共阴数码管实时显示播放的乐谱序号。

weixin_53402301的博客 3334

基于51单片机的多音阶电子琴proteus仿真程序原理设计

硬件设计 仿真图1: 总共二十几个按,高中低音阶,同时可以播放歌曲,开机可设置显示学号 仿真图2: 7个音符按,切换高中低音阶,同时可以播放歌曲 程序设计 #include<reg52.h> #define keyport P1 #define uchar unsigned char #define uint unsigned int uchar high,low; //定时器预装值的高8位和低8位 sbit speak=P3^0; sbit gao=P3^5; sbit di=P

weixin_44116175的博客 3687

基于51单片机简易电子琴设计

一.硬件方案 利用所给盘的1,2,3,4,5,6,7,8八个,能够发出7个不同的音调,而且有一个按可以自动播放歌曲,要求按按下时发声,松开延时一小段时间,中间再按别的则发另外一音调的声音,当系统扫描到盘按下,则快速检测出是哪一个按被按下,然后单片机的定时器启动,发出一定频率的脉冲,该频率的脉冲经喇叭驱动电路放大滤波后,就会发出相应的音调。 主要由51单片机+最小系统+蜂鸣器+按+数码管等;如图: 二.设计功能 (1)使用了8个按对应8个音符; (2)使用了3个按来播放内置的3首歌曲;

eletronicfish的博客 9441
上一篇: 基于51单片机的手机电话拨号盘模拟proteus仿真
下一篇: 基于STM32单片机的智能药盒带语音播报原理图程序
极寒钛科技
博客等级 码龄6年 826粉丝 · 161原创
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值