本设计C语言编译的基于51单片机的MP3设计,用的是BY8001串口语音芯片,主要问题是串口中断代码算法详解,不理解。


/*
   这个音乐模块,是一个串口控制的模块,我们提供的有串口命令手册,内存卡插上之后,想让模块去播放哪个音乐,加减音量,可以
通过串口命令去控制模块完成这写功能。
*/

#include<reg52.h>              //头文件
#define uchar unsigned char       //简化宏定义
#define uint unsigned int       //简化宏定义
#define LCD1602_dat P0         //LCD1602数据脚

uchar code ss[]= //LCD1602显示特殊符号去模数组
{
    0x00,0x00,0x0E,0x0E,0x0E,0x0E,0x00,0x00,/*"停止",0*/
    0x00,0x00,0x0A,0x0A,0x0A,0x0A,0x00,0x00,/*"暂停",0*/    
    0x00,0x08,0x0C,0x0E,0x0F,0x0E,0x0C,0x08,/*"播放",0*/
    0x00,0x00,0x03,0x03,0x03,0x03,0x00,0x00,/*"喇叭01",0*/
    0x04,0x0C,0x1C,0x1C,0x1C,0x1C,0x0C,0x04,/*"喇叭02",0*/
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
};


/*
    1602液晶,是常用的显示器件,一共是16个管脚,其中有八个管脚是数据传输管脚,有三个管脚是数据命令使能端管脚,还有两组电源管脚,
其中一组电源管脚是给整个液晶进行供电的,还有一组电源是单纯的背景光电源,还剩下的最后一个管脚是对比度调节管脚,一般接上一个3K电
阻再接地即可。
一般我们用的函数,无非就是  LCD1602_write 和 LCD1602_writebyte
LCD1602_write(x,y);   这个函数括号里面可以填写两个数据,第一个数据只能是 0  1 ,是0就说明第二个数据对液晶来说就是命令,填1就说明
第二个数据对于液晶来说就是要显示的数据。
LCD1602_writebyte();  这个函数里面直接填上要显示的字符串即可,自动进行显示
 
*/

/*
数据显示的时候一般的处理:

    首先,无论是数码管显示还是液晶显示,进行显示的时候绝对都是一个一个进行显示的,那么,比如说一个数据123,一百二十三,
进行显示的时候,要先显示1,然后是2,然后是3,那么怎么把数据提取出来??   
提取百位    123/100=1
提取十位    123/10=12      12%10=2     “%”是取余的意思,像这个,就是12对10取余,换句话说,12除以10,然后取余数,就是2
提取个位    123%10=3       解释同上

取余的用法也有很多种,大家只要知道出现这个的时候,一般都是进行数据提取的就行


然后
如果您是数码管显示数据,将提取的数据放到段码数组里面送给IO即可,
如果是液晶显示,需要将数据转化成字符,因为液晶是字符屏,只能显示字符数据,数据0对应的字符是0x30,数据1对应的字符是0x31,
所以将提取出的数据直接加上0x30送给液晶即可,或者加上'0' 也是一样的 


 
*/

sbit LCD1602_rs=P2^5;       //LCD1602控制脚
sbit LCD1602_rw=P2^6;       //LCD1602控制脚
sbit LCD1602_e=P2^7;       //LCD1602控制脚

uint times,timer;
uchar dat[20],bz,time,ACIS,shu[4],bits,vox=15;
uchar sec,min,min1,sec1,mode,vol=15,state,EQ,ms;
uint sum,sum_song;
bit SD,Again,wc;

uchar once;

/********************************************************************
* 名称 : delay()
* 功能 : 小延时。                                                     
* 输入 : 无
* 输出 : 无
***********************************************************************/

void delay(uint T)
{
    while(T--);
}

/********************************************************************
* 名称 : LCD1602_write(uchar order,dat)
* 功能 : 1602写如数据函数
* 输入 : 输入的命令值
* 输出 : 无
***********************************************************************/
void LCD1602_write(uchar order,dat)                  //1602 一个字节  处理     order为命令与数据选择    dat为写的数据内容
{
    LCD1602_rs=order;
    LCD1602_dat=dat;

    LCD1602_e=1;
    delay(1);
    LCD1602_e=0;                                                                                                     
}
/********************************************************************
* 名称 : LCD1602_writebye(uchar *prointer)
* 功能 : 1602写入数据函数  指针式
* 输入 : 输入的命令值
* 输出 : 无
***********************************************************************/
void LCD1602_writebyte(uchar *prointer)                   //1602 字符串    处理
{
    while(*prointer!='\0')
    {
        LCD1602_write(1,*prointer);
        prointer++;                                                
    }
}

