51单片机+SI4703模块做FM调频广播收音机

📅 2026/7/6 15:02:58
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数字解码。