51单片机+SI4703模块做FM调频广播收音机
继上次用si4713做发射器后这次做si4703收音机期间也是经历了某些波折不过最后解决了先贴代码#include STC8051U.H #include intrins.h #define uchar unsigned char #define uint unsigned int #define ulong unsigned long #define u8 unsigned char #define u16 unsigned int #define u32 unsigned long /* 引脚定义 */ sbit SI4703_RST P1^2; sbit I2C_SCL P1^1; sbit I2C_SDA P1^0; /* SI4703 I2C地址 */ #define SI4703_WR_ADDR 0x20 #define SI4703_RD_ADDR 0x21 /* SI4703寄存器索引宏定义 对照AN230文档 */ #define POWERCFG 0x02 #define SYSCONFIG1 0x04 #define SYSCONFIG2 0x05 #define CHANNEL_REG 0x03 #define STATUSRSSI 0x0A #define READCHAN 0x0B #define RDSA 0x0C #define RDSB 0x0D #define RDSC 0x0E #define RDSD 0x0F /* SI4703寄存器位定义 */ #define ENABLE_BIT (10) #define DISABLE_BIT (16) #define RDS_EN (112) #define DE_50US (111) #define SPACE_100K (14) #define TUNE_BIT (115) #define STC_BIT (114) #define SEEK_BIT (18) #define SEEKUP_BIT (19) #define SKMODE_BIT (110) #define RDSR_BIT (115) /* SI4703全局寄存器缓存 16个16位寄存器 0x00~0x0F */ uint si4703_regs[16]; uint millis_num0;//超时计数 const uchar ASCLL[]{0,1,2,3,4,5,6,7,8,9}; bit KeySta0 1, //KEY0当前按键状态 KeySta1 1, //KEY1当前按键状态 KeySta2 1, //KEY2当前按键状态 KeySta3 1; //KEY3当前按键状态 bit backup0 1, //KEY0按键值备份保存前一次的扫描值 backup1 1, //KEY1按键值备份保存前一次的扫描值 backup2 1, //KEY2按键值备份保存前一次的扫描值 backup3 1; //KEY3按键值备份保存前一次的扫描值 sbit KEY0P2^0; //上下搜台按键 sbit KEY1P2^1; // sbit KEY2P3^2; //音量 -按键 sbit KEY3P2^2; // uchar vol_num15;//音量值 void delay_ms(uint time) //11.0592MHz { unsigned long edata i; for(;time0;time--) { _nop_(); _nop_(); i 2763UL; while (i) i--; } } void delay_us(uint time) //11.0592MHz { unsigned long edata i; for(time;time0;time--) { _nop_(); _nop_(); i 1UL; while (i) i--; } } /**************** I2C底层驱动 C89 ****************/ void i2c_start(void) { I2C_SDA1; I2C_SCL1; delay_us(5); I2C_SDA0; delay_us(5); I2C_SCL0; } void i2c_stop(void) { I2C_SDA0; I2C_SCL1; delay_us(5); I2C_SDA1; delay_us(5); } /* I2C写一字节返回应答 */ uchar i2c_write_byte(uchar dat) { uchar i,ack0; for(i0;i8;i) { I2C_SDA (dat 0x80) ? 1 : 0; dat 1; I2C_SCL1; delay_us(5); I2C_SCL0; delay_us(5); } I2C_SDA1; I2C_SCL1; delay_us(5); ack I2C_SDA; I2C_SCL0; return ack; } /* I2C读一字节ack0应答ack1无应答 */ uchar i2c_read_byte(uchar ack) { uchar i,dat0; I2C_SDA1; for(i0;i8;i) { I2C_SCL1; delay_us(5); dat 1; if(I2C_SDA) dat | 0x01; I2C_SCL0; delay_us(5); } if(ack0) I2C_SDA0; else I2C_SDA1; I2C_SCL1; delay_us(5); I2C_SCL0; return dat; } /**************** SI4703寄存器读写 参照AN230两线时序 ****************/ /* 读取全部16寄存器I2C读从0x0A开始循环参考原厂驱动readRegisters() */ void si4703_read_all_reg(void) { uchar i,high,low,idx; i2c_start(); i2c_write_byte(SI4703_RD_ADDR); for(i0;i16;i) { high i2c_read_byte(0); low i2c_read_byte((i15)?1:0); idx (i0x0A)%16; si4703_regs[idx] ((uint)high8) | low; } i2c_stop(); } /* 写入0x02~0x07共6组寄存器原厂updateRegisters() */ void si4703_update_reg(void) { uchar reg; i2c_start(); i2c_write_byte(SI4703_WR_ADDR); for(reg0x02;reg0x07;reg) { i2c_write_byte((si4703_regs[reg]8)0xFF); i2c_write_byte(si4703_regs[reg]0xFF); } i2c_stop(); delay_ms(5); } /**************** SI4703初始化【严格遵循AN230上电复位时序】**************** AN230时序要求 1.RST拉低→SDIO拉低(锁定两线I2C)→RST拉高锁定I2C模式 2.配置0x070x8100开启内部晶振延时500ms 3.POWERCFG0x4001使能芯片 4.开启RDS、欧洲50us去加重、100K频道间隔、初始音量1 ********************************************************/ void si4703_init(void) { /* 步骤1硬件复位配置总线模式【关键时序】 */ SI4703_RST 0; I2C_SDA 0; /* SDIO低2线I2C */ delay_ms(100); SI4703_RST 1; /* 释放复位锁定I2C模式 */ delay_ms(100); /* 启动I2C */ si4703_read_all_reg(); /* 步骤2寄存器0x070x8100 开启内部32.768K晶振 XOSCEN1 */ si4703_regs[0x07] 0x8100; si4703_update_reg(); delay_ms(500); /* AN230要求晶振稳定500ms */ /* 步骤3电源配置 POWERCFG0x4001 ENABLE1,DISABLE0开机 */ si4703_read_all_reg(); si4703_regs[POWERCFG] 0x4001; /* 欧洲区域配置RDS开启、DE1(50us)、SPACE01(100KHz步长) */ si4703_regs[SYSCONFIG1] | RDS_EN | DE_50US; si4703_regs[SYSCONFIG2] | SPACE_100K; si4703_regs[SYSCONFIG2] | 0X1800;//设置搜台rssi锁定阈值[15;8]0x18 /* 初始音量VOLUME1(最低) */ si4703_regs[SYSCONFIG2] 0xFFF0; si4703_regs[SYSCONFIG2] | 0x0001; si4703_update_reg(); delay_ms(110); /* AN230最大上电等待110ms */ } /**************** 频点换算MHz*10 → CHAN值 F0.1*C87.5MHz ****************/ /* freq:97397.3MHz → chan98 */ uint freq_to_chan(uint freq) { uint ch; ch freq - 875; return ch; } /* 写频点调谐对应setChannel()等待STC调谐完成 */ void si4703_set_freq(uint freq) { uint ch_val; // ulong t_start; /* C89可用ulong */ ch_val freq_to_chan(freq); si4703_read_all_reg(); si4703_regs[CHANNEL_REG] 0xFE00;/*清空原频道位*/ si4703_regs[CHANNEL_REG] | ch_val; si4703_regs[CHANNEL_REG] | TUNE_BIT;/*置TUNE启动调谐*/ si4703_update_reg(); /* 等待STC置11s超时 */ // t_start millis(); millis_num0; while(1) { si4703_read_all_reg(); if(si4703_regs[STATUSRSSI] STC_BIT) break; if(millis_num500) break;//超过1s跳出 } /* 清除TUNE位 */ si4703_read_all_reg(); si4703_regs[CHANNEL_REG] ~TUNE_BIT; si4703_update_reg(); /* 等待STC自动清零 */ while(1) { si4703_read_all_reg(); if((si4703_regs[STATUSRSSI]STC_BIT)0) break; } } /* 获取当前频点返回973代表97.3MHz */ uint si4703_get_freq(void) { uint ch; si4703_read_all_reg(); ch si4703_regs[READCHAN] 0x03FF; ch 875; return ch; } /* 设置音量 vol:0~15 */ void si4703_set_vol(uchar vol) { if(vol15) vol15; si4703_read_all_reg(); si4703_regs[SYSCONFIG2] 0xFFF0; si4703_regs[SYSCONFIG2] | vol; si4703_update_reg(); } /* 搜台dir1向上搜0向下搜返回有效频点0无台 */ uint si4703_seek(uchar dir) { uint cur_freq0; uchar sfbl; si4703_read_all_reg(); si4703_regs[POWERCFG] ~SKMODE_BIT;/*搜到边界自动循环*/ if(dir1) si4703_regs[POWERCFG] | SEEKUP_BIT; else si4703_regs[POWERCFG] ~SEEKUP_BIT; si4703_regs[POWERCFG] | SEEK_BIT;/*启动搜台*/ si4703_update_reg(); /* 等待STC */ while(1) { si4703_read_all_reg(); if(si4703_regs[STATUSRSSI]STC_BIT) break; } /* 判断是否到边界 */ sfbl (si4703_regs[STATUSRSSI]14)0x01; si4703_regs[POWERCFG] ~SEEK_BIT; si4703_update_reg(); /* 等待STC清零 */ while(1) { si4703_read_all_reg(); if((si4703_regs[STATUSRSSI]STC_BIT)0) break; } if(sfbl) return 0; cur_freq si4703_get_freq(); return cur_freq; } /* 读取RDS电台名称buf[8]存储超时为空 */ /*void si4703_read_rds(uchar *buf,uint timeout) { uchar cnt0,idx,complete[4]{0}; ulong end; uchar dh,dl; // end millis()timeout; buf[0]0; /* while(cnt4 millis()end) { si4703_read_all_reg(); if(si4703_regs[STATUSRSSI]RDSR_BIT) { idx si4703_regs[RDSB]0x03; if(complete[idx]0 si4703_regs[RDSB]500) { complete[idx]1; cnt; dh (si4703_regs[RDSD]8)0xFF; dl si4703_regs[RDSD]0xFF; buf[idx*2]dh; buf[idx*21]dl; } delay_ms(40); } else delay_ms(30); } buf[8]0; }*/ void Uart1_Init(void) //9600bps11.0592MHz { SCON 0x50; //8位数据,可变波特率 AUXR | 0x40; //定时器时钟1T模式 AUXR 0xFE; //串口1选择定时器1为波特率发生器 TMOD 0x0F; //设置定时器模式 TL1 0xE0; //设置定时初始值 TH1 0xFE; //设置定时初始值 ET1 0; //禁止定时器中断 TR1 1; //定时器1开始计时 } void UART_Send_Byte(unsigned char mydata) //串口发送一字节 { SBUFmydata; while(!TI); TI0; } // 串口发送字符串 void UART_SendString(char *str) { while (*str) { UART_Send_Byte(*str); } } void Timer0_Init(void) //2毫秒11.0592MHz { AUXR | 0x80; //定时器时钟1T模式 TMOD 0xF0; //设置定时器模式 TL0 0x9A; //设置定时初始值 TH0 0xA9; //设置定时初始值 TF0 0; //清除TF0标志 TR0 1; //定时器0开始计时 ET0 1; //使能定时器中断 } void main () { uchar i; uint Fei; uchar Rssi_num0; WTST 0;//设置程序指令延时参数赋值为0可将CPU执行指令的速度设置为最快 EAXFR 1; //扩展寄存器(XFR)访问使能 CKCON 0; //提高访问XRAM速度 P0M00X00;P0M10X00;P1M00X04;P1M10X00; P2M00X00;P2M10X00;P3M00X20;P3M10X00; P4M00X20;P4M10X00;P5M00X00;P5M10X00; S1_S10;S1_S01;//串口1切换到3.6 3.7 Uart1_Init(); EA1; Timer0_Init(); //2毫秒11.0592MHz si4703_init(); si4703_set_vol(vol_num); si4703_set_freq(1041); while(1) { si4703_read_all_reg(); /* for(i0;i16;i) { UART_Send_Byte((uchar)(si4703_regs[i]8)); UART_Send_Byte((uchar)(si4703_regs[i])); }*/ if (KeySta0 ! backup0) { //当前值与前次值不相等说明此时KEY0按键有动作 si4703_seek(0); backup0 KeySta0;//更新备份为当前值以备进行下次比较 } if (KeySta1 ! backup1) { //当前值与前次值不相等说明此时KEY1按键有动作 si4703_seek(1); backup1 KeySta1;//更新备份为当前值以备进行下次比较 } if (KeySta2 ! backup2) { //当前值与前次值不相等说明此时KEY2按键有动作 if(vol_num15) { vol_num; si4703_set_vol(vol_num); } backup2 KeySta2;//更新备份为当前值以备进行下次比较 } if (KeySta3 ! backup3) { //当前值与前次值不相等说明此时KEY3按键有动作 if(vol_num) { vol_num--; si4703_set_vol(vol_num); } backup3 KeySta3;//更新备份为当前值以备进行下次比较 } delay_ms(200); // 刷新率 /***********串口输出电台信息**************/ Feisi4703_get_freq(); UART_SendString(FM); UART_Send_Byte(ASCLL[Fei/1000%10]); UART_Send_Byte(ASCLL[Fei/100%10]); UART_Send_Byte(ASCLL[Fei/10%10]); UART_SendString(.); UART_Send_Byte(ASCLL[Fei%10]); UART_SendString(MHz); UART_SendString(\r\n); Feisi4703_regs[0x0A] 0X00FF; UART_SendString(RSSI); UART_Send_Byte(ASCLL[Fei/100%10]); UART_Send_Byte(ASCLL[Fei/10%10]); UART_Send_Byte(ASCLL[Fei%10]); UART_SendString(dBuV); if(si4703_regs[0x01] ! 0x1253)//检测Si4703C19复位值出错了重启芯片 { si4703_init(); si4703_set_vol(vol_num); si4703_set_freq(1041); } P00!P00;//接LED闪烁 } } void TM0_Isr() interrupt 1 { static unsigned char keybuf [4] {0xFF,0xFF,0xFF,0xFF};//按键扫描缓冲区保存一段时间内的扫描值 static uint cnt0; cnt; if(cnt500) { cnt0; P07 !P07; //1s测试端口 } if(millis_num60000) millis_num; keybuf[0] (keybuf[0]1) | KEY0;//缓冲区左移一位并将当前KEY0扫描值移入最低位 keybuf[1] (keybuf[1]1) | KEY1;//缓冲区左移一位并将当前KEY1扫描值移入最低位 keybuf[2] (keybuf[2]1) | KEY2;//缓冲区左移一位并将当前KEY2扫描值移入最低位 keybuf[3] (keybuf[3]1) | KEY3;//缓冲区左移一位并将当前KEY3扫描值移入最低位 if (keybuf[0] 0x00) KeySta0 0;//连续 8 次扫描值都为 0即 16ms 内都只检测到按下状态时可认为KEY0按键已按下 else if (keybuf[0] 0xFF) KeySta0 1;//连续 8 次扫描值都为 1即 16ms 内都只检测到弹起状态时可认为KEY0按键已弹起 if (keybuf[1] 0x00) KeySta1 0;//连续 8 次扫描值都为 0即 16ms 内都只检测到按下状态时可认为KEY1按键已按下 else if (keybuf[1] 0xFF) KeySta1 1;//连续 8 次扫描值都为 1即 16ms 内都只检测到弹起状态时可认为KEY1按键已弹起 if (keybuf[2] 0x00) KeySta2 0;//连续 8 次扫描值都为 0即 16ms 内都只检测到按下状态时可认为KEY2按键已按下 else if (keybuf[2] 0xFF) KeySta2 1;//连续 8 次扫描值都为 1即 16ms 内都只检测到弹起状态时可认为KEY2按键已弹起 if (keybuf[3] 0x00) KeySta3 0;//连续 8 次扫描值都为 0即 16ms 内都只检测到按下状态时可认为KEY1按键已按下 else if (keybuf[3] 0xFF) KeySta3 1;//连续 8 次扫描值都为 1即 16ms 内都只检测到弹起状态时可认为KEY3按键已弹起 }基于stc8051单片机控制si4703模块,单片机只用接模块的SCLK,SDIO,RST引脚跟单片机共用3.3V和GND电源四个按键对应上下搜台和音量加减功能。因为模块的RST引脚内部加了下拉如果单片机也用弱上拉控制的话会拉不动所以跟模块RST相连的单片机引脚一定要配置成强推挽输出。不然控制失败此模块还没出现晶振不起振情况。如果搜不到台可以更改第210行4703_regs[SYSCONFIG2] | 0X1800;//设置搜台rssi锁定阈值[15;8]0x18的配置值,配置到0000灵敏度最好但也容易被底噪干扰搜台。调试出信号后经单片机串口9600波特率输出信息电台频率和信号强弱值目前还未实验RDS数字解码。

