项目总结报告
一、项目介绍
该项目是硬禾学堂发布的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库中注释较为齐全,可以让我们体会不同架构的单片机。