也来一个简易FM收音机,,,祝各位春节愉快!
之所以简易:1,只用8个LED显示频道号2进代码,
2,因为FM只是本地广播,故需要根据当地频点编程时写入,也用不着搜索,只是针对固定频点收听
3,使用RDA5807,可以使用TEA5767也可以使用5807模式编程
4,RDA自带耳机输出和电子音量,省掉了相关电路和元件
电路示意:M8人人明白,就不啰嗦,默认内部1MHZ.5807模块也就是10个脚,只用到7个,按说它内部有50K上拉电阻供TWI,不过还是加了20K外部上拉,注意耳机地线通过高频LC入地,兼做收音天线用。
http://cache.amobbs.com/bbs_upload782111/files_26/ourdev_533600.JPG
(原文件名:SIMP_FM.JPG)
可见简易至极,电源5V就取自电脑USB。
程序:由两个.C文件构成:
1:radiotwi.c:
#include <util/twi.h>
#include <util/delay.h>
/////////以下书上都有,不用解释吧//////////////////////
#define TWI_STARTTWCR=(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_ACKTWCR|=(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={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电池。 没有电感的? 图上画的一个小方框在电容旁边,就一个贴片电感。 实物照:
http://cache.amobbs.com/bbs_upload782111/files_26/ourdev_533617.JPG
(原文件名:newer 001MI.JPG)
http://cache.amobbs.com/bbs_upload782111/files_26/ourdev_533618.JPG
(原文件名:newer 002MI.JPG) 不错,请问收音模块在哪买的? 淘宝,不过要到春节以后了 收音机模块多少MONEY? 贵倒不贵,快递费却高得多。http://item.taobao.com/auction/item_detail-0db2-448b97ff7b70f037855e1171a914bf1d.jhtml?cm_cat=0 mark~ 貌似很好玩,哈哈 MARK MARK jh 没带显示的呀 因为本身就是简易的东西,没有加上频率显示否则要复杂点,用LED数码管耗电较大,用1602体积大,一般3V也不好用,用笔段LCD也比较麻烦,所以就使用最土的办法。这是不足之处。 不着急的话叫卖家用信封给你寄过去!
我就是那样子买了两个模块的! 加入自动搜索保存会比较好点吧?
比如搜索到第一个台时保存时就点亮第一个LED,
第二个对应第二个。。。。。。。。。。
最后搜完就播放第一个台,
还有可以加多一排做十位数指示,
还可以做省电状态模式-只保留基本收听,
选完台后全部灭掉LED 本论坛有卖就好了,还没去淘宝买过不放心。 回复【16楼】bbsniua 鹏
-----------------------------------------------------------------------
很好的建议啊!AVR本身就具有EEPROM,存台也不用外加存储器 记号。 说一下搜索电台的灵敏度如何? mark 发现程序按键处理的音量调节部分有点毛病作如下更正:
原文:
///////////////////////////////按键处理
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;
。。。。。。。。
修改为:
///////////////////////////////按键处理
void key_act(void)
{
static uchar i=0,ns=0,n=0;
static uchar vol=8;
ucharj,k;
uint x05=0xbca0;======================x05为寄存器05的预置值,音量仅为低4位
switch(keyno)
{
case 0x0e:
if (vol<0x0f) //========================音量+ 16级控制
vol++;
x05+=vol;
wri_5807(0x05,x05);
break;
case 0x0d:
if (vol>0) //========================音量-
vol--;
x05+=vol;
wri_5807(0x05,x05);
break; 记号!有空玩玩! 楼主,能否上传RDA5807P的官方文档,看看和TEA5767的有哪些差别?? 回复【24楼】hamipeter
-----------------------------------------------------------------------
RDA5807有两种方式编程,一中和5767相同,另一种完全不一样,编程指南我也是在本站下载的:http://www.ourdev.cn/bbs/bbs_content_all.jsp?bbs_sn=3553907 严重mark 用2313也可以,不过要软模拟I2C,不如M8方便,另外,模块内已经有了50K上拉,所以外接上拉可以省去 mark 有空做一个 mark 为什么写地址是0x22,读地址是0x23????
以下是编程指南的内容:
当控制接口为I2C时用CHIP ID来区分工作模式:
TEA5767 MODE CHIP ID = 1100000B;
RDA5807P MODE CHIP ID = 0010000B.
R是1,W是0
那么应该分别是0x20和0x21吧! mark MARK 记号下~ mark...,... mark...,... 谢谢楼主,有时间也玩一个 学习一下 感谢楼主给我们这些小白分享,不过手头只有51单片机
页:
[1]