Funpack第二季第二期-基于TC264的呼吸灯设计
本次设计,基于Infineon AURIX TC275 Lite Kit开发板,使用板子上的多个外设,通过旋转电位器控制LED呼吸灯的闪烁频率。
标签
Funpack活动
PWM
MCU
infineon
离绪痒个锤子
更新2022-10-10
哈尔滨理工大学
592

硬件介绍

在本次设计中,主要的实验板卡为英飞凌公司官方所推出的AURIX TC275 Lite Kit评估套件,主控为TC275高性能单片机,TC275高性能微控制器适用于价格敏感、高实时性、高计算需求、高带宽、高能效要求的嵌入式控制器设计,其较强的计算性能以及丰富的外设可以满足较多场景。办卡整体套件如下图所示,板卡整体制作精美,并且预留了兼容Arduino Uno的接口,可以兼容一些Arduino套件。

FjmtthMzH7U29BpD9Slf-fHwOJ81

TC275隶属于英飞凌AURIX微控制器,其为32位三核单片机,CPU主频为200MHz,程序存储Flash大小达到4MByte,三个核心可使用的RAM大小分别为112KB、120KB以及120KB,并且每个核心均配有FPU浮点运算单元。从CPU的运行速度以及Flash以及RAM空间可以看出来,TC275就算是仅仅只使用了一个核心,其性能在FPU行业中也处于较高水平。

在三个核心中,核心0为能效核心,主要对于能效敏感型应用所优化,其采用了可以动态预测的1到2级流水线设计;核心1和核心2为性能核心,其采用了可以动态预测的1到3级流水线设计。

英飞凌在近年提供了ADS(AURIX Development Studio)开发套件,可以完成程序代码的编写以及仿真,极大地降低了设计者的开发门槛,有助于电子设计爱好者了解和使用英飞凌TC系列单片机。

由于本次设计较为简单基础,所要执行的任务较为简单,所以我们并不需要太过于关注于芯片外设的资源设计。


任务介绍

本次设计主要完成的任务是设计一个呼吸灯,通过旋转板卡上的电位计,改变呼吸灯闪烁速率,同时将ADC采集的数据通过串口,发送到电脑上显示。


ADS工程新建以及测试

在安装好ADS设计套件之后,新建代码工程,按照软件提示进行下一步操作即可,要注意的是在进行开发板设备选择的时候选择KIT_AURIX_TC275_LITE即可快速搭建工程环境。

FiaeyoCLCrf4fCm7pn4ke6dneuCM

搭建好以上环境之后,将开发板与电脑连接,点击Flash current project,即可完成代码的编译以及下载。调试器的驱动在ADS安装时就以及安装完毕。

FvJnH2O37Z8vVJsRQVMgntCiAyIY


例程下载以及导入

英飞凌官方将适用于ADS的例程上传于其官方github账户中,在windows中安装git工具之后使用以下命令将名为AURIX_code_examples的仓库克隆到电脑本地。

git clone https://github.com/Infineon/AURIX_code_examples.git

在ADS中点击File→Import→Infineon→AURIX Develpoment Studio Project后选择仓库地址即可导入例程,导入时记得选择带有TC275关键词的例程。

FgPuhV6nHu67Kb6ZVK5dUYyqv5-o

导入后即可尝试进行编译以及程序烧录,不出意外的话程序都可以在开发板上运行。


GPIO驱动

使用ADS中集成的TASKING Pin Manager对GPIO进行初始化配置,由于设计较为简单,这里只初始化了两个GPIO用于驱动一个LED(用于驱动LED呼吸灯的GPIO在PWM功能初始化中)。

FvJnH2O37Z8vVJsRQVMgntCiAyIYFp7xUaN6OF4bsBjHomw-sCd2kY8e

按下CTRL+S保存设置后即可自动生成GPIO初始化函数,在程序中调用gpio_init_pins()即可实现函数初始化。


delay延时实现

这里参考了逐飞科技的TC264库函数,使用TC275的STM0模块来实现延迟功能。逐飞科技仓库的地址:https://gitee.com/seekfree/TC264_Library

STM是TC系列单片机的系统定时器,可以实现较高时间要求以及较长时间要求的定时功能。

以下为ns级延时的实现,不过经过先前的测试,该延时函数非常不准确,仅仅可以用来做较为简单基础的延时。

void systick_delay(uint32 time, uint32 num)
{
    uint32 stm_clk;
    uint32 delay_time;
    stm_clk = IfxStm_getFrequency(IfxStm_getAddress((IfxStm_Index)IfxStm_Index_0));
    delay_time = (uint32)(stm_clk/1000000*time/1000);

    while(num--)
    {
        IfxStm_waitTicks(IfxStm_getAddress((IfxStm_Index)IfxStm_Index_0), delay_time);
    }
}

再加入以下的宏定义方便使用:

#define systick_delay_ms(time)    systick_delay(1000000, time)  //设置延时时间  单位ms
#define systick_delay_us(time)    systick_delay(time*1000, 1)   //设置延时时间  单位us
#define systick_delay_ns(time)    systick_delay(time, 1)        //设置延时时间  单位ns

CCU定时器中断

CCU定时器中断使用方式参考Infineon官方的CCU6_Interrupt_1_KIT_TC275_LK例程,初始化部分代码以及定时器中断服务函数表达形式如下:

void Timer_Init(void)
{
    IfxCcu6_Timer_Config timerConfig;                   /* Structure for timer configuration                        */
    IfxCcu6_Timer_initModuleConfig(&timerConfig, CCU6); /* Initialize the timer module structure with default values*/

    timerConfig.base.t12Frequency = CCU6_TIMER_FREQ;                /* Configure the frequency of the timer module */
    timerConfig.base.t12Period = CCU6_TIMER_PERIOD;                 /* Configure the period of the timer (16-bit)  */
    timerConfig.timer = IfxCcu6_TimerId_t12;                        /* Select the timer to be started              */
    timerConfig.interrupt1.source = IfxCcu6_InterruptSource_t12PeriodMatch; /* Set interrupt source                */
    timerConfig.interrupt1.priority = ISR_PRIORITY_CCU6_INT1;       /* Set the priority of the ISR                 */
    timerConfig.interrupt1.typeOfService = IfxSrc_Tos_cpu0;         /* Set the type of service for the interrupt   */
    timerConfig.trigger.t13InSyncWithT12 = FALSE;                   /* Configure timers synchronization            */

    IfxCcu6_Timer_initModule(&timer, &timerConfig);               /* Initialize the CCU6 module                  */
    IfxCcu6_Timer_start(&timer);

    IfxCpu_enableInterrupts();
}

IFX_INTERRUPT(ccu60ISR_Timer, 0, ISR_PRIORITY_CCU6_INT1);
void ccu60ISR_Timer(void)
{
    //user code
	
}

需要注意的是,timerConfig.interrupt1.priority的值需要与定时器中断服务函数的中断优先值相同,即ISR_PRIORITY_CCU6_INT1。若修改timerConfig.interrupt1.typeOfService的参数值可以设置处理定时器中断的cpu号,这里不做改动。

需要着重关注的是宏定义部分:

#define ISR_PRIORITY_CCU6_INT1  40                                          /* Priority for interrupt ISR           */
#define CCU6_TIMER_FREQ         25000000                                    /* Timer module frequency in Hz         */
#define CCU6_ISR_FREQ           1000                                        /* ISR frequency in Hz                  */
#define CCU6_TIMER_PERIOD       (CCU6_TIMER_FREQ / CCU6_ISR_FREQ) - 1       /* Timer module period in ticks         */
/* CCU6 */
#define CCU6                    &MODULE_CCU60

ISR_PRIORITY_CCU6_INT1定义了中断优先级,数值1-255,数值越大优先级越高。CCU6_TIMER_FREQ定义了定时器计数时钟的频率,CCU6_ISR_FREQ定义了定时器中断频率,设置时一方面要确保CCU6_TIMER_FREQ为200M主频的整数倍分频,一方面要确保(CCU6_TIMER_FREQ / CCU6_ISR_FREQ) - 1 < 65536。


STM PWM发生器

STM PWM发生器的使用方法参考GTM_ATOM_PWM_1_KIT_TC275_LK例程,可以将GTM_ATOM_PWM.c以及GTM_ATOM_PWM.h直接拷贝到工程目录下。着重需要注意的是GTM_ATOM_PWM.c下的宏定义。

#define LED                IfxGtm_ATOM1_4_TOUT14_P00_5_OUT      /* LED which will be driven by the PWM              */
#define PWM_PERIOD         1000                                 /* PWM period for the ATOM                          */
#define FADE_STEP          PWM_PERIOD / 100                     /* PWM duty cycle for the ATOM                      */
#define CLK_FREQ           10000000.0f                          /* CMU clock frequency, in Hertz

LED为开发板上LED的GPIO,CLK_FREQ为PWM发生器计数器的频率,PWM_PERIOD为PWM周期寄存器,最后的PWM频率为CLK_FREQ/PWM_PERIOD,占空比可调范围1-PWM_PERIOD;在本次设计中PWM频率设置为10kHz,占空比可调范围0-1000。

FADE_STEP为例程中的呼吸灯的闪烁参数,由于没有使用到这里不做改动。

STM PWM发生器初始化函数为initGtmATomPwm(),PWM占空比装填函数为setDutyCycle(<duty>)。


ADC的使用

这里参考了ADC_Filtering_1_KIT_TC275_LK例程,并在最后的ADC处理结果中使用了4次结果取均值。

可以将例程中的ADC_Filtering.c和ADC_Filtering.h拷贝至工程目录下。

要修改的部分如下:

#define CHANNELS_NUM                1
channel g_chn[] = { {&IfxVadc_G0_0_AN0_IN, (IfxVadc_ChannelResult) 0  },   /* AN0 pin    (Average filter channel)   /
{&IfxVadc_G4_6_P40_8_IN, (IfxVadc_ChannelResult) 7  },   / AN38 pin    (FIR filter channel)       /
{&IfxVadc_G4_5_P40_7_IN, (IfxVadc_ChannelResult) 15 },   / AN37 pin    (IIR filter channel)       /
{&IfxVadc_G4_4_P40_6_IN, (IfxVadc_ChannelResult) 1  }    / AN36 pin    (No data modification)     */
};

void initVADCGroup(void)
{
    /* Initialize the groups */
    IfxVadc_Adc_GroupConfig adcGroupConf;                               /* Define a configuration structure         */
    IfxVadc_Adc_initGroupConfig(&adcGroupConf, &g_vadc);                /* Fill it with default values              */

    /* Enable the background scan source and the background auto scan functionality */
    adcGroupConf.arbiter.requestSlotBackgroundScanEnabled = TRUE;
    adcGroupConf.backgroundScanRequest.autoBackgroundScanEnabled = TRUE;

    /* Enable the gate in "always" mode (no edge detection) */
    adcGroupConf.backgroundScanRequest.triggerConfig.gatingMode = IfxVadc_GatingMode_always;

    /* Set the group 0 as master group */
    adcGroupConf.master = IfxVadc_GroupId_0;
    adcGroupConf.groupId = IfxVadc_GroupId_0;

    /* Apply the configuration to the group, with group 4 as a master:                                          */
    IfxVadc_Adc_initGroup(&g_vadcGroup, &adcGroupConf);
}

将转换通道个数设置为1,将四个通道的第一个通道修改为开发板上电位器对应的ADC引脚AN0。

由于AN0属于ADC Group 0,所以在ADC初始化中需要将adcGroupConf结构体中的master和groupId参数修改为IfxVadc_GroupId_0。

随后在主函数中调用initADC()即可完成ADC初始化,调用readADCValue(AN0)即可读取ADC转换结果。


UART串口的使用

UART串口的使用我也使用了上节ADC使用中的ADC_Filtering_1_KIT_TC275_LK例程。初始化函数为initUART(),数据发送函数为IfxStdIf_DPipe_print(),使用方法如IfxStdIf_DPipe_print(&g_stdInterface, “Hello World!\r\n”),g_stdInterface为串口模块所绑定的标准接口的实例。


整体程序结构设计

在设计中在核心0的主循环中完成ADC的采样以及串口信息的发送,在1ms的定时器中断中完成PWM占空比数据的装填,程序流程图如下图所示。

Fu-V3V5hEWSboPInaHcCyGGr73aQ

主函数中主要代码如下所示:

initGtmATomPwm(); // 初始化PWM
    setDutyCycle(10); // 装填PWM占空比初值
    Timer_Init();     // 初始化1ms定时器
    initADC();        // 初始化ADC
    initUART();       // 初始化UART串口
    gpio_init_pins(); // 初始化GPIO

    measureStats AVRGmeasure;

    char rxData[RX_LENGTH]; /* Placeholder for the data received over UART */

    while(1)
    {
        /* Reset the received command */
        rxData[0] = RESET_CHARACTER;

        /* If any data has been received over UART, read it */
        if(isDataAvailable())
        {
            receiveData(rxData, RX_LENGTH); /* Read one byte */
        }

        AVRGmeasure.currentValue = readADCValue(AN0); // 读取adc转换结果
        // ADC数据通过UART串口发送
        IfxStdIf_DPipe_print(&g_stdInterface, "Dadc:\t %d \t Vpp:\t%.3f V\r\n", AVRGmeasure.currentValue, (AVRGmeasure.currentValue * 3.3) / 4096);
        // 简单计算呼吸灯呼吸频率
        breath_speed = AVRGmeasure.currentValue / 200 + 1;
        // 延时500ms
        systick_delay_ms(500);
    }

1ms定时器中断服务函数如下所示:

IFX_INTERRUPT(ccu60ISR_Timer, 0, ISR_PRIORITY_CCU6_INT1);
void ccu60ISR_Timer(void)
{
    // user code
    // breath led
    static int brightness = 0;
    static uint8 bri_dir = 0;
    // 呼吸灯呼吸逻辑
    if(brightness >= 990) bri_dir = 1;
    if(brightness <= 10) bri_dir = 0;
    if(!bri_dir)
        brightness += breath_speed;
    else
        brightness -= breath_speed;
    // 装填PWM占空比
    setDutyCycle(brightness);
}

总结

在本次设计中,利用英飞凌AURIX TC275 Lite Kit完成了一次小设计。利用开发板上的电位器控制开发板上LED呼吸灯难过的呼吸频率。此次设计虽然简单但是利用了英飞凌AURIX单片机上的多个功能外设模块。从前几节的内容中不难看出利用ADS开发套件以及官方所提供的例程可以较为方便地完成各种外设模块基础功能的设计,并且ADS还可以通过板上的仿真器实现对单片机各个核心程序的仿真。

最后要感谢硬禾学堂以及Digi-Key所发起的Funpack活动,让我得以有机会接触AURIX TC275三核芯单片机开发板,也是我接触到的性能最为强劲的单片机,让我对高性能单片机的底层程序开发有了新的认识。

附件下载
Funpack2-2_0918_Source_code.rar
项目源代码
团队介绍
个人设计,团队丢失,暂无团队。
评论
0 / 100
查看更多
目录
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2023 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号