ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

单片机+TDA5767收音机:Proteus仿真到实物出声全攻略

单片机+TDA5767收音机:Proteus仿真到实物出声全攻略

简介:这是一套面向电子工程专业学生与嵌入式初学者的收音机系统设计资料,以C51单片机为核心,配合TDA5767调频/调幅立体声解码芯片,完整呈现从硬件电路到软件编程的实现路径。压缩包共9个文件,约22KB,包含4个Proteus原理图文件(涵盖CPU、LCD按键、FM等模块)、3个C语言源文件与2个头文件,分别对应主程序逻辑、液晶显示驱动、延时与I2C、SPI通信接口,便于读者对照电路理解寄存器配置与芯片数据交互过程。已有119人学习下载。借助Proteus仿真,可在无实物硬件的条件下搭建电路模型、观察工作状态并验证参数调整效果,降低调试门槛。源代码则提供了操作C51寄存器、驱动TDA5767及处理音频输出的编程参考,适合作为课程设计、毕业设计或自学嵌入式系统与数字信号处理的实践素材。

1. 单片机+TDA5767 收音机:从 Proteus 仿真到能响的板子,中间隔着什么

很多人搜「基于单片机+TDA5767的收音机系统设计电路proteus仿真+软件code源代码」,心里想的其实是一件事:我手上有一份毕设或者练手项目,想把它跑起来,最好还能改成自己的东西。TDA5767 是飞利浦早年的一颗电调谐调频收音芯片,I²C 控制,外围元件少,配合 51 或 STC 单片机做频率设定、按键扫描、音量控制非常合适。它不像 BF998 那种分立本振电路需要自己绕线圈调中周,TDA5767 把高频头、中放、鉴频都集成进去了,你只需要给它一个干净的电源、一条 I²C 总线、一根天线,它就能输出音频。这也是为什么它成了单片机收音机类题目里出现频率很高的方案。

但「有仿真+有源码」不等于「能出声」。Proteus 里跑通和焊出实物之间,差着电源纹波、I²C 上拉、天线匹配、音频功放这几道坎。这篇笔记就按我实际做这类题目的顺序,把 Proteus 仿真怎么搭、代码怎么写、参数怎么调、实物为什么不出声一条条讲清楚。适合正在做单片机课设、毕设,或者想拿 TDA5767 练 I²C 和模拟电路配合的人。

2. TDA5767 到底怎么被单片机控制:I²C 时序与寄存器映射

2.1 TDA5767 的寄存器结构和写频率的算法

TDA5767 内部有 5 个可写寄存器,但实际做收音机只需要关心前 4 个字节的写入序列。它的 I²C 从机地址是 0x60(7 位),写操作时地址字节为 0xC0。上电后芯片处于静音状态,必须写入配置字才会输出音频。

核心是频率合成公式。TDA5767 的锁相环参考频率是 32768 Hz,分频比 N 和频率 f 的关系是:

N = 4 × (f + 225) / 32768 × 2^14

实际写代码时不用每次算浮点,常见做法是先把目标频率(单位 kHz)代入,算出 14 位的 N 值,再拆成高 6 位和低 8 位分别放进寄存器。下面这段是 51 单片机上的频率计算和写入函数,可以直接抄:

#include <reg52.h> #include <intrins.h> #define TDA5767_ADDR_W 0xC0 sbit SCL = P2^0; sbit SDA = P2^1; // I2C 起始 void I2C_Start(void) { SDA = 1; SCL = 1; _nop_(); _nop_(); SDA = 0; _nop_(); _nop_(); SCL = 0; } // I2C 停止 void I2C_Stop(void) { SDA = 0; SCL = 1; _nop_(); _nop_(); SDA = 1; _nop_(); _nop_(); } // 发送一个字节,返回应答 bit I2C_SendByte(unsigned char dat) { unsigned char i; bit ack; for (i = 0; i < 8; i++) { SDA = (dat & 0x80) ? 1 : 0; dat <<= 1; SCL = 1; _nop_(); _nop_(); SCL = 0; } SDA = 1; SCL = 1; _nop_(); _nop_(); ack = SDA; // 0 表示应答 SCL = 0; return ack; } // 设置频率,freq_khz 单位 kHz,范围 87500~108000 void TDA5767_SetFreq(unsigned int freq_khz) { unsigned long n; unsigned char hl, ll; n = (unsigned long)(freq_khz + 225) * 4; n = (n << 14) / 32768; // 得到 14 位分频比 hl = (unsigned char)((n >> 8) & 0x3F); // 高 6 位 ll = (unsigned char)(n & 0xFF); // 低 8 位 I2C_Start(); I2C_SendByte(TDA5767_ADDR_W); I2C_SendByte(hl); // 寄存器 1:高 6 位 + 默认位 I2C_SendByte(ll); // 寄存器 2:低 8 位 I2C_SendByte(0xB0); // 寄存器 3:搜索模式、立体声等 I2C_SendByte(0x10); // 寄存器 4:音频输出设置 I2C_Stop(); }

