본문 바로가기
Embedded SW/Embedded SW Introduction

SPI 예제 코드를 포팅, 동작 확인 1편 (T275 인피니언 MCU)

by 방구석 임베디드 2022. 9. 12.
반응형

인피니언 MCU인 TC275를 이용하여 SPI 예제코드를 포팅해서

동작해 보도록 하겠습니다.

 

어느 MCU를 사용하던지 보통 예제코드가 존재하기 마련입니다.

인피니언 역시 예제코드를 제공해 줍니다.

 

SPI를 이용하여 예제코드를 포팅하고 동작시키기 전에

아래 글을 먼저 읽고 오시면 좋을 것 같습니다.

https://embeddedchallenge.tistory.com/58

 

05. SPI 통신이란 무엇인가?

SPI(Serial Peripheral Interface)는 Electric Control Unit안에서 IC(Integrated Chip) 끼리 통신을 수행하기 위한 통신 기법입니다. 아래 두개의 자동차 ECU가 있습니다. VCM(Vehicle Control Module) 와 BCM(Bo..

embeddedchallenge.tistory.com

위의 글을 요약 정리하면

SPI는 Serial Peripheral Interface의 약자로

MCU안에 존재하는 주변기기 입니다.

이 주변기기는 ECU(Electric Control Unit)기판안에서

MCU와 IC(Integrated Chip)과 통신을 수행할때 사용되는 주변기기 입니다.

 

물론 MCU와 MCU끼리 통신도 가능합니다.

그러면 이제 SPI를 동작 시켜 보도록 하겠습니다.

 

참고로 작업한 코드는 글 아래에서

다운받을 수 있는 사이트를 제공하고 있으니 참고해 주세요.

 

먼저 예제 코드는 다음과 같습니다.

여기 예제코드를 이용하여 SPI를 한번 동작 시켜볼 것입니다.

이 예제 코드는 MCU와 MCU끼리 SPI통신을 수행하는 예제 코드 입니다.

 

그러면 한번 코드를 살펴 보도록 하겠습니다.

하나의 MCU를 이용하여 2개의 SPI모듈을 사용하여

통신을 수행하고 있습니다.

 

저는 보드가 2개니까

2개의 MCU를 이용하여 통신을 수행해 보도록 하겠습니다.

static void IfxQspi_Masterinit(void)
{
    uint32                          i;
    IfxQspi_SpiMaster_Config        spiMasterConfig;
    IfxQspi_SpiMaster_ChannelConfig spiMasterChannelConfig;

    {
        /* create module config */
        IfxQspi_SpiMaster_initModuleConfig(&spiMasterConfig, &MODULE_QSPI0);

        /* set the maximum baudrate */
        spiMasterConfig.base.maximumBaudrate = 10000000;

        /* ISR priorities and interrupt target */
        spiMasterConfig.base.txPriority  = ISR_PRIORITY_QSPI0_TX;
        spiMasterConfig.base.rxPriority  = ISR_PRIORITY_QSPI0_RX;
        spiMasterConfig.base.erPriority  = ISR_PRIORITY_QSPI0_ER;
        spiMasterConfig.base.isrProvider = (IfxSrc_Tos)IfxCpu_getCoreIndex();

        /* pin configuration */
        const IfxQspi_SpiMaster_Pins pins = {&IfxQspi0_SCLK_P20_11_OUT,                               /* SCLK */
                                             IfxPort_OutputMode_pushPull,
                                             &IfxQspi0_MTSR_P20_14_OUT,  IfxPort_OutputMode_pushPull, /* MTSR */
                                             &IfxQspi0_MRSTA_P20_12_IN,  IfxPort_InputMode_pullDown,  /* MRST */
                                             IfxPort_PadDriver_cmosAutomotiveSpeed3                   /* pad driver mode */
        };
        spiMasterConfig.pins = &pins;

        /* initialize module */
        IfxQspi_SpiMaster_initModule(&g_QspiCpu.drivers.spiMaster, &spiMasterConfig);
    }

    {
        /* create channel config */
        IfxQspi_SpiMaster_initChannelConfig(&spiMasterChannelConfig,
            &g_QspiCpu.drivers.spiMaster);

        /* set the baudrate for this channel */
        spiMasterChannelConfig.base.baudrate = 5000000;

        const IfxQspi_SpiMaster_Output slsOutput = {&IfxQspi0_SLSO7_P33_5_OUT,
                                                    IfxPort_OutputMode_pushPull,
                                                    IfxPort_PadDriver_cmosAutomotiveSpeed1};

        spiMasterChannelConfig.sls.output.pin    = slsOutput.pin;
        spiMasterChannelConfig.sls.output.mode   = slsOutput.mode;
        spiMasterChannelConfig.sls.output.driver = slsOutput.driver;

        /* initialize channel */
        IfxQspi_SpiMaster_initChannel(&g_QspiCpu.drivers.spiMasterChannel,
            &spiMasterChannelConfig);
    }

    /* init tx buffer area */
    for (i = 0; i < SPI_BUFFER_SIZE; i++)
    {
        g_QspiCpu.qspiBuffer.spi0TxBuffer[i] = (uint8)(i + 1);
        g_QspiCpu.qspiBuffer.spi2TxBuffer[i] = (uint8)(i + 100);

        g_QspiCpu.qspiBuffer.spi0RxBuffer[i] = (uint8)(0);
        g_QspiCpu.qspiBuffer.spi2RxBuffer[i] = (uint8)(0);
    }
}


/** \brief Qspi Slave initilisation
 *
 * This function initialises Qspi2 in Slave mode.
 */
static void IfxQspi_Slaveinit(void)
{
    IfxQspi_SpiSlave_Config spiSlaveConfig;

    {
        /* create module config */
        IfxQspi_SpiSlave_initModuleConfig(&spiSlaveConfig, &MODULE_QSPI2);

        /* set the maximum baudrate */
        spiSlaveConfig.base.maximumBaudrate = 10000000;

        /* ISR priorities and interrupt target */
        spiSlaveConfig.base.txPriority  = ISR_PRIORITY_QSPI2_TX;
        spiSlaveConfig.base.rxPriority  = ISR_PRIORITY_QSPI2_RX;
        spiSlaveConfig.base.erPriority  = ISR_PRIORITY_QSPI2_ER;
        spiSlaveConfig.base.isrProvider = (IfxSrc_Tos)IfxCpu_getCoreIndex();

        /* pin configuration */
        const IfxQspi_SpiSlave_Pins slavePins = {&IfxQspi2_SCLKB_P15_8_IN,                               /* SCLK Pin */
                                                 IfxPort_InputMode_pullDown,
                                                 &IfxQspi2_MTSRA_P15_5_IN,  IfxPort_InputMode_pullDown,  /* MTSR Pin */
                                                 &IfxQspi2_MRST_P15_7_OUT,  IfxPort_OutputMode_pushPull, /* MRST Pin */
                                                 &IfxQspi2_SLSIB_P15_1_IN,  IfxPort_InputMode_pullDown,  /* SLSI Pin */
                                                 IfxPort_PadDriver_cmosAutomotiveSpeed3                  /* pad driver mode */
        };
        spiSlaveConfig.pins = &slavePins;

        /* initialize module */
        IfxQspi_SpiSlave_initModule(&g_QspiCpu.drivers.spiSlave, &spiSlaveConfig);
    }
}


/** \brief Demo init API
 *
 * This function is called from main during initialization phase
 */
void IfxQspiCpuDemo_init(void)
{
    /* disable interrupts */
    boolean interruptState = IfxCpu_disableInterrupts();

    IfxQspi_Slaveinit();
    IfxQspi_Masterinit();

    /* enable interrupts again */
    IfxCpu_restoreInterrupts(interruptState);
}

따라서 프로젝트를 2개 만들어 보도록 할게요.

MCU를 2개 사용할 것이고

하나는 Master MCU

다른 하나는 Slave MCU라고 이름을 붙이도록 하겠습니다.

우선 아래와 같이 Master SPI 부분만 Master MCU프로젝트에 포팅을 하였습니다.

다행히 컴파일 에러는 없습니다.

 

IFX_INTERRUPT(SPI0_TxHandler, 0, ISR_PRIORITY_QSPI0_TX);
IFX_INTERRUPT(SPI0_RxHandler, 0, ISR_PRIORITY_QSPI0_RX);
IFX_INTERRUPT(SPI0_ErHandler, 0, ISR_PRIORITY_QSPI0_ER);

void SPI0_TxHandler(void)
{
    IfxCpu_enableInterrupts();
    IfxQspi_SpiMaster_isrTransmit(&g_QspiCpu.drivers.spiMaster);
}

void SPI0_RxHandler(void)
{
    IfxCpu_enableInterrupts();
    IfxQspi_SpiMaster_isrReceive(&g_QspiCpu.drivers.spiMaster);
}

void SPI0_ErHandler(void)
{
    IfxCpu_enableInterrupts();
    IfxQspi_SpiMaster_isrError(&g_QspiCpu.drivers.spiMaster);
}

static void Driver_Spi0_Init(void)
{
    uint32                          i;
    IfxQspi_SpiMaster_Config        spiMasterConfig;
    IfxQspi_SpiMaster_ChannelConfig spiMasterChannelConfig;

    {
        /* create module config */
        IfxQspi_SpiMaster_initModuleConfig(&spiMasterConfig, &MODULE_QSPI0);

        /* set the maximum baudrate */
        spiMasterConfig.base.maximumBaudrate = 10000000;

        /* ISR priorities and interrupt target */
        spiMasterConfig.base.txPriority  = ISR_PRIORITY_QSPI0_TX;
        spiMasterConfig.base.rxPriority  = ISR_PRIORITY_QSPI0_RX;
        spiMasterConfig.base.erPriority  = ISR_PRIORITY_QSPI0_ER;
        spiMasterConfig.base.isrProvider = (IfxSrc_Tos)IfxCpu_getCoreIndex();

        /* pin configuration */
        const IfxQspi_SpiMaster_Pins pins = {&IfxQspi0_SCLK_P20_11_OUT,                               /* SCLK */
                                             IfxPort_OutputMode_pushPull,
                                             &IfxQspi0_MTSR_P20_14_OUT,  IfxPort_OutputMode_pushPull, /* MTSR */
                                             &IfxQspi0_MRSTA_P20_12_IN,  IfxPort_InputMode_pullDown,  /* MRST */
                                             IfxPort_PadDriver_cmosAutomotiveSpeed3                   /* pad driver mode */
        };
        spiMasterConfig.pins = &pins;

        /* initialize module */
        IfxQspi_SpiMaster_initModule(&g_QspiCpu.drivers.spiMaster, &spiMasterConfig);
    }

    {
        /* create channel config */
        IfxQspi_SpiMaster_initChannelConfig(&spiMasterChannelConfig,
            &g_QspiCpu.drivers.spiMaster);

        /* set the baudrate for this channel */
        spiMasterChannelConfig.base.baudrate = 5000000;

        const IfxQspi_SpiMaster_Output slsOutput = {&IfxQspi0_SLSO7_P33_5_OUT,
                                                    IfxPort_OutputMode_pushPull,
                                                    IfxPort_PadDriver_cmosAutomotiveSpeed1};

        spiMasterChannelConfig.sls.output.pin    = slsOutput.pin;
        spiMasterChannelConfig.sls.output.mode   = slsOutput.mode;
        spiMasterChannelConfig.sls.output.driver = slsOutput.driver;

        /* initialize channel */
        IfxQspi_SpiMaster_initChannel(&g_QspiCpu.drivers.spiMasterChannel,
            &spiMasterChannelConfig);
    }

    /* init tx buffer area */
    for (i = 0; i < SPI_BUFFER_SIZE; i++)
    {
        g_QspiCpu.qspiBuffer.spi0TxBuffer[i] = (uint8)(i + 1);
        g_QspiCpu.qspiBuffer.spi0RxBuffer[i] = (uint8)(0);
    }
}

void Driver_Spi_Init(void)
{
    /* disable interrupts */
    boolean interruptState = IfxCpu_disableInterrupts();

    Driver_Spi0_Init();

    /* enable interrupts again */
    IfxCpu_restoreInterrupts(interruptState);
}

void Driver_Spi_TxTest1(void)
{
    uint32 size = SPI_BUFFER_SIZE;

    /* Master transfer */
    while (IfxQspi_SpiMaster_getStatus(&g_QspiCpu.drivers.spiMasterChannel)== SpiIf_Status_busy){}

    IfxQspi_SpiMaster_exchange(&g_QspiCpu.drivers.spiMasterChannel, &g_QspiCpu.qspiBuffer.spi0TxBuffer[0],
        &g_QspiCpu.qspiBuffer.spi0RxBuffer[0], SPI_BUFFER_SIZE);
}

 

그럼 동작을 한번 시켜 보도록 하겠습니다.

아래 TC275 MCU레퍼런스 메뉴얼 파일을 참고해 주세요.

Infineon-AURIX_TC275_Lite_Kit-UserManual-v01_00-EN.pdf
2.89MB

SCLK : p20.11

MTSR (MOSI) : p20.14

MRST (MISO) : p20.12

CS : p33.5

MOSI (Master Out Slave Input) : Master에서 IC 쪽으로 전달하는 라인

MISO (Master Input Slave Out) : IC에서 Master쪽으로 정보를 전달하는 라인

그런데 핀이 아래가 더 잘 배치가 된것 같아서

변경하도록 하겠습니다.

핀을 바꾸게 되면

SPI 모듈이

QSPI0 -> QSPI1로 변경이 되어서

코드에서 변경을 하였습니다.

 

그리고 아래 API는 이제 10ms 마다 Call(불리게)될것입니다.

void Driver_Spi_TxTest1(void)
{
    uint32 size = SPI_BUFFER_SIZE;

    /* Master transfer */
    while (IfxQspi_SpiMaster_getStatus(&g_QspiCpu.drivers.spiMasterChannel)== SpiIf_Status_busy){}

    IfxQspi_SpiMaster_exchange(&g_QspiCpu.drivers.spiMasterChannel, &g_QspiCpu.qspiBuffer.spi1TxBuffer[0],
        &g_QspiCpu.qspiBuffer.spi1RxBuffer[0], SPI_BUFFER_SIZE);
}

 

그 전에 SPI에서 가장 중요한 설정을 한번 살펴 볼게요.

그전에 SPI에서는 CPOL, CPHA의 설정인데요.

한번 어떻게 설정되어 있을까요?

Clock polarity(CPOL)와 Clock phase(CPHA)는 아래와 같이 설정하네요.

config->mode.clockPolarity = SpiIf_ClockPolarity_idleLow;

config->mode.shiftClock = SpiIf_ShiftClock_shiftTransmitDataOnLeadingEdge;

이것을 코드를 따라가 보면

CPOL = 0;

econ.B.CPH = (chConfig->mode.shiftClock == SpiIf_ShiftClock_shiftTransmitDataOnLeadingEdge) ? 1 : 0;

CPHA = 1

이렇게 설정이 되어 있는 것을 확인 하실 수 있습니다.

그러면 데이터 취득은 아래와 같이 될것입니다.

그러면 이제 Logi Aalzer를 이용해서 한번

동작성을 확인해 보도록 하겠습니다.

Logic Analyzer를 켜시고,

1) -> 2) -> 3) 번을 눌러주세요.

