Funpack第2季第2期演示视频-TC275LiteKit通过电位计改变呼吸灯闪烁频率并将ADC采集值传输到另一台设备
TC275LiteKit通过电位计改变呼吸灯闪烁频率并将ADC采集值传输到另一台设备
标签
嵌入式系统
ADC
PWM
呼吸灯
TC275LiteKit
串口通信
Snapdragon
更新2022-10-12
397

一、板卡介绍

   此次活动的板卡是来自英飞凌的TC275 Lite Kit,资源如下:

   1、主控芯片TC275TP,主要参数为:

      ① 三核200MHz,有DSP;高达4KB的Flash和472KB的ECC RAM

      ② 60+通道12bitADC;多种传感器接口。

      ③ 1x以太网控制器;4xCAN FD;4xASCLIN;4xQSPI;1xI²C;等。

      ④ 高安全性;车规级宽温度范围-40℃~+150℃。

   2、板载FT2232HL调试器。

   3、大功率LDO供电。

   4、CAN总线收发器。

   5、一个用户按键、两个用户LED。

二、任务描述

   我选择的是第二个任务:“设计一个呼吸灯,通过旋转板卡上的电位计,改变呼吸灯闪烁速率,同时将ADC采集的数据通过串口/CAN,发送到另一台设备上显示”

   具体的实现为:采用LED1作为呼吸灯,通过R32-AN0改变输出的电压值,ADC采样根据采样值改变呼吸灯的闪烁频率,通过串口发送到一个具有TFT显示屏的设备显示采样值,并通过进度条展示。

三、代码解释

   1、呼吸灯实现

      呼吸灯的亮度通过PWM的占空比调节,呼吸功能在main函数中通过延时定时,轮询实现。具体的效果可以参考视频。

#define ISR_PRIORITY_ATOM  20                                   /* Interrupt priority number                        */
#define CLK_FREQ           1000000.0f                           /* CMU clock frequency, in Hertz                    */

// period 范围 5000 - 200000 200Hz ~ 5Hz
uint32 g_pwmPeriod = 5000;

IfxGtm_Atom_Pwm_Config g_atomConfig;                            /* Timer configuration structure                    */
IfxGtm_Atom_Pwm_Driver g_atomDriver;                            /* Timer Driver structure                           */

void PWM_LED_SetDutyCycle(uint8 dutyCycle)
{
    if (dutyCycle > 100)
        dutyCycle = 100;
    dutyCycle = 100 - dutyCycle;                              /* 转换方向              */
    g_atomConfig.dutyCycle = g_pwmPeriod / 100 * dutyCycle ;  /* Set duty cycle        */
    IfxGtm_Atom_Pwm_init(&g_atomDriver, &g_atomConfig);       /* Re-initialize the PWM */
}

/* This function initializes the ATOM */
void PWM_LED_Init()
{
    IfxGtm_enable(&MODULE_GTM); /* Enable GTM */

    IfxGtm_Cmu_setClkFrequency(&MODULE_GTM, IfxGtm_Cmu_Clk_0, CLK_FREQ);        /* Set the CMU clock 0 frequency    */
    IfxGtm_Cmu_enableClocks(&MODULE_GTM, IFXGTM_CMU_CLKEN_CLK0);                /* Enable the CMU clock 0           */

    IfxGtm_Atom_Pwm_initConfig(&g_atomConfig, &MODULE_GTM);                     /* Initialize default parameters    */

    g_atomConfig.atom = PWM_LED.atom;                                   /* Select the ATOM depending on the LED     */
    g_atomConfig.atomChannel = PWM_LED.channel;                         /* Select the channel depending on the LED  */
    g_atomConfig.period = g_pwmPeriod;                                  /* Set timer period                         */
    g_atomConfig.pin.outputPin = &PWM_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);                         /* Start the PWM                            */
}

PWM_LED_Init()是PWM的初始化和占空比调节的函数,这里直接把LED所在的IO作为输出。

PWM_LED_SetDutyCycle(uint8 dutyCycle)是改变PWM占空比的函数,范围为0~100;这是实现呼吸灯的关键。

 if ((milliseconds % 20) == 0)
        {
            PWM_LED_SetDutyCycle(dutyCycle);
            if (dutyCycle >= 100)
                dutyDir = -1;
            if (dutyCycle <= 0)
                dutyDir = 1;
            dutyCycle += dutyDir;
        }

这是在main函数中轮询实现每20毫秒改变一次PWM的占空比,实现呼吸功能。milliseconds是毫秒计数。

   2、ADC采样实现

typedef struct
{
    IfxVadc_Adc               vadc;               /* VADC configuration     */
    IfxVadc_Adc_Group         adcGroup;           /* Group configuration    */
    IfxVadc_Adc_ChannelConfig adcChannelConfig;   /* Channel configuration  */
    IfxVadc_Adc_Channel       adcChannel;         /* Channel                */
} ApplicationVadcBackgroundScan;

#define VADC_GROUP                  IfxVadc_GroupId_0           /* Use the ADC group 0                              */
#define CHANNEL_ID                  0                           /* Use the Channel 0                                */
#define CHANNEL_RESULT_REGISTER     5                           /* Use the Result Register 5                        */

ApplicationVadcBackgroundScan g_vadcBackgroundScan;

uint16 VADC_GetValue()
{
    Ifx_VADC_RES conversionResult;

    /* Retrieve the conversion value until valid flag of the result register is true */
    do
    {
        conversionResult = IfxVadc_Adc_getResult(&g_vadcBackgroundScan.adcChannel);
    }
    while (!conversionResult.B.VF);

    return (uint16)conversionResult.B.RESULT;
}

/* The VADC module and group are initialized */
void VADC_BackgroundScanInit()
{
    /* VADC module configuration */
    /* Create VADC configuration */
    IfxVadc_Adc_Config adcConfig;

    /* Initialize the VADC configuration with default values */
    IfxVadc_Adc_initModuleConfig(&adcConfig, &MODULE_VADC);

    /* Initialize the VADC module using the VADC configuration */
    IfxVadc_Adc_initModule(&g_vadcBackgroundScan.vadc, &adcConfig);

    /* VADC group configuration */
    /* Create group configuration */
    IfxVadc_Adc_GroupConfig adcGroupConfig;

    /* Initialize the group configuration with default values */
    IfxVadc_Adc_initGroupConfig(&adcGroupConfig, &g_vadcBackgroundScan.vadc);

    /* Define which ADC group is going to be used */
    adcGroupConfig.groupId = VADC_GROUP;
    adcGroupConfig.master = VADC_GROUP;

    /* Enable background scan source */
    adcGroupConfig.arbiter.requestSlotBackgroundScanEnabled = TRUE;

    /* Enable background auto scan mode */
    adcGroupConfig.backgroundScanRequest.autoBackgroundScanEnabled = TRUE;

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

    /* Initialize the group using the group configuration */
    IfxVadc_Adc_initGroup(&g_vadcBackgroundScan.adcGroup, &adcGroupConfig);
}

/* The input channels to be used are setup and the VADC is set into run mode */
void VADC_BackgroundScanRun()
{
    /* Initialize the channel configuration of application handle g_vadcBackgroundScan with default values */
    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;

    /* Initialize the channel of application handle g_VadcBackgroundScan using the channel configuration */
    IfxVadc_Adc_initChannel(&g_vadcBackgroundScan.adcChannel, &g_vadcBackgroundScan.adcChannelConfig);

    /* Enable background scan for the channel */
    IfxVadc_Adc_setBackgroundScan(&g_vadcBackgroundScan.vadc,
                                  &g_vadcBackgroundScan.adcGroup,
                                  (1 << (IfxVadc_ChannelId)CHANNEL_ID),
                                  (1 << (IfxVadc_ChannelId)CHANNEL_ID));

    /* Start background scan conversion */
    IfxVadc_Adc_startBackgroundScan(&g_vadcBackgroundScan.vadc);
}

ADC采样的实现是参考官方的Demo,具体的电路可以参考官方原理图。这里可以简单理解为选择电位计可以改变ADC的采样值,范围为0~4095。

   3、改变呼吸灯频率的实现。

#define ISR_PRIORITY_ATOM  20                                   /* Interrupt priority number                        */
#define CLK_FREQ           1000000.0f                           /* CMU clock frequency, in Hertz                    */

// period 范围 5000 - 200000 200Hz ~ 5Hz
uint32 g_pwmPeriod = 5000;

void PWM_LED_SetPeriod(uint32 period)
{
    g_pwmPeriod = period;
    g_atomConfig.period = g_pwmPeriod;
    IfxGtm_Atom_Pwm_stop(&g_atomDriver, TRUE);
    IfxGtm_Atom_Pwm_init(&g_atomDriver, &g_atomConfig);
    IfxGtm_Atom_Pwm_start(&g_atomDriver, TRUE);
}

PWM_LED_SetPeriod(uint32 period)是实现改变呼吸灯闪烁频率的代码。从上面的宏可以看到定时器的时钟为1MHz。函数的参数是定时周期,闪烁频率=参考时钟/周期,这里周期的范围是5k~200k,对应的闪烁频率为200Hz~5Hz。