这段代码里hl和ll的拆分是 TDA5767 数据手册规定的:第一个数据字节的高 6 位是 N 的高位,低 2 位是默认的 0;第二个字节是 N 的低 8 位。第三个字节 0xB0 是常见配置,打开立体声、关闭软静音、搜索停止。第四个字节 0x10 设置音频输出为立体声模式。参数不是死的,如果你发现声音小或者只有单声道,先回来改这两个字节。

2.2 上拉电阻和总线速率:为什么仿真能过、实物不一定

I²C 是开漏总线,SCL 和 SDA 必须接上拉电阻。Proteus 里如果你用的是 I²C 调试器或者理想上拉模型,不加上拉也能跑;但实物上不加上拉,波形上升沿会塌,TDA5767 根本收不到正确的地址。常见做法是 4.7kΩ 到 10kΩ 之间,3.3V 系统用 4.7k,5V 系统用 10k 也可以。我一般会在 PCB 上留两个 0805 焊盘,调试时先焊 10k,波形不好再换 4.7k。

总线速率方面,TDA5767 支持标准模式 100kHz 和快速模式 400kHz。51 单片机用软件模拟 I²C 时,延时长短决定了速率。上面代码里_nop_()只给了两个空操作,在 12MHz 晶振下大概能跑到 100kHz 出头。如果你用 STC 单片机跑 11.0592MHz,这个延时也够。但如果你把延时去掉,速率可能超过 400kHz,TDA5767 会丢数据。调试时用逻辑分析仪抓一下 SCL 周期,确认高电平时间大于 1.3μs。

提示:Proteus 仿真里 I²C 总线不需要上拉也能出波形,但实物必须加。这是仿真和实物最大的差异之一,别被仿真骗了。

3. Proteus 仿真怎么搭:从元件库对照到能出波形

3.1 元件选取和原理图连接

Proteus 里搜 TDA5767 不一定能直接找到,常见做法是用 TDA5767 的等效模型或者直接用 I²C 调试器模拟。如果你装的 Proteus 8.17 版本,元件库对照表里搜「TDA5767」没有结果,可以搜「TDA5767T」或者用「I2C Debugger」加一个音频输出模型代替。单片机部分用 AT89C51 或 STC89C52 都可以,仿真里两者差别不大。

连接顺序:单片机 P2.0 接 SCL,P2.1 接 SDA,两个引脚各接一个 10k 上拉电阻到 +5V。TDA5767 的音频输出脚接一个扬声器模型或者示波器。天线部分在仿真里可以用一个信号发生器代替,输出 100MHz 左右的调频信号,幅度调到 1mV 左右,模拟真实天线接收到的微弱信号。

电源部分要注意:Proteus 里默认的 +5V 电源没有纹波,所以仿真永远比实物干净。如果你想在仿真里看到电源影响,可以串一个交流源模拟纹波,但一般课设不需要做到这一步。

3.2 仿真里验证 I²C 写入是否成功

搭好图之后,先别急着看有没有声音。第一步是确认 I²C 写入成功。在 Proteus 里放一个 I²C Debugger,把它挂在 SCL 和 SDA 上,运行仿真。如果代码正确,Debugger 窗口会显示类似这样的序列:

S 0xC0 A 0x2C A 0x8A A 0xB0 A 0x10 A P

其中 S 是起始,A 是应答,P 是停止。如果看到 N(无应答),说明地址不对或者上拉有问题。地址 0xC0 是写地址,如果你写成 0xC1 就会变成读操作,TDA5767 不会应答。

频率验证:假设你设的频率是 100.0MHz,代入公式算出来的 N 值应该是 0xB28A 左右。在 Debugger 里看到第二个字节是 0x8A,第一个字节高 6 位是 0x2C,就说明频率字写对了。如果不对,回去检查freq_khz的单位是不是 kHz,很多人传进去的是 MHz,结果算出来完全不对。

注意:Proteus 仿真里 TDA5767 模型不一定完全符合真实芯片的时序,如果 Debugger 显示应答但实物不出声,优先查实物电源和天线,不要死磕仿真。

4. 软件代码结构:按键、频率显示和状态机怎么写

4.1 按键扫描和频率步进

收音机需要两个按键:一个加频率,一个减频率。51 单片机做按键扫描常见做法是定时器中断里每 10ms 扫一次,消抖用计数法。下面是一个简单的步进逻辑:

unsigned int freq = 100000; // 当前频率,单位 kHz void KeyScan(void) { static unsigned char cnt_up = 0, cnt_down = 0; if (KEY_UP == 0) { if (++cnt_up >= 3) { // 连续 3 次扫描到低电平,约 30ms cnt_up = 0; freq += 100; // 步进 100kHz if (freq > 108000) freq = 87500; TDA5767_SetFreq(freq); } } else { cnt_up = 0; } // 减频率同理 }

步进值 100kHz 是 FM 广播的常见间隔,国内电台基本按 100kHz 递增。如果你想做自动搜台,步进可以设成 100kHz,每步写一次频率,然后读 TDA5767 的寄存器 0x0A 判断是否收到有效信号。读操作比写多一个重复起始和读地址 0xC1,代码稍长,但逻辑不复杂。

4.2 频率显示:用 OLED12864 还是数码管

Proteus 里 OLED12864 的 I²C 模型和 TDA5767 共用一条总线是常见做法,但要注意两个设备的地址不能冲突。TDA5767 是 0x60,OLED 一般是 0x78 或 0x3C,不冲突。如果你用数码管,动态扫描会占用定时器,和按键扫描抢资源,建议把按键扫描放在主循环,显示放在定时器中断。

频率显示格式一般是「100.0 MHz」,整数部分三位,小数一位。转换时注意freq / 1000得到 MHz 整数,(freq % 1000) / 100得到小数第一位。不要用浮点,51 上浮点运算又慢又占空间。

提示:Proteus 仿真 OLED12864 I²C 时,如果显示花屏,先检查初始化命令里的对比度设置,很多模型对 0x81 命令后的参数敏感。

5. 避坑与排查:仿真过了实物不出声的 5 个原因

5.1 现象:仿真有波形,实物扬声器一点声音没有

原因:TDA5767 的音频输出是差分输出,很多封装有两个输出脚,只接一个到扬声器另一端悬空,等于没形成回路。解决:查数据手册确认输出脚编号,两个输出脚之间接扬声器,或者用单端输出时另一个脚通过电容接地。

5.2 现象:I²C 地址写对了,但芯片不应答

原因:上拉电阻没焊,或者焊了但阻值太大。3.3V 系统用 10k 上拉,上升沿太慢,100kHz 下勉强能跑,400kHz 直接丢数据。解决:换成 4.7k,用示波器看 SDA 上升沿,从 0.3VCC 到 0.7VCC 的时间要小于 1μs。

5.3 现象:能收到台但噪声巨大,立体声指示灯闪

原因:天线太长或太短,或者没有做阻抗匹配。TDA5767 的输入阻抗是 75Ω 左右,直接接一根导线等于失配。解决:用 75Ω 同轴电缆或者加一个巴伦,天线长度取 FM 波段四分之一波长,大约 75cm。

5.4 现象:频率写进去后声音断断续续

原因:电源纹波太大,TDA5767 的 PLL 失锁。解决:在 TDA5767 的电源脚旁边并一个 100μF 电解加一个 0.1μF 陶瓷电容,地线走单点接地,不要和单片机数字地混在一起。

5.5 现象:Proteus 仿真里 I²C Debugger 显示应答,但代码里读回来全是 0xFF

原因:读操作时序不对,TDA5767 读寄存器需要先写地址再重复起始,然后发读地址。很多人只发了一次起始就切读,芯片不响应。解决:按数据手册的读时序图,写地址、写寄存器指针、重复起始、读地址、读数据、NACK、停止,一步不能少。

6. 从仿真到实物:验证方法和一个调台技巧

仿真跑通之后,下一步是打板或者用洞洞板搭实物。我一般会分三步验证:第一步,只焊单片机和 TDA5767 最小系统,不焊音频功放,用示波器看 TDA5767 音频输出脚有没有 1Vpp 左右的波形;第二步,接上功放和扬声器,听有没有底噪,底噪正常说明芯片工作了;第三步,写频率,听有没有电台。

调台的时候有一个技巧:不要只按 100kHz 步进硬扫,先写一个已知本地强台的频率,比如你在城市里,先试 101.7MHz 或者 97.4MHz 这种常见频点。收到之后再用步进微调,找到信号最强的点。TDA5767 的寄存器 0x0A 读回来第 7 位是立体声指示,第 6 位是信号强度,写一个循环每 100kHz 读一次,记录信号强度最大的频点,就是自动搜台的原理。

最后说一个我踩过的坑:Proteus 仿真里 TDA5767 的模型对电源完全不敏感,但实物上如果你用 5V 给单片机、3.3V 给 TDA5767,I²C 电平不匹配,TDA5767 可能识别不了 5V 的高电平。要么统一 3.3V,要么加电平转换。我现在的习惯是整板 3.3V,STC8G1K17 这类 3.3V 单片机配 TDA5767 最省事,不用转换,I²C 上拉直接接 3.3V。希望帮到你。

本文还有配套的精品资源,点击获取

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进