相关新闻

实用IDM激活脚本完整指南:3种方法永久解锁高速下载

实用IDM激活脚本完整指南:3种方法永久解锁高速下载

实用IDM激活脚本完整指南:3种方法永久解锁高速下载 【免费下载链接】IDM-Activation-Script IDM Activation & Trail Reset Script 项目地址: https://gitcode.com/gh_mirrors/id/IDM-Activation-Script 还在为Internet Download Manager(IDM…

2026/7/6 15:01:53 阅读更多 →
无符号整数支持_add-uint-support

无符号整数支持_add-uint-support

以下为本文档的中文说明该技能专门用于为PyTorch算子添加无符号整数(uint)类型支持,通过更新AT_DISPATCH宏来实现。其核心问题是PyTorch中许多算子默认只支持有符号整数和有符号浮点类型,当用户需要使用uint16、uint32、uint64类型…

2026/7/6 14:57:50 阅读更多 →
红队测试插件开发_redteam-plugin-development

红队测试插件开发_redteam-plugin-development

以下为本文档的中文说明该技能用于开发Promptfoo红队测试插件,帮助安全团队对AI应用进行对抗性测试。它支持自定义测试策略、注入攻击模拟、输出评估和报告生成。Promptfoo是一个AI安全评估框架,此技能扩展其红队测试能力,适用于需要系统性评…

