본문 바로가기
Embedded SW/[Infineon] TC275 English Version

08. Let's design a timer using Infineon TC275 MCU

by 방구석 임베디드 2023. 8. 27.
반응형

Hello.
Today, we will take the time to understand the interrupt of Infineon MCU.

 

This article is one of the articles that serialize the overall contents of development.

So, if you look at the previous article, I think it will be more helpful.

I will link the article below. Read it if you need to.

(Of course, there is no big problem if you just read this article.)

 

00. Embedded SW development using TC275 (Prologue)
01. What is embedded SW and what is MCU (Micro Control Unit)?
02. How to purchase an embedded development board (TC275, Infineon)
03. How to set up an embedded SW development environment (what is a source code editor, compiler, and debugger environment)
04. How to create an MCU TC275 project (Infineon)

05. Digital output settings and LED blinking using TC275 (push-pull, open drain)

06. Understanding MCU Clock, Oscillator (Infineon TC275)

07. What is PLL in MCU? PLL and Clock settings on Infineon TC275

 

While studying Embedded SW, the concepts you must know are Polling and Interrupt.
First, let's look at polling and interrupts.

 

1. What do polling and interrupts mean?

Polling and interrupts are communication methods between CPU and I/O devices.

 1) Interrupt: When the CPU is executing a program, it is a communication method that informs the CPU with an electrical signal when processing is required in the I/O hardware device so that it can process it.

 2) Polling: This is a communication method in which the CPU periodically monitors the hardware device and processes it when processing is required in the I/O hardware device.

 In other words, while polling is a concept in which the CPU periodically monitors and processes a task when a situation occurs, interrupt is a request from the other side to the CPU to process a task.

 Let's take a look at an example.

 

Let's assume that the CPU needs to keep checking the voltage connected to that leg.

  When 5V is input to the red-colored part of the bridge, the GPIO module connected to that bridge recognizes 5V as 1 and stores the information of 1 in a specific register (memory).

The CPU must check the information in the port register to detonate the airbag if it is 1 and to prevent the airbag from exploding if it is 0.

If the airbag is supposed to explode, the CPU has to constantly check the register of the corresponding port on that leg.

Then the code would be implemented as below.

The CPU has to keep paying attention and checking that state.

And when the 5V voltage comes in and pin_state becomes 1,

The airbag should work.

 

Since the airbag has to explode when the condition is met, the CPU has no choice but to continuously and repeatedly execute that code.
Come to think of it, useless CPU operation continues.

Then, how about working with a system like the one below?

The CPU doesn't keep checking and does other things.

By the way, 5V voltage is coming to that pin.

The voltage is supplied to a module called Interrupt Router.

Oh, what a big deal! The iterrupt router informs the CPU of this situation by sending an electrical signal.


Then, the CPU stops the operation in the main function and operates the promised ISR_Temp function as shown below.

In ISR_Temp, the airbag explodes right away.

And after finishing it, it returns to the main function and continues to operate.

This is the interrupt method.

 The CPU doesn't have to keep auditing that port to pop the airbag.

If you have to keep an eye on it, and there are multiple signals as important as airbags,

The CPU has to do a lot of work just to watch.

 Therefore, if you use interrupts, you can reduce useless CPU operations and perform input signal processing immediately.

 Then, from now on, let's design the interrupt of Infineon MCU directly.

 

2. Interrupt design using Infineon System Timer Module

Interrupt informs the processing request to the internal I/O device as an electrical signal to the CPU.

In addition, peripheral devices in the MCU can request processing to the CPU through the interrupt router.

 In this example, we will use the STM (System Timer Module) in the MCU to generate an interrupt every 1 ms.

 STM is a peripheral device included in the MCU.

The 20MHz oscillator clock we mentioned earlier is made into a 100MHz Spb Clock, which is transmitted to the STM.

STM is a module that continuously counts the clock.

  That is, 100 000 000 times per second.

If it is configured to generate an interrupt every 1000 times, the STM will notify the interrupt router every 1 ms, and the interrupt router will give an electrical signal to the CPU.

 

The meaning of the electric signal is "Stop what you are doing for a while and run the System Timer Module ISR (Interrupt Service Routine)!"

The ISR function was created as follows.

void ISR_Timer(void)
{
	static unsigned long Cnt = 0u;
	Cnt ++;   
}

Eventually, the above Main infinite loop function stops, the above ISR function is executed, and it returns to the Main function again.

At this time, before jumping to the ISR function, the current register information and addresses to return to are saved in the Stack.

So far, we have talked about the overall interrupt flow.

