找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 4607|回复: 7
打印 上一主题 下一主题
收起左侧

STM32与TEA5767收音机OLED显示、自动/手动搜台,DS1302显示时间 源程序

  [复制链接]
跳转到指定楼层
楼主
接上TEA5767模块后可以接受FM电台87500——108000KHZ, 音量调节只能静音或者打开,因为模块本身不具有调节选项;可以手动设置频率(软件步进精度10KHZ,模块的角度不清楚)也可以自动搜台,但只能应对强台的搜索。OLED我用的是1.3寸的,0.96寸也是兼容的,但貌似1.3寸版本多了两列像素,所以可能边缘的显示0.96会出点小问题。收音模块我是用的带功放的,但最好买单独的模块来做,因为这个音量不可用程序调节!
主要模块和功能如下:
1、OLED屏:操作界面显示(选中时反显)
2、三个10K电阻下拉的按键:左边功能选择减,中间兼“确认”和“减”功能,右边“加”功能
3、DS1302:提供实时时钟:可修改时间
4、TEA5767完整带功放模块:收音的核心,静音音量示数为0其余都表示声音“开”,配有音频口用于接入天线(实测插一个耳机效果最好)
5、STM32F103C6T6单片机:C8T6也可以用,完全兼容;不过程序也就18K左右用C8浪费了,SRAM也完全够了

模块的接线为:1、OLED: SCL->PA5  SDA->PA7  3.3V电源
2、按键:10K电阻下拉,接PA2、3、4,分别对应按键1,2,3
3、TEA5767模块:SCL->PB7   SDA->PB5
4、DS1302模块:  RST->PB10   DAT->PB1   CLK->PB0

制作出来的实物图如下:

收音机整体模块


菜单界面


手动搜台,步进或设置


收音机主界面


存台取台


三个按键进行操作

实测多次代码各项功能都没有问题可以运行,另外程序中有一些功能选项没有写实现代码,例如“恢复设置”,它们不影响最终功能的实现可以自由写入需要恢复的变量;还有闹钟功能,只需要按照其他界面的设计逻辑写一些“if else”语句就可以实现,由于没有蜂鸣器就没写了。不过这些都不影响主要的功能的实现,毕竟它只是一个收音机...

部分STM32单片机代码如下:
#include "delay.h"
#include "sys.h"
#include "stdio.h"
#include "interrupt.h"
#include "stdlib.h"
#include "oled.h"
#include "tea5657.h"
#include "ds1302.h"

#define StartPosition  0x08005000 //存取台在flash中的起始位置,程序大概有18KB,不能与程序冲突


u32 Frequence; //频率全局变量,单位为10KHZ
u16 count=0,result,SystemTime,Channel[41]; //存取40个台,可更改
u8  KeyState[3],Menu1, Menu2, ItemSwitch,Menu2_1;
unsigned char countNumber = 0, SetNumber[5], Time[8], TimeBuff[8], Laud, ChannelSet=1,Signal, Brightness = 3; //初始亮度为3(1—5)
unsigned char radio_write_data[5]={0x29,0xc2,0x40,0x12,0x40};      
unsigned char radio_read_data[5];        //TEA5767读出的状态


void DrawFirst1(void); //分别为各个界面的驱动函数
void DrawFirst2(void);
void DrawFirst3(void);
void DrawFirst4(void);
void DrawFirst5(void);
void DrawFirst6(void);
void DrawFirst7(void);
void SetPicture1_1(void);
void SetPicture2_1(void);
void SetPicture2_2(void);
void SetPicture2_5(void);        
void SetPicture2_3(void);
void SetPicture2_6(void);        
void ClockTime(void); //定时读取DS1302
void InputA_Init(uint16_t a); //输入配置函数
uint16_t FLASH_ReadHalfWord(uint32_t address); //到address读一个16位数据
void FLASH_WriteMoreData(uint32_t startAddress,uint16_t *writeData,uint16_t countToWrite); //address写入任意长度的16位数据
void Updata_Channel(void);   //更新电台存储,将数组中的电台写入FLASH中
void Updata_W_Channel(void); //将Flash中的电台频率读到数组中





