【Funpack2-2 】基于英飞凌AURIX TC275 Lite的三核轮休工程
本项目基于AURIX TC275 Lite开发板套件,使用AURIX Development Studio开发,实现了简单的三核轮休:CPU0检测按键按下,之后唤醒CPU1并翻转LED1,1秒后唤醒CPU2并翻转LED2,之后进入系统休眠
标签
嵌入式系统
C
TC275
AURIX
infineon
PMU
葉SiR
更新2022-10-09
北京大学
573

项目介绍

本项目基于AURIX TC275 Lite开发板套件,使用AURIX Development Studio开发,实现了简单的三核轮休:CPU0检测按键按下,之后唤醒CPU1并翻转LED1,1秒后唤醒CPU2并翻转LED2,之后进入系统休眠状态。

硬件介绍

AURIX TM TC275 lite套件配备了基于32位单芯片AURIX TM TriCore的微控制器Aurix TM TC275。它可以与一系列开发工具一起使用,包括AURIX Development Studio、英飞凌免费的基于Eclipse的IDE,或来自Hightec/PLS/Infineon的基于Eclipse的FreeEntryToolchain。

特性:

 - Arduino 连接器
 - Arduino ICSP 连接器
 - 稳压器 5V 至 3.3V
 - 可选 0 欧姆电阻器(1210英制中的R39_opt/R40_opt)
 - Arduino 连接器(数字)
 - 用于 AURIX™的20 MHz晶振和用于OCDS的12MHz晶振
 - 用于 WIFI/BLE 的 Mikrobus 连接器
 - 英飞凌 CAN 收发器 TLE9251VSJ 和 CAN 连接器
 - 针连接器 X2
 - 电源指示灯 (D5)
 - LED D1/D2用于ADBUS7/4和LED3用于ESR0信号(低电平有效)
 - Arduino 针连接器(电源和模拟输入)
 - 电位器 (10 kOhm) 和可焊接的0Ohm电阻器(0805 英制中的R33)
 - 微型 USB(推荐USB3.0)
 - 10 针 DAP 连接器
 - 复位按钮
 - 2 x Shield2GO 连接器,用于Infineon Maker Shields
 - EEPROM 1Kbit

开发板电路结构框图

👉 更多介绍:Funpack第二季第二期:KIT_AURIX_TC275_LITE

电源管理

该章节主要是参考芯片手册7.3.2节。

电源管理方案允许激活电源关闭模式,从而使系统以对应应用状态所需的最小电源运行。通过分别调用Idle、Sleep或Standby模式,可以逐步降低功耗。Idle模式是特定于每个CPU的,而Sleep和Standby模式会影响整个系统。

对于单个CPU,有两种工作状态:Run/Idle。运行状态下,CPU时钟启动且代码会不断运行,而idle下CPU时钟失能且代码不继续运行,外设时钟保持开启。

对于整个系统,有三种电源模式: Run/Sleep/Standby:

  • Run:至少有一个主CPU没有请求休眠模式或备用模式,并且处于运行模式。所有外设都已激活。
  • Sleep:CPU代码暂停执行,进入Idle状态。如果外设置位各自的CLCx.EDIS位,则进入休眠状态。端口保留其早期的程序状态。
  • Standby:构成备用RAM和唤醒单元的备用域仍然供应。芯片其他部分的电源完全关闭

本工程比较关心的是系统如何进入系统休眠(system sleep)及如何从休眠中唤醒CPU。

系统不同工作模式的切换

从system run进入system sleep有两种方式:

  1. CPUSEL = 111b且PMCSRy.REQSLP = 10b (y = all CPUs);
  2. 通过CPUSEL,设置CPUy为主CPU,且其PMCSRy.REQSLPb = 10b;

从system sleep唤醒的方法在手册中写的很全面,但是本工程主要利用定时器(STM)中断唤醒不同的CPU。当然,手册里所说的基本都需要操作CPU的寄存器,看也看不懂,但可以参考英飞凌提供的示例SCU_Power_Down_Sleep。

代码结构及说明

三核主程序

