Funpack第二季第二期KIT_AURIX_TC275_LITE呼吸灯项目
本次选择的是任务二:
设计一个呼吸灯,通过旋转板卡上的电位计,改变呼吸灯闪烁速率,同时将ADC采集的数据通过串口/CAN,发送到另一台设备上显示
一、开发版介绍
KIT_AURIX_TC275_LITE家庭微控制器产品描述AURIX™ TC275 lite 套件配备了基于 32 位单芯片 AurixTM TriCoreTM 的微控制器 AurixTM TC275
从PCB板的布局来看,板载的资源还是挺丰富的,主要包括:
- 稳压器 5V 至 3.3V
- 电源指示灯 (D5)
- LED D1/D2 和 LED3
- 电位器 (10 kOhm)
- 10 针 DAP 连接器
- 复位按钮
- EEPROM 1Kbit
下图是开发板上的器件的结构图
下图是开发板上的电源系统
板载的是型号为SK-TC275TP-64的芯片
值得一提的是这块TC275芯片中,有3个核心,为多进程任务提供了运行的基础
主要参数:
二、开发环境准备
1.AURIX™ Development Studio,用于编程调试下载
2.ComAssistant,用于串口调试
三、项目任务的代码实现
这款开发板配套的例程非常丰富,几乎全部涵盖了所有内部资源的使用,在参看了几个历程后,便完成了此次项目
首先分析一下任务的需求,(一个呼吸灯,通过旋转板卡上的电位计,改变呼吸灯闪烁速率,同时将ADC采集的数据通过串口/CAN,发送到另一台设备上显示)
经过思考,判断完成此次任务所需使用的芯片内部资源及完成的功能:需使用ADC功能获取板载电位器的电压值,需使用PWM发生器来产生驱动LED产生呼吸灯的效果,需使用UART功能将ADC采集的数据发送到电脑进行显示,需使用定时器功能来进行各项任务的运行。
经过查找资料,发现其有非常丰富的历程,每个历程都还有一份配套的文档,可以说是极大程度地降低了入门的难度。
1.ADC的采集参考ADC_Single_Channel例程
首先进行初始化配置
/* The VADC module and group are initialized */
/*初始化VADC模块和组*/
void vadcBackgroundScanInit(void)
{
/* VADC模块配置*/
/*创建VADC配置*/
IfxVadc_Adc_Config adcConfig;
/*使用默认值初始化VADC配置*/
IfxVadc_Adc_initModuleConfig(&adcConfig, &MODULE_VADC);
/*使用VADC配置初始化VADC模块*/
IfxVadc_Adc_initModule(&g_vadcBackgroundScan.vadc, &adcConfig);
/* VADC组配置*/
/*创建组配置*/
IfxVadc_Adc_GroupConfig adcGroupConfig;
/*使用默认值初始化组配置*/
IfxVadc_Adc_initGroupConfig(&adcGroupConfig, &g_vadcBackgroundScan.vadc);
/*定义将使用哪个ADC组*/
adcGroupConfig.groupId = VADC_GROUP;
adcGroupConfig.master = VADC_GROUP;
/*启用后台扫描源*/
adcGroupConfig.arbiter.requestSlotBackgroundScanEnabled = TRUE;
/*开启后台自动扫描模式*/
adcGroupConfig.backgroundScanRequest.autoBackgroundScanEnabled = TRUE;
/*开启“always”模式(无边缘检测)*/
adcGroupConfig.backgroundScanRequest.triggerConfig.gatingMode = IfxVadc_GatingMode_always;
/*使用组配置初始化组*/
IfxVadc_Adc_initGroup(&g_vadcBackgroundScan.adcGroup, &adcGroupConfig);
}
配置完成后,启动ADC功能,开始进行转换
/* The input channels to be used are setup and the VADC is set into run mode */
/*要使用的输入通道已设置,VADC设置为运行模式*/
void vadcBackgroundScanRun(void)
{
/*初始化应用句柄g_vadcBackgroundScan的通道配置,默认值*/
IfxVadc_Adc_initChannelConfig(&g_vadcBackgroundScan.adcChannelConfig, &g_vadcBackgroundScan.adcGroup);
g_vadcBackgroundScan.adcChannelConfig.channelId = (IfxVadc_ChannelId)CHANNEL_ID;
g_vadcBackgroundScan.adcChannelConfig.resultRegister = (IfxVadc_ChannelResult)CHANNEL_RESULT_REGISTER;
g_vadcBackgroundScan.adcChannelConfig.backgroundChannel = TRUE;
/*使用通道配置初始化应用程序句柄g_VadcBackgroundScan的通道*/
IfxVadc_Adc_initChannel(&g_vadcBackgroundScan.adcChannel, &g_vadcBackgroundScan.adcChannelConfig);
/*启用通道的后台扫描*/
IfxVadc_Adc_setBackgroundScan(&g_vadcBackgroundScan.vadc,
&g_vadcBackgroundScan.adcGroup,
(1 << (IfxVadc_ChannelId)CHANNEL_ID),
(1 << (IfxVadc_ChannelId)CHANNEL_ID));
/*启动后台扫描转换*/
IfxVadc_Adc_startBackgroundScan(&g_vadcBackgroundScan.vadc);
}
2.串口的使用参考UART_VCOM例程
首先进行初始化配置
void Init_Uart(void)
{
/*初始化IfxAsclin_Asc_Config实例,默认值为*/
IfxAsclin_Asc_Config ascConfig;
IfxAsclin_Asc_initModuleConfig(&ascConfig, UART_PIN_TX.module);
/*设置期望的波特率*/
ascConfig.baudrate.baudrate = SERIAL_BAUDRATE;
/*ISR优先级和中断目标*/
ascConfig.interrupt.txPriority = INTPRIO_ASCLIN0_TX;
ascConfig.interrupt.rxPriority = INTPRIO_ASCLIN0_RX;
ascConfig.interrupt.typeOfService = IfxCpu_Irq_getTos(IfxCpu_getCoreIndex());
/*fifo组态*/
ascConfig.txBuffer = &g_ascTxBuffer;
ascConfig.txBufferSize = ASC_TX_BUFFER_SIZE;
ascConfig.rxBuffer = &g_ascRxBuffer;
ascConfig.rxBufferSize = ASC_RX_BUFFER_SIZE;
/*端口引脚配置*/
const IfxAsclin_Asc_Pins pins =
{
.cts = NULL_PTR,
.ctsMode = IfxPort_InputMode_noPullDevice,
.rx = &UART_PIN_RX,
.rxMode = IfxPort_InputMode_noPullDevice,
.rts = NULL_PTR,
.rtsMode = IfxPort_OutputMode_pushPull,
.tx = &UART_PIN_TX,
.txMode = IfxPort_OutputMode_pushPull,
.pinDriver = IfxPort_PadDriver_cmosAutomotiveSpeed1
};
ascConfig.pins = &pins;
IfxAsclin_Asc_initModule(&g_asc, &ascConfig); /* Initialize module with above parameters */
}
配置完成后,编写串口中断函数,发送/接收个1个,本次只使用了发送,还未使用到接收
IFX_INTERRUPT(asclin0_Tx_ISR, 0, INTPRIO_ASCLIN0_TX); /* 添加中断服务例程Adding the Interrupt Service Routine */
void asclin0_Tx_ISR(void)
{
IfxAsclin_Asc_isrTransmit(&g_asc);
}
IFX_INTERRUPT(asclin0_Rx_ISR, 0, INTPRIO_ASCLIN0_RX); /* 添加中断服务例程Adding the Interrupt Service Routine */
void asclin0_Rx_ISR(void)
{
IfxAsclin_Asc_isrReceive(&g_asc);
}
再编写数据发送函数,根据例程,该函数传入一字符串,并获取到字符串长度,方可将其正常通过串口进行发送
void send_UART_message(char* str)
{
Ifx_SizeT str_len = (Ifx_SizeT)strlen(str);
IfxAsclin_Asc_write(&g_asc, str, &str_len, TIME_INFINITE);
}
3.PWM发生器的使用参考GTM_ATOM_PWM例程
同样,首先进行初始化设置
void Init_PWM(void)
{
IfxGtm_enable(&MODULE_GTM); /* Enable GTM */
IfxGtm_Cmu_setClkFrequency(&MODULE_GTM, IfxGtm_Cmu_Clk_0, CLK_FREQ); /* 设置CMU时钟0的频率Set the CMU clock 0 frequency */
IfxGtm_Cmu_enableClocks(&MODULE_GTM, IFXGTM_CMU_CLKEN_CLK0); /* 使能CMU时钟0Enable the CMU clock 0 */
IfxGtm_Atom_Pwm_initConfig(&g_atomConfig, &MODULE_GTM); /* 初始化默认参数Initialize default parameters */
g_atomConfig.atom = LED.atom; /* 根据LED选择ATOMSelect the ATOM depending on the LED */
g_atomConfig.atomChannel = LED.channel; /* 根据LED选择通道Select the channel depending on the LED */
g_atomConfig.period = PWM_PERIOD; /* 设置定时器期间Set timer period */
g_atomConfig.pin.outputPin = &LED; /* 设置LED为输出Set LED as output */
g_atomConfig.synchronousUpdateEnabled = TRUE; /* 启用同步更新Enable synchronous update */
IfxGtm_Atom_Pwm_init(&g_atomDriver, &g_atomConfig); /* 初始化脉宽调制Initialize the PWM */
IfxGtm_Atom_Pwm_start(&g_atomDriver, TRUE); /* 启动PWM,Start the PWM */
}
编写设置占空比(周期)的函数,传入的是高电平的持续时间,需要使用占空比0-100换算一下
void setDutyCycle(unsigned long dutyCycle)
{
g_atomConfig.dutyCycle = dutyCycle; /* Set duty cycle */
IfxGtm_Atom_Pwm_init(&g_atomDriver, &g_atomConfig); /* Re-initialize the PWM */
}
编写呼吸灯效果函数,PWM_PERIOD为满周期,当占空比为100%时,开始递减,当占空比为0%时,开始递增,每执行一次该函数,便可改变占空比值,从而控制LED的亮度。
void fadeLED(void)
{
if(g_fadeValue >= PWM_PERIOD)
{
g_fadeDir = -1; /* Set the direction of the fade */
}
else if(g_fadeValue <= 0)
{
g_fadeDir = 1; /* Set the direction of the fade */
}
g_fadeValue += g_fadeDir * FADE_STEP; /* Calculation of the new duty cycle */
setDutyCycle(g_fadeValue); /* Call the function which is setting the duty cycle of the PWM */
}
4.定时器的使用参考STM_System_Time例程
为了确保整个系统已一个相对的时间来运行各个程序,使用定时器来控制时间,当定时时间到后,触发中断便可开始执行,
使用的是System_Timer(STM),一共有三个定时器可以使用,本次选择了其中的两个,分别以不同的定时时间运行。
首先是初始化设置
void Init_STM(void)
{
/* STM0初始化---Initialize time constant */
g_ticksFor10ms = IfxStm_getTicksFromMilliseconds(BSP_DEFAULT_TIMER, TIMER_INT_TIME);
IfxStm_initCompareConfig(&g_STMConf); /* Initialize the configuration structure with default values */
g_STMConf.triggerPriority = ISR_PRIORITY_STM; /* Set the priority of the interrupt */
g_STMConf.typeOfService = IfxSrc_Tos_cpu0; /* Set the service provider for the interrupts */
g_STMConf.ticks = g_ticksFor10ms; /* Set the number of ticks after which the timer triggers an
* interrupt for the first time */
IfxStm_initCompare(STM, &g_STMConf); /* Initialize the STM with the user configuration */
/* STM1初始化---Initialize time constant */
g_ticksForLED = IfxStm_getTicksFromMilliseconds(&MODULE_STM1, 500);
IfxStm_initCompareConfig(&g_STM1Conf); /* Initialize the configuration structure with default values */
g_STM1Conf.triggerPriority = ISR_PRIORITY_STM1; /* Set the priority of the interrupt */
g_STM1Conf.typeOfService = IfxSrc_Tos_cpu0; /* Set the service provider for the interrupts */
g_STM1Conf.ticks = g_ticksForLED; /* Set the number of ticks after which the timer triggers an
* interrupt for the first time */
IfxStm_initCompare(STM1, &g_STM1Conf); /* 使用用户配置初始化STM//Initialize the STM with the user configuration */
}
再编写中断服务程序,STM0每10ms1次,主要用于计时
IFX_INTERRUPT(isrSTM, 0, ISR_PRIORITY_STM);
/*中断服务程序*/
void isrSTM(void)
{
/* Update the compare register value that will trigger the next interrupt */
/*更新将触发下一个中断的比较寄存器值 */
IfxStm_increaseCompare(STM, g_STMConf.comparator, g_ticksFor10ms);
STM_times++;
}
IFX_INTERRUPT(isrSTM1, 0, ISR_PRIORITY_STM1);
/*中断服务程序*/
void isrSTM1(void)
{
/* Update the compare register value that will trigger the next interrupt */
/*更新将触发下一个中断的比较寄存器值 */
IfxStm_increaseCompare(STM1, g_STM1Conf.comparator, g_ticksForLED);
fadeLED();
}
想要实现呼吸灯,并且改变呼吸灯速率的效果,就需要控制执行上一步的fadeLED函数执行次数,据此,使用STM1来控制这个次数,编写修改STM1定时时间的函数
void Update_STM1(unsigned int cycle)
{
g_ticksForLED = IfxStm_getTicksFromMilliseconds(&MODULE_STM1, cycle);
g_STM1Conf.ticks = g_ticksForLED; /* Set the number of ticks after which the timer triggers an
* interrupt for the first time */
IfxStm_initCompare(STM1, &g_STM1Conf); /* 使用用户配置初始化STM//Initialize the STM with the user configuration */
}
5.主函数部分
首先是对各个部件进行初始化,在while循环中,判断STM0进入中断的次数(10ms/次),以减少执行的次数,这样每经过50ms就会启动一次ADC转换,并将结果从0-4095换算为5-100(对应0.5s-10s,通过串口将数据发送到电脑,然后判断呼吸灯的周期是否需要改变,若需要,则将其改变,最后重置该中断计次次数,以便进行下一次的判断。
int core0_main(void)
{
IfxCpu_enableInterrupts();
/* !!WATCHDOG0 AND SAFETY WATCHDOG ARE DISABLED HERE!!
* Enable the watch dogs 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);
Init_Uart(); /* Initialize the module */
Init_STM();
Init_ADC();
Init_PWM();
while(1)
{
//adc数值越大,周期越大,越慢,0-4095对应,周期500ms-10000ms,单次更新占空比的时间是5-100ms;越小越慢,周期10s
if (STM_times >=5)
{
ADC_Result = ADC_conversion();
sprintf(Uart_sendstr, "{ADC:%d}\n", (int)ADC_Result);
send_UART_message(Uart_sendstr);
PWM_cycle = (ADC_Result*95/4095)+5;
if(PWM_cycle_last != PWM_cycle)
{
Update_STM1(PWM_cycle);
}
PWM_cycle_last = PWM_cycle;
sprintf(Uart_sendstr, "{Cycle:%d}\n", (int)PWM_cycle);
send_UART_message(Uart_sendstr);
STM_times = 0;
}
}
return (1);
}
四、功能展示
使用ComAssistant软件将数据进行可视化显示出来,可以非常直观地看见数据的变化情况
如下图所示
ADC的值即为经过ADC转换后的数据,Cycle的值为计算出来的呼吸灯周期/100(0.5s-10s)
五、对本活动的心得体会
首先非常高兴能参加此次的活动,感觉本次的任务比较简单,也没有遇到太多的问题,主要也是因为英飞凌提供了非常丰富且详尽的资料及历程,才能让我们迅速上手,也希望以后可以使用到英飞凌的产品。
六、参考资料
1.英飞凌官方历程https://github.com/Infineon/AURIX_code_examples
2.AURIX™ Development Studio https://www.infineon.com/cms/en/tools/aurix-tools/free-tools/infineon/
3.CSDN专栏 英飞凌AURIX 牙擦苏-kuan https://blog.csdn.net/wukuan_123/category_9214281.html