int main(void)
{
                        delay_init(); //延时函数初始化
                        OLED_Init();  //1.3寸或0.96寸OLED均可以驱动
                        TIM3_INIT_Config(10);                          //定时10ms中断
                        NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断优先级分组为组2:2位抢占优先级,2位响应优先级
                //        OLED_ColorTurn(0);                             //0正常显示,1 反色显示
                //        OLED_DisplayTurn(0);                           //0正常显示 1 屏幕翻转显示
                        Tea_Init(1);                                 //收音机模块初始化
                        DrawFirst1();                                //主界面
                        OLED_Refresh();                              //更新显示
            DS1302_init();                               //DS1302初始化
            InputA_Init(GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4); //按键输入设置
                //        Search(Frequence*10);                        //搜索
        while(1)
        {
                if(KeyState[0]==255){
                         KeyState[0] = 0;
                         Menu1++;
                        if(Menu1>2) Menu1=0; // 三种状态中切换:菜单 音量 无
                }
                  DrawFirst1();   //画出基本界面
                          switch (Menu1){                  //设定区域反显操作
                                case 1: OLED_ShowChinese(0,2,3,16,0),
                            OLED_ShowChinese(16,2,4,16,0);   break; //菜单
                                case 2: OLED_ShowChinese(75,2,5,16,0),
                            OLED_ShowChinese(91,2,6,16,0);   break; //音量
                                default:break;
                }
       if(Menu1 == 1){ //菜单键 为了保持程序可读性和整洁性,重复判断了两次Menu1的状态,可以将上面的显示更新代码移到if的大括号中
                         if(KeyState[1]==255)
       SetPicture1_1();                 //进入菜单界面                 
       }
                         else if(Menu1 == 2){ //调节音量,由于模块本身不具有音量调节功能,故需要自行开发推荐用PWM来完成,音量大小用全局变量Laud表示,每次调音量都重新写入
                           if(KeyState[1]==255)      {Laud++,KeyState[1]=0,Search(Frequence*10);}
                                 else if(KeyState[2]==255) {Laud--,KeyState[2]=0,Search(Frequence*10);} //9:01001 16: 10000 15:01111
         if(Laud>19) Laud = 0; //19级可调
       }
                        OLED_Refresh(); //更新显示
                }
}



void InputA_Init(uint16_t a){ //按键初始化
GPIO_InitTypeDef  GPIO_InitStructure;     
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);      
GPIO_InitStructure.GPIO_Pin = a;                     
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //当按键没有按下时,IO口检测为高电平,为上拉电阻模式,按下IO口与地连接,为低电平   
GPIO_Init(GPIOA ,&GPIO_InitStructure);  
}



void Updata_R_Channel(void)
{
        u8 i;
        for(i=0; i<40; i++)
        {
                Channel[ i] = FLASH_ReadHalfWord(StartPosition + 2*i);
        }
        
}




void Updata_W_Channel(void)
{
        FLASH_WriteMoreData(StartPosition,Channel,40);
}






uint16_t FLASH_ReadHalfWord(uint32_t address)
{
   return *(__IO uint16_t*)address;
}







void FLASH_WriteMoreData(uint32_t startAddress,uint16_t *writeData,uint16_t countToWrite)
  {   
      uint32_t offsetAddress=startAddress - 0X08000000;               //计算去掉0X08000000后的实际偏移地址
      uint32_t sectorPosition=offsetAddress/2048;            //计算扇区地址,对于STM32F103VET6为0~255
      uint32_t sectorStartAddress=sectorPosition*2048+FLASH_BASE;    //对应扇区的首地址
      uint16_t dataIndex;


   FLASH_Unlock();         //解锁写保护

   FLASH_ErasePage(sectorStartAddress);//擦除这个扇区

   for(dataIndex=0;dataIndex<countToWrite;dataIndex++)
   {
     FLASH_ProgramHalfWord(startAddress+dataIndex*2,writeData[dataIndex]);
   }

   FLASH_Lock();//上锁写保护
}