Then, in the next article, we will now use the System Timer Module to design and verify that ISR occurs every 1 ms.

Then, from now on, we will use the System Timer Module to periodically generate an interrupt every 1 ms.

Infineon provides Demo codes as shown below.

Engineers can save time by making the most of the Demo Code provided by the company.

​Then, let's open this example code.

Here's the StmDemo.
Now, let's use this to operate the timer once.

int core0_main(void)
{
    /*
     * !!WATCHDOG0 AND SAFETY WATCHDOG ARE DISABLED HERE!!
     * Enable the watchdog in the demo if it is required and also service the watchdog periodically
     * */
    IfxScuWdt_disableCpuWatchdog(IfxScuWdt_getCpuWatchdogPassword());
    IfxScuWdt_disableSafetyWatchdog(IfxScuWdt_getSafetyWatchdogPassword());

    /* Initialise the application state */
    g_AppCpu0.info.pllFreq = IfxScuCcu_getPllFrequency();
    g_AppCpu0.info.cpuFreq = IfxScuCcu_getCpuFrequency(IfxCpu_getCoreIndex());
    g_AppCpu0.info.sysFreq = IfxScuCcu_getSpbFrequency();
    g_AppCpu0.info.stmFreq = IfxStm_getFrequency(&MODULE_STM0);

    /* Enable the global interrupts of this CPU */
    IfxCpu_enableInterrupts();

    /* Demo init */
    IfxStmDemo_init();

    /* background endless loop */
    while (TRUE)
    {
        IfxStmDemo_run();

        REGRESSION_RUN_STOP_PASS;
    }

    return 0;
}

This is example code.

First, we see a function called IfxStmDemo_init().

This seems to be the function that initializes the System Timer Module.

And within the while statement, the IfxStmDemo_run() function works.

​So let's take a look at these two functions.

​First, let's take a look at the IfxStmDemo_init function.