CPU0负责GPIO初始化,包括板上两个LED与按键,并置LED初态为熄灭、初始化并启动STM0(后续会讲到),最后Cpu0_req_sleep(),以CPU0请求系统进入系统休眠状态。当其进入工作模式后,判断_flag2标志位,请求系统休眠。Cpu0_Main.c代码如下:

```c
#include <bsp_led_button.h>
#include <bsp_stm.h>
#include <scu_sleep.h>
#include "Ifx_Types.h"
#include "IfxCpu.h"
#include "IfxScuWdt.h"

IfxCpu_syncEvent g_cpuSyncEvent = 0;

int core0_main(void)
{
    IfxCpu_enableInterrupts();
    IfxScuWdt_disableCpuWatchdog(IfxScuWdt_getCpuWatchdogPassword());
    IfxScuWdt_disableSafetyWatchdog(IfxScuWdt_getSafetyWatchdogPassword());
    
    /* Wait for CPU sync event */
    IfxCpu_emitEvent(&g_cpuSyncEvent);
    IfxCpu_waitEvent(&g_cpuSyncEvent, 1);
    
    init_GPIOs();
    initStm0();
    Cpu0_req_sleep();

    while(1)
    {
        if (_flag2 == TRUE) {
            _flag2 = FALSE;
            Cpu0_req_sleep();
        }
    }
    return (1);
}
```

CPU1先直接进入idle状态,并等待唤醒后判断标志位_flag1,负责翻转LED1并打开定时器STM1,最后再进入idle。Cpu1_Main.c代码如下:

```c
#include "Ifx_Types.h"
#include "IfxCpu.h"
#include "IfxScuWdt.h"
#include <scu_sleep.h>
#include <bsp_stm.h>
#include <bsp_led_button.h>

extern IfxCpu_syncEvent g_cpuSyncEvent;

int core1_main(void)
{
    IfxCpu_enableInterrupts();
    IfxScuWdt_disableCpuWatchdog(IfxScuWdt_getCpuWatchdogPassword());
    
    /* Wait for CPU sync event */
    IfxCpu_emitEvent(&g_cpuSyncEvent);
    IfxCpu_waitEvent(&g_cpuSyncEvent, 1);

    IfxCpu_setCoreMode(&MODULE_CPU1, IfxCpu_CoreMode_idle);

    while(1)
    {
        if (_flag1 == TRUE)
        {
            _flag1 = FALSE;
            led_toggle(LED1);
            runStm1();
            IfxCpu_setCoreMode(&MODULE_CPU1, IfxCpu_CoreMode_idle);
        }
    }
    return (1);
}
```

CPU2首先初始化但不启动STM1(后续会讲到),而后进入idle等待唤醒。Cpu2_Main.c代码如下:

```c
#include "Ifx_Types.h"
#include "IfxCpu.h"
#include "IfxScuWdt.h"
#include <scu_sleep.h>
#include <bsp_led_button.h>
#include <bsp_stm.h>

extern IfxCpu_syncEvent g_cpuSyncEvent;

int core2_main(void)
{
    IfxCpu_enableInterrupts();
    IfxScuWdt_disableCpuWatchdog(IfxScuWdt_getCpuWatchdogPassword());
    
    /* Wait for CPU sync event */
    IfxCpu_emitEvent(&g_cpuSyncEvent);
    IfxCpu_waitEvent(&g_cpuSyncEvent, 1);

    initStm1();
    IfxCpu_setCoreMode(&MODULE_CPU2, IfxCpu_CoreMode_idle);

    while(1)
    {
    }
    return (1);
}
```

GPIO

所用到的GPIO仅两个LED与按键BUTTON,较为简单:

```c
#ifndef BSP_LED_BUTTON_H_
#define BSP_LED_BUTTON_H_

#include "IfxPort.h"
#include "Ifx_Types.h"

#define LED1         &MODULE_P00,5
#define LED2         &MODULE_P00,6
#define BUTTON       &MODULE_P00,7

void init_GPIOs(void);
void led_toggle(Ifx_P *port, uint8 pinIndex);

#endif /* BSP_LED_BUTTON_H_ */
//--------------------
#include <bsp_led_button.h>
#include "Bsp.h"
#include <scu_sleep.h>

/* This function initializes the port pin which drives the LED */
void init_GPIOs(void)
{
    IfxPort_setPinMode(LED1, IfxPort_Mode_outputPushPullGeneral);
    IfxPort_setPinMode(LED2, IfxPort_Mode_outputPushPullGeneral);
    IfxPort_setPinMode(BUTTON, IfxPort_Mode_inputPullUp);

    /* Switch OFF the LED (low-level active) */
    IfxPort_setPinState(LED1, IfxPort_State_high);
    IfxPort_setPinState(LED2, IfxPort_State_high);
}

/* This function toggles the port pin and wait 1000 milliseconds */
void led_toggle(Ifx_P *port, uint8 pinIndex)
{
    IfxPort_togglePin(port, pinIndex);
}
```

CPU0请求系统休眠

该部分主要参考英飞凌提供的示例。

```c
#include <scu_sleep.h>
#include "IfxCpu.h"
#include "IfxScuWdt.h"

#define BLOCK_SLEEP_MODE    0x1                 /* Block sleep mode for STM                                         */
#define PMSWCR1_CPUSEL      0x1                 /* Set the CPU0 as CPU master                                       */
#define PMSWCR2_CPUSEL      0x2                 /* Set the CPU1 as CPU master                                       */
#define PMSWCR3_CPUSEL      0x4                 /* Set the CPU2 as CPU master                                       */
#define PMCSR0_REQSLP       0x2                 /* Request sleep mode                                               */

void Cpu0_req_sleep(void)
{
    /* Clear safety EndInit protection */
    IfxScuWdt_clearSafetyEndinitInline(IfxScuWdt_getSafetyWatchdogPasswordInline());
    /* Clear EndInit protection */
    IfxScuWdt_clearCpuEndinit(IfxScuWdt_getCpuWatchdogPassword());

    STM0_CLC.B.EDIS = BLOCK_SLEEP_MODE;

    SCU_PMSWCR1.B.CPUSEL = PMSWCR1_CPUSEL;      /* Set the CPU0 as CPU master to trigger a power down mode      */
    SCU_PMCSR0.B.REQSLP = PMCSR0_REQSLP;        /* Request System Sleep Mode CPU0                               */

    /* Set safety EndInit protection */
    IfxScuWdt_setSafetyEndinitInline(IfxScuWdt_getSafetyWatchdogPasswordInline());
    /* Set EndInit protection */
    IfxScuWdt_setCpuEndinit(IfxScuWdt_getCpuWatchdogPassword());
}
```

两个系统定时器中断

参考系统定时器(GTM)相关示例,可以写出定时器中断极其中断服务函数。

STM0中断由CPU0申请,使用MODULE_STM0,频率20Hz,在其中断服务函数stmIsr0中,对按键进行扫描(即扫描按键频率为20Hz),判断其是否按下,若按下,则停止本计时器(为一个自动重载定时器),并唤醒CPU1,置标志位_flag1,从而使得CPU1继续运行其主程序中的代码。