void DrawFirst1(void)
{
          OLED_ShowNum(47,28,Time[4],1,16,1);//时间数字
    OLED_ShowNum(55,28,Time[5],1,16,1);
          OLED_ShowString(63,28,":",16,1);
          OLED_ShowNum(71,28,Time[0],1,16,1);
          OLED_ShowNum(79,28,Time[1],1,16,1);
          OLED_ShowNum(90,35,Time[2],1,8,1);
          OLED_ShowNum(96,35,Time[3],1,8,1);
               
          OLED_ShowNum(47,48,Frequence/100,3,16,1);//频率
          OLED_ShowNum(80,48,Frequence%100,2,16,1);
          OLED_ShowString(72,48,".",16,1);
          OLED_ShowString(100,48,"MHZ",16,1);
        
          OLED_ShowChinese(0,2,3,16,1); //菜单
                OLED_ShowChinese(16,2,4,16,1);
        
          OLED_ShowChinese(75,2,5,16,1); //音量
                OLED_ShowChinese(91,2,6,16,1);
          OLED_ShowNum(110,2,Laud,2,16,1);
        
                OLED_ShowChinese(0,28,7,16,1); //时间
                OLED_ShowChinese(16,28,8,16,1);
                OLED_ShowString(32,28,":",16,1);
        
                OLED_ShowChinese(0,48,13,16,1); //频率
                OLED_ShowChinese(16,48,19,16,1);
                OLED_ShowString(32,48,":",16,1);
               
                OLED_DrawLine(0,21,130,21,1);
}



void DrawFirst2(void)
{
            
                  OLED_ShowChinese(0,0-ItemSwitch,14,16,1); //自动搜台
                                                OLED_ShowChinese(16,0-ItemSwitch,15,16,1);
                                          OLED_ShowChinese(32,0-ItemSwitch,16,16,1);
                                          OLED_ShowChinese(48,0-ItemSwitch,17,16,1);
                                         
                                          OLED_ShowChinese(0,16-ItemSwitch,18,16,1); //手动搜台
                                                OLED_ShowChinese(16,16-ItemSwitch,15,16,1);
                                          OLED_ShowChinese(32,16-ItemSwitch,16,16,1);
                                          OLED_ShowChinese(48,16-ItemSwitch,17,16,1);
                                         
                                                OLED_ShowChinese(0,32-ItemSwitch,29,16,1), //存取电台
                                          OLED_ShowChinese(16,32-ItemSwitch,33,16,1),
                                                OLED_ShowChinese(32,32-ItemSwitch,28,16,1),
                                          OLED_ShowChinese(48,32-ItemSwitch,17,16,1);
                                                                        
                                                OLED_ShowChinese(0,48-ItemSwitch,22,16,1), //设置闹钟
                                                OLED_ShowChinese(16,48-ItemSwitch,23,16,1),
                                                OLED_ShowChinese(32,48-ItemSwitch,9,16,1),
                                                OLED_ShowChinese(48,48-ItemSwitch,10,16,1);
        
            OLED_ShowChinese(0,64-ItemSwitch,22,16,1), //设置时间
                                                OLED_ShowChinese(16,64-ItemSwitch,23,16,1),
                                                OLED_ShowChinese(32,64-ItemSwitch,7,16,1),
                                                OLED_ShowChinese(48,64-ItemSwitch,8,16,1);
                                                
                                                OLED_ShowChinese(0,80-ItemSwitch,37,16,1); //设置亮度
                                                OLED_ShowChinese(16,80-ItemSwitch,38,16,1);
                                          OLED_ShowChinese(32,80-ItemSwitch,39,16,1);
                                          OLED_ShowChinese(48,80-ItemSwitch,40,16,1);
                                                                        
                                                OLED_ShowChinese(0,96-ItemSwitch,31,16,1),//恢复设置
                                                OLED_ShowChinese(16,96-ItemSwitch,32,16,1),
                                                OLED_ShowChinese(32,96-ItemSwitch,22,16,1),
                                                OLED_ShowChinese(48,96-ItemSwitch,23,16,1);
                                                                        
                                                OLED_ShowChinese(94,48,20,16,1),//返回
                                                OLED_ShowChinese(110,48,21,16,1);
}



void DrawFirst3(void)
{
                  OLED_ShowChinese(32,0,22,16,1); //设置频率
                                                OLED_ShowChinese(48,0,23,16,1);
                                          OLED_ShowChinese(64,0,13,16,1);
                                          OLED_ShowChinese(80,0,19,16,1);

                  OLED_ShowChinese(94,48,20,16,1);//返回
                                          OLED_ShowChinese(110,48,21,16,1);
        
                  OLED_ShowChinese(0,48,24,16,1); //确认
                                                OLED_ShowChinese(16,48,25,16,1);
        
                  OLED_ShowChinese(48,48,26,16,1); //步进
                                          OLED_ShowChinese(64,48,27,16,1);

        
            OLED_ShowString(48,24,".",8,1); //频率数据
                  OLED_ShowString(78,24,"MHZ",8,1);
                        OLED_ShowNum(20,24,SetNumber[0],1,8,1);
                                                OLED_ShowNum(30,24,SetNumber[1],1,8,1);
                                                OLED_ShowNum(40,24,SetNumber[2],1,8,1);
                                                OLED_ShowNum(56,24,SetNumber[3],1,8,1);
                  OLED_ShowNum(66,24,SetNumber[4],1,8,1);
                                                
                                                OLED_ShowNum(110,24,Signal,2,8,1); //信号强度
}



