数字化语音存储与回放系统的设计班级:电信1202 姓名:吴建亮学号:201203090224 一、设计题目设计一个数字语音存储与回放系统,其系统框图如图图1.1所示。
设计要求如下:语音录放时间≧60s;语音输出功率≧0.5W,回放语音质量良好;设置“录音”、“放音”键,能显示录放时间;采用语音压缩算法,在不增加存储器容量的基础上增加录放时间。
麦克风语音输入通道ADC喇叭语音输出通道DAC微控置器存储器图1.1 数字化语音存储与回放系统框图二、方案设计数字化语音存储与回放系统的基本原理:语音的存储与回放系统将语音信号转化为电信号,经放大、滤波处理后通过A/D转换器转化为数字信号,然后将数字化的语音信号存放在大容量的存储器中;回放时,从存储器中取出数字化的语音信号,经D/A转化器转化为模拟信号,经滤波放大后驱动扬声器发出声音。
如图2.1所示为以C8051F360为核心的语音存储与回放系统原理框图。
麦克风前置放大器带通滤波器C8051F360ADC0 IDA0带通滤波器功率放大器喇叭键盘和显示外部存储器图2.1 语音存储回放系统原理框图三、模拟子系统设计1.语音输入通道电路的设计在语音存储与回放系统中,通过麦克风将声音信号转化为电信号。
麦克风内部含有一个电容元件和场效应管构成的内部前置放大器。
电容随机械振动发生变化,从而产生与声波成比例的变化电压。
麦克风在使用时需要通过一个电阻R1连接到电源对其进行偏置。
R1的阻值决定了麦克风的输出电阻和增益,通常在1~10kΩ之间。
麦克风输出的电信号比较微弱,信号增值在1~20mV之间。
前置放大器就是对麦克风输出的语音信号进行放大一边对其进一步处理。
前置放大电路有两种设计方案。
一种方案是针对双麦克风设计的前置放大器,由一级差分放大器和一级增益可调反向放大器组成的设计方案。
本次系统设计中,为了是器材简单,使用了后一种。
语音存储与回放系统中,模拟量输入通道和输出通道均需要带通滤波器。
带通滤波器的通带范围为300Hz~3。
4kHz,主要实现的功能如下:(1)保证300Hz~3。
4kHz的语音信号不失真的通过滤波器。
(2)滤除带通外的低频信号,减少工频等分量的干扰,减小噪声影响。
(3)滤除带通外的告辞谐波信号,减少因8kHz采样率引起的混叠失真,根据实际情况,该上限频率在2.7kHz左右。
根据上述条件设计的语音输入通道如图3.1所示。
图3.1语音输入通道2.语音输出通道设计当语音回放是,语音信号从C8051F360单片机的IDA0输出。
IDA0输出的语音心哈既包含了直流分量,也包含了由于最小分辨电压产生的高频噪声。
因此在语言输出通道应设置带通滤波电路。
为了能提供0.5W的功率输出,语音信号还要经过功放电路进行功率放大。
为了简化电路设计,语音输出通道选用了滤波和功放二合一的设计方案,其原理图如图3.2所示。
图3.2 语音输出通道电路原理图四、大容量存储接口设计M25P16与C8051F360单片机的接口如图4.1所示。
通过交叉开关将4线制SPI总线配置到P3.1、P3.2、P3.3、P3.4引脚上,分别与M25P16的时钟输入端C、数据输出端Q、数据输入端D和片选信号S相连。
图4.1中的W为写保护引脚,用于防止芯片内某些区域的数据被擦除或修改,当W接高电平时,写保护功能无效。
M25P16的HOLD引脚为保持信号,低电平时用于暂停芯片的串行通信。
SCK(P3.1) MISO(P3.2) MOSI(P3.3) NSS(P3.4)CQDVCC SHOLDVSSWC8051F360M25P16+5V 图4.1 C8051单片机和M25P16的接口五、系统软件设计语音存储回放系统软件的基本功能是通过按键控制系统实现录音和放音。
录音时,采集语音信号并将采集的数据存入M25P16中,放音时,从M25P16中读取数据送入DAC,系统分为以下几个框架:(1)人机接口的功能设计;(2)键盘处理程序设计;(3)M25P16的读写方案设计;(4)A/D和D/A转换器的控制;(5)擦除、录音和放音及时;(6)C8051F360单片机内部资源的使用;在语音存储与回放系统中,需要使用C8051F360单片机的ADC0、IDA0、SP10、定时器T0、定时器T1、定时器T2等资源,因此,在主程序的初始化程序中,需要对这些内部资源初始化。
根据上述分析。
语音存储回放系统的软件由主程序、键盘中断服务程序、ADC0中断服务程序、定时器T1中断服务程序、定时器T2中断服务程序组成。
主程序流程图5.1所示。
在主程序中完成初始化然后循环检测有无按键输入,并根据键值做相应的处理。
中断程序如图5.2、图5.3和图5.4所示。
详细的程序请见附件。
C8051F360和M25P16初始化开始禁止ADC0,禁止T1、T2中断显示初始界面键有效标志=1?清键有效标志显示“正在录音”允许ADC0,禁止T1中断M25P16地址初始化秒寄存器清零允许定时器T2中断显示“正在擦除”秒寄存器清零允许定时器T2中断调用M25P16擦除子程序禁止ADC0,禁止中T1、T2断显示初始界面显示“正在放音”禁止ADC0,允许T1中断M25P16地址初始化秒寄存器清零允许定时器T2中断根据键值散转“擦除”键?”放音“键?“录音”键?NY图5.1主程序流程图清T2中断标志软件计数器加11s时间到?Y软件计数器清零N 秒寄存器加1显示时间中断返回图5.2 T2中断服务程序中断返回清中断标志将A/D 转换值写入M25P16M25P16地址加1显示”录音结束“地址=200000H ?NY禁止ADC0,禁止T2中断图5.3 ADC0中断服务程序从M26P16读1字节数据到IDA0HM25P16地址加1地址=200000H?YN显示”放音结束“禁止T1、T2中断中断返回图5.4 T1中断服务程序四、系统调试1.单片机部分的调试单片机部分的调试主要是对系统部分进行调试,将EC5仿真器将单片机最小系统与PC机相连,采用C8051F单片机的集成开发环境Keil C51进行调试,主要步骤如下:(1)调试键盘显示程序;(2)测试单片机对M25P16读写是否正常;(3)测试C8051F360单片机的A/D和D/A转换器;2.模拟部分的调试(1)麦克风和前置放大器的调试;(2)带通滤波器的调试;(3)音频功放电路的调试;3.联机调试将语音输入通道的输出与单片机P2.0相连,单片机IDA0的输出与语言输出通道相连,将音频功放电路与0.5W,8Ω喇叭相连。
五、实验总结本次实验较为系统的设计和调试了基于80C51F360的数字语音存储回放系统,了解和掌握了外部RAM和单片机的SPI通信方法,更深一步了解了定时器和ADC0中断。
通过整个系统的设计,发现,容易出错的地方往往不是程序代码语法或者输入错误,而是系统逻辑问题,因此在以后的系统设计中,要重视逻辑的建立,在做系统之前,首先要画出没有挑剔的程序流程图,因为这是系统的思想路径,只有有了明确正确的路径,才能踏踏实实的编写程序,同时也会发现,以前恐惧的几百行代码已经变得多么易于理解了。
附件:/**************************************************************************************************/#include <C8051F360.H>#define uchar unsigned char#define uint unsigned int#define ulong unsigned longtypedef union ADDRESS{ulong addrlong;uchar addrbyte[4];}ADDRESS;#define CS1 0x4000 //外部扩展片选地址1#define CS2 0x8000 //外部扩展片选地址2#define LEDENCS 0xC004 //LCD模块背光控制片选信号#define WCOMADDR 0xC008 //写命令寄存器的地址#define WDATADDR 0xC009 //写数据寄存器的地址#define RCOMADDR 0xC00A //读命令寄存器的地址#define RDATADDR 0xC00B //读数据寄存器的地址#define KEYCS 0xC00C //读键盘的地址uint count; //软件计数器uint temp;uchar bbb; //存放键值uchar keycode; //LCD模块背光控制字,最低位为0时关LED背光,最低位为1时开启LED背光,uchar LEDCON=0x00;bit keysign=0;ulong addr=0x00;void OscInit(void); //振荡器初始化void PortIoInit(void); //端口初始化void XramInit(void); //void SmbInit(void); //I2C总线初始化void UartInit(void); //异步串口通信接口初始化void DacInit(void); //D/A转化器初始化void AdcInit(void); //A/D初始化void Int0Init(void); //外部中断初始化void TimerInit(void); //定时器初始化void PcaInit(void); //PCA初始化void InterruptsInit(void);// 中断系统初始化void InitDevice(void);void SPIInit(void); //spi初始化void LEDEN(); //开背光void CheckLcd(); //检查LCD是否空闲子程序void WriteCom(uchar n); //Lcd写指令子程序void WriteData(uchar m);//Lcd写数据子程序void InsitiLcd(); //Lcdvoid DispHan( uchar *a,uchar m,uchar k); //Lcd初始化子程序void Flash_Init(); //存储器初始化void Erase_All(); //存储器擦输void WR_flash(ulong addr,uchar data_byte); //写一字节uchar Read_flash(ulong addr); //读一字节/*********************************************主函数/**********************************************/void main(){InitDevice();Flash_Init();InsitiLcd();LEDEN();DispHan("擦去",0x80,0x04);DispHan("K1",0X90,0X02);DispHan("录音",0x83,0x04);DispHan("K2",0X93,0X02);DispHan("放音",0x86,0x04);DispHan("K3",0X96,0X02);while (1){if (keysign == 1){keysign = 0;switch(keycode){case 0x01:{count=0;temp=0;InsitiLcd();LEDEN();DispHan("擦写中",0x92,0x06); TR2=1;Erase_All();TR2=0;InsitiLcd();LEDEN();DispHan("擦除",0x80,0x04);DispHan("K1",0X90,0X02);DispHan("录音",0x83,0x04);DispHan("K2",0X93,0X02);DispHan("放音",0x86,0x04);DispHan("K3",0X96,0X02);break;}case 0x02:{count=0;temp=0;addr=0x000000;TR0=1;TR2=1;InsitiLcd();LEDEN();DispHan("录音中",0x92,0x06);break;}case 0x03:{count=0;temp=0;addr=0x000000;TR2=1;TR1=1;IDA0CN=0xf2;InsitiLcd();LEDEN();DispHan("放音中",0x92,0x06);break;}}}}}/*********************************************键盘中断读取程序**********************************************/ void ReadKey() interrupt 0{uchar xdata *addr;addr=KEYCS;keycode=*addr;keycode &= 0x0F;keysign =1;}/********************************************* 定时器1中断函数**********************************************/ void Timer1() interrupt 3{uchar a;if(addr<0x0fffff){a=Read_flash(addr);IDA0L=0x00;IDA0H=a;addr++;}else{TR1=0;TR2=0;IDA0CN=0x72;InsitiLcd();LEDEN();DispHan("擦除",0x80,0x04);DispHan("K1",0X90,0X02);DispHan("录音",0x83,0x04);DispHan("K2",0X93,0X02);DispHan("放音",0x86,0x04);DispHan("K3",0X96,0X02);}return;}/***********************定时器2中断函数**************************/void Timer2() interrupt 5//{TF2H=0;temp++;if(temp==100){uchar b[]="000s";int a;temp=0;count++;a=count;b[2]=a%10+'0';a=a/10;b[1]=a%10+'0';a=a/10;b[0]=a%10+'0';DispHan(b,0x98,0x04);}return;}void ADC() interrupt 10//ADC0中断函数{uchar a;if(addr<0x0fffff){AD0INT=0;a=ADC0H;WR_flash(addr,a);addr++;}else{TR0=0;TR2=0;AD0INT=0;InsitiLcd();LEDEN();DispHan("擦除",0x80,0x04);DispHan("K1",0X90,0X02);DispHan("录音",0x83,0x04);DispHan("K2",0X93,0X02);DispHan("放音",0x86,0x04);DispHan("K3",0X98,0X02);}return;}void OscInit(void) //内部振荡器初始化{SFRPAGE=0x0f; //选择特殊功能寄存器页地址OSCICL=OSCICL+4;OSCICN=0xc2; //允许内部振荡器,频率除2作为SYSCLK=12MHzCLKSEL=0x00; //选择内部振荡器SFRPAGE=0x00;}void PortIoInit(void) //I/O初始化{SFRPAGE=0x0f;P0MDIN=0xe7; //P0.3、P0.4模拟量输入P0MDOUT=0x83; //P0.0、P0.1、P0.7推拉式输出P0SKIP=0xf9; //P0.1、P0.2不被交叉开关跳过P1MDIN=0xff; //P1设置为数字量输入P1MDOUT=0xff; //P1设置为推拉式输出P1SKIP=0xff; //P1被交叉开关跳过P2MDIN=0xfe; //P2.0模拟P2MDOUT=0xff; //P2P2SKIP=0xff;P3MDIN=0xff; //P3设置为数字量输入P3MDOUT=0xff; //P3设置为推拉式输出P3SKIP=0x00;P4MDOUT=0xff; //P4设为推拉式输出XBR0=0x03; //SPI接到端口XBR1=0xC0; //禁止弱上拉,交叉开关允许SFRPAGE=0x00;return;}void SPIInit(void){SPI0CFG=0x40;SPI0CN=0x09;SPI0CKR=0x00;}void XramInit(void) //外部数据储存器初始化{SFRPAGE=0x0f;EMI0CF=0x07; //引脚复用方式SFRPAGE=0x00;return;}void SmbInit(void) //I2C总线初始化{SMB0CF=0xc1; //定时器T1溢出作为时钟return;}void UartInit(void) //异步串口通信接口初始化{SCON0=0x00; //10位UARTreturn;}void DacInit(void) //D/A转化器初始化{IDA0CN=0xf2; //IDA0使能,写IDA0H触发DAC输出更新,2mA满度输出return;}void AdcInit(void) //A/D初始化{REF0CN=0x08; //选择VDD作为电压基准AMX0P=0x08; //正端接P0AMX0N=0x1f; //负端接地ADC0CF=0x2c; //左对齐,转换时钟2MHzADC0CN=0x81;return;}void Int0Init(void) //外部中断初始化{IT01CF=0x05; //选择P0.5为INT0IT0=1; //INT0下降沿触发return;}void TimerInit(void) //定时器初始化{TMOD=0x22; //T0T1方式2 8位自动重装CKCON=0x00; //系统时钟12分频TL0=0x83; //0.125msTH0=0x83;TL1=0x83; //0.125msTH1=0x83;TMR2CN=0x00; //16位自动重装,系统时钟12分频TMR2RLL=0xf0; //10msTMR2RLH=0xd8;TR0=0; //定时器0,1,2禁止TR1=0;TR2=0;return;}void PcaInit(void) //PCA初始化{PCA0CN=0x40; //允许PCA计数器/定时器PCA0MD=0x00; //禁止看门狗定时器return;}void InterruptsInit(void) //中断系统初始化{IE0=0; //清INT0中断标志EX0=1;PX0=0;ET0=0;ET1=1;ET2=1;EIE1=0x08;//允许ADC中断//ES0=1;EA=1;return;}void InitDevice(void){OscInit();PortIoInit();XramInit();//SmbInit();//UartInit();DacInit();Int0Init();TimerInit();PcaInit();SPIInit();AdcInit();//Flash_Init();InterruptsInit();return;}/************************************************* Function: 检查LCD是否空闲子程序*************************************************/ void CheckLcd(){uchar temp = 0x00;uchar xdata *addr;while (1){addr=RCOMADDR;temp=*addr;temp &= 0x80;if(temp == 0x00)break;}}/************************************************* Function: Lcd写指令子程序*************************************************/ void WriteCom(uchar n){uchar xdata *addr;CheckLcd();addr=WCOMADDR;*addr=n;}/************************************************* Function: Lcd写数据子程序*************************************************/ void WriteData(uchar m){uchar xdata *addr;CheckLcd();addr=WDATADDR;*addr = m;}/*************************************************Function: Lcd初始化子程序*************************************************/void InsitiLcd(){WriteCom(0x30); //设为基本指令集WriteCom(0x01); //清屏WriteCom(0x0c); //开整体显示}/*********************汉字显示子程序*******************函数功能:在指定位置显示指定长度的字符串入口参数:*a是字符串 m表示显示的起始地址,k表示字符串长度******************************************************/ void DispHan( uchar *a,uchar m,uchar k){uchar dat, i, j, length;length = k/2;WriteCom(m);for (i=0;i < length;i++){j = 2*i;dat = a[j]; //?????????WriteData(dat);dat = a[j+1]; //?????????WriteData(dat);}}/*********************************************LCD背光使能/**********************************************/void LEDEN(){uchar xdata *addr;addr=LEDENCS;*addr=0x01;return;}/********************************************* M25P16初始化/**********************************************/ void Flash_Init(){uchar men_status=0x01;NSSMD0=0;SPI0DAT=0x06;while(SPIF!=1);SPIF=0;NSSMD0=1;NSSMD0=0;SPI0DAT=0x01;while(SPIF!=1);SPIF=0;SPI0DAT=0x00;while(SPIF!=1);SPIF=0;NSSMD0=1;NSSMD0=0;SPI0DAT=0x05;while(SPIF!=1);SPIF=0;while(men_status==0x01){SPI0DAT=0xFF;while(SPIF!=1);SPIF=0;men_status=SPI0DAT&0x01;}NSSMD0=1;}/********************************************* M25P16整片擦除/**********************************************/ void Erase_All(){uchar men_status=0x01;NSSMD0=0;SPI0DAT=0x06;while(SPIF!=1);SPIF=0;NSSMD0=1;NSSMD0=0;SPI0DAT=0xC7;while(SPIF!=1);SPIF=0;NSSMD0=1;NSSMD0=0;SPI0DAT=0x05;while(SPIF!=1);SPIF=0;while(men_status==0x01){SPI0DAT=0xFF;while(SPIF!=1);SPIF=0;men_status=SPI0DAT&0x01;}NSSMD0=1;}/********************************************* M25P16写一字节/**********************************************/ void WR_flash(ulong addr,uchar data_byte){ADDRESS temp_addr;temp_addr.addrlong=addr;NSSMD0=0;SPI0DAT=0x06;while(SPIF!=1);SPIF=0;NSSMD0=1;NSSMD0=0;SPI0DAT=0x02;while(SPIF!=1);SPIF=0;SPI0DAT=temp_addr.addrbyte[1];while(SPIF!=1);SPIF=0;SPI0DAT=temp_addr.addrbyte[2];while(SPIF!=1);SPIF=0;SPI0DAT=temp_addr.addrbyte[3];while(SPIF!=1);SPIF=0;SPI0DAT=data_byte;while(SPIF!=1);SPIF=0;NSSMD0=1;}/********************************************* M25P16读一字节/**********************************************/ uchar Read_flash(ulong addr){ADDRESS temp_addr;temp_addr.addrlong=addr;NSSMD0=0;SPI0DAT=0x06;while(SPIF!=1);SPIF=0;NSSMD0=1;NSSMD0=0;SPI0DAT=0x03;while(SPIF!=1);SPIF=0;SPI0DAT=temp_addr.addrbyte[1];while(SPIF!=1);SPIF=0;SPI0DAT=temp_addr.addrbyte[2];while(SPIF!=1);SPIF=0;SPI0DAT=temp_addr.addrbyte[3];while(SPIF!=1);SPIF=0;SPI0DAT=0xFF;while(SPIF!=1);SPIF=0;NSSMD0=1;return SPI0DAT;}。