继上次用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 (1<<0) #define DISABLE_BIT (1<<6) #define RDS_EN (1<<12) #define DE_50US (1<<11) #define SPACE_100K (1<<4) #define TUNE_BIT (1<<15) #define STC_BIT (1<<14) #define SEEK_BIT (1<<8) #define SEEKUP_BIT (1<<9) #define SKMODE_BIT (1<<10) #define RDSR_BIT (1<<15) /* SI4703全局寄存器缓存 16个16位寄存器 0x00~0x0F */ uint si4703_regs[16]; uint millis_num=0;//超时计数 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 KEY0=P2^0; //上下搜台按键 sbit KEY1=P2^1; // sbit KEY2=P3^2; //音量+ -按键 sbit KEY3=P2^2; // uchar vol_num=15;//音量值 void delay_ms(uint time) //@11.0592MHz { unsigned long edata i; for(;time>0;time--) { _nop_(); _nop_(); i = 2763UL; while (i) i--; } } void delay_us(uint time) //@11.0592MHz { unsigned long edata i; for(time;time>0;time--) { _nop_(); _nop_(); i = 1UL; while (i) i--; } } /**************** I2C底层驱动 C89 ****************/ void i2c_start(void) { I2C_SDA=1; I2C_SCL=1; delay_us(5); I2C_SDA=0; delay_us(5); I2C_SCL=0; } void i2c_stop(void) { I2C_SDA=0; I2C_SCL=1; delay_us(5); I2C_SDA=1; delay_us(5); } /* I2C写一字节,返回应答 */ uchar i2c_write_byte(uchar dat) { uchar i,ack=0; for(i=0;i<8;i++) { I2C_SDA = (dat & 0x80) ? 1 : 0; dat <<= 1; I2C_SCL=1; delay_us(5); I2C_SCL=0; delay_us(5); } I2C_SDA=1; I2C_SCL=1; delay_us(5); ack = I2C_SDA; I2C_SCL=0; return ack; } /* I2C读一字节,ack=0应答,ack=1无应答 */ uchar i2c_read_byte(uchar ack) { uchar i,dat=0; I2C_SDA=1; for(i=0;i<8;i++) { I2C_SCL=1; delay_us(5); dat <<=1; if(I2C_SDA) dat |= 0x01; I2C_SCL=0; delay_us(5); } if(ack==0) I2C_SDA=0; else I2C_SDA=1; I2C_SCL=1; delay_us(5); I2C_SCL=0; 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(i=0;i<16;i++) { high = i2c_read_byte(0); low = i2c_read_byte((i==15)?1:0); idx = (i+0x0A)%16; si4703_regs[idx] = ((uint)high<<8) | low; } i2c_stop(); } /* 写入0x02~0x07共6组寄存器,原厂updateRegisters() */ void si4703_update_reg(void) { uchar reg; i2c_start(); i2c_write_byte(SI4703_WR_ADDR); for(reg=0x02;reg<=0x07;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.配置0x07=0x8100开启内部晶振,延时500ms 3.POWERCFG=0x4001使能芯片 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:寄存器0x07=0x8100 开启内部32.768K晶振 XOSCEN=1 */ si4703_regs[0x07] = 0x8100; si4703_update_reg(); delay_ms(500); /* AN230要求晶振稳定500ms */ /* 步骤3:电源配置 POWERCFG=0x4001 ENABLE=1,DISABLE=0开机 */ si4703_read_all_reg(); si4703_regs[POWERCFG] = 0x4001; /* 欧洲区域配置:RDS开启、DE=1(50us)、SPACE0=1(100KHz步长) */ si4703_regs[SYSCONFIG1] |= RDS_EN | DE_50US; si4703_regs[SYSCONFIG2] |= SPACE_100K; si4703_regs[SYSCONFIG2] |= 0X1800;//设置搜台rssi锁定阈值[15;8]=0x18 /* 初始音量VOLUME=1(最低) */ si4703_regs[SYSCONFIG2] &= 0xFFF0; si4703_regs[SYSCONFIG2] |= 0x0001; si4703_update_reg(); delay_ms(110); /* AN230最大上电等待110ms */ } /**************** 频点换算:MHz*10 → CHAN值 F=0.1*C+87.5MHz ****************/ /* freq:973=97.3MHz → chan=98 */ 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置1,1s超时 */ // t_start = millis(); millis_num=0; while(1) { si4703_read_all_reg(); if(si4703_regs[STATUSRSSI] & STC_BIT) break; if(millis_num>500) 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(vol>15) vol=15; si4703_read_all_reg(); si4703_regs[SYSCONFIG2] &= 0xFFF0; si4703_regs[SYSCONFIG2] |= vol; si4703_update_reg(); } /* 搜台:dir=1向上搜,0向下搜,返回有效频点,0无台 */ uint si4703_seek(uchar dir) { uint cur_freq=0; uchar sfbl; si4703_read_all_reg(); si4703_regs[POWERCFG] &= ~SKMODE_BIT;/*搜到边界自动循环*/ if(dir==1) 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 cnt=0,idx,complete[4]={0}; ulong end; uchar dh,dl; // end = millis()+timeout; buf[0]=0; /* while(cnt<4 && 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*2+1]=dl; } delay_ms(40); } else delay_ms(30); } buf[8]=0; }*/ void Uart1_Init(void) //9600bps@11.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) //串口发送一字节 { SBUF=mydata; while(!TI); TI=0; } // 串口发送字符串 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_num=0; WTST = 0;//设置程序指令延时参数,赋值为0可将CPU执行指令的速度设置为最快 EAXFR = 1; //扩展寄存器(XFR)访问使能 CKCON = 0; //提高访问XRAM速度 P0M0=0X00;P0M1=0X00;P1M0=0X04;P1M1=0X00; P2M0=0X00;P2M1=0X00;P3M0=0X20;P3M1=0X00; P4M0=0X20;P4M1=0X00;P5M0=0X00;P5M1=0X00; S1_S1=0;S1_S0=1;//串口1切换到3.6 3.7 Uart1_Init(); EA=1; Timer0_Init(); //2毫秒@11.0592MHz si4703_init(); si4703_set_vol(vol_num); si4703_set_freq(1041); while(1) { si4703_read_all_reg(); /* for(i=0;i<16;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_num<15) { 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); // 刷新率 /***********串口输出电台信息**************/ Fei=si4703_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"); Fei=si4703_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 cnt=0; cnt++; if(cnt>=500) { cnt=0; P07 = !P07; //1s测试端口 } if(millis_num<60000) 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数字解码。