2026/7/6 14:57:50 阅读更多 →

最新新闻

Fashion-MNIST实战指南:从传统MNIST到现代计算机视觉的进阶之路

Fashion-MNIST实战指南:从传统MNIST到现代计算机视觉的进阶之路

Fashion-MNIST实战指南:从传统MNIST到现代计算机视觉的进阶之路 【免费下载链接】fashion-mnist A MNIST-like fashion product database. Benchmark :point_down: 项目地址: https://gitcode.com/gh_mirrors/fa/fashion-mnist 在机器学习的演进历程中&…

2026/7/6 15:48:45 阅读更多 →
深度解析Marp插件开发:5大进阶实战技巧与架构设计指南

深度解析Marp插件开发:5大进阶实战技巧与架构设计指南

深度解析Marp插件开发:5大进阶实战技巧与架构设计指南 【免费下载链接】marp The entrance repository of Markdown presentation ecosystem 项目地址: https://gitcode.com/gh_mirrors/mar/marp Marp作为现代化的Markdown演示文稿生态系统,为开发…

2026/7/6 15:48:45 阅读更多 →
Lightpanda:9倍内存效率的革命性无头浏览器,专为AI时代打造

Lightpanda:9倍内存效率的革命性无头浏览器,专为AI时代打造

Lightpanda:9倍内存效率的革命性无头浏览器,专为AI时代打造 【免费下载链接】browser Lightpanda: the headless browser designed for AI and automation 项目地址: https://gitcode.com/GitHub_Trending/browser32/browser 在AI代理、自动化测试…