/********************************************************************
* 名称 : LCD1602_Save(uchar add,uchar *pic_num)   add为地址    pic_num 为存储数据  
* 功能 : 自定义字符                         地址为0~7
* 输入 : 无
* 输出 : 无
***********************************************************************/
void LCD1602_Save(uchar *pic_num)
{
    uchar a1;
    uchar a2;
    uchar add;
    for(a1=0;a1<8;a1++)
    {
        add=a1<<3;
        for(a2=0;a2<8;a2++)
        {
            LCD1602_write(0,0x40|add+a2);
            LCD1602_write(1,*pic_num++);
        }
    }
}

/********************************************************************
* 名称 : LCD1602_cls()
* 功能 : 初始化1602液晶 
* 输入 : 无
* 输出 : 无
***********************************************************************/
void LCD1602_cls()                                     //1602 初始化
{
    LCD1602_rw=0;
    LCD1602_write(0,0x01);     //1602 清屏 指令
    delay(1500);
    LCD1602_write(0,0x38);     // 功能设置 8位、5*7点阵
    delay(1500);
    LCD1602_write(0,0x0c);     //设置 光标   不显示开关、不显示光标、字符不闪烁
    LCD1602_write(0,0x06);
    LCD1602_write(0,0xd0);
    delay(1500);
    LCD1602_write(0,0x80);
    LCD1602_writebyte("    MP3_Init    ");
}

/********************************************************************
* 名称 : void write_send(uchar dat)
* 功能 : 串口数据发送函数
* 输入 : 需要发送的数据    dat
* 输出 : 无
***********************************************************************/
void write_send(uchar dat)
{
    SBUF=dat;
    while(!TI);      //数据发送接受判断
    TI=0;         //置0
}

/********************************************************************
* 名称 : void send_6(uchar a1,a2,a3,a4,a5,a6)
* 功能 : 串口数据串发送6位数据
* 输入 : a1————a6  发送数据的内容
* 输出 : 无
***********************************************************************/
void send_6(uchar a1,a2,a3,a4,a5,a6)
{
    uchar a7;
    write_send(a1);
    write_send(a2);
    write_send(a3);
    write_send(a4);
    a7=a2^a3^a4;    //求校验位
    write_send(a7);
    write_send(a6);
}
/********************************************************************
* 名称 : void send_5(uchar a1,a2,a3,a4,a5)
* 功能 : 串口数据串发送5位数据
* 输入 : a1————a5  发送数据的内容
* 输出 : 无
***********************************************************************/
void send_5(uchar a1,a2,a3,a4,a5)
{
    uchar a6;
    write_send(a1);
    write_send(a2);
    write_send(a3);
    a6=a2^a3;       //求校验位
    write_send(a6);
    write_send(a5);
}