void IfxStmDemo_init(void)
{
    printf("IfxStmDemo_init() called\n");

    /* disable interrupts */
    boolean interruptState = IfxCpu_disableInterrupts();

    g_Stm.LedBlink = 0;
    g_Stm.counter  = 0;

    initTime();

    g_Stm.stmSfr = &MODULE_STM0;
    IfxStm_initCompareConfig(&g_Stm.stmConfig);

    g_Stm.stmConfig.triggerPriority = ISR_PRIORITY_STM_INT0;
    g_Stm.stmConfig.typeOfService   = IfxSrc_Tos_cpu0;
#ifdef SIMULATION
    g_SrcSwInt.stmConfig.ticks      = 1000;
#else
    g_Stm.stmConfig.ticks           = TimeConst_1s;
#endif
    IfxStm_initCompare(g_Stm.stmSfr, &g_Stm.stmConfig);

    IfxBlinkLed_Init();

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

The rest is the part that initializes the LED for Blinking.

So you can see that the core is the lower part.

 g_Stm.stmSfr = &MODULE_STM0;
    IfxStm_initCompareConfig(&g_Stm.stmConfig);

    g_Stm.stmConfig.triggerPriority = ISR_PRIORITY_STM_INT0;
    g_Stm.stmConfig.typeOfService   = IfxSrc_Tos_cpu0;
#ifdef SIMULATION
    g_SrcSwInt.stmConfig.ticks      = 1000;
#else
    g_Stm.stmConfig.ticks           = TimeConst_1s;
#endif
    IfxStm_initCompare(g_Stm.stmSfr, &g_Stm.stmConfig);

Functions like IfxStm_initCompare are ILLD functions.
ILLD stands for Infineon Low Level Driver and is a driver stack created by Infineon to facilitate driver initialization and API use.
Other MCUs can be directly accessed and set, but the more flexible the MCU, the more convenient it is to use these APIs.
However, as a rule, you should read the Reference Manual and clearly know how registers work.
And later you will find out that the ILLD Driver also has errors.
Engineers need to check, fix, and use it on their own.
Now, let's initialize the timer using ILLD.
For reference, ILLD is automatically created when we create a project in the IDE.
Of course, you must directly port to these ILLDs and use them by connecting them to the Makefile.

  g_Stm.stmSfr = &MODULE_STM0;
-> It means to use module 0 for STM.

    IfxStm_initCompareConfig(&g_Stm.stmConfig);
-> And this part is not a part to initialize the registers, but to insert the parameters to be initialized in the structure called g_Stm.stmConfig.
Eventually, based on this parameter, it is later put as a parameter to the ILLD Driver, so the register is initialized.

    g_Stm.stmConfig.triggerPriority = ISR_PRIORITY_STM_INT0;
-> This is the part where priority is put in to generate an interrupt.
It can be from 0 to 255, with 255 being the highest priority.

    g_Stm.stmConfig.typeOfService   = IfxSrc_Tos_cpu0;
-> This is the part that indicates which CPU is notified when an interrupt occurs.
We intend to use only Core0.
So these interrupts will inform CPU0.

#ifdef SIMULATION
    g_SrcSwInt.stmConfig.ticks      = 1000;
#else
    g_Stm.stmConfig.ticks           = TimeConst_1s;
-> The example is trying to generate an interrupt every second.
#endif
    IfxStm_initCompare(g_Stm.stmSfr, &g_Stm.stmConfig);
-> This part is important.
Now initialize the STM register by putting the initialized g_Stm.stmConfig structure as a parameter.

How much does TimeConst_1s represent?

It is 100000000u.

The reason is that the STM peripheral is operating at 100MHz.

Therefore, the count goes up 100000000 times in 1 trillion.

Therefore, when the STM module counts 100000000 times, it means that it has become 1 second.

​Now that you know the initialization code, let's take a moment to analyze the Reference Manaul.

​STM is visible.

It has a 64-bit counter.

Of these, we'll only be using 32-bit.

Then, how are the most important clocks connected?

fSPB = 100 MHz

fSTM = 100 MHz

Both of these clocks are 100MHz and you can see that they are connected to STM.

STM is a timer that operates at 100 MHz.



Now let's go figure out the Register.

boolean IfxStm_initCompare(Ifx_STM *stm, const IfxStm_CompareConfig *config)
{
    sint32        index;
    boolean       result;
    Ifx_STM_CMCON comcon = stm->CMCON;
    Ifx_STM_ICR   icr    = stm->ICR;

    if (config->comparator == 0)
    {
        comcon.B.MSIZE0  = config->compareSize;
        comcon.B.MSTART0 = config->compareOffset;
        icr.B.CMP0OS     = config->comparatorInterrupt;
        result           = TRUE;
    }
    else if (config->comparator == 1)
    {
        comcon.B.MSIZE1  = config->compareSize;
        comcon.B.MSTART1 = config->compareOffset;
        icr.B.CMP1OS     = config->comparatorInterrupt;
        result           = TRUE;
    }
    else
    {
        /*Invalid value */
        result = FALSE;
    }

    stm->ICR.U   = icr.U;
    stm->CMCON.U = comcon.U;

    /* configure interrupt */
    index = IfxStm_getIndex(stm);

    if (config->triggerPriority > 0)
    {
        volatile Ifx_SRC_SRCR *srcr;

        if (config->comparatorInterrupt == IfxStm_ComparatorInterrupt_ir0)
        {
            srcr = &(MODULE_SRC.STM.STM[index].SR0);
        }
        else
        {
            srcr = &(MODULE_SRC.STM.STM[index].SR1);
        }

        IfxSrc_init(srcr, config->typeOfService, config->triggerPriority);
        IfxSrc_enable(srcr);
    }

    /*Configure the comparator ticks to current value to avoid any wrong triggering*/
    stm->CMP[config->comparator].U = IfxStm_getOffsetTimer(stm, (uint8)config->compareOffset);

    /* clear the interrupt flag of the selected comparator before enabling the interrupt */
    /* this is to avaoid the unneccesary interrupt for the compare match of reset values of the registers */
    IfxStm_clearCompareFlag(stm, config->comparator);
    /* enable the interrupt for the selected comparator */
    IfxStm_enableComparatorInterrupt(stm, config->comparator);

    /*Configure the comparator ticks */
    stm->CMP[config->comparator].U = IfxStm_getOffsetTimer(stm, (uint8)config->compareOffset) + config->ticks;

    return result;
}

Perhaps the last part is the most important part.

stm->CMP[config->comparator].U = IfxStm_getOffsetTimer(stm, (uint8)config->compareOffset) + config->ticks;

In other words, the 1 second Counter set earlier is being added.

​In other words, it is updating the CMP register.

If that red color is the counter corresponding to 1 second, CMP has the value of the current STM Counter plus the number of counters corresponding to 1 second.

​Eventually it will be like this.

The picture is a bit messy, but when the STM Coutner matches the counter written in the CMP, an interrupt will occur.

Then, let's find out what role the CMP register plays.

It is made up of 32 bits.

Then, I will port this example code once and send it to you.

/**********************************************************************************************************************
 * \file Cpu0_Main.c
 * \copyright Copyright (C) Infineon Technologies AG 2019
 * 
 * Use of this file is subject to the terms of use agreed between (i) you or the company in which ordinary course of 
 * business you are acting and (ii) Infineon Technologies AG or its licensees. If and as long as no such terms of use
 * are agreed, use of this file is subject to following:
 * 
 * Boost Software License - Version 1.0 - August 17th, 2003
 * 
 * Permission is hereby granted, free of charge, to any person or organization obtaining a copy of the software and 
 * accompanying documentation covered by this license (the "Software") to use, reproduce, display, distribute, execute,
 * and transmit the Software, and to prepare derivative works of the Software, and to permit third-parties to whom the
 * Software is furnished to do so, all subject to the following:
 * 
 * The copyright notices in the Software and this entire statement, including the above license grant, this restriction
 * and the following disclaimer, must be included in all copies of the Software, in whole or in part, and all 
 * derivative works of the Software, unless such copies or derivative works are solely in the form of 
 * machine-executable object code generated by a source language processor.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE 
 * COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN 
 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 
 * IN THE SOFTWARE.
 *********************************************************************************************************************/
#include "Ifx_Types.h"
#include "IfxCpu.h"
#include "IfxScuWdt.h"

#include "IfxPort.h"
#include "IfxPort_PinMap.h"

#include "IfxStm.h"
#include "IfxCpu_Irq.h"

typedef struct
{
    Ifx_STM             *stmSfr;            /**< \brief Pointer to Stm register base */
    IfxStm_CompareConfig stmConfig;         /**< \brief Stm Configuration structure */
    volatile uint8       LedBlink;          /**< \brief LED state variable */
    volatile uint32      counter;           /**< \brief interrupt counter */
} App_Stm;

App_Stm g_Stm; /**< \brief Stm global data */

IfxCpu_syncEvent g_cpuSyncEvent = 0;

void IfxStmDemo_init(void);

int core0_main(void)
{
    IfxCpu_enableInterrupts();
    
    /* !!WATCHDOG0 AND SAFETY WATCHDOG ARE DISABLED HERE!!
     * Enable the watchdogs and service them periodically if it is required
     */
    IfxScuWdt_disableCpuWatchdog(IfxScuWdt_getCpuWatchdogPassword());
    IfxScuWdt_disableSafetyWatchdog(IfxScuWdt_getSafetyWatchdogPassword());
    
    /* Wait for CPU sync event */
    IfxCpu_emitEvent(&g_cpuSyncEvent);
    IfxCpu_waitEvent(&g_cpuSyncEvent, 1);

    IfxStmDemo_init();

    /*P00_5    Digital Output*/
    IfxPort_setPinModeOutput(IfxPort_P00_5.port, IfxPort_P00_5.pinIndex, IfxPort_OutputMode_openDrain, IfxPort_OutputIdx_general);
    IfxPort_setPinLow(IfxPort_P00_5.port, IfxPort_P00_5.pinIndex);

    while(1)
    {

    }
    return (1);
}

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

    g_Stm.stmSfr = &MODULE_STM0;
    IfxStm_initCompareConfig(&g_Stm.stmConfig);

    g_Stm.stmConfig.triggerPriority = 100u;
    g_Stm.stmConfig.typeOfService   = IfxSrc_Tos_cpu0;
    g_Stm.stmConfig.ticks           = 100000000;

    IfxStm_initCompare(g_Stm.stmSfr, &g_Stm.stmConfig);

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

And after compiling, I downloaded and executed it, and I can see that it fell into the trap as shown below.

Falling into a trap means that an exception occurred.

​The reason was that I did not register the ISR function.

An interrupt occurred, but the function was not defined, so I accessed it with the NULL function.

​thus,

IFX_INTERRUPT(STM_Int0Handler, 0, 100);

void STM_Int0Handler(void)
{
    IfxStm_clearCompareFlag(g_Stm.stmSfr, g_Stm.stmConfig.comparator);
    IfxStm_increaseCompare(g_Stm.stmSfr, g_Stm.stmConfig.comparator, 100000000u);
    IfxCpu_enableInterrupts();
}

I added the above code.

IFX_INTERRUPT is a function that registers the ISR function.

And the STM_Init0Handler function is defined as above.



Then, let's compile again.

Now I don't fall into the exception!! lol



By the way, it would be nice to add this function in STM.

It means that when we stop with the debugger, we want the Timer to stop too.

IfxStm_enableOcdsSuspend(&MODULE_STM0);



Now, let's try Blinking the LED in that function.

Let's add a function like this:

The final code is below.

/**********************************************************************************************************************
 * \file Cpu0_Main.c
 * \copyright Copyright (C) Infineon Technologies AG 2019
 * 
 * Use of this file is subject to the terms of use agreed between (i) you or the company in which ordinary course of 
 * business you are acting and (ii) Infineon Technologies AG or its licensees. If and as long as no such terms of use
 * are agreed, use of this file is subject to following:
 * 
 * Boost Software License - Version 1.0 - August 17th, 2003
 * 
 * Permission is hereby granted, free of charge, to any person or organization obtaining a copy of the software and 
 * accompanying documentation covered by this license (the "Software") to use, reproduce, display, distribute, execute,
 * and transmit the Software, and to prepare derivative works of the Software, and to permit third-parties to whom the
 * Software is furnished to do so, all subject to the following:
 * 
 * The copyright notices in the Software and this entire statement, including the above license grant, this restriction
 * and the following disclaimer, must be included in all copies of the Software, in whole or in part, and all 
 * derivative works of the Software, unless such copies or derivative works are solely in the form of 
 * machine-executable object code generated by a source language processor.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE 
 * COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN 
 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 
 * IN THE SOFTWARE.
 *********************************************************************************************************************/
#include "Ifx_Types.h"
#include "IfxCpu.h"
#include "IfxScuWdt.h"

#include "IfxPort.h"
#include "IfxPort_PinMap.h"

#include "IfxStm.h"
#include "IfxCpu_Irq.h"

typedef struct
{
    Ifx_STM             *stmSfr;            /**< \brief Pointer to Stm register base */
    IfxStm_CompareConfig stmConfig;         /**< \brief Stm Configuration structure */
    volatile uint8       LedBlink;          /**< \brief LED state variable */
    volatile uint32      counter;           /**< \brief interrupt counter */
} App_Stm;

App_Stm g_Stm; /**< \brief Stm global data */

IfxCpu_syncEvent g_cpuSyncEvent = 0;

void IfxStmDemo_init(void);

int core0_main(void)
{
    IfxCpu_enableInterrupts();
    
    /* !!WATCHDOG0 AND SAFETY WATCHDOG ARE DISABLED HERE!!
     * Enable the watchdogs and service them periodically if it is required
     */
    IfxScuWdt_disableCpuWatchdog(IfxScuWdt_getCpuWatchdogPassword());
    IfxScuWdt_disableSafetyWatchdog(IfxScuWdt_getSafetyWatchdogPassword());
    
    /* Wait for CPU sync event */
    IfxCpu_emitEvent(&g_cpuSyncEvent);
    IfxCpu_waitEvent(&g_cpuSyncEvent, 1);

    IfxStmDemo_init();

    /*P00_5    Digital Output*/
    IfxPort_setPinModeOutput(IfxPort_P00_5.port, IfxPort_P00_5.pinIndex, IfxPort_OutputMode_pushPull, IfxPort_OutputIdx_general);
    IfxPort_setPinLow(IfxPort_P00_5.port, IfxPort_P00_5.pinIndex);

    while(1)
    {

    }
    return (1);
}

IFX_INTERRUPT(STM_Int0Handler, 0, 100);

void STM_Int0Handler(void)
{
    static int flag = 0;
    static int cnt = 0;
    
    IfxStm_clearCompareFlag(g_Stm.stmSfr, g_Stm.stmConfig.comparator);
    IfxStm_increaseCompare(g_Stm.stmSfr, g_Stm.stmConfig.comparator, 100000000u);

    cnt++;

    if(flag == 0)
    {
        IfxPort_setPinLow(IfxPort_P00_5.port, IfxPort_P00_5.pinIndex);
        flag = 1;
    }
    else
    {
        IfxPort_setPinHigh(IfxPort_P00_5.port, IfxPort_P00_5.pinIndex);
        flag = 0;
    }
    
    IfxCpu_enableInterrupts();
}

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

    IfxStm_enableOcdsSuspend(&MODULE_STM0);

    g_Stm.stmSfr = &MODULE_STM0;
    IfxStm_initCompareConfig(&g_Stm.stmConfig);

    g_Stm.stmConfig.triggerPriority = 100u;
    g_Stm.stmConfig.typeOfService   = IfxSrc_Tos_cpu0;
    g_Stm.stmConfig.ticks           = 100000000;

    IfxStm_initCompare(g_Stm.stmSfr, &g_Stm.stmConfig);

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

If you operate like this, the operation of turning on after 1 second and turning off after 1 second is repeated.

Please note that the code b

elow has been added.

TC275_Project1_STM.zip
4.06MB

반응형

댓글