SPI는 아래와 같이 설정합니다.

CPOL과 CPHA가 0과 1로 설정하는 것을 볼 수 있습니다.

계측기도 동일하게 설정해 주어야 합니다.

이제 한번 동자시켜보겠습니다.

그런데 계측기에서 데이터를 해석하지 못하고 있습니다.

이유는 SCK이 CS보다 빨리 동작을 하고 있기 때문입니다.

따라서 아래와 같이 SPI Clock부분이 CS(Enable) 보다 늦게 시작되도록

Delay가 되도록 설정을 해보도록 하겠습니다.

MCU 레퍼런스 메뉴얼을 살펴보니

Leading Delay라고 부르는 부분이 있습니다.

이 부분들을 다음 장에서 정확하게 Delay 설정을 하는 방법을 정리할게요.

우선 이번에는 대략적으로 늦추어 보도록 하겠습니다.

아래와 같이 코드를 변경하고

config->mode.csLeadDelay = SpiIf_SlsoTiming_4;

config->mode.csTrailDelay = SpiIf_SlsoTiming_4;

 

동작시켜보면 아래 동그라미 부분과 같이 Delay가 되어 들어간 것을 확인하실 수 있습니다.

그리고 Master MCU에서 1~10까지의 데이터가 전달되고 있는 것을 확인 하실수 있습니다.

지금 까지 작업한 코드는 아래 카페에서 다운로드 가능하니 참고해 주세요.

https://cafe.naver.com/binaryembedded/140

이제 다음글에서 Slave Project를 만들고

예제 코드를 포팅해서 Master MCU와 Slave MCU끼리 한번 통신을 시켜 보도록 하겠습니다. 

반응형

댓글