ADC 모듈을 사용하기 위해 초기화 코드를 공부해보겠다.
static void Driver_Adc0_Init(void)
{
uint32 chnIx;
/* VADC Configuation */
/* create configuration */
IfxVadc_Adc_Config adcConfig;
IfxVadc_Adc_initModuleConfig(&adcConfig, &MODULE_VADC);
/* initialize module */
IfxVadc_Adc_initModule(&g_VadcAutoScan.vadc, &adcConfig);
/* create group config */
IfxVadc_Adc_GroupConfig adcGroupConfig;
IfxVadc_Adc_initGroupConfig(&adcGroupConfig, &g_VadcAutoScan.vadc);
/* with group 0 */
adcGroupConfig.groupId = IfxVadc_GroupId_0;
adcGroupConfig.master = adcGroupConfig.groupId;
/* enable scan source */
adcGroupConfig.arbiter.requestSlotScanEnabled = TRUE;
/* enable auto scan */
adcGroupConfig.scanRequest.autoscanEnabled = TRUE;
/* enable all gates in "always" mode (no edge detection) */
adcGroupConfig.scanRequest.triggerConfig.gatingMode = IfxVadc_GatingMode_always;
/* initialize the group */
IfxVadc_Adc_initGroup(&g_VadcAutoScan.adcGroup, &adcGroupConfig);
/* channel init */
for (chnIx = 0; chnIx < 5; ++chnIx) {
IfxVadc_Adc_initChannelConfig(&adcChannelConfig[chnIx], &g_VadcAutoScan.adcGroup);
adcChannelConfig[chnIx].channelId = (IfxVadc_ChannelId)(chnIx);
adcChannelConfig[chnIx].resultRegister = (IfxVadc_ChannelResult)(chnIx);
/* initialize the channel*/
IfxVadc_Adc_initChannel(&adcChannel[chnIx], &adcChannelConfig[chnIx]);
/* add to scan */
unsigned channels = ( 1 << adcChannelConfig[chnIx].channelId);
unsigned mask = channels;
IfxVadc_Adc_setScan(&g_VadcAutoScan.adcGroup,channels,mask);
}
}
먼저 ADC 모듈 초기화를 위한 구조체 변수를 선언하고 초기값을 받아온다.
IfxVadc_Adc_Config adcConfig;
IfxVadc_Adc_initModuleConfig(&adcConfig, &MODULE_VADC);
IfxVadc_Adc_Config 구조체는 ADC 설정을 위한 구조체이다.
void IfxVadc_Adc_initModuleConfig(IfxVadc_Adc_Config *config, Ifx_VADC *vadc)
{
config->vadc = vadc;
config->analogFrequency = IFXVADC_DEFAULT_ANALOG_FREQ;
config->digitalFrequency = IfxVadc_getAdcDigitalFrequency(vadc);
config->moduleFrequency = IfxScuCcu_getSpbFrequency();
config->globalInputClass[0].resolution = IfxVadc_ChannelResolution_12bit;
config->globalInputClass[0].sampleTime = 1.0e-6;
config->globalInputClass[1].resolution = IfxVadc_ChannelResolution_12bit;
config->globalInputClass[1].sampleTime = 1.0e-6;
config->startupCalibration = FALSE;
config->supplyVoltage = IfxVadc_LowSupplyVoltageSelect_5V;
}
해당 구조체의 주소값을 IfxVadc_Adc_initModuleConfig 함수에 넘겨 MODULE_VADC의 초기값을 저장한다.
IfxVadc_Adc_initModule(&g_VadcAutoScan.vadc, &adcConfig);
해당 함수를 통해 adcConfig에 저장된 설정값을 레지스터에 쓴다.
IfxVadc_Status IfxVadc_Adc_initModule(IfxVadc_Adc *vadc, const IfxVadc_Adc_Config *config)
{
IfxVadc_Status status = IfxVadc_Status_noError;
Ifx_VADC *vadcSFR = config->vadc;
vadc->vadc = vadcSFR;
float32 analogFrequency;
uint8 inputClassNum, groupNum;
/* Enable VADC kernel clock */
IfxVadc_enableModule(vadcSFR);
IfxVadc_selectPowerSupplyVoltage(vadcSFR, config->supplyVoltage);
/* Set Analog Frequency */
if (IfxVadc_initializeFAdcI(vadcSFR, config->analogFrequency) == 0)
{
return IfxVadc_Status_notInitialised;
}
else
{
/* do nothing */
}
/* Set digital Frequency */
IfxVadc_initializeFAdcD(vadcSFR, config->digitalFrequency);
analogFrequency = IfxVadc_getAdcAnalogFrequency(vadcSFR);
/* configure Global input class registers */
for (inputClassNum = 0; inputClassNum < IFXVADC_NUM_GLOBAL_INPUTCLASSES; inputClassNum++)
{
/* configure ADC channel resolution ( conversion mode ) */
IfxVadc_setGlobalResolution(vadcSFR, inputClassNum, config->globalInputClass[inputClassNum].resolution);
/* configure Sample time ticks */
IfxVadc_setGlobalSampleTime(vadcSFR, inputClassNum, analogFrequency, config->globalInputClass[inputClassNum].sampleTime);
}
/* Start up calibration is requested */
if (config->startupCalibration == TRUE)
{
/* Ensure that all groups are enabled */
for (groupNum = 0; groupNum < IFXVADC_NUM_ADC_GROUPS; groupNum++)
{
IfxVadc_enableAccess(vadcSFR, (IfxVadc_Protection)(IfxVadc_Protection_initGroup0 + groupNum));
IfxVadc_setAnalogConvertControl(&vadcSFR->G[groupNum], IfxVadc_AnalogConverterMode_normalOperation);
IfxVadc_disableAccess(vadcSFR, (IfxVadc_Protection)(IfxVadc_Protection_initGroup0 + groupNum));
}
// execute calibration
IfxVadc_startupCalibration(vadcSFR);
}
return status;
}
*vadcSFR에 vadc 레지스터의 시작 주소를 받는다고 한다.
IfxVadc_enableModule(vadcSFR) 함수를 살펴보겠다.
IFX_INLINE void IfxVadc_enableModule(Ifx_VADC *vadc)
{
uint16 passwd = IfxScuWdt_getCpuWatchdogPassword();
IfxScuWdt_clearCpuEndinit(passwd);
vadc->CLC.U = 0x00000000;
IfxScuWdt_setCpuEndinit(passwd);
}
vadc->CLC.U = 0x00000000;
vadc 모듈의 CLC 레지스터에 0을 넣는다고 한다.

