测试芯片:STM32L151RCT6
功能要求:
? ? ? ? 通过外部触发,使能脉冲生成同时使能捕获定时器抓取所生产脉冲的上升沿,然后计算从触发到捕获到上升沿的时间。在测量飞行时间的应用场景,例如超声波测距,超声波测流量等场景对时间测量要求比较高,此时就可以用此方法来做。
实现代码如下:
/** 全局变量 */
uint32_t Cap_Cnt; /*!< 存放捕获值 */
uint8_t Status; /*!< 捕获状态 */
uint8_t Process; /*!< 状态切换 */
/**
* 说明 : 产生单脉冲,用于捕获定时器捕获(此定时器开始后会自动关闭)
* 参数 : 无
* 返回 : 无
*/
void Pulse_Timer_Init1(void)
{
LL_TIM_OC_InitTypeDef TIM_OC_InitStruct = {0};
LL_GPIO_InitTypeDef GPIO_InitStruct = {0};
LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOA);
LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_TIM3);
/** PWM_OUT->PA6->TIM3_CH1 */
GPIO_InitStruct.Pin=LL_GPIO_PIN_6;
GPIO_InitStruct.Mode=LL_GPIO_MODE_ALTERNATE;
GPIO_InitStruct.Speed=LL_GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.Pull=LL_GPIO_PULL_DOWN;
GPIO_InitStruct.Alternate=LL_GPIO_AF_2;
LL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/** Cap_IN->PA7->TIM3_CH2 */
GPIO_InitStruct.Pin=LL_GPIO_PIN_7;
LL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/** 定时器时基配置(Fpwm=32M/32/100=10KHz) */
LL_TIM_SetPrescaler(TIM3, 31);
LL_TIM_SetAutoReload(TIM3, 599); /*!< 单脉冲周期600us */
LL_TIM_SetCounterMode(TIM3, LL_TIM_COUNTERMODE_UP);
LL_TIM_EnableARRPreload(TIM3);
LL_TIM_SetClockSource(TIM3, LL_TIM_CLOCKSOURCE_INTERNAL);
/** 使能单脉冲模式 */
LL_TIM_SetOnePulseMode(TIM3, LL_TIM_ONEPULSEMODE_SINGLE);
/** 触发设置 */
/* 通道映射:CH2->TI2 */
LL_TIM_IC_SetActiveInput(TIM3, LL_TIM_CHANNEL_CH2, LL_TIM_ACTIVEINPUT_DIRECTTI);
/* 选择触发极性:上升沿 */
LL_TIM_IC_SetPolarity(TIM3, LL_TIM_CHANNEL_CH2, LL_TIM_IC_POLARITY_RISING);
/* 设置触发输入通道 */
LL_TIM_SetTriggerInput(TIM3, LL_TIM_TS_TI2FP2);
/* 设置从机模式:触发模式 */
LL_TIM_SetSlaveMode(TIM3, LL_TIM_SLAVEMODE_TRIGGER);
/** 输出模式配置:CH1 */
TIM_OC_InitStruct.OCMode = LL_TIM_OCMODE_PWM1;
TIM_OC_InitStruct.OCState = LL_TIM_OCSTATE_DISABLE;
TIM_OC_InitStruct.CompareValue = 400; /*!< 单脉冲低电平时间400us */
TIM_OC_InitStruct.OCPolarity = LL_TIM_OCPOLARITY_LOW;
LL_TIM_OC_Init(TIM3, LL_TIM_CHANNEL_CH1, &TIM_OC_InitStruct);
LL_TIM_OC_EnablePreload(TIM3, LL_TIM_CHANNEL_CH1);
LL_TIM_CC_EnableChannel(TIM3, LL_TIM_CHANNEL_CH1);
LL_TIM_GenerateEvent_UPDATE(TIM3); /*!< 记得调用这个函数 */
}
/**
* 说明 : 用来捕获上升沿
* 参数 : 无
* 返回 : 无
*/
void Cap_Timer_Init(void)
{
LL_TIM_IC_InitTypeDef TIM_IC_InitStruct = {0};
LL_GPIO_InitTypeDef GPIO_InitStruct = {0};
LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOA);
LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_TIM2);
/** CAP_CH1->PA0->TIM2_CH1 */
GPIO_InitStruct.Pin=LL_GPIO_PIN_0;
GPIO_InitStruct.Mode=LL_GPIO_MODE_ALTERNATE;
GPIO_InitStruct.Speed=LL_GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.Pull=LL_GPIO_PULL_DOWN;
GPIO_InitStruct.Alternate=LL_GPIO_AF_1;
LL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/** Cap_IN->PA1->TIM2_CH2 */
GPIO_InitStruct.Pin=LL_GPIO_PIN_1;
LL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/** 定时器时基配置 */
LL_TIM_SetPrescaler(TIM2, 0);
LL_TIM_SetAutoReload(TIM2, 65535);
LL_TIM_SetCounterMode(TIM2, LL_TIM_COUNTERMODE_UP);
LL_TIM_EnableARRPreload(TIM2);
LL_TIM_SetClockSource(TIM2, LL_TIM_CLOCKSOURCE_INTERNAL);
/** 触发设置 */
/* 通道映射:CH2->TI2 */
LL_TIM_IC_SetActiveInput(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_ACTIVEINPUT_DIRECTTI);
/* 选择触发极性:上升沿 */
LL_TIM_IC_SetPolarity(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_IC_POLARITY_RISING);
/* 设置触发输入通道 */
LL_TIM_SetTriggerInput(TIM2, LL_TIM_TS_TI2FP2);
/* 设置从机模式:触发模式 */
LL_TIM_SetSlaveMode(TIM2, LL_TIM_SLAVEMODE_TRIGGER);
/** 捕获通道配置 */
TIM_IC_InitStruct.ICActiveInput = LL_TIM_ACTIVEINPUT_DIRECTTI;
TIM_IC_InitStruct.ICFilter = LL_TIM_IC_FILTER_FDIV1;
TIM_IC_InitStruct.ICPolarity = LL_TIM_IC_POLARITY_RISING;
TIM_IC_InitStruct.ICPrescaler = LL_TIM_ICPSC_DIV1;
LL_TIM_IC_Init(TIM2, LL_TIM_CHANNEL_CH1, &TIM_IC_InitStruct);
/** 使能通道 */
LL_TIM_CC_EnableChannel(TIM2, LL_TIM_CHANNEL_CH1);
/** 配置中断 */
LL_TIM_EnableIT_CC1(TIM2);
LL_TIM_EnableIT_UPDATE(TIM2);
NVIC_EnableIRQ(TIM2_IRQn);
NVIC_SetPriority(TIM2_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 2, 0));
LL_TIM_GenerateEvent_UPDATE(TIM2);
LL_TIM_ClearFlag_CC1(TIM2);
LL_TIM_ClearFlag_UPDATE(TIM2);
}
/**
* 说明 : 定时器2中断服务函数
* 参数 : 无
* 返回 : 无
*/
void TIM2_IRQHandler(void)
{
/** 捕获中断,捕获成功后失能定时器,等待下次捕获 */
if(LL_TIM_IsActiveFlag_CC1(TIM2) != RESET)
{
Cap_Cnt = LL_TIM_IC_GetCaptureCH1(TIM2);
LL_TIM_DisableCounter(TIM2);
Status=0xAA;
}
/** 更新中断,产生更新中断说明捕获超时,失能定时器,等待下次捕获 */
if(LL_TIM_IsActiveFlag_UPDATE(TIM2) != RESET)
{
LL_TIM_ClearFlag_UPDATE(TIM2);
LL_TIM_DisableCounter(TIM2);
Status=0xFF;
}
}
/*
* 说明: 初始化开发板控制IO(这里用PB6来实现触发)
* 参数: 无
* 参数: 无
*/
void EVE_GPIO_Init(void)
{
LL_GPIO_InitTypeDef GPIO_InitStruct={0};
LL_EXTI_InitTypeDef EXTI_InitStruct = {0};
/* GPIO Ports Clock Enable */
LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOC);
LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOA);
LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOB);
/* 置位 */
LL_GPIO_SetOutputPin(GPIOA, LL_GPIO_PIN_8);
LL_GPIO_ResetOutputPin(GPIOB, LL_GPIO_PIN_6);
LL_GPIO_SetOutputPin(GPIOC, LL_GPIO_PIN_8);
LL_GPIO_SetOutputPin(GPIOC, LL_GPIO_PIN_9);
/* LED1-> PA8, LED2->PC8, LED3->PC9, PB6 */
GPIO_InitStruct.Pin = LL_GPIO_PIN_8;
GPIO_InitStruct.Mode = LL_GPIO_MODE_OUTPUT;
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
LL_GPIO_Init(GPIOA, &GPIO_InitStruct);
GPIO_InitStruct.Pin = LL_GPIO_PIN_6;
LL_GPIO_Init(GPIOB, &GPIO_InitStruct);
GPIO_InitStruct.Pin = LL_GPIO_PIN_8|LL_GPIO_PIN_9;
LL_GPIO_Init(GPIOC, &GPIO_InitStruct);
}
/*
* 说明: 用户线程(重复捕获,测试一致性),用户主循环调用
* 参数: 无
* 参数: 无
*/
void App_Handle(void)
{
switch(Process)
{
case 0:
Pulse_Timer_Init();
Cap_Timer_Init();
Process = 1;
break;
case 1:
/** 拉高PB6此时定时器同步使能并开始工作 */
LL_mDelay(1);
PB6_H();
Process = 2;
break;
case 2:
/** 等待捕获 */
if(Status == 0xAA)
{
printf("%d\n", Cap_Cnt);
/** 刷新定时器寄存器 */
LL_TIM_DisableUpdateEvent(TIM2);
LL_TIM_GenerateEvent_UPDATE(TIM2);
LL_TIM_EnableUpdateEvent(TIM2);
LL_TIM_GenerateEvent_UPDATE(TIM3);
PB6_L();
Status = 0;
Process = 3;
}
break;
case 3:
LED1_Toggle();
LL_mDelay(100);
Process = 1;
break;
}
}
连线:
PB6->PA7 PB6->PA1
PA6->PA0
测试结果:
串口打印值:
?时序波形如下:
?从上述结果可以看出用此方法计时,准确度和一致性还是很好的。
|