2026/7/6 15:46:42 阅读更多 →
如何在3分钟内用Open-Sora免费创作专业级AI视频:完整指南

如何在3分钟内用Open-Sora免费创作专业级AI视频:完整指南

如何在3分钟内用Open-Sora免费创作专业级AI视频:完整指南 【免费下载链接】Open-Sora Open-Sora: Democratizing Efficient Video Production for All 项目地址: https://gitcode.com/GitHub_Trending/op/Open-Sora 想象一下,只需输入几个简单的文…

2026/7/6 15:44:40 阅读更多 →
如何将闲置USB-C显示屏变身高颜值系统监控中心?

如何将闲置USB-C显示屏变身高颜值系统监控中心?

如何将闲置USB-C显示屏变身高颜值系统监控中心? 【免费下载链接】turing-smart-screen-python Unofficial Python system monitor and library for small IPS USB-C displays like Turing Smart Screen / TURZX 项目地址: https://gitcode.com/GitHub_Trending/tu…

2026/7/6 15:44:40 阅读更多 →
SVG Gobbler:矢量资源管理的终极解决方案,彻底改变你的设计工作流

SVG Gobbler:矢量资源管理的终极解决方案,彻底改变你的设计工作流

SVG Gobbler:矢量资源管理的终极解决方案,彻底改变你的设计工作流 【免费下载链接】svg-gobbler Open source browser extension for finding, editing, exporting, optimizing, and managing SVG content. 项目地址: https://gitcode.com/gh_mirrors/…

