KT24-1110_65E-HA-651B/apps/common/device/fm/qn8035/QN8035.c
2024-11-10 18:44:17 +08:00

713 lines
19 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*--------------------------------------------------------------------------*/
/**@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 1当前频点有台0当前频点无台
@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模块返回1否则返回0
@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