```c
#ifndef BSP_STM_H_
#define BSP_STM_H_

void initStm0(void);
void initStm1(void);
void runStm0(void);
void runStm1(void);

extern uint16 _flag0;
extern uint16 _flag1;
extern uint16 _flag2;

#endif /* BSP_STM_H_ */
//--------------------
#include <bsp_led_button.h>
#include <bsp_stm.h>
#include "IfxStm_Timer.h"
#include <scu_sleep.h>

#define ISR_PRIORITY_STM    20                  /* STM Interrupt priority for interrupt ISR                         */

uint16 _flag0 = FALSE;
uint16 _flag1 = FALSE;
uint16 _flag2 = FALSE;

IfxStm_Timer g_stmTimer0;
IfxStm_Timer g_stmTimer1;
IFX_INTERRUPT(stmIsr0, 0, ISR_PRIORITY_STM);
IFX_INTERRUPT(stmIsr1, 2, ISR_PRIORITY_STM);

void initStm0(void)
{
    IfxStm_Timer_Config timerConfig;                                /* Timer configuration structure                */

    IfxStm_Timer_initConfig(&timerConfig, &MODULE_STM0);            /* Initialize it with default values            */

    timerConfig.base.frequency = 20;                                 /* Interrupt rate every STM_PERIOD seconds      */
    timerConfig.base.isrPriority = ISR_PRIORITY_STM;                /* Interrupt priority                           */
    timerConfig.base.isrProvider = IfxSrc_Tos_cpu0;                 /* CPU0 to trigger the interrupt                */
    timerConfig.comparator = IfxStm_Comparator_0;                   /* Comparator 0 register is used                */

    IfxStm_Timer_init(&g_stmTimer0, &timerConfig);                   /* Use timerConfig to initialize the STM        */
    IfxStm_Timer_run(&g_stmTimer0);
}

void initStm1(void)
{
    IfxStm_Timer_Config timerConfig;                                /* Timer configuration structure                */

    IfxStm_Timer_initConfig(&timerConfig, &MODULE_STM1);            /* Initialize it with default values            */

    timerConfig.base.frequency = 1;                                 /* Interrupt rate every STM_PERIOD seconds      */
    timerConfig.base.isrPriority = ISR_PRIORITY_STM;                /* Interrupt priority                           */
    timerConfig.base.isrProvider = IfxSrc_Tos_cpu2;                 /* CPU2 to trigger the interrupt                */
    timerConfig.comparator = IfxStm_Comparator_0;                   /* Comparator 0 register is used                */

    IfxStm_Timer_init(&g_stmTimer1, &timerConfig);                   /* Use timerConfig to initialize the STM        */
}

void runStm0(void)
{
    IfxStm_Timer_run(&g_stmTimer0);                                  /* Run the STM and set the compare Value        */
}

void runStm1(void)
{
    IfxStm_Timer_run(&g_stmTimer1);                                  /* Run the STM and set the compare Value        */
}

void stmIsr0(void)
{
    /* Enabling interrupts as ISR disables it */
    IfxCpu_enableInterrupts();

    /* Clear the timer event */
    IfxStm_Timer_acknowledgeTimerIrq(&g_stmTimer0);

    if (IfxPort_getPinState(BUTTON) == 0)
    {
        IfxStm_Timer_stop(&g_stmTimer0);
        IfxCpu_setCoreMode(&MODULE_CPU1, IfxCpu_CoreMode_run);
        _flag1 = TRUE;
    }
}

void stmIsr1(void)
{
    /* Enabling interrupts as ISR disables it */
    IfxCpu_enableInterrupts();

    /* Clear the timer event */
    IfxStm_Timer_acknowledgeTimerIrq(&g_stmTimer1);

    IfxStm_Timer_stop(&g_stmTimer1);

    led_toggle(LED2);
    runStm0();
    _flag2 = TRUE;
    IfxCpu_setCoreMode(&MODULE_CPU2, IfxCpu_CoreMode_idle);
}
```

STM1的中断由CPU2请求,当CPU1被唤醒,其开启STM1运行。STM1中断频率为1Hz(定时1秒),且当中断触发时关闭定时器(单次定时器),翻转LED2,重新开启STM0(重新开启按键扫描),最后置_flag2标志位(之后CPU0请求进入系统休眠),CPU2自己进入idle。

功能展示

参见工程演示视频。

👉 B站:基于AURIX TC275 Lite的三核轮休工程

项目总结

此次工程主要参考TC275芯片手册,略微了解了一下各CPU的工作模式,以及系统的几种工作模式的切换,实现了三核分工、三核轮休。与一般的单核MCU开发板不同,TC275是三核并行,其对于单个CPU的工作状态与整体系统的工作状态之间,切换条件更为复杂。

即使笔者参考了英飞凌提供的示例程序,但它也仅是CPU0请求系统休眠的功能,对于以CPU1/CPU2作为Master CPU请求系统休眠,笔者做了尝试但不保证正确,因此该部分代码没有提供,且该工程也仅有CPU0作为Master CPU请求系统休眠。关于系统休眠与否,笔者也没有很好的调试方法(调试时,貌似休眠CPU线程会被挂起,导致调试挂起)。

团队介绍
北京大学 软微学院 电子信息专业就读
团队成员
葉SiR
二次元の开发者;👉 GitHub: https://github.com/KafCoppelia
评论
0 / 100
查看更多
目录
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2023 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号