/********************************************************************
* 名称 : void show()
* 功能 : 系统显示函
* 输入 : 无
* 输出 : 无
***********************************************************************/
void show()
{
    LCD1602_write(0,0x80);      //LCD1602显示内容位置设定
    LCD1602_write(1,0x30+min/10%10);       //显示当前播放时间分
    LCD1602_write(1,0x30+min%10);
    LCD1602_writebyte(":");
    LCD1602_write(1,0x30+sec/10%10);       //显示当前时间播放秒
    LCD1602_write(1,0x30+sec%10);
    LCD1602_writebyte("-");
    LCD1602_write(1,0x30+min1/10%10);  //总时间分
    LCD1602_write(1,0x30+min1%10);
    LCD1602_writebyte(":");
    LCD1602_write(1,0x30+sec1/10%10);  //总时间秒
    LCD1602_write(1,0x30+sec1%10);
    LCD1602_writebyte(" ");
    LCD1602_write(1,3);
    LCD1602_write(1,4);
    LCD1602_write(1,0x30+vox/10%10);//显示当前音量
    LCD1602_write(1,0x30+vox%10);

    
    LCD1602_write(0,0xC0);       //LCD1602显示内容位置设定
    LCD1602_write(1,0x30+sum_song/100%10);    //显示总歌曲数
    LCD1602_write(1,0x30+sum_song/10%10);
    LCD1602_write(1,0x30+sum_song%10);
    LCD1602_writebyte("/");
    LCD1602_write(1,0x30+sum/100%10);  //显示当前歌曲数
    LCD1602_write(1,0x30+sum/10%10);
    LCD1602_write(1,0x30+sum%10);
    LCD1602_writebyte(" ");


/*
N   --   NO  原音
P   --   POP 流行音乐
R   --   ROCK 摇滚
J    --   JAZZ  爵士
C   --   CLASSIC 古典
B   --   BASS  低音效
*/
    switch(EQ)                //判断显示当前音效
    {                            
        case 0:
        LCD1602_writebyte("N");        //显示当前音效
        break;

        case 1:
        LCD1602_writebyte("P");       //显示当前音效
        break;

        case 2:
        LCD1602_writebyte("R");     //显示当前音效
        break;

        case 3:
        LCD1602_writebyte("J");      //显示当前音效
        break;

        case 4:
        LCD1602_writebyte("C");     //显示当前音效
        break;

        case 5:
        LCD1602_writebyte("B");       //显示当前音效
        break;
    }


    LCD1602_writebyte(" ");
    switch(state)         //系统状态显示
    {
        case 0:
        LCD1602_write(1,0);
        break;

        case 1:
        LCD1602_write(1,2);
        break;

        case 2:
        LCD1602_write(1,1);
        break;
    }
    LCD1602_writebyte(" ");
/*
N   --   NO 无循环(播放完当前歌曲停止)
A   --   ALL  全盘循环
F   --   FOLDER  文件夹循环
O   --   ONE   单曲循环
R   --   RANDOM 随机播放
*/
    switch(mode)   //显示系统循环模式
    {
        case 0:
        LCD1602_writebyte("A");    //系统循环模式
        break;

        case 1:                      //系统循环模式
        LCD1602_writebyte("F");
        break;

        case 2:
        LCD1602_writebyte("O");     //系统循环模式
        break;

        case 3:
        LCD1602_writebyte("R");
        break;                     //系统循环模式

        case 4:
        LCD1602_writebyte("N");    //系统循环模式
        break;
    }



    LCD1602_writebyte("  ");

    if(SD==0)                  //显示当前应用的设备
    {
        LCD1602_writebyte("U");         //U盘
    }else
    {
        LCD1602_writebyte("S");              //SD卡
    }
}


/********************************************************************
* 名称 : void read_dat()
* 功能 : 读取系统状态数据
* 输入 : 无
* 输出 : 无
***********************************************************************/
void read_dat()
{
    if(state!=1&&ACIS>7) ACIS=0;  //循环检测
    switch(ACIS)  
    {
        case 0:
            send_5(0x7E,0x03,0x13,0x01,0xEF);//    播放模式    
        break;

        case 1:
            send_5(0x7E,0x03,0x12,0x01,0xEF);//    查看 当前EQ 音效    
        break;

        case 7:
            send_5(0x7E,0x03,0x10,0x01,0xEF);//    播放状态
        break;

        case 2:
            send_5(0x7E,0x03,0x1C,0x01,0xEF);//    歌曲播放时间
        break;

        case 3:
            send_5(0x7E,0x03,0x1D,0x01,0xEF);//    歌曲总时间
        break;

        case 4:
            send_5(0x7E,0x03,0x18,0x01,0xEF);//    查看当前设备 U  S
        break;

        case 5:
            if(SD==1)
            {
                send_5(0x7E,0x03,0x15,0x01,0xEF);     //SD 卡总曲目
            }else
            {
                send_5(0x7E,0x03,0x16,0x01,0xEF);    //U盘  总曲目
            }    
        break;

        case 6:
            if(SD==1)
            {
                send_5(0x7E,0x03,0x19,0x01,0xEF);     //SD播放当前曲目数
            }else
            {
                send_5(0x7E,0x03,0x1A,0x01,0xEF);     //U盘当前曲目
            }
        break;        
    }
        
}


