KT24-1110_65E-HA-651B/apps/common/device/fm/qn8035/QN8035.c

713 lines
19 KiB
C
Raw Normal View History

2024-11-10 10:44:17 +00:00
/*--------------------------------------------------------------------------*/
/**@file qn8035.c
@brief qn8035收音底层驱动
@details
@author
@date 2011-11-24
@note
*/
/*----------------------------------------------------------------------------*/
#include "app_config.h"
#include "system/includes.h"
#include "fm/fm_manage.h"
#include "QN8035.h"
#if(TCFG_FM_QN8035_ENABLE && TCHFG_SOFT_I2C_ENABLE)
//#include "sdmmc_api.h"
//if antenna match circuit is used a inductormacro USING_INDUCTOR will be set to 1
#define USING_INDUCTOR 0
#define INVERSE_IMR 1
//if using san noise floor as CCA algorithm,macro SCAN_NOISE_FLOOR_CCA will be set to 1
#define SCAN_NOISE_FLOOR_CCA 1
//if using pilot as CCA algorithm,macro PILOT_CCA will be set to 1
//#define PILOT_CCA 0
//长天线配置:
//u8 _xdata qnd_PreNoiseFloor = 40,qnd_NoiseFloor = 40;
bool qn8035_online;
//短天线配置:
static struct _fm_dev_info *fm_dev_info = NULL;
extern void delay_2ms(int cnt);
#define delay_n10ms(x) \
delay_2ms(x*5)
u16 aa;
u8 qnd_PrevMode;
u8 qnd_CH_STEP = 1;
u8 qnd_ChipID;
/*----------------------------------------------------------------------------*/
/**@brief QN8035 读寄存器函数
@param addr
@return
@note u8 QND_ReadReg(u8 addr)
//5_5_9_5
//4_5_8_5
//2_5_8_5
*/
#define qn8035_rssi 4 //越大台越少
#define qn8035_if2 0x05 //越大台越多
#define qn8035_snr_th1 0x08 //越大台越少 //
#define qn8035_snr_th2 0x05
#define qn8035_pilot_cca 0
#define qn8035_inductor 1
#define qn8075_snr 25
u8 qnd_PreNoiseFloor = 35; //越大台越少
u8 qnd_NoiseFloor = 35;
u8 QND_ReadReg(u8 addr)
{
u8 byte;
iic_start(fm_dev_info->iic_hdl); //I2C启动
iic_tx_byte(fm_dev_info->iic_hdl, 0x20); //写命令
iic_tx_byte(fm_dev_info->iic_hdl, addr); //写地址
iic_start(fm_dev_info->iic_hdl); //写转为读命令需要再次启动I2C
iic_tx_byte(fm_dev_info->iic_hdl, 0x21); //读命令
byte = iic_rx_byte(fm_dev_info->iic_hdl, 1);
iic_stop(fm_dev_info->iic_hdl); //I2C停止
return byte;
}
#if 0 //IIC SD IO复用
u8 sd_iic_readQN(u8 addr)
{
u8 p_status;
u8 res;
/*Wait SD I/O release*/
if (device_active == DEVICE_SDMMCA) {
p_status = pause_decode(0);
}
sdmmca_force_idle();
sdmmcb_force_idle();
while (check_sd_controller()); //根据SD ID等待控制器繁忙
IO_MC0 &= ~BIT(3);
/*IIC Communicate*/
res = QND_ReadReg(addr);
/*Reset SD I/O*/
IO_MC0 |= BIT(3);
unlock_cmd_chk();
if ((p_status == MAD_PLAY) && (device_active == DEVICE_SDMMCA)) {
start_decode();
}
return res;
}
#endif
u8 qn8035_iic_write(u8 w_chip_id, u8 register_address, u8 *buf, u32 data_len)
{
u8 ret = 1;
if (fm_dev_info == NULL) {
puts("fm_dev_info not init !!!!!");
return 0;
}
iic_start(fm_dev_info->iic_hdl);
if (0 == iic_tx_byte(fm_dev_info->iic_hdl, w_chip_id)) {
ret = 0;
log_e("\n fm iic wr err 0\n");
goto __gcend;
}
delay(fm_dev_info->iic_delay);
if (0 == iic_tx_byte(fm_dev_info->iic_hdl, register_address)) {
ret = 0;
log_e("\n fm iic wr err 1\n");
goto __gcend;
}
u8 *pbuf = buf;
while (data_len--) {
delay(fm_dev_info->iic_delay);
if (0 == iic_tx_byte(fm_dev_info->iic_hdl, *pbuf++)) {
ret = 0;
log_e("\n fm iic wr err 2\n");
goto __gcend;
}
}
__gcend:
iic_stop(fm_dev_info->iic_hdl);
return ret;
}
u8 qn8035_iic_readn(u8 r_chip_id, u8 register_address, u8 *buf, u8 data_len)
{
u8 read_len = 0;
if (fm_dev_info == NULL) {
puts("fm_dev_info not init !!!!!");
return 0;
}
iic_start(fm_dev_info->iic_hdl);
if (0 == iic_tx_byte(fm_dev_info->iic_hdl, (r_chip_id & 0x01) ? (r_chip_id - 1) : (r_chip_id))) {
log_e("\n fm iic rd err 0\n");
read_len = 0;
goto __gdend;
}
delay(fm_dev_info->iic_delay);
if (0 == iic_tx_byte(fm_dev_info->iic_hdl, register_address)) {
log_e("\n fm iic rd err 1\n");
read_len = 0;
goto __gdend;
}
/* delay(fm_dev_info->iic_delay); */
/* iic_start(fm_dev_info->iic_hdl); */
/* if (0 == iic_tx_byte(fm_dev_info->iic_hdl, r_chip_id)) { */
/* log_e("\n fm iic rd err 2\n"); */
/* read_len = 0; */
/* goto __gdend; */
/* } */
delay(fm_dev_info->iic_delay);
for (; data_len > 1; data_len--) {
*buf++ = iic_rx_byte(fm_dev_info->iic_hdl, 1);
read_len ++;
}
*buf = iic_rx_byte(fm_dev_info->iic_hdl, 0);
__gdend:
iic_stop(fm_dev_info->iic_hdl);
delay(fm_dev_info->iic_delay);
return read_len;
}
#define app_IIC_write qn8035_iic_write
#define app_IIC_readn qn8035_iic_readn
/*----------------------------------------------------------------------------*/
/**@brief QN8035 写寄存器函数
@param addr data
@return
@note void QND_WriteReg(u8 addr,u8 data)
*/
/*----------------------------------------------------------------------------*/
void QND_WriteReg(u8 addr, u8 data)
{
app_IIC_write(0x20, addr, &data, 1);
}
/**********************************************************************
void QNF_SetCh(UINT16 start,UINT16 stop,UINT8 step)
**********************************************************************
Description: set channel frequency
Parameters:
freq: channel frequency to be set,frequency unit is 10KHZ
Return Value:
None
**********************************************************************/
void QNF_SetCh(u16 start, u16 stop, u8 step)
{
u8 temp;
start = FREQ2CHREG(start);
stop = FREQ2CHREG(stop);
//writing lower 8 bits of CCA channel start index
QND_WriteReg(CH_START, (u8)start);
//writing lower 8 bits of CCA channel stop index
QND_WriteReg(CH_STOP, (u8)stop);
//writing lower 8 bits of channel index
QND_WriteReg(CH, (u8)start);
//writing higher bits of CCA channel start,stop and step index
temp = (u8)((start >> 8) & CH_CH);
temp |= ((u8)(start >> 6) & CH_CH_START);
temp |= ((u8)(stop >> 4) & CH_CH_STOP);
temp |= (step << 6);
QND_WriteReg(CH_STEP, temp);
}
/**********************************************************************
int QND_Delay()
**********************************************************************
Description: Delay for some ms, to be customized according to user
application platform
Parameters:
ms: ms counts
Return Value:
None
**********************************************************************/
void QND_Delay(u16 ms)
{
// Delay(25*ms); //rc
// delay16(1500*ms);
delay_n10ms(1);
}
void qn8035_mute1(bool On)
{
QND_WriteReg(0x4a, On ? 0x30 : 0x10);
//QND_WriteReg(0x4a, 0x30);
}
#define QNF_SetMute(x,y) qn8035_mute(x,y)
/**********************************************************************
void QNF_SetMute(u8 On)
**********************************************************************
Description: set register specified bit
Parameters:
On: 1: mute, 0: unmute
Return Value:
None
**********************************************************************/
u8 qn8035_mute(void *priv, u8 On)
{
QND_WriteReg(REG_DAC, On ? 0x1B : 0x10);
//QND_WriteReg(0x4a, On?0x30:0x10);
return 0;
}
#if SCAN_NOISE_FLOOR_CCA
/***********************************************************************
Description: scan a noise floor from 87.5M to 108M by step 200K
Parameters:
Return Value:
1: scan a noise floor successfully.
0: chip would not normally work.
**********************************************************************/
u8 QND_ScanNoiseFloor(u16 start, u16 stop)
{
u8 regValue;
u8 timeOut = 255; //time out is 2.55S
QND_WriteReg(CCA_SNR_TH_1, 0x00);
QND_WriteReg(CCA_SNR_TH_2, 0x05);
QND_WriteReg(0x40, 0xf0);
//config CCS frequency rang by step 200KHZ
QNF_SetCh(start, stop, 2);
/*
QND_WriteReg(CH_START,0x26);
QND_WriteReg(CH_STOP,0xc0);
QND_WriteReg(CH_STEP,0xb8);
*/
//enter CCA mode,channel index is decided by internal CCA
QND_WriteReg(SYSTEM1, 0x12);
while (1) {
regValue = QN_IIC_read(SYSTEM1);
//if it seeks a potential channel, the loop will be quited
if ((regValue & CHSC) == 0) {
break;
}
delay_n10ms(1); //delay 10ms
//if it was time out,chip would not normally work.
if ((timeOut--) == 0) {
QND_WriteReg(0x40, 0x70);
return 0;
}
}
QND_WriteReg(0x40, 0x70);
qnd_NoiseFloor = QN_IIC_read(0x3f);
if (((qnd_PreNoiseFloor - qnd_NoiseFloor) > 2) || ((qnd_NoiseFloor - qnd_PreNoiseFloor) > 2)) {
qnd_PreNoiseFloor = qnd_NoiseFloor;
}
//TRACE("NF:%d,timeOut:%d\n",qnd_NoiseFloor,255-timeOut);
return 1;
}
#endif
/**********************************************************************
void QNF_SetRegBit(u8 reg, u8 bitMask, u8 data_val)
**********************************************************************
Description: set register specified bit
Parameters:
reg: register that will be set
bitMask: mask specified bit of register
data_val: data will be set for specified bit
Return Value:
None
***********************************************************************/
void QND_RXSetTH(void)
{
u8 rssi_th;
rssi_th = qnd_PreNoiseFloor + qn8035_rssi - 28 ; //10 越小台多0-
///increase reference PLL charge pump current.
QND_WriteReg(REG_REF, 0x7a);
//NFILT program is enabled
QND_WriteReg(0x1b, 0x78);
//using Filter3
QND_WriteReg(CCA1, 0x75);
//setting CCA IF counter error range value(768).
QND_WriteReg(CCA_CNT2, qn8035_if2); //0x03 大台多 1-5
//#if PILOT_CCA
if (qn8035_pilot_cca) {
QND_WriteReg(PLT1, 0x00);
}
//#endif
//selection the time of CCA FSM wait SNR calculator to settle:20ms
//0x00: 20ms(default)
//0x40: 40ms
//0x80: 60ms
//0xC0: 100m
// QNF_SetRegBit(CCA_SNR_TH_1 , 0xC0, 0x00);
//selection the time of CCA FSM wait RF front end and AGC to settle:20ms
//0x00: 10ms
//0x40: 20ms(default)
//0x80: 40ms
//0xC0: 60ms
// QNF_SetRegBit(CCA_SNR_TH_2, 0xC0, 0x40);
// QNF_SetRegBit(CCA, 30); //setting CCA RSSI threshold is 30
QND_WriteReg(CCA_SNR_TH_2, qn8035_snr_th2); //0xc5
QND_WriteReg(CCA, (QN_IIC_read(CCA) & 0xc0) | rssi_th);
//#if PILOT_CCA
if (qn8035_pilot_cca) {
QND_WriteReg(CCA_SNR_TH_1, qn8035_snr_th1); //setting SNR threshold for CCA
} else {
QND_WriteReg(CCA_SNR_TH_1, qn8035_snr_th1); //小台多 8-12 //setting SNR threshold for CCA 9
}
//#endif
}
/*----------------------------------------------------------------------------*/
/**@brief QN0835 初始化
@param
@return
@note u8 qn8035_init(void *priv)
*/
/*----------------------------------------------------------------------------*/
u8 qn8035_init(void *priv)
{
if (fm_dev_info == NULL) {
fm_dev_info = (struct _fm_dev_info *)priv;
}
puts("qn8035_init\n");
QND_WriteReg(0x00, 0x81);
delay_n10ms(1);
/*********User sets chip working clock **********/
//Following is where change the input clock wave type,as sine-wave or square-wave.
//default set is 32.768KHZ square-wave input.
QND_WriteReg(0x01, QND_SINE_WAVE_CLOCK);
//QND_WriteReg(0x58,0x93);
//Following is where change the input clock frequency.
QND_WriteReg(XTAL_DIV0, QND_XTAL_DIV0);
QND_WriteReg(XTAL_DIV1, QND_XTAL_DIV1);
QND_WriteReg(XTAL_DIV2, QND_XTAL_DIV2);
/********User sets chip working clock end ********/
QND_WriteReg(0x54, 0x47);//mod PLL setting
//select SNR as filter3,SM step is 2db
QND_WriteReg(0x19, 0xc4);
QND_WriteReg(0x33, 0x9e);//set HCC and SM Hystersis 5db
QND_WriteReg(0x2d, 0xd6);//notch filter threshold adjusting
QND_WriteReg(0x43, 0x10);//notch filter threshold enable
QND_WriteReg(0x47, 0x39);
//QND_WriteReg(0x57, 0x21);//only for qn8035B test
//enter receiver mode directly
QND_WriteReg(0x00, 0x11);
//Enable the channel condition filter3 adaptation,Let ccfilter3 adjust freely
QND_WriteReg(0x1d, 0xa9);
QND_WriteReg(0x4f, 0x40);//dsiable auto tuning
QND_WriteReg(0x34, SMSTART_VAL); ///set SMSTART
QND_WriteReg(0x35, SNCSTART_VAL); ///set SNCSTART
QND_WriteReg(0x36, HCCSTART_VAL); ///set HCCSTART
QNF_SetMute((void *)fm_dev_info, 0);
//dac_channel_on(FM_IIC_CHANNEL, FADE_ON);
return 0;
}
/*----------------------------------------------------------------------------*/
/**@brief 关闭 QN0835的电源
@param
@return
@note u8 QN8035_powerdown(void *priv)
*/
/*----------------------------------------------------------------------------*/
u8 qn8035_powerdown(void *priv)
{
if (fm_dev_info == NULL) {
fm_dev_info = (struct _fm_dev_info *)priv;
}
puts("qn8035_powerdown\n");
// QND_SetSysMode(0);
QNF_SetMute((void *)fm_dev_info, 1);
QND_WriteReg(SYSTEM1, 0x20);
//dac_channel_off(FM_IIC_CHANNEL, FADE_ON);
return 0;
}
/**********************************************************************
void QND_TuneToCH(UINT16 ch)
**********************************************************************
Description: Tune to the specific channel.
Parameters:
ch:Set the frequency (10kHz) to be tuned,
eg: 101.30MHz will be set to 10130.
Return Value:
None
**********************************************************************/
void QND_TuneToCH(u16 channel)
{
u8 reg;
u16 ch;
ch = channel * 10;
//increase reference PLL charge pump current.
QND_WriteReg(REG_REF, 0x7a);
/********** QNF_RXInit ****************/
QND_WriteReg(0x1b, 0x70); //Let NFILT adjust freely
QND_WriteReg(0x2c, 0x52);
QND_WriteReg(0x45, 0x50); //Set aud_thrd will affect ccfilter3's tap number
QND_WriteReg(0x40, 0x70); //set SNR as SM,SNC,HCC MPX
QND_WriteReg(0x41, 0xca);
/********** End of QNF_RXInit ****************/
#if INVERSE_IMR
reg = QN_IIC_read(CCA) & ~0x40;
if ((ch == 9340) || (ch == 9390) || (ch == 9530) || (ch == 9980) || (ch == 10480)) {
reg |= 0x40; // inverse IMR.
} else {
reg &= ~0x40;
}
QND_WriteReg(CCA, reg);
#endif
QNF_SetMute((void *)fm_dev_info, 1);
QNF_SetCh(ch, ch, 1);
//enable CCA mode with user write into frequency
QND_WriteReg(0x00, 0x13);
//#if USING_INDUCTOR
if (qn8035_inductor) {
//Auto tuning
QND_WriteReg(0x4f, 0x80);
reg = QN_IIC_read(0x4f);
reg >>= 1;
QND_WriteReg(0x4f, reg);
}
//#endif
///avoid the "POP" noise.
//QND_Delay(CH_SETUP_DELAY_TIME);
//QND_Delay(500);
delay_n10ms(10);
///decrease reference PLL charge pump current.
QND_WriteReg(REG_REF, 0x70);
QNF_SetMute((void *)fm_dev_info, 0);
}
/*----------------------------------------------------------------------------*/
/**@brief 设置一个频点QN0835
@param fre 875~1080
@return 10
@note bool set_fre_QN8035(u16 freq)
*/
/*----------------------------------------------------------------------------*/
bool QND_RXValidCH(u16 freq)
{
/* printf("set_8035_frq %d ", freq); */
u8 regValue;
u8 timeOut;
u8 isValidChannelFlag;
// UINT8 snr,snc,temp1,temp2;
#if PILOT_CCA
u8 snr, readCnt, stereoCount = 0;
#endif
/* freq = freq * 10; */
#if SCAN_NOISE_FLOOR_CCA
switch (freq) {
case 8750:
QND_ScanNoiseFloor(8750, 8800);
QND_RXSetTH();
break;
case 8810:
QND_ScanNoiseFloor(8810, 9200);
QND_RXSetTH();
break;
case 9210:
QND_ScanNoiseFloor(9210, 9600);
QND_RXSetTH();
break;
case 9610:
QND_ScanNoiseFloor(9610, 10000);
QND_RXSetTH();
break;
case 10010:
QND_ScanNoiseFloor(10010, 10400);
QND_RXSetTH();
break;
case 10410:
QND_ScanNoiseFloor(10410, 10800);
QND_RXSetTH();
break;
default:
//QND_Delay(350);
break;
}
#endif
QNF_SetCh(freq, freq, 1);
//#if USING_INDUCTOR
if (qn8035_inductor) {
//Auto tuning
QND_WriteReg(0x00, 0x11);
QND_WriteReg(0x4f, 0x80);
regValue = QN_IIC_read(0x4f);
regValue = (regValue >> 1);
QND_WriteReg(0x4f, regValue);
}
//#endif
//entering into RX mode and CCA mode,channels index decide by CCA.
QND_WriteReg(0x00, 0x12);
timeOut = 20; // time out is 100ms
while (1) {
regValue = QN_IIC_read(SYSTEM1);
//if it seeks a potential channel, the loop will be quited
if ((regValue & CHSC) == 0) {
break;
}
delay_n10ms(1); //delay 5ms
//if it was time out,chip would not normally work.
if ((timeOut--) == 0) {
return 0;
}
}
//reading out the rxcca_fail flag of RXCCA status
isValidChannelFlag = (QN_IIC_read(STATUS1) & RXCCA_FAIL ? 0 : 1);
if (isValidChannelFlag) {
//#if PILOT_CCA
if (qn8035_pilot_cca) {
for (readCnt = 10; readCnt > 0; readCnt--) {
delay_n10ms(2);
stereoCount += ((QN_IIC_read(STATUS1) & ST_MO_RX) ? 0 : 1);
if (stereoCount >= 3) {
return 1;
}
}
delay_n10ms(10);
snr = QN_IIC_read(SNR);
if (snr > qn8075_snr) {
return 1;
}
} else {
return 1;
}
//#endif
}
return 0;
}
/**/
u8 qn8035_set_fre(void *priv, u16 fre)
{
if (QND_RXValidCH(fre)) {
QND_TuneToCH(fre);
return TRUE;
}
return FALSE;
}
void QND_SetVol(u8 vol)
{
u8 sVol;
u8 regVal;
sVol = vol * 3 + 2;
regVal = QN_IIC_read(VOL_CTL);
regVal = (regVal & 0xC0) | (sVol / 6) | ((5 - (sVol % 6)) << 3);
//regVal = (regVal&0xC0)|0x02|0x06;
QND_WriteReg(VOL_CTL, regVal);
}
/*
void QND_SetVol(u8 vol)
{
u8 sVol;
u8 regVal;
sVol=vol*3+2;
regVal = QN_IIC_read(VOL_CTL);
regVal = regVal&0xC0;
regVal |= (BIT(0)|BIT(1)|BIT(2)|BIT(3)|BIT(5));
regVal &= ~BIT(4);
QND_WriteReg(VOL_CTL,regVal);
}
*/
/*----------------------------------------------------------------------------*/
/**@brief FM模块检测获取QN0835 模块ID
@param
@return QN0835模块返回10
@note u8 QN8035_Read_ID(void *priv)
*/
/*----------------------------------------------------------------------------*/
u8 qn8035_read_id(void *priv)
{
u8 cChipID;
if (fm_dev_info == NULL) {
fm_dev_info = (struct _fm_dev_info *)priv;
}
cChipID = QN_IIC_read(CID2);
cChipID &= 0xfc;
if (0x84 == cChipID) {
qn8035_online = 1;
return 1;
} else {
qn8035_online = 0;
return 0;
}
}
void qn8035_setch(u8 db)
{
QND_RXSetTH();
}
REGISTER_FM(qn8035) = {
.logo = "qn8035",
.init = qn8035_init,
.close = qn8035_powerdown,
.set_fre = qn8035_set_fre,
.mute = qn8035_mute,
.read_id = qn8035_read_id,
};
#endif