while(1)
    {
        uint16 adcValue = VADC_GetValue();

        waitTime(IfxStm_getTicksFromMilliseconds(BSP_DEFAULT_TIMER, 1));
        milliseconds++;

        if (mainAds(lastAdcValue, adcValue) > 10)
        {
            uint32 period = (uint32)(200000 - (float)(47.62f * adcValue));
            PWM_LED_SetPeriod(period);
        }

这是main函数中每隔1毫秒ADC采样一次,当前采样值和上次采样值差距超过10时,改变呼吸灯的频率,这里需要将ADC的值和PWM定时器周期的值对应起来,具体的公式为period=200k-采样值*(200k-5k)/4095。

   4、串口发送

      由于没有焊接排针,这里用PC对串口发送做了转发,TC275->PC;PC->显示设备。

#define SERIAL_PIN_RX           IfxAsclin0_RXA_P14_1_IN                     /* RX pin of the board                  */
#define SERIAL_PIN_TX           IfxAsclin0_TX_P14_0_OUT                     /* TX pin of the board                  */

#define INTPRIO_ASCLIN0_TX      19                                          /* Priority of the ISR                  */

#define ASC_TX_BUFFER_SIZE      64                                          /* Definition of the buffer size        */

IfxAsclin_Asc g_asc;                                                        /* Declaration of the ASC handle        */
uint8 g_ascTxBuffer[ASC_TX_BUFFER_SIZE + sizeof(Ifx_Fifo) + 8];             /* Declaration of the FIFOs parameters  */

IFX_INTERRUPT(asclin0_Tx_ISR, 0, INTPRIO_ASCLIN0_TX);                       /* Adding the Interrupt Service Routine */

void asclin0_Tx_ISR(void)
{
    IfxAsclin_Asc_isrTransmit(&g_asc);
}

void UART_Init(uint32 baudRate)
{
    /* Initialize an instance of IfxAsclin_Asc_Config with default values */
    IfxAsclin_Asc_Config ascConfig;
    IfxAsclin_Asc_initModuleConfig(&ascConfig, SERIAL_PIN_TX.module);

    /* Set the desired baud rate */
    ascConfig.baudrate.baudrate = (float32)baudRate;

    /* ISR priorities and interrupt target */
    ascConfig.interrupt.txPriority = INTPRIO_ASCLIN0_TX;
    ascConfig.interrupt.typeOfService = IfxCpu_Irq_getTos(IfxCpu_getCoreIndex());

    /* FIFO configuration */
    ascConfig.txBuffer = &g_ascTxBuffer;
    ascConfig.txBufferSize = ASC_TX_BUFFER_SIZE;

    /* Port pins configuration */
    const IfxAsclin_Asc_Pins pins =
    {
        NULL_PTR,         IfxPort_InputMode_pullUp,     /* CTS pin not used     */
        &SERIAL_PIN_RX,   IfxPort_InputMode_pullUp,     /* RX pin not used      */
        NULL_PTR,         IfxPort_OutputMode_pushPull,  /* RTS pin not used     */
        &SERIAL_PIN_TX,   IfxPort_OutputMode_pushPull,  /* TX pin               */
        IfxPort_PadDriver_cmosAutomotiveSpeed1
    };
    ascConfig.pins = &pins;

    IfxAsclin_Asc_initModule(&g_asc, &ascConfig);                       /* Initialize module with above parameters  */
}

void UART_Transmit(uint8 *pBuffer, Ifx_SizeT bufferSize)
{
    IfxAsclin_Asc_write(&g_asc, pBuffer, &bufferSize, TIME_INFINITE);
}

void UART_SendChar(char ch)
{
    UART_Transmit((uint8 *)&ch, 1);
}

void UART_SendUnsignedInteger(uint32 integer)
{
    static uint8 buffer[11] = {0};
    uint8 *pIntString = &buffer[11];
    Ifx_SizeT integerLength = 0;

    do
    {

        *--pIntString = integer % 10 + '0';
        integerLength++;
        integer /= 10;
    } while (integer);

    UART_Transmit(pIntString, integerLength);
}

void UART_SendFloat(float fn)
{
    if (fn < 0.0f)
    {
        UART_SendChar('-');
        fn = -fn;
    }
    uint32 integer = (uint32)fn;
    uint32 decimal = (fn - integer) * 1e6;       // 保留5位小数,第6位四舍五入
//    UART_SendUnsignedInteger(decimal);
    if (decimal % 10 >= 5)
        decimal += 10;
    decimal /= 10;
    if (decimal >= 1e5)
    {
        decimal = 0;
        integer++;
    }
    while (decimal && !(decimal % 10))
    {
        decimal /= 10;
    }
    UART_SendUnsignedInteger(integer);
    if (decimal)
    {
        UART_SendChar('.');
        UART_SendUnsignedInteger(decimal);
    }
}

void UART_SendString(const char *string)
{
    UART_Transmit((uint8 *)string, (Ifx_SizeT)strlen(string));
}

这是串口的驱动部分,部分代码参考的官方Demo。

    while(1)
    {
        uint16 adcValue = VADC_GetValue();

        waitTime(IfxStm_getTicksFromMilliseconds(BSP_DEFAULT_TIMER, 1));
        milliseconds++;

        if (mainAds(lastAdcValue, adcValue) > 10)
        {
            char v = adcValue >> 8;                   // 4096->256  12->8
            UART_SendChar(v);
            v = adcValue & 0xFF;
            UART_SendChar(v);
            lastAdcValue = adcValue;

这里和改变呼吸灯闪烁频率的部分一样,采样值差距大于10会触发一次串口发送。由于采样值为16位,这里分两次发送,采用大端模式,先发送高位,再发送低位。

四、总结

   TC275作为高性能的三核单片机,很遗憾暂时没有时间去探索多核心的优势,但是就实现的这个任务来看,单核就具有很高的性能和灵活性。

附件下载
Funpack2_2代码.zip
团队介绍
嵌入式工程师-陈海 来自山东
团队成员
Snapdragon
评论
0 / 100
查看更多
目录
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2023 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号