/********************************************************************
* 名称 : void key()
* 功能 : 系统按键处理函数
* 输入 : 无
* 输出 : 无
***********************************************************************/
void key()
{
    uchar fs=255;  //暂存按键数据变量
    P1=0xff;
    if(P1!=0xff)   //判断是否有按键按下
    {
        delay(1000);  //按键延时去抖
        P1=0xff;
        if(P1!=0xff)   //再次判断 是否有按键按下
        {
            fs=P1;
            time=0;
            P1=0xff;
            while(P1!=0xff)        //等待按键释放            松开
            {
                if(time>39&&Again==0)
                {
                    Again=1;
                //    time=40;
                    switch(fs)      //判断按下的按键
                    {
                        case 0xfe:                 //音量++++
                            if(vol<20) vol=15;
                            if(vox<20) vox++;
                            send_6(0x7E,0x04,0x31,vox,0x22,0xEF);
                        break;

                        case 0xfd:        //音量-----
                            if(vol>0) vol=15;
                            if(vox>0) vox--;
                            send_6(0x7E,0x04,0x31,vox,0x22,0xEF);
                        break;

                        case 0xfb:        //快进
                            times+=28;
                            if(times>timer) times=timer;
                            min=times/600;
                            sec=times/10%60;
                            send_5(0x7E,0x03,0x0A,0x09,0xEF);
                        break;

                        case 0xf7:          //快退
                            if(times>27)times-=28;
                            else times=0;
                            min=times/600;
                            sec=times/10%60;
                            send_5(0x7E,0x03,0x0B,0x08,0xEF);
                        break;

                    }
                }
                show();//调出显示函数    
            }
            switch(fs)
            {
                case 0xfe:
                    if(time<40)          //上一曲
                    {
                        send_5(0x7E,0x03,0x04,0x07,0xEF);
                        state=1;
                        once=1;
                        ACIS=6;
                    }    
                break;
                
                case 0xfd:
                    if(time<40)       //下一曲
                    {
                        send_5(0x7E,0x03,0x03,0x00,0xEF);
                        state=1;
                        once=1;
                        ACIS=6;
                    }
                break;
                
                case 0xfb:
                    if(time<40)
                    {                 //快进
                        times+=27;
                        if(times>timer) times=timer;
                        min=times/600;
                        sec=times/10%60;
                        send_5(0x7E,0x03,0x0A,0x09,0xEF);
                    }
                break;
                
                case 0xf7:
                    if(time<40)        //快退
                    {
                        if(times>26)times-=27;
                        else times=0;
                        min=times/600;
                        sec=times/10%60;
                        send_5(0x7E,0x03,0x0B,0x08,0xEF);            
                    }
                break;
                
                case 0xef:            //模式切换
                    if(state==2|state==0)
                    {
                        state=1;
                        send_5(0x7E,0x03,0x01,0x02,0xEF);
                    }else
                    {
                        state=2;
                        send_5(0x7E,0x03,0x02,0x01,0xEF);                    
                    }
                break;
                
                case 0xdf:      //停止
                    state=0;
                    send_5(0x7E,0x03,0x0E,0x0D,0xEF);
                break;
                
                case 0xbf:     //音效
                    EQ=(EQ+1)%6;
                    send_6(0x7E,0x04,0x32,EQ,0x36,0xEF);
                break;
                
                case 0x7f:        //模式切换
                    mode=(mode+1)%5;
                    send_6(0x7E,0x04,0x33,mode,0x35,0xEF);
                break;
                
                    
            }    
        }
    }    
}


/********************************************************************
* 名称 :void main()
* 功能 : 主函数
* 输入 : 无
* 输出 : 无
***********************************************************************/
void main()
{
    TMOD=0x21;          //定时器初始化
    TH1=0xfd;          //波特率  9600  初始化
    TL1=0xfd;
    TH0=0x3c;        //定时器0定时初值
    TL0=0xb0;
    SCON=0x50;    //发送接收
    EA=1;          //打开终端
    ES=1;         //打开串口中断
    ET0=1;          //打开定时器0 
    TR1=1;
    LCD1602_cls();     //LCD1602初始化
    LCD1602_Save(ss);     //LCD1602存储符号
    TR0=1;
    send_6(0x7E,0x04,0x31,15,0x22,0xEF);     //音量设置
    while(1)
    {
        times=min*600+sec*10;           //当前时间计算
        timer=min1*600+sec1*10;           //总歌曲时间计算
        show();                           //显示函数调用
        key();                            //按键函数调用
        read_dat();                       //读取系统状态
    }
}




