基于TEA5767模块的数字FM收音机设计
一.绪 论
1.1课题背景
1.2课题概述
二.设计要求与思路
2.1收音机的设计要求:
可对无线接收机的频率进行控制。通过键盘可以设置接收频率,接收频率在88-108MHz内自选20M的带
2.2系统设计整体思路
三、主要电路模块的实现方案比较及选择
4.1微控制器模块
4.2FM模块
4.2.1 FM模块介绍
4.3 工作原理:
由于TEA5767收音机模块,必须要由单片机通过控制总线向芯片内的寄存器写入控制字才能正常工作
我们通过往单片机写入相关程序,然后通过I2C总线,实现单片机与TEA5767模块之间的双向转。利用不
4.3.1.串行总线工作模式:
串行总线在传送数据过程中共有三种类型信号,它们分别是:开始信号,结束信号和应答信号。
开始信号:CLOCK为高电平时,DATA由高电平向低电平跳变,开始传送数据;
结束信号:CLOCK为高电平时,DATA由低电平向高电平跳变,结束传送数据;
应答信号:接收数据的IC在接收到8bit数据后,向发送数据的IC发出特定的低电平脉冲,表示已收到数据
4.3.2 串行总线基本操作:
⑴串行规程运用主/从双向通讯。器件发送数据到总线上,则定义为发送器,器件接收数据则定义为接收器。主器
参见下图:
⑵控制字节:
在起始条件之后,必须是器件的控制字节,其中高四位为器件类型识别符接着三位为片选位,前7位结合起来表示
如下图所示:
⑶写入模式:
写入多个字节时,收到每个字节后发出一应答信号(一个时钟长度低电平脉冲)。控制器收到应答信号
⑷读取模式
读取模式时,当IC接受到控制器发送的地址和读操作后产生一低电平脉冲应答信号后,被读IC发送第1字节数
4.3.3数据传送:
数据序列:address,byte1,byte2,byte3,byte4,byte5
address中Bit0(LSB)=0表示对CS3667的写操作,Bit7为每字节的最高位(MSB)
每位数据在CLOCK下跳沿产生,一直稳定到CLOCK上升沿后,任何一个字节后发出的停止条件可以缩短传
1)还未被写入的字节将保持原值。
2)如果第一个数据字节没有被写完,则已写位被写入值覆盖,但不执行新的锁台信息。
4.3.4、三线总线工作模式:
三线总线通过控制 W/R,CLOCK,DATA 三信号线进行操作。最大工作时钟频率为400kHz。
⑴数据传送
数据序列:byte1,byte2,byte3,byte4,byte5
写入信号:CLOCK为低电平时,W/R由低电平向高电平跳变,开始写入数据。
读取信号:CLOCK为低电平时,W/R由高电平向低电平跳变,结束读取数据当CLOCK为低电平时,W/
当CLOCK为低电平时,W/R由高电平向低电平跳变,结束读取数据。伴随着下跳沿,BYTE1的最高位已
如果做连续的写入或者读取操作,PIN W/R至少要被触发一个时钟周期。
五.系统软件设计
六.硬件电路测试与检测
6.1 硬件装配
(4)天线安装尽量靠近芯片引脚,一定要加上匹配电容
6.2 系统测试
七.结束语
八.参考文献
[1]刘瑞新。单片机原理及应用教程[M].北京:机械工业出版社,2003.7.[2]康华光。电子技
[4]谭浩强。C 程序设计[M]. 北京:清华大学出版社,2005.7.[5]李朝青。单片机原理及
[6]阮维国,黄建宇。电子技术实验[M].北京:兵器工业出版社,2006.12.
九.致 谢
10.1电路原理图:
10.2电路PCB图:
10.3电路实物图:
10.4元器件清单:
序号
名称
型号
数量
序号
名称
型号
数量
1
排针
200
2
电解电容
100UF/16V
1
3
电容
104
5
4
电容
30PF
2
5
电容
10UF
5
6
电容
10UF
5
7
电容
0.1UF
1
8
电阻
10K
9
9
电阻
620R
1
10
电阻
4.7K
8
11
电阻
220R
1
12
电阻
1K
1
13
晶振
12M
1
14
按键
两脚按键
7
15
按键
六脚按键
7
16
芯片
STC89C52RC
1
17
模块
TEA5767
1
18
芯片
TDA2822
2
19
发光二极管
1
20
蜂鸣器
PHONELACK2
1
21
插座
1
22
显示屏
LCD1602
1
#include
#include
#include "TEA5767.h"
#include "I2C.h"
#include "Lcd1602.h"
///////////端口定义////////////////////////
sbit Key1=P2^0;
sbit Key2=P2^1;
sbit Key3=P2^2;
sbit Key4=P2^3;
sbit Key5=P2^4;
sbit Key6=P2^5;
///////////////////////////////////////////
///////////////////////////////////////////
#define max_freq 108000 //108Mhz 898
#define min_freq 87500 //87.5Mhz
#define max_pll 0x339b //108MHz时的pl
#define min_pll 0x299d //87.5MHz时的p
#define Add_Freq 1
#define Dec_Freq 0
#define REFERENCE_FREQ 32.768 //TEA5767晶振
unsigned char radio_write_data[5]={0x2A,0x9E,0xC0,
unsigned char rdata[5]={0x2A,0x9E,0xC0,0x17,0x00};
unsigned char radio_read_data[5];
unsigned int Pll_Data; //定义频率
unsigned long Frequency_Data=89600;// 设置初始频率为8
unsigned char index=1;//天线感应信号的能力,
///////////////////////////////////////
//////延迟程序,@12.000MHz,50ms////////
void Delay()
{
unsigned char i, j;
i = 98;
j = 67;
do
{
while (--j);
} while (--i);
}
////////////////////////////////////////
/////////////////频率显示地址///////////
void LCDshow(void)
{
unsigned char str[8];
unsigned char num1,num2,num3,num4,num5;
num1=(Frequency_Data/100000)%10;
num2=(Frequency_Data/10000)%10;
num3=(Frequency_Data/1000)%10;
num4=(Frequency_Data/100)%10;
num5=(Frequency_Data/10)%10;
str[0]=num1+'0';
str[1]=num2+'0';
str[2]=num3+'0';
str[3]='.';
str[4]=num4+'0';
str[5]=num5+'0';
str[6]='\0';
LcdShowStr(0,1,str);
}
///////////////////////////////////////////
////////读TEA5767状态,并转换成PLL值//////
void Radio_Read(void)
{
unsigned char temp_l,temp_h;
Pll_Data = 0;
ATIICxx_PRead(&radio_read_data[0],5);
temp_l = radio_read_data[1]; //PLL值
temp_h = radio_read_data[0]; //PLL值
temp_h &= 0x3f;
Pll_Data = temp_h*256+temp_l;
Get_Frequency();
}
////////////////////////////////////////
//由PLL计算频率
void Get_Frequency(void)
{
unsigned char hlsi;
unsigned int npll = 0;
npll = Pll_Data;
hlsi = radio_read_data[2]&0x10;
if (hlsi)
Frequency_Data = (unsignedlong)((float)(np
else
Frequency_Data = (unsignedlong)((float)(np
//由频率计算PLL
void Get_Pll(void)
{
unsigned char hlsi;
hlsi = radio_read_data[2]&0x10;
if (hlsi)
Pll_Data = (unsigned int)((float)((Frequen
else
Pll_Data = (unsigned int)((float)((Frequen
}
///////自动搜台,mode=1,频率增加搜台; mode="0:频率减小搜台".///////
void Auto_Search(unsigned char mode)
{
LcdShowStr(10,0,"Auto "); //当搜索时,显示Auto
if(mode) //mode=1,自动向上搜索
{
switch(index) //电平转换
{
case 0:
radio_write_data[2] = 0xA0;//低电平 低本振立体声非静音
break;
case 1:
radio_write_data[2] = 0xC0;//中低电平 低本振立体声非静音
break;
case 2:
radio_write_data[2] = 0xE0;//高电平 低本振立体声非静音
break;
}
if(Pll_Data > max_pll) //当频率处于最高时,
{
Pll_Data = min_pll; //自动转为最低频率
}
}
else//向下搜索
{
switch(index)
{
case 0:
radio_write_data[2]=0x20; //低电平
break;
case 1:
radio_write_data[2]=0x40; //中低电平
break;
case 2:
radio_write_data[2]=0x60; //高电平
break;
}
if(Pll_Data < min_pll)
{
Pll_Data = max_pll;
}
}
//////////调用I2C总线的写模式////////
ATIICxx_PWrite(&radio_write_data[0],5);
Delay();//延时
Radio_Read();
LCDshow();
while(!(radio_read_data[0]&0x80))//RF电台就绪标志 若R
{
Delay();//延时
Radio_Read();
if((radio_read_data[0]&0x40)==0x40)//搜索到头
{
if(mode)
{
//ATIICxx_PWrite(&rdata[0],5);//初始化TEA5767(89.
}
else
{
//ATIICxx_PWrite(&radio_write_data[0],5
}
}
LCDshow();
}
LcdShowStr(10,0,"Normal");
}
/////////////微调程序///////////////////////
void Search10(unsigned char mode)
{
LcdShowStr(10,0,"hand ");
Delay();//延时
if(mode)
{
Frequency_Data += 10;
if(Frequency_Data > max_freq)
Frequency_Data = min_freq;
radio_write_data[2] = 0xA0;//低电平 低本振立体声非静音
}
else//向下搜索
{
Frequency_Data -= 10;
if(Frequency_Data < min_freq)
Frequency_Data = max_freq;
radio_write_data[2]=0x20; //低电平 ,向下搜索模式
}
Get_Pll();
radio_write_data[0] = Pll_Data/256;
radio_write_data[1] = Pll_Data%256;
radio_write_data[3] = 0x17;//去噪
radio_write_data[4]=0x00;
ATIICxx_PWrite(&radio_write_data[0],5);
LCDshow();
LcdShowStr(10,0,"Normal");
}
////////////////////////////////////////
/////////抖动延迟程序///////////////////
void delay15ms(void) //误差 0us
{
unsigned char i, j;
i = 30;
j = 43;
do
{
while (--j);
} while (--i);
}
/////////////////////////////////////////
//////////////按键功能///////////////////
void Key()
{
unsigned char str[3];
if(Key1==0)
{
delay15ms();
while(Key1==0);//自动频道+
Auto_Search(Add_Freq);
}
else if(Key2==0)//自动频道-
{
delay15ms();
while(Key2==0);
Auto_Search(Dec_Freq);
}
else if(Key3==0) //手动微加0.01
{
delay15ms();
if(Key3==0)
{
Search10(Add_Freq);
}
}
else if(Key4==0) //手动微减0.01
{
delay15ms();
if(Key4==0)
{
Search10(Dec_Freq);
}
}
else if(Key5==0)//
{
delay15ms();
while(!Key5);
if(index<2)
{
index++;
}
else
{
index=0;
}
str[0]=index+'0';
str[1]='\0';
LcdShowStr(15,1,str);
}
}
/////////////////////////////////////
//////////////屏幕初始化/////////////
void InitLCD()
{
unsigned char str[3];
str[0]=index+'0';
str[1]='\0';
LcdInit();
LcdShowStr(0,0,"FM radio");
LcdShowStr(7,1,"MHZ");
LcdShowStr(10,0,"Normal");
LcdShowStr(15,1,str);
}
//////////////////////////////////////
void main(void)
{
InitLCD();//屏幕设置
ATIICxx_PWrite(&radio_write_data[0],5);//初始化TEA57
Delay();
Radio_Read(); //把输入TEA5767的地址转换为频率
LCDshow(); //在屏幕上显示出来
while(1) //按键的不同操作
{
Key();
}
}
12.2 I2C总线程序:
#include
#include
#include "I2C.h"
/*************************************************
/* IIC读写程序芯片型号*/
/*************************************************
sbit I2C_SCK=P0^0; /*实时时钟时钟线引脚 */
sbit I2C_SDA=P0^1; /*实时时钟数据线引脚 */
/*************************************************
#define ATIIcxxDriverAddressW 0xC0
#define ATIIcxxDriverAddressR 0xC1
#define _Nop() _nop_(),_nop_(),_nop_(),_nop_(),_n
/*************************************************
struct bytedata_2
{
unsigned char ByteH;
unsigned char ByteL;
};
union int2byte
{
unsigned int IntData;
struct bytedata_2 ByteData;
};
/*************************************************
//启动I2C总线,退出时SCL为低
void I2C_Start(void)
{
I2C_SDA=1; /*发送起始条件的数据信号*/
_Nop();
I2C_SCK=1;
_Nop(); /*起始条件建立时间大于4.7us,延时*/
_Nop();
_Nop();
_Nop();
_Nop();
I2C_SDA=0; /*发送起始信号*/
_Nop(); /* 起始条件锁定时间大于4μs*/
_Nop();
_Nop();
_Nop();
_Nop();
I2C_SCK=0; /*钳住I2C总线,准备发送或接收数据 */
}
//*停止I2C总线
void I2C_Stop(void)
{
I2C_SCK=0;
I2C_SDA=0; /*发送结束条件的数据信号*/
_Nop(); /*发送结束条件的时钟信号*/
I2C_SCK=1; /*结束条件建立时间大于4μs*/
_Nop();
_Nop();
_Nop();
_Nop();
_Nop();
I2C_SDA=1; /*发送I2C总线结束信号*/
}
//MCU等待应答位(返回0表示应答)
bit I2C_WaitAck(void)
{
unsigned char ucErrTime = 200; //因故障接收方无ACK,超时值
I2C_SCK=0;
I2C_SDA=1;
_Nop();
I2C_SCK=1;
while(I2C_SDA)
{
ucErrTime--;
if (ucErrTime == 0)
{
I2C_Stop();
return 0;
}
}
I2C_SCK=0;
return 1;
}
//MCU应答信号
void I2C_Ack(void)
{
I2C_SCK=0;
I2C_SDA=0;
_Nop();
_Nop();
_Nop();
_Nop();
_Nop();
I2C_SCK=1;
_Nop();
_Nop();
_Nop();
_Nop();
_Nop();
I2C_SCK=0;
}
//MCU发送非应答信号
void I2C_Noack(void)
{
I2C_SCK=0;
I2C_SDA=1;
_Nop();
_Nop();
I2C_SCK=1;
_Nop();
_Nop();
I2C_SCK=0;
}
void I2C_Send_Byte(unsigned char sendbyte)
{
unsigned char i = 8;
while( i-- )
{
I2C_SCK = 0;
_Nop(); //_Nop();
if ( sendbyte &0x80 ) I2C_SDA =1;
else I2C_SDA =0;
_Nop(); //_Nop();
I2C_SCK = 1;
_Nop(); //_Nop();
sendbyte <<= 1;
}
I2C_WaitAck();
}
static unsigned char I2C_Receive_Byte(void)
{
unsigned char i = 8, data_buffer;
I2C_SDA = 1;
while ( i--)
{
I2C_SCK =0;
_Nop();_Nop();
I2C_SCK =1;
_Nop();_Nop();
data_buffer <<= 1;
if ( I2C_SDA ) data_buffer++;
}
return (data_buffer);
}
void ATIICxx_PWrite(unsigned char *McuAddress,unsi
{
I2C_Start();
I2C_Send_Byte( ATIIcxxDriverAddressW );
while(count--)//写入数据
{
I2C_Send_Byte( *(unsigned char*)McuAddress );
((unsigned char*)McuAddress)++;
}
I2C_Stop();
}
void ATIICxx_PRead(unsigned char *McuAddress,unsig
{
I2C_Start();//发送起始信号
I2C_Send_Byte( ATIIcxxDriverAddressR );//发送器件地址
while(count--)
{
*McuAddress = I2C_Receive_Byte();
I2C_Ack();
McuAddress++;
}
I2C_Noack();//应答位
I2C_Stop();//发送停止信号
}
12.3 Lcd1602程序:
#include
#define LCD1602_DB P1
sbit LCD1602_RS = P2^7;
sbit LCD1602_RW = P2^6;
sbit LCD1602_E = P3^4;
void LcdWaitReady() //等待液晶准备好
{
unsigned char sta;
LCD1602_DB = 0xFF;
LCD1602_RS = 0;
LCD1602_RW = 1;
do
{
LCD1602_E = 1;
sta = LCD1602_DB; //读取状态字
LCD1602_E = 0;
} while (sta & 0x80); //bit7 等于1 表示液晶正忙,重复检测直到其等于0
}
void LcdWriteCmd(unsigned char cmd) //写入命令函数
{
LcdWaitReady();
LCD1602_RS = 0;
LCD1602_RW = 0;
LCD1602_DB = cmd;
LCD1602_E = 1;
LCD1602_E = 0;
}
void LcdWriteDat(unsigned char dat) //写入数据函数
{
LcdWaitReady();
LCD1602_RS = 1;
LCD1602_RW = 0;
LCD1602_DB = dat;
LCD1602_E = 1;
LCD1602_E = 0;
}
void LcdShowStr(unsigned char x, unsigned char y,
{
unsigned char addr;
//由输入的显示坐标计算显示RAM 的地址
if (y == 0)
addr = 0x00 + x; //第一行字符地址从0x00 起始
else
addr = 0x40 + x; //第二行字符地址从0x40 起始
//由起始显示RAM 地址连续写入字符串
LcdWriteCmd(addr | 0x80); //写入起始地址
while (*str != '\0') //连续写入字符串数据,直到检测到结束符
{
LcdWriteDat(*str);
str++;
}
}
void LcdInit() //液晶初始化函数
{
LcdWriteCmd(0x38); //16*2 显示,5*7 点阵,8 位数据接口
LcdWriteCmd(0x0C); //显示器开,光标关闭
LcdWriteCmd(0x06); //文字不动,地址自动+1
LcdWriteCmd(0x01); //清屏