void DrawFirst4(void)
{
                  OLED_ShowChinese(32,0,22,16,1); //设置时间
                                                OLED_ShowChinese(48,0,23,16,1);
                                          OLED_ShowChinese(64,0,7,16,1);
                                          OLED_ShowChinese(80,0,8,16,1);

                  OLED_ShowChinese(94,48,20,16,1);//返回
                                          OLED_ShowChinese(110,48,21,16,1);
        
                  OLED_ShowChinese(0,48,24,16,1); //确认
                                                OLED_ShowChinese(16,48,25,16,1);

                        OLED_ShowNum(50,26,TimeBuff[0],2,8,1); //时间
                  OLED_ShowString(63,26,":",8,1);
                                                OLED_ShowNum(68,26,TimeBuff[1],2,8,1);
}        



void DrawFirst5(void)
{
          OLED_ShowString(20,0,"Channel:",16,1); //频道显示
    OLED_ShowNum(84,0,ChannelSet,2,16,1);        
        
          OLED_ShowNum(22,25,Channel[ChannelSet]/100,3,16,1);//频率数据
          OLED_ShowNum(55,25,Channel[ChannelSet]%100,2,16,1);
          OLED_ShowString(47,25,".",16,1);
          OLED_ShowString(75,25,"MHZ",16,1);
        
          OLED_ShowChinese(0,48,33,16,1);  //取台
                OLED_ShowChinese(16,48,17,16,1);
                OLED_ShowChinese(48,48,29,16,1); //存台
                OLED_ShowChinese(64,48,17,16,1);
          OLED_ShowChinese(94,48,20,16,1); //返回
                OLED_ShowChinese(110,48,21,16,1);
        
}

......限于篇幅余下代码下载附件......

代码工程51hei下载地址:
TEA5767收音机.7z (630.89 KB, 下载次数: 178)

评分

参与人数 1黑币 +100 收起 理由
admin + 100 共享资料的黑币奖励!

查看全部评分

分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏17 分享淘帖 顶 踩
回复

使用道具 举报

沙发
ID:170270 发表于 2021-8-13 14:37 | 只看该作者
能收到几个台?为啥我做的总是只有3到4个清晰地台。。。。
回复

使用道具 举报

板凳
ID:338727 发表于 2021-8-14 13:34 来自手机 | 只看该作者
依然是你 发表于 2021-8-13 14:37
能收到几个台?为啥我做的总是只有3到4个清晰地台。。。。

我做的只能收到强台,弱台不清晰
回复

使用道具 举报

地板
ID:525016 发表于 2022-12-3 15:27 | 只看该作者
请问楼主,能分享一下原理图吗?谢谢
回复

使用道具 举报

5#
ID:651494 发表于 2022-12-10 16:27 | 只看该作者
的沈娇妮 发表于 2021-8-14 13:34
我做的只能收到强台,弱台不清晰

OLED会干扰FM接收灵敏度,解决的方法:1,软件OLED写入只在数据更新时写入一次,不要频繁循环写入;2,OLED数据时钟线使用三极管隔离反向,滤波电容处理,可以增加阻抗,从而降低IIC辐射干扰;3,硬件收音模块,OLED电源等滤波,OLED有条件可以加装接地屏蔽罩
回复

使用道具 举报

6#
ID:1062732 发表于 2023-2-14 23:04 | 只看该作者
请问楼主只能收到电流声,收不到电台里的声音是怎么回事
回复

使用道具 举报

7#
ID:72088 发表于 2023-2-27 18:11 | 只看该作者
数码管静态显示屏才能避免干扰收听广播,多年经验总结,
回复

使用道具 举报

8#
ID:996773 发表于 2023-3-1 13:18 | 只看该作者


我是刚学汇编两年没多久,抛砖引玉,汇编程序短小精悍,4k容量51单片机足够跑了,

同时驱动液晶和LED,想用哪个就哪个,直接驱动松下大厂的原装收音板,松下的品质

我就不用说了。有频道模式和频率双模式,内部eeprom存储30个频道的频率,立体声和

单声道可选
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表