/********************************************************************
* 名称 :void main()
* 功能 : 主函数
* 输入 : 无
* 输出 : 无
***********************************************************************/
void init_1() interrupt 1
{
    TH0=0x3c;       //重新附定时器初值
    TL0=0xb0;
    time++;             //计时变量计时自+++
    if(time>40&&time%5==0)   //按键长按处理
    {
        Again=0;      
    }
    if(time>99) time=60;   //按键长按处理
    ms++;
    if((ms==5|ms==15)&&state==1)  //定时读取系统状态
    {
        ACIS=2;            //读取系统状态
    }
    if(ms==10&&once!=0)
    {
        if(once==1)ACIS=6;
        else if(once==2) ACIS=3;
        once++;
        if(once>2) once=0;
    }
    if(ms>19)     //定时一秒          50模式*20  ===1000ms
    {
        ms=0;
        if(state==1&&once==0) ACIS=7;
        else if(once!=0)
        {
            if(once==1)ACIS=6;
            else if(once==2) ACIS=3;
            once++;
            if(once>2) once=0;
        }
    }
}


/********************************************************************
* 名称 : void init_4() interrupt 4
* 功能 : 串口中断函数
* 输入 : 无
* 输出 : 无
***********************************************************************/
void init_4() interrupt 4
{
    if(RI)     //中断标志位
    {
        RI=0;    //置0
        dat[bz]=SBUF;     //缓冲暂存数据
        switch(bz)        //判断数据内容
        {
            case 0:
            if(dat[bz]=='O')     //OK 判断
            {
                bz=1;
            }else if(dat[bz]=='S')      //STOP 判断
            {
                bz=10;
            }else
            {
                bz=0;
            }
            break;
            
            case 1:
            if(dat[bz]=='K')      //OK 判断
            {
                bz=2; 
                bits=0;
            }else
            {
                bz=0;
            }
            break;
            
            case 2:
            if(dat[bz]==0x0D)
            {
                bz=3;
            }else
            {
                if(dat[bz]<=0x39)shu[bits]=dat[bz]-0x30;
                else shu[bits]=dat[bz]-0x61+10;
                bits++;
                if(bits>4) 
                {
                    bits=0;
                    bz=0;
                }
            }
            break;
            
            case 3:
            switch(ACIS)  //判断MP3模块返回的数据内容
            {
                case 0:
                mode=shu[0]*4096+shu[1]*256+shu[2]*16+shu[3];//循环模式
                break;

                case 1:
                EQ=shu[0]*4096+shu[1]*256+shu[2]*16+shu[3];        //音效
                break;

                case 2:
                min=(shu[0]*4096+shu[1]*256+shu[2]*16+shu[3])/60; //当前播放时间
                sec=(shu[0]*4096+shu[1]*256+shu[2]*16+shu[3])%60;
                break;

                case 3:
                min1=(shu[0]*4096+shu[1]*256+shu[2]*16+shu[3])/60;     //系统总时间
                sec1=(shu[0]*4096+shu[1]*256+shu[2]*16+shu[3])%60;
                
                break;

                case 4:
                SD=shu[0]*4096+shu[1]*256+shu[2]*16+shu[3];      //播放外接设备
                break;

                case 5:
                sum=shu[0]*4096+shu[1]*256+shu[2]*16+shu[3];   //当前歌曲序号
                break;

                case 6:
                sum_song=shu[0]*4096+shu[1]*256+shu[2]*16+shu[3];    //总的歌曲数
                
                break;

                case 7:
                state=shu[0]*4096+shu[1]*256+shu[2]*16+shu[3];      //系统状态
                if(state>2) state=1;
                break;
            }
            if(state!=1)    ACIS=(ACIS+1)%8;   
            else ACIS=8;
            bz=0;
            break;
            
            case 10:
                if(dat[bz]=='T')   //STOP 判断
                {
                    bz=11;
                }else
                {
                    bz=0;
                }        
            break;

            case 11:
                if(dat[bz]=='O')    //STOP 判断
                {
                    bz=12;
                }else
                {
                    bz=0;
                }    
            break;

            case 12:
                if(dat[bz]=='P')    //STOP 判断
                {
                    state=2;
                    bz=0;
                }else
                {
                    bz=0;
                }    
            break;
                        
        }
    }
}












你好,我是有问必答小助手,非常抱歉,本次您提出的有问必答问题,技术专家团超时未为您做出解答


本次提问扣除的有问必答次数,将会以问答VIP体验卡(1次有问必答机会、商城购买实体图书享受95折优惠)的形式为您补发到账户。


因为有问必答VIP体验卡有效期仅有1天,您在需要使用的时候【私信】联系我,我会为您补发。