2026/7/6 15:42:36 阅读更多 →

日新闻

H2 与 MySQL 单元测试兼容性:5 个关键 SQL 语句差异与规避方案

H2 与 MySQL 单元测试兼容性:5 个关键 SQL 语句差异与规避方案

H2与MySQL单元测试兼容性:5个关键SQL语句差异与规避方案1. 单元测试中的数据库兼容性挑战在Java开发领域,单元测试是保证代码质量的重要环节。当应用涉及数据库操作时,测试环境的搭建往往成为开发者的痛点。H2数据库因其轻量级、内存模式和快…

2026/7/6 0:01:17 阅读更多 →
Windows任务栏终极清理指南:用RBTray一键隐藏窗口到系统托盘

Windows任务栏终极清理指南:用RBTray一键隐藏窗口到系统托盘

Windows任务栏终极清理指南:用RBTray一键隐藏窗口到系统托盘 【免费下载链接】rbtray A fork of RBTray from http://sourceforge.net/p/rbtray/code/. 项目地址: https://gitcode.com/gh_mirrors/rb/rbtray 你是否厌倦了Windows任务栏上密密麻麻的图标&…

2026/7/6 0:01:17 阅读更多 →
Visual C++ 运行时库一键安装终极指南:告别DLL缺失烦恼

