效果图:
淘宝网址:http://item.taobao.com/item.htm?id=15953525846
程序:
- #include <reg52.h>
- #include <intrins.h>
- #define uchar unsigned char
- #define uint unsigned int
- //录音和放音键IO口定义:
- sbit AN=P2^6;//放音键控制接口
- sbit set_key=P2^7;//录音键控制口
- // ISD4004控制口定义:
- sbit SS =P1^0; //4004片选
- sbit MOSI=P1^1; //4004数据输入
- sbit MISO=P1^2; //4004数据输出
- sbit SCLK=P1^3; //ISD4004时钟
- sbit INT =P1^4; //4004中断
- sbit STOP=P3^4; //4004复位
- sbit LED1 =P1^6; //录音指示灯
- //===============================LCD1602接口定义=====================
- /*-----------------------------------------------------
- |DB0-----P2.0 | DB4-----P2.4 | RW-------P0.1 |
- |DB1-----P2.1 | DB5-----P2.5 | RS-------P0.2 |
- |DB2-----P2.2 | DB6-----P2.6 | E--------P0.0 |
- |DB3-----P2.3 | DB7-----P2.7 | 注意,P0.0到P0.2需要接上拉电阻
- ---------------------------------------------------
- =============================================================*/
- #define LCM_Data P0 //LCD1602数据接口
- sbit LCM_RW = P2^3; //读写控制输入端,LCD1602的第五脚
- sbit LCM_RS = P2^4; //寄存器选择输入端,LCD1602的第四脚
- sbit LCM_E = P2^2; //使能信号输入端,LCD1602的第6脚
- //***************函数声明************************************************
- void WriteDataLCM(uchar WDLCM);//LCD模块写数据
- void WriteCommandLCM(uchar WCLCM,BuysC); //LCD模块写指令
- uchar ReadStatusLCM(void);//读LCD模块的忙标
- void DisplayOneChar(uchar X,uchar Y,uchar ASCII);//在第X+1行的第Y+1位置显示一个字符
- void LCMInit(void);
- void DelayUs(uint us); //微妙延时程序
- void DelayMs(uint Ms);//毫秒延时程序
- void init_t0();//定时器0初始化函数
- void setkey_treat(void);//录音键处理程序
- void upkey_treat(void);//播放键处理程序
- void display();//显示处理程序
- void isd_setrec(uchar adl,uchar adh);//发送setrec指令
- void isd_rec();//发送rec指令
- void isd_stop();//stop指令(停止当前操作)
- void isd_powerup();//发送上电指令
- void isd_stopwrdn();//发送掉电指令
- void isd_send(uchar isdx);//spi串行发送子程序,8位数据
- void isd_setplay(uchar adl,uchar adh);
- void isd_play();
- //程序中的一些常量定义
- uint time_total,st_add,end_add=0;
- uint adds[25];//25段语音的起始地址暂存
- uint adde[25];//25段语音的结束地址暂时
- uchar t0_crycle,count,count_flag,flag2,flag3,flag4;
- uchar second_count=170,msecond_count=0;
- //second_count为芯片录音的起始地址,起始地址本来是A0,也就是160,
- //我们从170开始录音吧。
- #define Busy 0x80 //用于检测LCM状态字中的Busy标识
- /*===========================================================================
- 主程序
- =============================================================================*/
- void main(void)
- {
- LED1=0;//灭录音指示灯
- flag3=0;
- flag4=0;
- time_total=340;//录音地址从170开始,对应的单片机开始计时的时间就是340*0.1秒
- adds[0]=170;
- count=0;
- LCMInit(); //1602初始化
- init_t0();//定时器初始化
- DisplayOneChar( 0,5,'I'); //开机时显示000 ISD4004-X
- DisplayOneChar( 0,6,'S');
- DisplayOneChar( 0,7,'D');
- DisplayOneChar( 0,8,'4');
- DisplayOneChar( 0,9,'0');
- DisplayOneChar( 0,10,'0');
- DisplayOneChar( 0,11,'4');
- DisplayOneChar( 0,12,'-');
- DisplayOneChar( 0,13,'X');
- while(1)
- {
- display();//显示处理
- upkey_treat();//放音键处理
- setkey_treat();//录音键处理
- }
- }
- //*******************************************
- //录音键处理程序
- //从指定地址开始录音的程序就是在这段里面
- void setkey_treat(void)
- {
- set_key=1;//置IO口为1,准备读入数据
- DelayUs(1);
- if(set_key==0)
- {
- if(flag3==0)//录音键和放音键互锁,录音好后,禁止再次录音。如果要再次录音,那就要复位单片机,重新开始录音
- {
- if(count==0)//判断是否为上电或复位以来第一次按录音键
- {
- st_add=170;
- }
- else
- {
- st_add=end_add+3;
- }//每段语言间隔3个地址
- adds[count]=st_add;//每段语音的起始地址暂时
- if(count>=25)//判断语音段数时候超过25段,因为单片机内存的关系?
- //本程序只录音25段,如果要录更多的语音,改为不可查询的即可
- {//如果超过25段,则覆盖之前的语音,从新开始录音
- count=0;
- st_add=170;
- time_total=340;
- }
- isd_powerup(); //AN键按下,ISD上电并延迟50ms
- isd_stopwrdn();
- isd_powerup();
- LED1=1;//录音指示灯亮,表示录音模式
- isd_setrec(st_add&0x00ff,st_add>>8); //从指定的地址
- if(INT==1)// 判定芯片有没有溢出
- {
- isd_rec(); //发送录音指令
- }
- time_total=st_add*2;//计时初始值计算
- TR0=1;//开计时器
- while(set_key==0);//等待本次录音结束
- TR0=0;//录音结束后停止计时
- isd_stop(); //发送4004停止命令
- end_add=time_total/2+2;//计算语音的结束地址
- adde[count]=end_add;//本段语音结束地址暂存
- LED1=0; //录音完毕,LED熄灭
- count++;//录音段数自加
- count_flag=count;//录音段数寄存
- flag2=1;
- flag4=1;//解锁放音键
- }
- }
- }
- //=================================================
- //放音机处理程序
- //从指定地址开始放本段语音就是这段程序
- void upkey_treat(void)
- {
- uchar ovflog;
- AN=1;//准备读入数据
- DelayUs(1);
- if(AN==0)//判断放音键是否动作
- {
- // if(flag4==1)//互锁录音键
- // {
- if(flag2==1)//判断是否为录音好后的第一次放音
- {
- count=0;//从第0段开始播放
- }
- isd_powerup(); //AN键按下,ISD上电并延迟50ms
- isd_stopwrdn();
- isd_powerup();
- //170 184 196 211
- // st_add=adds[count];//送当前语音的起始地址
- st_add=211;//送当前语音的起始地址
- isd_setplay(st_add&0x00ff,st_add>>8); //发送setplay指令,从指定地址开始放音
- isd_play(); //发送放音指令
- DelayUs(20);
- while(INT==1); //等待放音完毕的EOM中断信号
- isd_stop(); //放音完毕,发送stop指令
- while(AN==0); //
- isd_stop();
- count++;//语音段数自加
- flag2=0;
- flag3=1;
- if(count>=count_flag)//如果播放到最后一段后还按加键,则从第一段重新播放
- {
- count=0;
- }
- // }
- }
- }
- //************************************************?
- //发送rec指令
- void isd_rec()
- {
- isd_send(0xb0);
- SS=1;
- }
- //****************************************
- //发送setrec指令
- void isd_setrec(unsigned char adl,unsigned char adh)
- {
- DelayMs(1);
- isd_send(adl); //发送放音起始地址低位
- DelayUs(2);
- isd_send(adh); //发送放音起始地址高位
- DelayUs(2);
- isd_send(0xa0); //发送setplay指令字节
- SS=1;
- }
- //=============================================================================
- //**********************************************
- //定时器0中断程序
- void timer0() interrupt 1
- {
- TH0=(65536-50000)/256;
- TL0=(65536-50000)%256;
- t0_crycle++;
- if(t0_crycle==2)// 0.1秒
- {
- t0_crycle=0;
- time_total++;
- msecond_count++;
- if(msecond_count==10)//1秒
- {
- msecond_count=0;
- second_count++;
- if(second_count==60)
- {
- second_count=0;
- }
- }
- if(time_total==4800)time_total=0;
- }
- }
- //********************************************************************************************
- //定时器0初始化函数
- void init_t0()
- {
- TMOD=0x01;//设定定时器工作方式1,定时器定时50毫秒
- TH0=(65536-50000)/256;
- TL0=(65536-50000)%256;
- EA=1;//开总中断
- ET0=1;//允许定时器0中断
- t0_crycle=0;//定时器中断次数计数单元
- }
- //******************************************
- //显示处理程序
- void display()
- {
- uchar x;
- if(flag3==1||flag4==1)//判断是否有录音过或者放音过
- {
- x=count-1;
- if(x==255){x=count_flag-1;}
- }
- DisplayOneChar( 0,0,x/100+0x30); //显示当前语音是第几段
- DisplayOneChar( 0,1,x/10%10+0x30);
- DisplayOneChar( 0,2,x%10+0x30);
- if(flag3==0)//录音时显示本段语音的起始和结束地址
- {
- DisplayOneChar( 1,0,st_add/1000+0x30);//计算并显示千位
- DisplayOneChar( 1,1,st_add/100%10+0x30);
- DisplayOneChar( 1,2,st_add/10%10+0x30);
- DisplayOneChar( 1,3,st_add%10+0x30);
- DisplayOneChar( 1,4,'-');
- DisplayOneChar( 1,5,'-');
- DisplayOneChar( 1,6,end_add/1000+0x30);
- DisplayOneChar( 1,7,end_add/100%10+0x30);
- DisplayOneChar( 1,8,end_add/10%10+0x30);
- DisplayOneChar( 1,9,end_add%10+0x30);
- }
- if(flag4==1)//放音时显示本段语音的起始和结束地址
- {
- DisplayOneChar( 1,0,adds[x]/1000+0x30);
- DisplayOneChar( 1,1,adds[x]/100%10+0x30);
- DisplayOneChar( 1,2,adds[x]/10%10+0x30);
- DisplayOneChar( 1,3,adds[x]%10+0x30);
- DisplayOneChar( 1,4,'-');
- DisplayOneChar( 1,5,'-');
- DisplayOneChar( 1,6,adde[x]/1000+0x30);
- DisplayOneChar( 1,7,adde[x]/100%10+0x30);
- DisplayOneChar( 1,8,adde[x]/10%10+0x30);
- DisplayOneChar( 1,9,adde[x]%10+0x30);
- }
- }
- //======================================================================
- // LCM初始化
- //======================================================================
- void LCMInit(void)
- {
- LCM_Data = 0;
- WriteCommandLCM(0x38,0); //三次显示模式设置,不检测忙信号
- DelayMs(5);
- WriteCommandLCM(0x38,0);
- DelayMs(5);
- WriteCommandLCM(0x38,0);
- DelayMs(5);
- WriteCommandLCM(0x38,1); //显示模式设置,开始要求每次检测忙信号
- WriteCommandLCM(0x08,1); //关闭显示
- WriteCommandLCM(0x01,1); //显示清屏
- WriteCommandLCM(0x06,1); // 显示光标移动设置
- WriteCommandLCM(0x0C,1); // 显示开及光标设置
- DelayMs(100);
- }
- //*=====================================================================
- // 写数据函数: E =高脉冲 RS=1 RW=0
- //======================================================================
- void WriteDataLCM(uchar WDLCM)
- {
- ReadStatusLCM(); //检测忙
- LCM_Data = WDLCM;
- LCM_RS = 1;
- LCM_RW = 0;
- LCM_E = 0; //若晶振速度太高可以在这后加小的延时
- LCM_E = 0; //延时
- LCM_E = 1;
- }
- //*====================================================================
- // 写指令函数: E=高脉冲 RS=0 RW=0
- //======================================================================
- void WriteCommandLCM(unsigned char WCLCM,BuysC) //BuysC为0时忽略忙检测
- {
- if (BuysC) ReadStatusLCM(); //根据需要检测忙
- LCM_Data = WCLCM;
- LCM_RS = 0;
- LCM_RW = 0;
- LCM_E = 0;
- LCM_E = 0;
- LCM_E = 1;
- }
- //*====================================================================
- // 正常读写操作之前必须检测LCD控制器状态:E=1 RS=0 RW=1;
- // DB7: 0 LCD控制器空闲,1 LCD控制器忙。
- // 读状态
- //======================================================================
- unsigned char ReadStatusLCM(void)
- {
- LCM_Data = 0xFF;
- LCM_RS = 0;
- LCM_RW = 1;
- LCM_E = 0;
- LCM_E = 0;
- LCM_E = 1;
- while (LCM_Data & Busy); //检测忙信号
- return(LCM_Data);
- }
- //======================================================================
- //功 能: 在1602 指定位置显示一个字符:第一行位置0~15,第二行16~31
- //说 明: 第 X 行,第 y 列 注意:字符串不能长于16个字符
- //======================================================================
- void DisplayOneChar( unsigned char X, unsigned char Y, unsigned char ASCII)
- {
- X &= 0x1;
- Y &= 0xF; //限制Y不能大于15,X不能大于1
- if (X) Y |= 0x40; //当要显示第二行时地址码+0x40;
- Y |= 0x80; // 算出指令码
- WriteCommandLCM(Y, 0); //这里不检测忙信号,发送地址码
- WriteDataLCM(ASCII);
- }
- //======================================================================
- //spi串行发送子程序,8位数据
- void isd_send(uchar isdx)
- {
- uchar isx_counter;
- SS=0;//ss=0,打开spi通信端
- SCLK=0;
- for(isx_counter=0;isx_counter<8;isx_counter++)//先发低位再发高位,依次发送。
- {
- if((isdx&0x01)==1)
- MOSI=1;
- else
- MOSI=0;
- isdx=isdx>>1;
- SCLK=1;
- DelayUs(2);
- SCLK=0;
- DelayUs(2);
- }
- }
- //======================================================================
- //stop指令(停止当前操作)
- void isd_stop()//
- {
- DelayUs(10);
- isd_send(0x30);
- SS=1;
- DelayMs(50);
- }
- //======================================================================
- //发送上电指令
- void isd_powerup()//
- {
- DelayUs(10);
- SS=0;
- isd_send(0x20);
- SS=1;
- DelayMs(50);
- }
- //======================================================================
- //发送掉电指令
- void isd_stopwrdn()//
- {
- DelayUs(10);
- isd_send(0x10);
- SS=1;
- DelayMs(50);
- }
- void isd_play()//发送play指令
- {
- isd_send(0xf0);
- SS=1;
- }
- void isd_setplay(uchar adl,uchar adh)//发送setplay指令
- {
- DelayMs(1);
- isd_send(adl); //发送放音起始地址低位
- DelayUs(2);
- isd_send(adh); //发送放音起始地址高位
- DelayUs(2);
- isd_send(0xe0); //发送setplay指令字节
- SS=1;
- }
- void DelayUs(uint us)
- {
- while(us--);
- }
- //====================================================================
- // 设定延时时间:x*1ms
- //====================================================================
- void DelayMs(uint Ms)
- {
- uint i,TempCyc;
- for(i=0;i<Ms;i++)
- {
- TempCyc = 250;
- while(TempCyc--);
- }
- }