ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

51单片机驱动RDA5807 FM收音机:从I2C地址到Keil调试全解析

51单片机驱动RDA5807 FM收音机:从I2C地址到Keil调试全解析 简介本资源是一套基于RDA5807 FM收音芯片的C51嵌入式开发完整工程面向单片机初学者、电子设计爱好者及8051平台开发者解决数字调谐FM接收模块在51系MCU上的驱动与功能实现问题适用于便携收音机、车载音频扩展等低功耗音频应用。压缩包共42个文件含4个C源码如main.c、IIC.c、RDA5807.c、3个头文件lcd.h、delay.h、IIC.h、1个Keil工程文件uvproj、1个可烧录hex文件及若干编译中间文件obj、lst、m51等完整覆盖初始化、I²C通信、频率调谐、音量控制与LCD显示逻辑包体仅73KB轻量易导入。已有874人学习下载。读者可直接编译运行该Demo工程快速掌握RDA5807寄存器配置方法、按键扫描与中断响应流程、软延时与I²C底层驱动编写技巧并参考其模块化目录结构含独立LCD、IIC、延时子模块开展二次开发。 第一次把RDA5807点亮的时候我盯着数码管上跳出来的“98.5”愣了好一会儿因为在这之前我已经在I2C地址上折腾了整整一个下午。RDA5807这颗FM收音芯片搭配51单片机C51是我见过性价比最高、也最容易让新手怀疑人生的组合芯片几块钱模块二十块出头却能做到带数码管显示、按键调台、立体声耳机输出的完整收音机。这篇文章我就把从硬件接线到Keil工程配置的完整过程捋一遍包括那些网上没写明白的地址坑、寄存器坑和调试方法。不管你是刚入门51还是想给毕设加一个收音功能这套方案都能直接抄。1. 项目选型为什么是一颗RDA5807配51单片机1.1 RDA5807能做什么、整个收音系统怎么搭RDA5807是一颗数字调谐立体声FM收音芯片工作频段覆盖65MHz到108MHz支持50kHz/100kHz/200kHz三种步进内置低噪声放大器、AGC、AFC、RDS解码和立体声解码输出可以直接驱动耳机也可以接到功放。相比早年的TEA5767它的灵敏度更高、底噪更低、功耗更小还多了RDS电台信息价格却没贵多少。模块化的RDA5807P/RDA5807M成品在电商平台上一搜一大把引脚基本就是电源、地、I2CSDA/SCL、左右声道输出、天线焊四根线就能跑起来。整个系统的架构其实非常简单51单片机负责通过I2C向RDA5807写频率和音量配置RDA5807负责接收无线电信号并输出音频数码管负责显示当前频率和信号强度。按键可以做成手动调台、自动搜台下一次。单片机本身不参与音频解码所以对主频和处理能力没有什么要求STC89C52这种经典的51就完全够用这也是这个项目适合新手练手的重要原因。1.2 I2C地址到底是多少0x20还是0x44这个坑我必须要放在最前面说。RDA5807不是一个统一的地址不同后缀版本、不同模块厂家用的I2C地址可能不一样。最常见的有两套一套是7位地址0x10换算成带读写位的8位地址就是写0x20/读0x21另一套是7位地址0x22对应写0x44/读0x45。网上很多例程直接写#define RDA5807_ADDR 0x20也有写0x44的你抄错一套就完全不通。我的判断方法是看模块丝印和卖家资料。N20模块、很多RDA5807M小主板用0x20/0x21这套的居多而RDA5807P的裸芯片核心板用0x44/0x45的也不少。如果代码写进去程序正常但I2C一直不应答优先把这两个地址换着试一下。还有一种更稳妥的办法写一个I2C地址扫描程序从0x00到0x7F逐个发地址看哪些有应答一次就能找到芯片的真实7位地址。后面所有例程我先按0x20/0x21这套地址来写用0x44的读者只需要把宏定义改掉。1.3 硬件接线电平匹配与天线RDA5807的I2C引脚耐压我建议不要直接接5V单片机的IO口虽然不少模块上自带了电平转换电阻但裸芯片或者某些精简模块未必有。最稳的做法是模块用3.3V供电SDA/SCL串一个1kΩ电阻再接51的P2.0/P2.1或者直接用5V兼容的模块。电源方面RDA5807内部有LDO但音频输出对电源纹波比较敏感我实测同一块板子上数字地和模拟地不分开的话底噪会明显变大最好在模块电源脚就近放一个10uF电解电容和一个104瓷片电容。天线是这个项目最容易被忽略的部分。RDA5807的ANT引脚不能空着也不能像以前收音机那样接一根很长的线推荐直接用一根20到30厘米的漆包线或杜邦线焊到ANT引脚竖直放置。线太短收台少线太长反而会引入干扰。我见过很多人抱怨信号差、杂音大最后发现就是天线没接或者接法不对。2. C51软件I2C驱动从零开始写读写时序2.1 为什么用软件模拟I2C51单片机多数没有硬件I2C外设部分增强型如STC15/STC8有而且RDA5807对I2C时序要求并不苛刻使用普通IO口软件模拟是效率最高、可移植性最好的方案。软件模拟的好处是时序直观可控出了bug能拿着逻辑分析仪一帧一帧看波形坏处是CPU占用高但这颗芯片只需要在设置频率和读状态的时候通信一下完全不影响音频输出。I2C本质上就是两根线的协议SCL提供时钟SDA传数据。RDA5807的I2C时钟频率支持到400kHz快速模式51单片机跑12MHz晶振软件模拟的主频大概在几十kHz级别完全满足要求。需要注意的是RDA5807在写寄存器时寄存器地址是8位但每个寄存器数据是16位所以每次写操作要发4个字节器件地址、寄存器地址、数据高字节、数据低字节。2.2 底层时序代码我用的IO口定义是P2.0接SCL、P2.1接SDA代码基于STC89C52兼容普通8051。_nop_()来自intrins.h用于产生微小的延时保证时序满足芯片要求。#include reg52.h #include intrins.h sbit SCL P2^0; sbit SDA P2^1; #define RDA5807_ADDR_W 0x20 #define RDA5807_ADDR_R 0x21 void I2C_Delay(void) { _nop_(); _nop_(); _nop_(); _nop_(); } void I2C_Start(void) { SDA 1; SCL 1; I2C_Delay(); SDA 0; I2C_Delay(); SCL 0; } void I2C_Stop(void) { SDA 0; SCL 1; I2C_Delay(); SDA 1; I2C_Delay(); } bit I2C_SendByte(unsigned char dat) { unsigned char i; for (i 0; i 8; i) { SDA (dat 0x80) ? 1 : 0; dat 1; SCL 1; I2C_Delay(); SCL 0; I2C_Delay(); } SDA 1; // 释放总线 SCL 1; I2C_Delay(); bit ack SDA; // 从机应答 SCL 0; I2C_Delay(); return ack; } unsigned char I2C_RecvByte(bit ack) { unsigned char i, dat 0; for (i 0; i 8; i) { dat 1; SDA 1; SCL 1; I2C_Delay(); if (SDA) dat | 0x01; SCL 0; I2C_Delay(); } SDA ack ? 0 : 1; // 0:继续读取 1:停止读取 SCL 1; I2C_Delay(); SCL 0; SDA 1; return dat; }这里有个细节_nop_()在12MHz下只有1us左右I2C时序中每个电平的保持时间在几百纳秒到几十微秒之间都能接受所以这个延时量级是有足够余量的。如果你用的是高倍频的STC单片机可以适当增加几个_nop_()不然通讯太快容易出错。2.3 读写寄存器封装RDA5807的寄存器是16位宽地址是8位。写一个寄存器的完整流程是起始信号、发送器件写地址、发送寄存器地址、发送数据高字节、发送数据低字节、停止信号。读寄存器稍微麻烦一点需要先写一次寄存器地址然后重新发起起始信号换成读地址再连续读两个字节。void RDA5807_WriteReg(unsigned char reg, unsigned int dat) { I2C_Start(); I2C_SendByte(RDA5807_ADDR_W); I2C_SendByte(reg); I2C_SendByte((unsigned char)(dat 8)); I2C_SendByte((unsigned char)(dat 0xFF)); I2C_Stop(); } unsigned int RDA5807_ReadReg(unsigned char reg) { unsigned char h, l; I2C_Start(); I2C_SendByte(RDA5807_ADDR_W); I2C_SendByte(reg); I2C_Start(); I2C_SendByte(RDA5807_ADDR_R); h I2C_RecvByte(1); // 第一个字节后回ACK l I2C_RecvByte(0); // 最后一个字节回NAK I2C_Stop(); return ((unsigned int)h 8) | l; }需要注意的是读操作第二次发起始信号时SDA和SCL的状态必须正确。很多人在I2C_Start()没写好的情况下会在这里卡住表现为读回来的数据全是0xFF或0x00。如果有逻辑分析仪建议在调试初期抓一下波形确认地址字节之后的ACK位是否被拉低这是判断芯片是否活着最直接的证据。3. 核心控制逻辑频率设置、音量调节与状态读取3.1 寄存器0x02/0x03拆解与频率计算公式频率设置是RDA5807最核心的操作涉及的寄存器是0x02和0x03。寄存器0x02的低8位是频道号CHAN的低8位同时它的高字节里还包含频段选择和步进设置寄存器0x03的高4位是CHAN的高4位bit11是TUNE调谐触发位。先看频段和步进BAND[1:0]频率范围起始频率0087-108MHz87.0MHz0176-91MHz76.0MHz1076-108MHz76.0MHz1165-76MHz65.0MHzSPACE[1:0]步进0050kHz01100kHz10200kHz11保留我们国内FM广播一般用87-108MHz步进习惯用100kHz所以BAND00、SPACE01。频道号CHAN的计算公式非常简单CHAN (目标频率MHz - 起始频率MHz) / 步进MHz比如要收到98.5MHz在87-108MHz频段、100kHz步进下就是CHAN (98.5 - 87.0) / 0.1 115。写成整数运算可以避免浮点数用“频率x10”作为单位98.5MHz记为985那么CHAN 985 - 870 115和上面的结果一致。这就是为什么很多C51程序里出现freq - 870这个表达式的来历。3.2 初始化与调谐完整代码初始化分几步先写一个预置值让芯片状态复位然后使能芯片内部LDO等待稳定后再写频段、步进和音量。注意第一次上电后要等100ms以上再操作否则芯片内部PLL还没稳定写寄存器可能不生效。void RDA5807_Init(void) { RDA5807_WriteReg(0x02, 0x0000); RDA5807_WriteReg(0x03, 0x0000); delay_ms(100); // ENABLE1启动芯片 RDA5807_WriteReg(0x04, 0x2000); delay_ms(200); // 音量设置0xC0CD 是网上例程常用的一个值音量位于中间偏大 RDA5807_WriteReg(0x05, 0xC0CD); delay_ms(50); }设置频率的函数如下。因为寄存器0x02的高字节还包含BAND和SPACE配置所以我用了一个全局变量rda5807_reg2_cfg来保存这些配置位设置频率时只改CHAN的低8位避免把频段配置冲掉。unsigned int rda5807_reg2_cfg 0x0000; // 默认BAND00, SPACE01 void RDA5807_SetFreq(unsigned int freq) { unsigned int ch; // freq 以0.1MHz为单位例如985表示98.5MHz if (freq 870 freq 1080) { ch freq - 870; } else if (freq 760 freq 1080) { ch freq - 760; } else if (freq 650 freq 760) { ch freq - 650; } else { return; } // 先取消调谐再写入频道 RDA5807_WriteReg(0x03, 0x0000); RDA5807_WriteReg(0x02, rda5807_reg2_cfg | (ch 0x00FF)); RDA5807_WriteReg(0x03, ((ch 8) 0x000F) | (1 11)); }这里必须解释一下TUNE位的作用。写0x03寄存器时bit11置1表示开始调谐调谐完成后芯片内部会自动把该位清零但程序层面并不会产生中断所以设置完频率后最好加一个几ms的延时等调谐完成再去读状态寄存器否则RSSI值可能还没更新。3.3 信号强度RSSI和立体声状态读取读频率状态下RDA5807的寄存器0x0A和0x0B包含了接收状态。0x0A里比较关键的有FM_READY位调谐完成标志、FM_TRUE位当前确实收到信号以及RSSI信号强度位0x0B里包含STEREO立体声标志和AFC相关信息。不同手册对位的定义画法不一样我实际用下来最稳的提取方式是unsigned char RDA5807_GetRssi(void) { unsigned int sta RDA5807_ReadReg(0x0A); return (unsigned char)((sta 9) 0x3F); // RSSI为6位0-63 } bit RDA5807_GetStereo(void) { unsigned int sta RDA5807_ReadReg(0x0B); return (sta 0x0800) ? 1 : 0; // STEREO标志位 }RSSI值在实际使用中可以作为信号强弱的量化指标值越大信号越强。我测试时发现城市环境下80%以上的电台RSSI能到30以上弱台可能在15-20之间。如果想做自动搜台可以用一个循环每次往频率上加一个步进写频率后延时几十ms读RSSI值如果超过某个阈值就停在当前频率这就是最朴素的搜台算法效果完全可用。3.4 数码管显示频率与信号强度显示部分我用的是四位共阳数码管动态扫描P0口接段码P2.2-P2.5接位选。为了不让扫描和I2C通信抢时间我建议在显示函数里只做段码切换频率值的更新放在主循环里低频执行。下面这段代码是以0.1MHz为单位显示频率比如985会显示成“98.5”需要手动把小数点点在第二位。unsigned char code seg_tab[] {0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90}; sbit DIG1 P2^2; sbit DIG2 P2^3; sbit DIG3 P2^4; sbit DIG4 P2^5; void Display_Freq(unsigned int freq) { unsigned char d1, d2, d3; d1 (freq / 100) % 10; d2 (freq / 10) % 10; d3 freq % 10; P0 seg_tab[d1]; DIG1 0; delay_us(200); DIG1 1; P0 seg_tab[d2] 0x7F; // 第二位带小数点 DIG2 0; delay_us(200); DIG2 1; P0 seg_tab[d3]; DIG3 0; delay_us(200); DIG3 1; }如果你用的是8位数码管可以左边显示频率、右边显示RSSI中间加一个分隔符。数码管驱动最好用三极管或ULN2003做位选直接接单片机的IO口长时间点亮可能会发热也容易把IO口烧掉。另外动态扫描的延时不能太长小于1ms才不会肉眼可见地闪烁。4. Keil5工程配置C51/MDK共存、仿真调试与工具链4.1 让C51和MDK共存热搜词里“keil5兼容c51和stm32安装”其实就是Keil的经典问题。Keil uVision5的IDE是同一套但C51编译器针对8051和MDK-ARM编译器针对ARM是分开的两个安装包很多人装了C51再去装MDK发现之前新建51工程的功能不见了以为被覆盖了。其实两个编译器是可以共存的安装顺序和目录是关键。我的做法是先装C51版再装MDK版两个都装到同一个父目录下比如D:\Keil_v5。安装C51后目录下会有C51文件夹装MDK后会有ARM文件夹两个编译器互不干扰而UV4.exe会被后装的版本覆盖成同一个IDE。装完之后新建工程选择芯片型号时如果选了51系列IDE自动调用C51编译器选了STM32系列则调用ARMCC编译器。不需要任何额外配置只要Pack Installer里装好对应芯片的支持包。C51的版本我建议用9.60以上早期9.51在Win10/Win11上有兼容性问题编译速度也慢。安装时千万不要选中文路径有些杀毒软件会把编译器目录里的文件误删建议先把Keil目录加入白名单。4.2 没有开发板也能调试“keil c51 是不是一定要先连接单片机才能调试”这个问题我经常在评论区看到。答案是不需要。Keil内置的Simulator可以模拟8051内核在没有开发板的情况下完成逻辑层面的调试。在Options for Target的Debug选项卡里选择“Use Simulator”然后点调试按钮就能单步执行、看变量值、看IO口翻转还可以用逻辑分析仪窗口观察引脚波形。这种模式非常适合调试频率计算、数码管扫描这类依赖逻辑正确性的代码。但Simulator永远替代不了真机因为RDA5807芯片并不在你的电脑里仿真环境中I2C读回来的数据永远是虚拟的没法真正验证芯片应答。所以我的建议是纯逻辑算法频率换算、RSSI阈值判断用Simulator调通硬件相关部分I2C时序、实际收音必须接真机用串口打印或者LED指示来辅助调试。4.3 printf重定向到串口调试RDA5807寄存器最方便的手段就是串口打印。C51的printf默认输出目标是串口但要重定向putchar函数才能真正把字符发出去。初始化串口后在工程里加一个putchar重写就能直接用printf打印频率、RSSI这些变量。#include stdio.h void UART_Init(void) { SCON 0x50; // 串口模式1允许接收 TMOD | 0x20; // 定时器1工作在模式2 TH1 0xFD; // 11.0592MHz晶振波特率9600 TL1 0 p a hrefhttps://download.csdn.net/download/weixin_42659196/86585401 stylecolor:#ec7500;font-size:14px; 本文还有配套的精品资源点击获取 /a img altmenu-r.4af5f7ec.gif srchttps://csdnimg.cn/release/wenkucmsfe/public/img/menu-r.4af5f7ec.gif stylewidth:16px;margin-left:4px;vertical-align:text-bottom;cursor:text; /p
返回列表