Visual C++ 运行时库一键安装终极指南:告别DLL缺失烦恼

Visual C 运行时库一键安装终极指南:告别DLL缺失烦恼 【免费下载链接】vcredist AIO Repack for latest Microsoft Visual C Redistributable Runtimes 项目地址: https://gitcode.com/gh_mirrors/vc/vcredist 你是否曾经遇到过这样的情况:下载了…

2026/7/6 0:05:19 阅读更多 →

周新闻

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容 【免费下载链接】BiliTools A cross-platform bilibili toolbox. 跨平台哔哩哔哩工具箱,支持下载视频、番剧等等各类资源 项目地址: https://gitcode.com/GitHub_Trending/bilit/BiliTools …

2026/7/6 8:11:50 阅读更多 →
威胁模型全解析:从新手入门到实战应用,助你构建安全产品!

威胁模型全解析:从新手入门到实战应用,助你构建安全产品!

威胁模型的陌生现状在忙碌疲惫的一天里,参与了关于混合后量子密码学的讨论,应付端点攻击找茬的人,还参与留言板讨论后,发现“威胁模型”对多数人仍是陌生概念,且多被当作时髦用语。有趣的相关画作有一幅由 Embyr 创作的…

2026/7/6 8:11:52 阅读更多 →
渗透测试入门指南:从零基础到实战环境搭建

渗透测试入门指南:从零基础到实战环境搭建

1. 从“看热闹”到“入门”:我理解的渗透测试到底是什么?每次看到新闻里说某个大公司的数据被“黑”了,或者某个网站被攻击导致服务瘫痪,你是不是和我一样,心里会冒出两个念头:一是“这黑客真厉害”&#x…

2026/7/6 6:52:56 阅读更多 →

月新闻