Funpack2-2 任务二
设计一个呼吸灯,通过旋转板卡上的电位计,改变呼吸灯闪烁速率,同时将ADC采集的数据通过串口,发送到另一台设备上显示。
硬件平台介绍
本次使用的是来自英飞凌公司的高性能、高可靠性的AURIX_TC275_LITE开发板。这块开发板搭载了一个有丰富外设和32位多核控制器,包含三个核,三核主频200Mhz,编程FLASH 4MB,内嵌HSM,芯片功能安全可以达到ASIL-D。另外电源模块,包括主电源芯片TLE7368-3E及组件,给系统和其他模块提供5V和3.3V电压。在外设方面包含CAN通信模块,包括CAN收发器和扩展连接器;LIN通信模块,包括LIN收发器和宽展连接器;以及一些常见的基本外设:蜂鸣器及驱动电路,GPIO和ADC扩展接口;按键输入模块;LED指示模块。同时还板载一个FT2232调试器,连接到芯片的调试端口和串口。
软件平台介绍
本次使用到AURIX™ Development Studio,这是专为TriCore MCU系列设计的集成开发环境,在eclipse环境下支持使用C语言和英飞凌底层驱动库(iLLD)开发,对多核心进行在线调试。同时,上位机平台采用Qt,是一个1991年由Qt Company开发的跨平台C++图形用户界面应用程序开发框架。
实现功能介绍
使用PWM的方式对LED的亮度进行控制,同时控制LED的亮度变化速度,另外使用ADC采样获取电位计的变化,将得到的值通过串口发送至上位机显示出ADC数值对应的电压值。
代码部分介绍
LED部分
本次使用到的是LED1,对应于P00.5,对该引脚进行初始化即可。参考了Blinky_LED_1_KIT_TC275_LK例程。
void LED_Init(void) {
IfxPort_setPinMode(LED_GPIO_Port, LED_Pin, IfxPort_Mode_outputPushPullGeneral);
}
void LED_SetState(uint8_t state) {
if (state) {
IfxPort_setPinState(LED_GPIO_Port, LED_Pin, IfxPort_State_low);
} else {
IfxPort_setPinState(LED_GPIO_Port, LED_Pin, IfxPort_State_high);
}
}
PWM部分
PWM使用到了通用定时器的PWM模式输出的方式,参考了GTM_TOM_PWM_1_KIT_TC275_LK的代码。然后自定义PWM占空比的函数。
#define LED IfxGtm_TOM1_4_TOUT14_P00_5_OUT
#define PWM_PERIOD 50000
IfxGtm_Tom_Pwm_Config g_tomConfig;
IfxGtm_Tom_Pwm_Driver g_tomDriver;
/* PWM初始化 */
void PWM_Init(void) {
IfxGtm_enable(&MODULE_GTM); /* Enable GTM */
IfxGtm_Cmu_enableClocks(&MODULE_GTM, IFXGTM_CMU_CLKEN_FXCLK); /* Enable the FXU clock */
/* Initialize the configuration structure with default parameters */
IfxGtm_Tom_Pwm_initConfig(&g_tomConfig, &MODULE_GTM);
g_tomConfig.tom = LED.tom; /* Select the TOM depending on the LED */
g_tomConfig.tomChannel = LED.channel; /* Select the channel depending on the LED */
g_tomConfig.period = PWM_PERIOD; /* Set the timer period */
g_tomConfig.pin.outputPin = &LED; /* Set the LED port pin as output */
g_tomConfig.synchronousUpdateEnabled = TRUE; /* Enable synchronous update */
IfxGtm_Tom_Pwm_init(&g_tomDriver, &g_tomConfig); /* Initialize the GTM TOM */
IfxGtm_Tom_Pwm_start(&g_tomDriver, TRUE);
}
/* duty should be between 0 and 100 */
void PWM_SetDuty(uint8_t dutyCycle) {
g_tomConfig.dutyCycle = dutyCycle * PWM_PERIOD / 100; /* Change the value of the duty cycle */
IfxGtm_Tom_Pwm_init(&g_tomDriver, &g_tomConfig); /* Re-initialize the PWM */
}
定时器部分
参考了STM_Interrupt_1_KIT_TC275_LK例程,使用普通的定时器来进行编写自定义的延时函数。
IfxStm_CompareConfig g_STMConf;
void Timer_Init(void) {
// init timer with default config
IfxStm_initCompareConfig(&g_STMConf);
IfxStm_initCompare(&MODULE_STM0, &g_STMConf);
}
void Timer_Delayms(uint16_t ms) {
uint32 tick = IfxStm_getTicksFromMilliseconds(&MODULE_STM0, ms);
IfxStm_waitTicks(&MODULE_STM0, tick);
}
ADC部分
参考ADC_Single_Channel_1_KIT_TC275_LK例程,选择单通道模式channel0,先进行配置,然后开始启动并获取到AD值。
ApplicationVadcBackgroundScan g_vadcBackgroundScan;
void Adc_Init(void) {
/* 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 Adc_Run(void) {
/* 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);
}
#define RETRY_MAX 0xf000
uint32_t Adc_GetValue(void) {
Ifx_VADC_RES conversionResult;
uint16_t retry = 0;
do {
conversionResult = IfxVadc_Adc_getResult(&g_vadcBackgroundScan.adcChannel);
if (retry++ > RETRY_MAX) {
return 0xffffffff;
}
} while (!conversionResult.B.VF);
return conversionResult.B.RESULT;
}
串口部分
参考ASCLIN_UART_1_KIT_TC275_LK例程,更改了串口接收和发送的引脚,其余配置基本相同。
void asclin0TxISR(void)
{
IfxAsclin_Asc_isrTransmit(&g_ascHandle);
}
IFX_INTERRUPT(asclin0RxISR, 0, INTPRIO_ASCLIN0_RX);
void asclin0RxISR(void)
{
IfxAsclin_Asc_isrReceive(&g_ascHandle);
}
/* This function initializes the ASCLIN UART module */
void Uart_Init(void) {
/* Initialize an instance of IfxAsclin_Asc_Config with default values */
IfxAsclin_Asc_Config ascConfig;
IfxAsclin_Asc_initModuleConfig(&ascConfig, &MODULE_ASCLIN0);
/* Set the desired baud rate */
ascConfig.baudrate.baudrate = 115200;
/* ISR priorities and interrupt target */
ascConfig.interrupt.txPriority = INTPRIO_ASCLIN0_TX;
ascConfig.interrupt.rxPriority = INTPRIO_ASCLIN0_RX;
ascConfig.interrupt.typeOfService = IfxCpu_Irq_getTos(IfxCpu_getCoreIndex());
/* FIFO configuration 缓冲区配置 */
ascConfig.txBuffer = &g_ascTxBuffer;
ascConfig.txBufferSize = UART_TX_BUFFER_SIZE;
ascConfig.rxBuffer = &g_ascRxBuffer;
ascConfig.rxBufferSize = UART_RX_BUFFER_SIZE;
/* Pin configuration */
const IfxAsclin_Asc_Pins pins = {
.rx = &UART_PIN_RX,
.rxMode = IfxPort_InputMode_noPullDevice,
.tx = &UART_PIN_TX,
.txMode = IfxPort_OutputMode_pushPull,
.cts = NULL_PTR,
.ctsMode = IfxPort_InputMode_noPullDevice,
.rts = NULL_PTR,
.rtsMode = IfxPort_OutputMode_pushPull,
.pinDriver = IfxPort_PadDriver_cmosAutomotiveSpeed1
};
ascConfig.pins = &pins;
IfxAsclin_Asc_initModule(&g_ascHandle, &ascConfig); /* Initialize module with above parameters */
}
void Uart_SendStr(char* str) {
Ifx_SizeT len = (Ifx_SizeT)strlen(str);
IfxAsclin_Asc_write(&g_ascHandle, str, &len, TIME_INFINITE); /* Transmit data via TX */
}
主函数部分
主函数中经过对前面个外设的初始化配置之后,进入到while(1)循环中,这里面主要是进行LED的闪烁控制,通过ADC的值决定每次占空比变化的延时时间,一个循环周期包括LED从暗到亮,在从亮到暗,即占空比从小到大,再从大到小。
int core0_main(void)
{
IfxCpu_enableInterrupts();
/* !!WATCHDOG0 AND SAFETY WATCHDOG ARE DISABLED HERE!!
* Enable the watchdogs and service them periodically if it is required
*/
IfxScuWdt_disableCpuWatchdog(IfxScuWdt_getCpuWatchdogPassword());
IfxScuWdt_disableSafetyWatchdog(IfxScuWdt_getSafetyWatchdogPassword());
/* Wait for CPU sync event */
IfxCpu_emitEvent(&g_cpuSyncEvent);
IfxCpu_waitEvent(&g_cpuSyncEvent, 1);
Uart_Init();
Adc_Init();
Adc_Run();
Timer_Init();
PWM_Init();
uint32_t adc_val;
uint8_t delay;
uint16_t send_count = 0;
float voltage;
char ch[32];
while(1) {
// adc的范围在0-4095
for (uint8_t i = 0; i < 100; i++) {
PWM_SetDuty(i);
adc_val = Adc_GetValue();
voltage = adc_val / 4096.0 * 3.3;
delay = 2.5 * pow(2, adc_val / 1024.0); // 4095/1024=3,2的3次方=8,8*2.5=20,2的0次方=1, 1*2.5=2.5
// 相当于有4档位:0-1023 delay=2.5 1024-2047,delay=2.5*2=5
// 2048-3071,delay=2.5*4=10,3072-4095,delay=2.5*8=20
Timer_Delayms(delay);
send_count += delay;
if (send_count >= 500) {
send_count -= 500;
sprintf(ch, "%.2f", voltage);
Uart_SendStr(ch);
}
}
for (uint8_t i = 100; i > 0; i--) {
PWM_SetDuty(i);
delay = 2.5 * pow(2, adc_val / 1024.0);
Timer_Delayms(delay);
send_count += delay;
if (send_count >= 500) {
send_count -= 500;
sprintf(ch, "%.2f", voltage);
Uart_SendStr(ch);
}
}
}
return (1);
}
功能展示及说明
从图中可以看到,我们设置好串口和波特率之后,成功将ADC捕获的电压值在自己制作的电压表中显示出来。
心得体会
第一次接触英飞凌的板子,上手还不太容易,新的开发环境,许多的英文文档资料。一开始进行一系列摸索,包括环境的搭建,资料的搜集,参考例程的阅读等。在这过程中也遇到一些问题,比如参考例程中许多封装函数看的不是很明白,只能是一点一点的进入到函数的定义中去仔细研究明白,了解函数的来源,作用。慢慢地,后面时间长了就慢慢习惯了,就是慢慢吸收例程,然后参考例程去根据自己所需要的功能去改进,实现自己的想法。