一、板卡介绍
此次活动的板卡是来自英飞凌的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作为高性能的三核单片机,很遗憾暂时没有时间去探索多核心的优势,但是就实现的这个任务来看,单核就具有很高的性能和灵活性。