AAVVRR 发表于 2010-2-12 00:13:48

也来一个简易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电池。

yiminglei 发表于 2010-2-12 00:24:14

没有电感的?

AAVVRR 发表于 2010-2-12 00:27:35

图上画的一个小方框在电容旁边,就一个贴片电感。

AAVVRR 发表于 2010-2-12 00:32:52

实物照:
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)

yemingxp 发表于 2010-2-12 09:34:06

不错,请问收音模块在哪买的?

AAVVRR 发表于 2010-2-12 09:39:09

淘宝,不过要到春节以后了

AVR_DIY 发表于 2010-2-12 13:32:29

收音机模块多少MONEY?

AAVVRR 发表于 2010-2-12 21:01:06

贵倒不贵,快递费却高得多。http://item.taobao.com/auction/item_detail-0db2-448b97ff7b70f037855e1171a914bf1d.jhtml?cm_cat=0

cinderellah 发表于 2010-2-12 21:20:21

mark~

skydog 发表于 2010-2-12 21:27:31

貌似很好玩,哈哈

hamipeter 发表于 2010-2-17 23:28:49

MARK

lili2005you 发表于 2010-2-18 22:32:44

MARK

ndust 发表于 2010-2-19 01:04:01

jh

brightsm 发表于 2010-2-19 12:10:36

没带显示的呀

AAVVRR 发表于 2010-2-19 12:49:08

因为本身就是简易的东西,没有加上频率显示否则要复杂点,用LED数码管耗电较大,用1602体积大,一般3V也不好用,用笔段LCD也比较麻烦,所以就使用最土的办法。这是不足之处。

bbsniua 发表于 2010-2-19 18:47:32

不着急的话叫卖家用信封给你寄过去!
我就是那样子买了两个模块的!

bbsniua 发表于 2010-2-19 18:59:11

加入自动搜索保存会比较好点吧?
比如搜索到第一个台时保存时就点亮第一个LED,
第二个对应第二个。。。。。。。。。。
最后搜完就播放第一个台,
还有可以加多一排做十位数指示,
还可以做省电状态模式-只保留基本收听,
选完台后全部灭掉LED

hsztc 发表于 2010-2-19 19:32:17

本论坛有卖就好了,还没去淘宝买过不放心。

AAVVRR 发表于 2010-2-20 22:13:36

回复【16楼】bbsniua 鹏
-----------------------------------------------------------------------

很好的建议啊!AVR本身就具有EEPROM,存台也不用外加存储器

Roader 发表于 2010-2-20 22:23:24

记号。

zlj2008 发表于 2010-2-28 20:34:44

说一下搜索电台的灵敏度如何?

zj_t 发表于 2010-2-28 23:34:19

mark

AAVVRR 发表于 2010-3-1 09:17:36

发现程序按键处理的音量调节部分有点毛病作如下更正:
原文:
///////////////////////////////按键处理

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;

Swallow 发表于 2010-3-1 10:52:32

记号!有空玩玩!

hamipeter 发表于 2010-3-2 00:59:53

楼主,能否上传RDA5807P的官方文档,看看和TEA5767的有哪些差别??

AAVVRR 发表于 2010-3-2 09:40:50

回复【24楼】hamipeter
-----------------------------------------------------------------------
RDA5807有两种方式编程,一中和5767相同,另一种完全不一样,编程指南我也是在本站下载的:http://www.ourdev.cn/bbs/bbs_content_all.jsp?bbs_sn=3553907

asdfzl 发表于 2010-4-9 17:02:02

严重mark

AAVVRR 发表于 2010-4-9 18:27:11

用2313也可以,不过要软模拟I2C,不如M8方便,另外,模块内已经有了50K上拉,所以外接上拉可以省去

luojiyin 发表于 2010-4-10 15:26:13

mark 有空做一个

relotus 发表于 2010-4-10 22:50:29

mark

zj_t 发表于 2010-7-13 20:53:04

为什么写地址是0x22,读地址是0x23????

以下是编程指南的内容:
当控制接口为I2C时用CHIP ID来区分工作模式:
TEA5767 MODE CHIP ID = 1100000B;
RDA5807P MODE CHIP ID = 0010000B.

R是1,W是0

那么应该分别是0x20和0x21吧!

411412 发表于 2010-7-13 21:04:23

mark

akcheng 发表于 2010-8-12 09:26:16

MARK

xiaomage_2000 发表于 2010-10-25 22:32:20

记号下~

sun20041 发表于 2012-5-12 16:51:02

mark...,...

sun20041 发表于 2012-5-12 16:58:33

mark...,...

xqn2012 发表于 2012-5-14 16:42:01

谢谢楼主,有时间也玩一个

xuzc 发表于 2012-7-24 10:54:12

学习一下

yishen 发表于 2013-3-8 10:41:13

感谢楼主给我们这些小白分享,不过手头只有51单片机
页: [1]
查看完整版本: 也来一个简易FM收音机,,,祝各位春节愉快!