|
之所以简易:
1,只用8个LED显示频道号2进代码,
2,因为FM只是本地广播,故需要根据当地频点编程时写入,也用不着搜索,只是针对固定频点收听
3,使用RDA5807,可以使用TEA5767也可以使用5807模式编程
4,RDA自带耳机输出和电子音量,省掉了相关电路和元件
电路示意:M8人人明白,就不啰嗦,默认内部1MHZ.5807模块也就是10个脚,只用到7个,按说它内部有50K上拉电阻供TWI,不过还是加了20K外部上拉,注意耳机地线通过高频LC入地,兼做收音天线用。
(原文件名:SIMP_FM.JPG)
可见简易至极,电源5V就取自电脑USB。
程序:由两个.C文件构成:
1:radiotwi.c:
#include <util/twi.h>
#include <util/delay.h>
/////////以下书上都有,不用解释吧//////////////////////
#define TWI_START TWCR=(1<<TWINT)|(1<<TWSTA)|(1<<TWEN)
#define TWI_STOP TWCR=(1<<TWINT)|(1<<TWSTO)|(1<<TWEN)
#define TWI_WAIT {while(!(TWCR&(1<<TWINT)));}
#define TWI_TEST_ACK TWSR&0XF8
#define TWI_SEND_ACK TWCR|=(1<<TWEA)
#define TWI_SEND_BYTE(X) {TWDR=(X);TWCR=(1<<TWINT)|(1<<TWEN);}
#define uchar unsigned char
#define uint unsigned int
void twi_ini()
{
TWCR=0X00; //关闭TWI总线
TWBR=0x11; //设置传输比特率
TWSR=0; //设置TWI总线的速度,
TWCR=0x04; //使能TWI总线
}
////////////写5807寄存器,它的02,03,04,05寄存器决定了收音机控制,函数参数是寄存器地址-控制字
uchar wri_5807(uchar r_adr,uint rx)
{
uchar rxh,rxl; //////////字分解为字节,TWI只能字节传送
rxl=rx;
rx>>=8;
rxh=rx;
TWI_START;
TWI_WAIT; //等待START发出:正在发时TWINT为0,傻等他发好了TWINT为1
TWI_TEST_ACK;
if((TWI_TEST_ACK)!=0x08)
{return 1;
}
TWI_SEND_BYTE(0X22); //再次置位这两个,就是开始发出写,5807器件地址0X22
TWI_WAIT; //等待START发出:正在发时TWINT为0,发好了TWINT为1;
if((TWI_TEST_ACK)!=0X18) //非正确返回值出错
{return 1;}
TWI_SEND_BYTE(r_adr); //写寄存器地址
TWI_WAIT; ; //等待发送完成及给出应答信号
if((TWI_TEST_ACK)!=0X28)
{return 1;}
TWI_SEND_BYTE(rxh); //写高字节
TWI_WAIT; //等待数据发送完成及给出应答信号
if((TWI_TEST_ACK)!=0X28)
{return 1;}
TWI_SEND_BYTE(rxl); //写低字节
TWI_WAIT; //等待数据发送完成及给出应答信号
if((TWI_TEST_ACK)!=0X28)
{return 1;}
TWI_STOP;
;
}
//////////////读5807寄存器,只有0A,0B,反映收音机状态,例如频道号码等等,函数参数就是0X0A或0X0B
uint read_5807(uchar r_adr)
{
uint read_rab;
uchar rabh,rabl;
TWI_START;
TWI_WAIT; //等待START发出:正在发时TWINT为0,发好了TWINT为1
if((TWSR&0XF8)!=0x08)
return 1;
TWI_SEND_BYTE(0X22); //发出写,5807器件地址0X22
TWI_WAIT;
if((TWSR&0XF8)!=0X18)
return 1;
TWI_SEND_BYTE(r_adr); //发出写,寄存器地址0X0A或0X0B
TWI_WAIT;
if((TWSR&0XF8)!=0X28)
return 1;
TWI_START; //再启动
TWI_WAIT;
if((TWSR&0XF8)!=0x10)
return 1;
TWI_SEND_BYTE(0X23); //读5807器件地址0X23
TWI_WAIT;
if((TWSR&0XF8)!=0X40)
return 1;
TWCR=(1<<TWINT)|(1<<TWEN)|(1<<TWEA);//接收数据后M8发出应答:1<<TWEA
TWI_WAIT;
if((TWSR&0XF8)!=0X50)
return 1;
rabh=TWDR; //读出寄存器高字节
TWCR&=~(1<<TWEA);
TWCR=(1<<TWINT)|(1<<TWEN);
TWI_WAIT;
if((TWSR&0XF8)!=0X58)
return 1;
rabl=TWDR; //读出寄存器低字节
TWI_STOP;
return 1;
read_rab=0;
read_rab+=rabh;
read_rab<<=8;
read_rab+=rabl;
return read_rab; //返回状态字
///////////////////////////////////////////////////////////
/////////下面是主程序avrfmradio.c//////////////////////////
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <avr/eeprom.h>
#define uint unsigned int
#define uchar unsigned char
uchar keyscan_t=0; ////////定时器中断标志,用来定时判断按键:状态机法
uchar dispdata=0; ////////显示缓冲
uchar keyno; ////////按键码
//////////////////////下面数组是本地电台频道表,0就是87MHZ,100KHZ步进,4就是87.4MHZ,依此类推
uchar tune_tab[26]={0,0x04,0x0a,0x0f,0x17,0x1f,0x2c,0x36,0x42,0x4c,0x4c,0x53,0x5c,0x61,0x68,0x73,0x7b,0x8b,0x93,0x9d,0xa6,0xb6,0xbf,0xc4,0xcb,0xcf };
uchar *p=tune_tab;
///////////////////////M8初始化
void m8init(void)
{
PORTD=0; ////PD接LED
DDRD=0xff;
DDRC=0XF0;
PORTC=0X0F; /////PC接按键,TWI
DDRB=0XF8;
PORTB=0XFF;
TCNT0=216; //40x256us=10.24ms扫键时间间隔
TIMSK|=0X01;
}
//////////////////////////////////
//////5897初始化,因比较慢,所以加以延时
void rda5807_ini(void)
{
wri_5807(0x02,0xd280) ; // 0b1101 0011 1000 0000//禁止,非静音,重低音,不搜索
_delay_ms(10);
wri_5807(0x03,0x0000); //0x00 //频率87.0-108.0MHZ,0频道(87)
_delay_ms(10);
wri_5807(0x04,0x0040); //0b0000 0000 0100 0000 //75us去加重,使能I2C
_delay_ms(10);
wri_5807(0x05,0xbca8); //0b1000 1110 1010 1000 //搜台门限,天线电流,电子音量
_delay_ms(10);
}
//////////////////////////////////
////////////////////////调到指定频率,参数是频道号
void tune(uchar chanl)
{
uint tmp;
uint ctr_word=0x0000; //////////频道号占03寄存器的6位到15位,先把6位加以处理
ctr_word+=chanl;
ctr_word<<=6;
ctr_word+=0x0010;
wri_5807(0x03,ctr_word);
_delay_ms(10);
wri_5807(0x02,0xd281); ////////现在使能芯片,立即针对频道调谐收音
_delay_ms(35); ////////要延时这么长,调好了
tmp=read_5807(0x0a); /////////读寄存器0X0A其低10位是调谐的频道号
dispdata=tmp; ////////用最容易的办法LED二进制显示频道号
}
/////////////////////////////////////////////
///////////////////////定时器溢出中断用于查键
ISR(TIMER0_OVF_vect)
{
TCNT0=216;
keyscan_t=1;//for keycheck
PORTD=dispdata;
}
////////////////////////////////////
///////////////////读取键码:状态机法
////////////////////////////////////
uchar readkey(void)
{
uchar keyval=0;
uchar newkey;
static uchar keystate=0;
uchar keyOK;
static uchar lastkey;
newkey=PINC&0X0F;
switch(keystate)
{
case 0:
if(newkey!=0x0f)
{
keystate=1;
lastkey=newkey;
}
break;
case 1:
if(newkey==lastkey)
{
keyOK=1;
keyval=newkey;
keystate=2;
}
else
keystate=0;
break;
case 2:
if(newkey==0x0f)
keystate=0;
break;
}
return keyval;
}
////////////////////////////////
///////////////////////////////按键处理
void key_act(void)
{
static uchar i=0,ns=0,n=0;
static uchar vol=8;
uchar x05,j,k;
switch(keyno)
{
case 0x0e:
if (vol<0xff) //音量+
vol++;
x05+=vol;
wri_5807(0x05,x05);
break;
case 0x0d:
if (vol>0) //音量- x05+=vol;
wri_5807(0x05,x05);
break;
case 0x0b:
tune(*p); //频道往下
if (ns>0)
ns--;
else
ns=25;
p+=ns;
break;
case 0x07:
tune(*p); //频道往上
if (ns<26)
ns++;
else
ns=0;
p+=ns;
break;
}
}
/////////////////////////////////////////////
////////////////////////////////
void main()
{
m8init();
twi_ini();
TCCR0=0X04; //
_delay_ms(20); //等待5897复位
rda5807_ini();
asm("sei");
while(1)
{
if(keyscan_t)
{
keyscan_t=0;
keyno=readkey();
key_act();
}
}
}
再说一句,频率表里的频道号要根据本地调频台确定,5897寄存器具体位含义参见编程指南,通电复位后按按键就可以收台了。简单吧。编程好了以后可以用3V电池。 |
阿莫论坛20周年了!感谢大家的支持与爱护!!
知道什么是神吗?其实神本来也是人,只不过神做了人做不到的事情 所以才成了神。 (头文字D, 杜汶泽)
|