项目总结报告
一、项目介绍
该项目是硬禾学堂发布的Funpack2-2项目,使用TC275开发板完成一系列任务,我选择完成任务二,设计一个呼吸灯,通过旋转板卡上的电位计,改变呼吸灯闪烁速率,同时将ADC采集的数据通过串口/CAN,发送到另一台设备上显示。我实现以下功能:
1.使用PWM波驱动LED灯,实现不同亮度,然后在定时器中断回调函数里改变PWM占空比,实现呼吸灯;
2.通过ADC模块采样得到电位器的电压值,根据该值大小改变呼吸频率;
3.通过Asclin模块实现串口通信,将PWM的占空比和ADC的采样值传输至电脑上位机显示。
功能展示如下:
电位器值较大,呼吸灯频率很快,上位机中蓝色为ADC读数,黄色为呼吸灯PWM占空比值:
将电位器拧动,使电阻值减小,ADC采样得到的电压值减小,呼吸灯频率减慢:
进一步减小电位器阻值,ADC采样值继续减小,呼吸灯频率更慢:
二、硬件介绍
TC275TP作为第一代 Aurix TC27xT系列产品,专为满足极高的安全标准,同时大幅提高性能而设计。采用创新多核心架构,三个独立的 32 位 TriCore CPU均可工作在200 MHz。可选择多种开发环境,包括Hightec/PLS/Infineon 的基于Eclipse的FreeEntryToolchain,或来自英飞凌基于Eslipse的免费IDE AURIX™ Development Studio,亦或是更多开发工具。
特性:
· 搭载了基于AURIX™ TriCore™ 单片三核微控制器TC275
· 板载Micro USB接口的miniWiggler调试器
· 两个Infineon Shield2Go扩展接口
· 兼容MikroBUS 和Arduino扩展连接
· 带有Infineon新一代CAN 收发器TLE9251VSJ ,可用于汽车和工业应用的HS CAN网络开发
· 已焊接可调旋转电位计,用于评估模拟电压的采集
· 一个用户输入按键
· 预留三个LED可作为工作指示灯
三、代码实现
1.PWM
主要实现初始化函数和设置占空比函数:
/** \brief 初始化指定PWM引脚
* \param pin PWM引脚
* \param freq PWM频率
* \param duty PWM占空比
* \return 返回HSIC_OK表明初始化成功
*
* Coding example:
* \code
* Hsic_pwmInit(IfxGtm_ATOM0_3_TOUT56_P21_5_OUT,1000,0.5);
* \endcode
*
*/
hsic_err_t Hsic_pwmInit(IfxGtm_Atom_ToutMap pin, float freq, float duty);
/** \brief 设定占空比
* \param pin PWM引脚
* \param duty PWM占空比
* \return 返回HSIC_OK表明操作成功
*
* Coding example:
* \code
* Hsic_pwmSetDuty(IfxGtm_ATOM0_3_TOUT56_P21_5_OUT,0.5);
* \endcode
*
*/
hsic_err_t Hsic_pwmSetDuty(IfxGtm_Atom_ToutMap pin, float duty);
{
frequency = IfxGtm_Cmu_getModuleFrequency(gtm);
IfxGtm_enable(gtm);
IfxGtm_Cmu_setGclkFrequency(gtm, frequency);
IfxGtm_Cmu_setClkFrequency(gtm, IfxGtm_Cmu_Clk_0, frequency);
IfxGtm_Cmu_enableClocks(gtm, IFXGTM_CMU_CLKEN_FXCLK | IFXGTM_CMU_CLKEN_CLK0);
IfxGtm_Atom_Pwm_Config atomConfig;
IfxGtm_Atom_Pwm_Driver atomHandle;
IfxGtm_Atom_Pwm_initConfig(&atomConfig, gtm);
atomConfig.atom = pin.atom;
atomConfig.atomChannel = pin.channel;
atomConfig.period = (uint32)(frequency / freq);
atomConfig.dutyCycle = (uint32)(duty * frequency / freq);
atomConfig.pin.outputPin = &pin;
atomConfig.synchronousUpdateEnabled = TRUE;
IfxGtm_Atom_Pwm_init(&atomHandle, &atomConfig);
IfxGtm_Atom_Pwm_start(&atomHandle, TRUE);
return HSIC_OK;
}
hsic_err_t Hsic_pwmSetDuty(IfxGtm_Atom_ToutMap pin, float duty)
{
uint32 period;
period = IfxGtm_Atom_Ch_getCompareZero(>m->ATOM[pin.atom], pin.channel);
IfxGtm_Atom_Ch_setCompareOneShadow(>m->ATOM[pin.atom], pin.channel, (uint32)(duty * period));
return HSIC_OK;
}
2.定时器中断
使用CCU6模块,初始化函数如下:
hsic_err_t Hsic_ccu6TimerIrqInit(hsic_ccu6_t ccu6, hsic_ccu6_timer_t tm, hsic_srctos_t srctos, uint8 prio, uint32 ms)
{
boolean interrupt_state = disableInterrupts();
uint64 timer_input_clk;
uint32 timer_period;
IfxCcu6_Timer timer;
IfxCcu6_Timer_Config timerConfig;
IfxCcu6_Timer_initModuleConfig(&timerConfig, (Ifx_CCU6*)ccu6);
timer_input_clk = IfxScuCcu_getSpbFrequency();
uint8 i = 0;
while(i<16)
{
timer_period = (uint32)(timer_input_clk * ms / 1000);
if(timer_period < 0xffff) break;
timer_input_clk >>= 1;
i++;
}
if(16 <= i) return HSIC_ERR_INVALID_ARG;
switch(tm)
{
case TM0:
{
if(CCU60 == ccu6)
{
timerConfig.interrupt1.typeOfService = (IfxSrc_Tos)srctos;
timerConfig.interrupt1.priority = INT_PRIO_CCU60_TM0;
}
else
{
timerConfig.interrupt1.typeOfService = (IfxSrc_Tos)srctos;
timerConfig.interrupt1.priority = INT_PRIO_CCU61_TM0;
}
timerConfig.timer = IfxCcu6_TimerId_t12;
timerConfig.interrupt1.source = IfxCcu6_InterruptSource_t12PeriodMatch;
timerConfig.interrupt1.serviceRequest = IfxCcu6_ServiceRequest_1;
timerConfig.base.t12Period = timer_period;
timerConfig.base.t12Frequency = (float)timer_input_clk;
timerConfig.clock.t12countingInputMode = IfxCcu6_CountingInputMode_internal;
}break;
case TM1:
{
if(CCU60 == ccu6)
{
timerConfig.interrupt2.typeOfService = (IfxSrc_Tos)srctos;
timerConfig.interrupt2.priority = INT_PRIO_CCU60_TM1;
}
else
{
timerConfig.interrupt2.typeOfService = (IfxSrc_Tos)srctos;
timerConfig.interrupt2.priority = INT_PRIO_CCU61_TM1;
}
timerConfig.timer = IfxCcu6_TimerId_t13;
timerConfig.interrupt2.source = IfxCcu6_InterruptSource_t13PeriodMatch;
timerConfig.interrupt2.serviceRequest = IfxCcu6_ServiceRequest_2;
timerConfig.base.t13Period = timer_period;
timerConfig.base.t13Frequency = (float)timer_input_clk;
timerConfig.clock.t13countingInputMode = IfxCcu6_CountingInputMode_internal;
}break;
}
timerConfig.timer12.counterValue = 0;
timerConfig.timer13.counterValue = 0;
timerConfig.trigger.t13InSyncWithT12 = FALSE;
IfxCcu6_Timer_initModule(&timer, &timerConfig);
restoreInterrupts(interrupt_state);
IfxCcu6_setSuspendMode((Ifx_CCU6*)ccu6, IfxCcu6_SuspendMode_hard);
IfxCcu6_Timer_start(&timer);
return HSIC_OK;
}
在中断回调函数中获取ADC采样值,根据值的大小设置呼吸的快慢,通过PWM输出:void CCU60_TM0_CallBack(void)
{
ADC_value = Hsic_adcGet(IfxVadc_G0_0_AN0_IN);
float steep = 0.2*ADC_value/(float)4096;
static float flag = 1;
duty += flag*steep;
if(duty>=1)
{
flag = -1;
duty = 1;
}
else if(duty<=0)
{
flag = 1;
duty = 0;
}
Hsic_pwmSetDuty(IfxGtm_ATOM0_4_TOUT14_P00_5_OUT,duty);
}
3.ADC采样
只需实现初始化函数和获取数值的函数:
hsic_err_t Hsic_adcInit(IfxVadc_Vadcg_In pin)
{
IfxVadc_Adc_ChannelConfig adcChannelConfig;
IfxVadc_Adc_Channel adcChannel;
if(vadc.vadc!=NULL) goto INITADCGROUP;
IfxVadc_Adc_Config adcConfig;
IfxVadc_Adc_initModuleConfig(&adcConfig, &MODULE_VADC);
IfxVadc_Adc_initModule(&vadc, &adcConfig);
INITADCGROUP:
if(adcGroup[pin.groupId].group!=NULL) goto INITADCCH;
IfxVadc_Adc_GroupConfig adcGroupConfig;
IfxVadc_Adc_initGroupConfig(&adcGroupConfig, &vadc);
adcGroupConfig.groupId = pin.groupId;
adcGroupConfig.master = pin.groupId;
adcGroupConfig.arbiter.requestSlotQueueEnabled = TRUE;
adcGroupConfig.arbiter.requestSlotScanEnabled = TRUE;
adcGroupConfig.arbiter.requestSlotBackgroundScanEnabled = TRUE;
adcGroupConfig.queueRequest.triggerConfig.gatingMode = IfxVadc_GatingMode_always;
adcGroupConfig.scanRequest.triggerConfig.gatingMode = IfxVadc_GatingMode_always;
adcGroupConfig.backgroundScanRequest.triggerConfig.gatingMode = IfxVadc_GatingMode_always;
adcGroupConfig.scanRequest.autoscanEnabled = TRUE;
adcGroupConfig.backgroundScanRequest.autoBackgroundScanEnabled = TRUE;
IfxVadc_Adc_initGroup(&adcGroup[pin.groupId], &adcGroupConfig);
INITADCCH:
IfxVadc_Adc_initChannelConfig(&adcChannelConfig, &adcGroup[pin.groupId]);
adcChannelConfig.channelId = pin.channelId;
adcChannelConfig.resultRegister = (IfxVadc_ChannelResult)pin.groupId;
adcChannelConfig.backgroundChannel = TRUE;
IfxVadc_Adc_initChannel(&adcChannel,&adcChannelConfig);
unsigned channels = (1 << adcChannelConfig.channelId);
unsigned mask = channels;
IfxVadc_Adc_setBackgroundScan(&vadc, &adcGroup[pin.groupId], channels, mask);
IfxVadc_Adc_startBackgroundScan(&vadc);
return HSIC_OK;
}
uint16 Hsic_adcGet(IfxVadc_Vadcg_In pin)
{
while(IfxVadc_getScanStatus(&pin.module->G[pin.groupId])==IfxVadc_Status_channelsStillPending);
Ifx_VADC_RES res = IfxVadc_getResult(&pin.module->G[pin.groupId],pin.channelId);
return (res.B.RESULT&0x0fff);
}
4.UART串口通信
使用Asclin模块,初始化函数:
hsic_err_t Hsic_uartInit(IfxAsclin_Tx_Out tx, IfxAsclin_Rx_In rx, uint32 baudrate, hsic_srctos_t srctos)
{
if(tx.module!=rx.module)return HSIC_ERR_INVALID_ARG;
uint8 i = 0xff&(((uint32)rx.module - (uint32)&MODULE_ASCLIN0)>>8);
IfxAsclin_Asc_Config ascConfig;
IfxAsclin_Asc_initModuleConfig(&ascConfig, tx.module);
ascConfig.baudrate.prescaler = 1;
ascConfig.baudrate.baudrate = (float32)baudrate;
// ISR priorities and interrupt target
if(i==0)
{
ascConfig.interrupt.txPriority = ASCLIN0_TX_INT_PRIO;
ascConfig.interrupt.rxPriority = ASCLIN0_RX_INT_PRIO;
ascConfig.interrupt.erPriority = ASCLIN0_ER_INT_PRIO;
}
else if(i==1)
{
ascConfig.interrupt.txPriority = ASCLIN1_TX_INT_PRIO;
ascConfig.interrupt.rxPriority = ASCLIN1_RX_INT_PRIO;
ascConfig.interrupt.erPriority = ASCLIN1_ER_INT_PRIO;
}
else if(i==2)
{
ascConfig.interrupt.txPriority = ASCLIN2_TX_INT_PRIO;
ascConfig.interrupt.rxPriority = ASCLIN2_RX_INT_PRIO;
ascConfig.interrupt.erPriority = ASCLIN2_ER_INT_PRIO;
}
else if(i==3)
{
ascConfig.interrupt.txPriority = ASCLIN3_TX_INT_PRIO;
ascConfig.interrupt.rxPriority = ASCLIN3_RX_INT_PRIO;
ascConfig.interrupt.erPriority = ASCLIN3_ER_INT_PRIO;
}
else
{
return HSIC_ERR_INVALID_ARG;
}
ascConfig.interrupt.typeOfService = (IfxSrc_Tos)srctos;
// FIFO configuration
ascConfig.txBuffer = &ascTxBuffer[i][0];
ascConfig.txBufferSize = ASC_TX_BUFFER_SIZE;
ascConfig.rxBuffer = &ascRxBuffer[i][0];
ascConfig.rxBufferSize = ASC_RX_BUFFER_SIZE;
// pin configuration
const IfxAsclin_Asc_Pins pins = {
NULL, IfxPort_InputMode_pullUp, // CTS pin not used
&rx, IfxPort_InputMode_pullUp, // Rx pin
NULL, IfxPort_OutputMode_pushPull, // RTS pin not used
&tx, IfxPort_OutputMode_pushPull, // Tx pin
IfxPort_PadDriver_cmosAutomotiveSpeed1
};
ascConfig.pins = &pins;
IfxAsclin_Asc_initModule(&asc[i], &ascConfig);
return HSIC_OK;
}
发送数据函数:
void Hsic_VarUpload(float *my_var, uint8 var_num, uint8 ascnum)
{
uint8 cmdf[7] =
{ 0x55, 0xaa, 0x11, 0x55, 0xaa, 0xff, 0x01 };
uint8 cmdr = 0x01;
uint8 begin_cmd[3] =
{ 0x55, 0xaa, 0x11 };
/*! 发送帧头 */
Hsic_uartWrite(begin_cmd, sizeof(begin_cmd), ascnum);
/*! 发送数据个数 */
Hsic_uartWrite(cmdf, sizeof(cmdf), ascnum);
Hsic_uartWrite(&var_num, 1, ascnum);
/*! 发送数据 */
Hsic_uartWrite((uint8* )(my_var), var_num * 4U, ascnum);
/*! 发送帧尾 */
Hsic_uartWrite(&cmdr, 1, ascnum);
}
四、遇到的问题
TC275的库和手册只有全英文的,阅读比较费力;其定时器的实现方法有很多,用CCU6模块只是其中之一,十分容易搞混。
Tricore架构和ARM不同,需要一段时间适应。
五、心得体会
这次使用的芯片较为冷门,中文资料较少,好在iLLD库中注释较为齐全,可以让我们体会不同架构的单片机。