vadc 모듈의 CLC 레지스터 정보이다.
전부 0 으로 설정 시 Adc 모듈이 Sytem Clock 을 이용하여 동작한다고 한다.
다음은 IfxVadc_selectPowerSupplyVoltage(vadcSFR, config->supplyVoltage); 부분이다.
void IfxVadc_selectPowerSupplyVoltage(Ifx_VADC *vadc, IfxVadc_LowSupplyVoltageSelect supplyVoltage)
{
Ifx_VADC_GLOBCFG tempGLOBCFG;
tempGLOBCFG.U = vadc->GLOBCFG.U;
tempGLOBCFG.B.LOSUP = supplyVoltage;
tempGLOBCFG.B.DIVWC = 1;
IfxVadc_enableAccess(vadc, IfxVadc_Protection_globalConfig);
vadc->GLOBCFG.U = tempGLOBCFG.U;
IfxVadc_disableAccess(vadc, IfxVadc_Protection_globalConfig);
}
해당 함수에서 VADC 모듈의 Global Configuration Register 을 수정한다.

LOSUP 과 DIVWC 필드를 수정하게 되는데

LOSUP 필드는 전압을 설정한다. supplyVoltage 값은 IfxVadc_Adc_initModuleConfig() 함수를 통해 가져온 기본값인 0을 입력하여 5V로 선택하였다.
DIVWC 필드는 1로 값을 설정하였다. 그 이유는 DIVA, DCMSB, DIVD, REFPC, LOCUP 필드에 값을 쓰기 위함이다.
다음 함수는 (IfxVadc_initializeFAdcI(vadcSFR, config->analogFrequency) 이다.
Ananlog Frequency를 설정하기 위한 함수이다.
uint32 IfxVadc_initializeFAdcI(Ifx_VADC *vadc, uint32 fAdcI)
{
uint32 divA;
uint32 result;
uint32 fadc = IfxScuCcu_getSpbFrequency();
/* DivA = min(max(0, Fadc / FAdcI - 1), 0x3F); */
divA = (fadc << 2) / fAdcI;
divA = (divA + 2) >> 2; /* Round to nearest integer */
divA = __minu(divA - 1, 0x1Fu);
result = fadc / (divA + 1);
if (result > IFXVADC_ANALOG_FREQUENCY_MAX)
{
divA = __minu(divA + 1, 0x1Fu);
result = fadc / (divA + 1);
}
else
{
/* do nothing */
}
if (!((result >= IFXVADC_ANALOG_FREQUENCY_MIN) && (result <= IFXVADC_ANALOG_FREQUENCY_MAX)))
{
result = 0; /* Min / Max FAdcI frequency */
}
else
{
IfxVadc_initialiseAdcConverterClock(vadc, divA);
}
return result;
}
해당 코드는 Fadc = 100Mhz 이고 FadcI (analog freqeuncy) 를 20Mhz 로 설정하기 위한 Global Configuration Register의 DIVA 필드 값을 찾는 코드이다.
0x1 이면 fadcI 는 100/2 Mhz
0x2 이면 fadcI 는 100/3 Mhz
0x3 이면 fadcI 는 100/4 Mhz
0x4 이면 fadcI 는 100/5 Mhz
이므로 초기 설정에서 fadcI를 20Mhz로 설정하였으니 이를 위해 divA의 값 0x4를 구하기 위한 코드이다.

divA = (fadc << 2) / fAdcI;
: fadc 를 2의2제곱(4) 만큼 곱한 값을 fAdcI 로 나눈다 ==> 20
divA = (divA + 2) >> 2; /* Round to nearest integer */
: 구한 값을 반올림 처리한다. divA = (20 + 2) >> 2 == > 4
divA = __minu(divA - 1, 0x1Fu);
: 계산된 divA에서 1을 빼고 결과값이 5비트 범위(0x1F, 즉 31) 을 초과하지 않도록 제한한다. divA = min(4,31) = 4
따라서 divA 필드에는 0x4의 값이 들어가게 된다.

디버거로 레지스터의 값을 확인해보니 정확하게 0x4가 잘 들어간 것을 확인할 수 있다.
다음은 Digital Frequency 설정이다.
uint32 IfxVadc_initializeFAdcD(Ifx_VADC *vadc, uint32 fAdcD)
{
uint32 divD;
uint32 result;
uint32 fadc = IfxScuCcu_getSpbFrequency();
divD = (fadc / fAdcD - 1);
divD = __minu(divD, 0x3u);
result = fadc / (divD + 1);
IfxVadc_initialiseAdcArbiterClock(vadc, divD);
return result;
}
Digital Frequency 100Mhz를 설정하기 위해 divD 필드 값을 구하고 설정하는 코드이다.

Digital Frequency 는 100Mhz로 모듈 frequency랑 같다.
즉 0x0의 값이 들어가야한다.
위에 디버거 사진을 보면 0x0이 설정된것을 볼 수 있다.
config->analogFrequency = IFXVADC_DEFAULT_ANALOG_FREQ;
config->digitalFrequency = IfxVadc_getAdcDigitalFrequency(vadc);
config->moduleFrequency = IfxScuCcu_getSpbFrequency();
ADC 모듈 설정값을 보면 총 3가지의 Frequency 값이 있다.
Module
analog
digital
Module은 ADC 모듈로 들어오는 system clock 값이다. fSPB 와 같이 사용하므로 100Mhz 가 들어온다.
analog 와 digital frequency는 각각 모듈내 Converter와 Arbiter 의 frequency 값이다.

사진에서 볼 수 있듯이, DIVD 값은 Arbiter, DIVA 값은 Converter로 들어간다.
Converter에는 3가지의 소스를 제공한다. AutoScan, BackgroundScan, Queue
하나의 Converter 안에는 여러 채널이 있는데 각 채널별로 서로 다른 소스를 나누어 사용할 수 있고,
Arbiter는 해당 소스별로 우선순위에 따라 채널의 데이터를 Convert 시켜주는 역할이다.