[Aurix TC275] ADC - 2

사이킷·2025년 1월 18일

AURIX MCU

목록 보기
4/13
post-thumbnail

ADC 모듈 초기화 코드

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 모듈 초기화

먼저 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이 설정된것을 볼 수 있다.

Arbiter? Converter?

    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 시켜주는 역할이다.

참조

https://cafe.naver.com/binaryembedded

profile
공부한거 정리, 잘못된 정보 태클은 언제나 환영입니다.

0개의 댓글