📄 lv2400x.c
字号:
if (byVolume < g_byDynBassBoost ) // Logical volume level doesn't exceed dynamic bass boost level
byTone = byVolume; // Let tone level follow volume level
else // Volume level exceeds dynamic bass boost
byTone = g_byDynBassBoost; // Keep tone level fixed at dynamic bass boost level
byTone = 15 - byTone; // Convert tone level to tone value (the 4 tone bits are inverted)
byTone <<= 4; // Shift tone to correction position
SetRegBits(IR01_AUDIO_CTRL1_REG, IR1_ACTL1_TONE_LVL, byTone);
}
}
break;
case IHCAP_HPA: // Headphone amplifier
// Input: 0=off 1=on
// Check if headphone support
if ( g_byHwFlag1 & HF1_HP)
DriveBit(IR01_PW_SCTRL_REG, IR2_PSCTL_HPA, byValue);
break;
// case IHCAP_CNT2:
// m_dwCnt2ExtClk = pValue->Cnt2.dwExtClock; // Save the external clock (in Hz) for later usage
// break;
// Software settings
case IHCAP_SCANLVL: // Scan level
g_byScanLevel = byValue;
break;
case IHCAP_SCANWRP: // Scan wrapping
g_byStnFlag &= (~STN_WRAP_MODE); // clear old setting
g_byStnFlag |= (byValue & STN_WRAP_MODE);
break;
case IHCAP_REGION: // Region
byResult = SetRegion(byValue);
break;
case IHCAP_BEEPVOL: // Audible feedback volume
g_byBeepVol = byValue;
#ifdef USE_EXTCLK
case IHCAP_EXTCLK:
g_bySwFlag1 &= (BYTE)(~SF1_EXTCLK);
g_bySwFlag1 |= (BYTE)(byValue & SF1_EXTCLK);
break;
#endif //USE_EXTCLK
default:
byResult = LVLS_NSU_FEATURE_ERR;
break;
} // EndSwitch
return(byResult);
} // End SetHwFeatureValue
void AdjustHwToneControl(void)
{
// Control the ToneMute bit (IR1_RCTL3_TONE_MUTE_L of IR01_RADIO_CTRL3_REG)
// Unmute the tone control if bass or treble is not 0
BYTE byTmp;
BOOL bState;
byTmp = GetSwRegValue(IR01_AUDIO_CTRL2_REG);
if (byTmp & (IR1_ATREBLE_MASK|IR1_ABASS_MASK)) // One or more tone bit is set
bState = FALSE; // Don't mute the tone
else
bState = TRUE; // All tone bits are 0, mute the tone control
DriveBitNeg(IR01_RADIO_CTRL3_REG, IR1_RCTL3_TONE_MUTE_L, bState);
} // End AdjustHwToneControl
//////////////////////////////////////////////////////////////////////
// LV24xxx Interrupt functions
//////////////////////////////////////////////////////////////////////
void EnableLv2400xIrq(void)
{
// Enable interrupt of 3-wire bus 1 (INT5 (vector 1Bh) - rising edge trigger)
// We use following LV24xxx interrupts:
// - Field strength
// - Stereo/Mono
// - AFC out of range
// Clear any pending interrupt of LV24xxx
ReadReg(IR01_CTRL_STAT_REG); // Read IR01_CTRL_STAT_REG to clear AFC interrupt
ReadReg(IR01_RADIO_STAT_REG); // Read IR01_RADIO_STAT_REG to clear field strength, Stereo interrupt
// Program the LV24xxx interrupt level: high active, enable Mono/Stereo, fieldstrength and AFC interrupt
WriteReg(IR01_IRQ_MSK_REG, IR1_IRQM_ACT_HIGH|IR1_IRQM_EN_MS|IR1_IRQM_EN_FS|IR1_IRQM_EN_AFC);
// Write to IR01_IRQ_OUT_REG-register to select interrupt output of LV24xxx
WriteReg(IR01_IRQ_OUT_REG, 0);
// Enable system interrupt INT5
Enable3wIrq(TRUE);
// Mark chip interrupt is on
g_byLvIrqFlg |= IFL_CHIP_IRQ_EN;
} // End EnableLv2400xIrq
void DisableLv2400xIrq(void)
{
// Disable system interrupt INT5
Enable3wIrq(FALSE);
} // End DisableLv2400xIrq
void HandleLv2400xIrq(void)
{
// We handle following LV24xxx interrupts:
// - Field strength: Update LCD display
// - Stereo/Mono: Update LCD display
// - AFC out of range: re-tune the frequency
BYTE byEnMask, byIrqOcc, byTmp1;
// Fetch the interrupt enable mask
byEnMask = GetSwRegValue(IR01_IRQ_MSK_REG);
// Read the interrupt ID register (see what interrupt occurred)
byIrqOcc = ReadReg(IR01_IRQ_ID_REG);
// Handle the interrupts
if ( byIrqOcc & (IR1_IRQID_FS_MS) ) // Mono or fieldstrength
{
// Read IR01_RADIO_STAT_REG to clear field strength, Stereo interrupt
byTmp1 = ReadReg(IR01_RADIO_STAT_REG);
// Hanlde field strength interrupt
if (byEnMask & IR1_IRQM_EN_FS) // Was field strength interrupt enabled?
{
CallBack(CRSN_FS_CHNG);
} // End fieldstrength interrupt handling
// Hanlde Mono/Stereo interrupt
if (byEnMask & IR1_IRQM_EN_MS) // Was its interrupt enabled?
{
CallBack(CRSN_MS_CHNG);
} // End Mono/Stereo interrupt handling
} // EndIf Field strength/Mono-stereo
// AFC interrupt
if (byIrqOcc & IR1_IRQID_AFC) // AFC interrupt occurred?
{
if (byEnMask & IR1_IRQM_EN_AFC) // Was its interrupt enabled?
{
CallBack(CRSN_AFC_FAILED);
}
}
} // End HandleLc2400xIrq
/* ************************************************************************************************
*
* Function: AudioFeedback
*
* Authors: Hung van Le
* Purpose: Generate beep with the LV2400x
* Input: byType: pre-defined beep type
* AUDFB_TYPE1: tone 500 Hz for 40ms
* AUDFB_TYPE2: tone 2kHz for 40ms, pause 40ms, tone 2kHz for 40ms
* Comments: - This function accesses the registers of LV2400x directly. These accesses can be moved to
* chip specific layer when multiple chip support is needed. For example the Stereo decoder PLL can be
* muted through a chip function like BOOL MuteStereoDecoderPll(BOOL bState) instead of driving the bit
* directly.
* - Function SetHwFeatureValue(IHCAP_BEEPSOURCE, byValue) can also be used to avoid accessing the LV2400x
* directly. byValue for LV2400x is: 0=beep off, 1=beep 500 Hz, 2=beep 1kHz, 3=beep 2kHz
* - Make sure that the LV2400x is in idle state before invoking this function.
* ************************************************************************************************
* Copyright (c) 2004. Semiconductor Ideas to the Market (ItoM) B.V. All rights reserved.
* ************************************************************************************************ */
void AudioFeedback(BYTE byType)
{
// Local equations
#define _AFB_DEMPLL ((BYTE)0x01)
#define _AFB_SDPLL ((BYTE)0x02)
#define _AFB_AUD_MUTE ((BYTE)0x04)
BYTE byOrgFlag, byCurVolume, byOrgBeep, byOrgBand;
// Do nothing if beep is not desired
if (g_byBeepVol==0)
return;
// Init local.
byOrgFlag = 0;
// Turn off radio band, enable beep source
byOrgBand = SetRegBits(IR01_RADIO_CTRL3_REG, (IR1_RCTL3_SE_FM|IR1_RCTL3_SE_BEEP), IR1_RCTL3_SE_BEEP);
// Turn on the stereo decoder PLL mute for stable beep frequency
if ( DriveBit(IR01_STEREO_CTRL_REG, IR1_STCTL_SD_PM, TRUE) )
byOrgFlag |= _AFB_SDPLL;
// Unmute audio if necessary
if ( GetHwFeatureValue(IHCAP_AMUTE) )
{
SetHwFeatureValue(IHCAP_AMUTE, 0); // Un-mute audio
byOrgFlag |= _AFB_AUD_MUTE;
}
// Set volume if necessary
byCurVolume = GetHwFeatureValue(IHCAP_VOLUME); // Determine current volume level
if (byCurVolume<g_byBeepVol)
SetHwFeatureValue(IHCAP_VOLUME, g_byBeepVol);
// turn on demodulator PLL mute to mute the radio
// if (DriveBitNeg(IR01_RADIO_CTRL2_REG, IR1_RCTL2_IF_PM_L, TRUE) )
// byOrgFlag |= _AFB_DEMPLL;
if (byType == AUDFB_TYPE1)
{
// Beep at 500 Hz
byOrgBeep = SetRegBits(IR01_AUDIO_CTRL2_REG, IR1_ACTL2_BPFREQ, IR1_BEEP_500Hz);
DelayUs(DLT_20ms);
DelayUs(DLT_20ms);
}
else //if (byType == AUDFB_TYPE2)
{
// Beep at 2kHz
byOrgBeep = SetRegBits(IR01_AUDIO_CTRL2_REG, IR1_ACTL2_BPFREQ, IR1_BEEP_2KHz);
DelayUs(DLT_20ms);
DelayUs(DLT_20ms);
// Pause
SetRegBits(IR01_AUDIO_CTRL2_REG, IR1_ACTL2_BPFREQ, IR1_BEEP_OFF);
DelayUs(DLT_20ms);
DelayUs(DLT_20ms);
// Beep at 2kHz
SetRegBits(IR01_AUDIO_CTRL2_REG, IR1_ACTL2_BPFREQ, IR1_BEEP_2KHz);
DelayUs(DLT_20ms);
DelayUs(DLT_20ms);
}
// Restore everything
if (byCurVolume != g_byBeepVol) // Restore volume
SetHwFeatureValue(IHCAP_VOLUME, byCurVolume);
// if ( (byOrgFlag &_AFB_DEMPLL)==0 ) // Restore demodulator PLL mute
// DriveBitNeg(IR01_RADIO_CTRL2_REG, IR1_RCTL2_IF_PM_L, FALSE);
if ( byOrgFlag & _AFB_AUD_MUTE ) // Restore audio mute
SetHwFeatureValue(IHCAP_AMUTE, 1);
if ( (byOrgFlag & _AFB_SDPLL)==0 ) // Restore the stereo decoder PLL mute
DriveBit(IR01_STEREO_CTRL_REG, IR1_STCTL_SD_PM, FALSE);
// Restore beep frequency bits
SetRegBits(IR01_AUDIO_CTRL2_REG, IR1_ACTL2_BPFREQ, byOrgBeep);
// Restore radio band, beep source setting
SetRegBits(IR01_RADIO_CTRL3_REG, (IR1_RCTL3_SE_FM|IR1_RCTL3_SE_BEEP), byOrgBand);
} // End AudioFeedback
//-----------------------------------------------------------------------------
// Helper functions for switch between USB and stand-alone mode
// Placed here because of the shadow list/value array
//-----------------------------------------------------------------------------
#ifdef USE_USB
void RestoreShadowReg()
{
// Restore shadow register of LV2400x when switching from USB back to stand-alone
BYTE i;
g_byBlock = 0xFF; // Mark shadowed block select is dirty to force setting it the 1st time
for (i=0; i<LVSHADOW_LSIZE; i++)
WriteReg(g_Lv24ShadowList[i], g_byaShwRegValue[i]);
} // End RestoreShadowReg
#endif //USE_USB
//-----------------------------------------------------------------------------
// Registers shadowing function
//-----------------------------------------------------------------------------
void ShadowReg(WORD wRegAddress, BYTE byValue)
{
BYTE i;
for (i=0; i<LVSHADOW_LSIZE; i++)
{
if ( g_Lv24ShadowList[i] == wRegAddress )
{
g_byaShwRegValue[i] = byValue;
break;
}
}
g_byBlock = MSB(wRegAddress); // Save last access block
} // End ShadowReg
BYTE GetSwRegValue(WORD wRegAddress)
{
BYTE i;
for (i=0; i<LVSHADOW_LSIZE; i++)
{
if ( g_Lv24ShadowList[i] == wRegAddress )
return(g_byaShwRegValue[i]);
}
return(0); // not found
} // End GetSwRegValue
void SelRegBlock(BYTE byBlock)
{
// Skip selecting the block when it's already selected
if (g_byBlock != byBlock)
{
g_byBlock = byBlock; // Save block number for next time
IoWrite3W(byBlock, BLK_SEL); // Write the block number to BLK_SEL register
}
} // End SelRegBlock
/* ************************************************************************************************
*
* Function: IsFrequencyOk
*
* Authors: Hung van Le
* Purpose: Verify if wCurFreq is in range of wExpFreq +/- margin
* Input:
* wCurFreq: current frequency
* wExpFreq: the expected frequency
* dwPrecise: precise of the compare (max=1000000, 1000000 is exact comparing)
* Output:
* 0 : wCurFreq within the margin
* -1: wCurFreq too low (i.e. wCurFreq < (wExpFreq - margin)
* +1: wCurFreq too high (i.e. wCurFreq > (wExpFreq + margin)
* Comments: margin = (1000000 * DividerFactor) / dwPrecise
* Divider factor is dependant on current output select of the chip
*
* ************************************************************************************************
* Copyright (c) 2004. Semiconductor Ideas to the Market (ItoM) B.V. All rights reserved.
* ************************************************************************************************ */
int IsFrequencyOk(WORD wCurFreq, WORD wExpFreq, DWORD dwPrecise)
{
WORD wMargin;
DWORD dwFactor;
if (dwPrecise != 1000000)
{
switch ( GetOutputSelect() )
{
case CO_RF_OSC:
// wMargin = (1000000 * (DWORD)GetDividerFactor())/dwPrecise;
// wMargin /= 10000; // 10 KHz unit for RF
dwFactor = 100 * IMR01_FM_DIVIDER;
break;
case CO_IF_OSC:
// wMargin = (1000000 * (DWORD)GetDividerFactor())/dwPrecise;
// wMargin /= 10; // 10 Hz unit for IF
dwFactor = 100000;
break;
//case CO_SD_OSC:
default:
// wMargin = (1000000 * (DWORD)GetDividerFactor())/dwPrecise;
// wMargin /= 1; // 1 Hz unit for other freq
// 1 Hz unit for SD and other freq.
dwFactor = 1000000;
break;
}
}
else
dwFactor = 0;
if (dwPrecise < dwFactor)
wMargin = (WORD)(dwFactor / dwPrecise);
else
wMargin = 0;
if ( wCurFreq < (wExpFreq - wMargin) )
return(-1);
else if ( wCurFreq > (wExpFreq + wMargin) )
return(1);
else
return(0);
} // End IsFrequencyOk
// ==============================================================================
#endif //USE_LVC2400x // The whole file can be discarded if LV2400x is not used
// ==============================================================================
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -