本项目基于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。
特性:
👉 更多介绍:Funpack第二季第二期:KIT_AURIX_TC275_LITE
该章节主要是参考芯片手册7.3.2节。
电源管理方案允许激活电源关闭模式,从而使系统以对应应用状态所需的最小电源运行。通过分别调用Idle、Sleep或Standby模式,可以逐步降低功耗。Idle模式是特定于每个CPU的,而Sleep和Standby模式会影响整个系统。
对于单个CPU,有两种工作状态:Run/Idle。运行状态下,CPU时钟启动且代码会不断运行,而idle下CPU时钟失能且代码不继续运行,外设时钟保持开启。
对于整个系统,有三种电源模式: Run/Sleep/Standby:
本工程比较关心的是系统如何进入系统休眠(system sleep)及如何从休眠中唤醒CPU。
从system run进入system sleep有两种方式:
从system sleep唤醒的方法在手册中写的很全面,但是本工程主要利用定时器(STM)中断唤醒不同的CPU。当然,手册里所说的基本都需要操作CPU的寄存器,看也看不懂,但可以参考英飞凌提供的示例SCU_Power_Down_Sleep。
CPU0负责GPIO初始化,包括板上两个LED与按键,并置LED初态为熄灭、初始化并启动STM0(后续会讲到),最后Cpu0_req_sleep()
,以CPU0请求系统进入系统休眠状态。当其进入工作模式后,判断_flag2
标志位,请求系统休眠。Cpu0_Main.c
代码如下:
#include
#include
#include
#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
代码如下:
#include "Ifx_Types.h"
#include "IfxCpu.h"
#include "IfxScuWdt.h"
#include
#include
#include
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
代码如下:
#include "Ifx_Types.h"
#include "IfxCpu.h"
#include "IfxScuWdt.h"
#include
#include
#include
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仅两个LED与按键BUTTON,较为简单:
#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
#include "Bsp.h"
#include
/* 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);
}
该部分主要参考英飞凌提供的示例。
#include
#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继续运行其主程序中的代码。
#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
#include
#include "IfxStm_Timer.h"
#include
#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线程会被挂起,导致调试挂起)。