中级会员
积分 384
金钱 384
注册时间 2017-5-21
在线时间 210 小时
1 金钱
本帖最后由 tanjing2017 于 2017-12-18 16:50 编辑
大家好,最近学了原子哥的输入捕获实验,然后自己用FreeRTOS + HAL库做了一遍,有一些疑问请教一下。
一、原子哥的案例简介。
我看的是原子哥HAL库的案例,大体思路是一个定时器输出PWM,每10ms改一次占空比,然后另一个定时器捕获高电平,每10ms输出一次结果。
主要的代码及输出效果图如下:
[mw_shl_code=c,true]while(1)
{
delay_ms(10);
TIM_SetTIM1Compare1(TIM_GetTIM1Capture1()+1);
if(TIM_GetTIM1Capture1()==499)TIM_SetTIM1Compare1(0);
if(TIM5CH1_CAPTURE_STA&0X80) //成功捕获到了一次高电平
{
temp=TIM5CH1_CAPTURE_STA&0X3F;
temp*=65536; //溢出时间总和
temp+=TIM5CH1_CAPTURE_VAL; //得到总的高电平时间
printf("HIGH:%lld us\r\n",temp);//打印总的高点平时间
TIM5CH1_CAPTURE_STA=0; //开启下一次捕获
}
}[/mw_shl_code]
二、我自己的代码A
我仿照原子哥的思路,自己做了下代码。
代码编辑方式是:用FreeRTOS的任务通知模拟事件标志组,在定时器5的更新中断和捕获中断里分别发送任务通知到TIM_IC_Task函数,在这个函数里处理相应的操作。然后在另外一个函数High_Out_Task里每10ms改变一个PWM的占空比,然后输出对应的捕获结果。
但是这种代码的写法输出有点问题,输出的结果比价乱,如下图:
my1
方案A的主要代码如下:
[mw_shl_code=c,true]/********************************************************************************
*函数名称:TIM_IC_Task
*函数句柄:TIM_IC_Task_Handle
*输入参数:无
*返回值 :无
*函数功能:输入捕获实验函数,并使用任务通知的事件标志组。
*备注 :此实验输入捕获功能,是实现高电平的捕获。
*日期 :2017/12/10
********************************************************************************/
void TIM_IC_Task(void const * argument)
{
//static uint32_t temp = 0;
static uint32_t NotifyValue = 0;
BaseType_t Info = 0;
while(1)
{
Info = xTaskNotifyWait( (uint32_t ) 0x00,//进入函数的时候不清除任务bit
(uint32_t ) 0xffffffff,//退出函数的时候清除所有的bit
(uint32_t * ) &NotifyValue,//保存任务通知值
(TickType_t ) portMAX_DELAY );//阻塞时间
if(Info == pdPASS )//如果任务通知获取成功
{
//printf("111\r\n");
if((NotifyValue & Event_1) != 0 )//如果Event_1(捕获中断)的事件发生了
{
//能运行到此,说明已经触发了输入捕获中断
if((TIM5CH2_CAPTURE_STA&0X80)==0)//如果还未成功捕获
{
if(TIM5CH2_CAPTURE_STA&0X40)//如果之前已经标记了上升沿被捕获,说明此次捕获的是低电平
{
TIM5CH2_CAPTURE_STA |= 0X80; //标记成功捕获到一次高电平脉宽
TIM5CH2_CAPTURE_VAL = HAL_TIM_ReadCapturedValue(&htim5,TIM_CHANNEL_2);//获取当前的捕获值.
TIM_RESET_CAPTUREPOLARITY(&htim5,TIM_CHANNEL_2); //一定要先清除原来的设置!!
TIM_SET_CAPTUREPOLARITY(&htim5,TIM_CHANNEL_2,TIM_ICPOLARITY_RISING);//配置TIM5通道2上升沿捕获
}
else//还未开始捕获,这次是第一次捕获,捕获的是上升沿
{
TIM5CH2_CAPTURE_STA = 0; //清空模拟状态寄存器
TIM5CH2_CAPTURE_VAL = 0; //清空数据缓存变量
TIM5CH2_CAPTURE_STA |= 0X40; //标记捕获到了上升沿
__HAL_TIM_DISABLE(&htim5); //关闭定时器5
__HAL_TIM_SET_COUNTER(&htim5,0); //往定时器5的计数器里写0
TIM_RESET_CAPTUREPOLARITY(&htim5,TIM_CHANNEL_2); //一定要先清除原来的设置!!
TIM_SET_CAPTUREPOLARITY(&htim5,TIM_CHANNEL_2,TIM_ICPOLARITY_FALLING);//定时器5通道2设置为下降沿捕获
__HAL_TIM_ENABLE(&htim5); //使能定时器5
}
}
}
else if ((NotifyValue & Event_2) != 0)//如果Event_2(更新中断)的事件发生了
{
//能运行到此,说明已经触发了定时器5的更新(溢出)中断
if((TIM5CH2_CAPTURE_STA & 0X80) == 0) //还未成功捕获
{
if(TIM5CH2_CAPTURE_STA & 0X40) //已经捕获到高电平了
{
if((TIM5CH2_CAPTURE_STA & 0X3F) == 0X3F) //高电平太长了
{
TIM5CH2_CAPTURE_STA |= 0X80; //标记成功捕获了一次
TIM5CH2_CAPTURE_VAL = 0XFFFF;
}
else
TIM5CH2_CAPTURE_STA ++;//正常记录溢出中断的次数
}
}
}
// if(TIM5CH2_CAPTURE_STA & 0X80)//如果已经标记成功捕获到了一次高电平(完成捕获)
// {
// temp = TIM5CH2_CAPTURE_STA & 0X3F;
// temp *= 50000; //溢出时间总和(这里需要看定时器的重装载值是多少)
// temp += TIM5CH2_CAPTURE_VAL; //得到总的高电平时间
// //temp /= 1000;
// printf("检测到高电平脉冲:%d us.\r\n",temp);//打印总的高点平时间
// TIM5CH2_CAPTURE_STA=0; //开启下一次捕获
// }
}
// vTaskDelay(10);
// TIM_SetTIM3Compare1(TIM_GetTIM3Capture1()+1);
// if(TIM_GetTIM3Capture1()==499)TIM_SetTIM3Compare1(0);
}
}
/********************************************************************************
*函数名称:High_Out_Task
*函数句柄:High_Out_Task_Handle
*输入参数:无
*返回值 :无
*函数功能:为输入捕获实验提供变化的高电平变化(GPIO翻转)
*备注 :
*日期 :2017/12/10
********************************************************************************/
void High_Out_Task(void const * argument)
{
//uint32_t C_time = 1;
static uint32_t temp = 0;
while(1)
{
// //HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_12);
// PBout(12) = ~PBout(12);
// C_time += 1;
// if(C_time >= 500) C_time = 1;
// delay_ms(C_time);
vTaskDelay(10);
TIM_SetTIM3Compare1(TIM_GetTIM3Capture1()+1);
if(TIM_GetTIM3Capture1()==499)TIM_SetTIM3Compare1(0);
if(TIM5CH2_CAPTURE_STA & 0X80)//如果已经标记成功捕获到了一次高电平(完成捕获)
{
temp = TIM5CH2_CAPTURE_STA & 0X3F;
temp *= 50000; //溢出时间总和(这里需要看定时器的重装载值是多少)
temp += TIM5CH2_CAPTURE_VAL; //得到总的高电平时间
//temp /= 1000;
printf("检测到高电平脉冲:%d us.\r\n",temp);//打印总的高点平时间
TIM5CH2_CAPTURE_STA=0; //开启下一次捕获
}
}
}[/mw_shl_code]
三、我自己的代码B
方案B的思路是把方案A里的两个中断处理部分的函数分开,用两个二值信号量函数来处理。其余部分都一样。
结果是输出的结果比A好了一点,但是输出结果仍然有点乱。如下图:
my2
my22
方案B的主要代码如下:
[mw_shl_code=c,true]/********************************************************************************
*函数名称:IC_Task
*函数句柄:IC_Task_Handle
*输入参数:无
*返回值 :无
*函数功能:输入捕获实验中,处理数据寄存器溢出(更新)的中断回调函数发送任务通知,此函数接受任务通知,并处理。
*备注 :此实验输入捕获功能,是实现高电平的捕获,并计时。
*日期 :2017/12/10
********************************************************************************/
void IC_Task(void const * argument)
{
while(1)
{
ulTaskNotifyTake(pdTRUE,portMAX_DELAY);//等待任务通知
//能运行到此,说明已经触发了定时器5的更新(溢出)中断
if((TIM5CH2_CAPTURE_STA & 0X80) == 0) //还未成功捕获
{
if(TIM5CH2_CAPTURE_STA & 0X40) //已经捕获到高电平了
{
if((TIM5CH2_CAPTURE_STA & 0X3F) == 0X3F) //高电平太长了
{
TIM5CH2_CAPTURE_STA |= 0X80; //标记成功捕获了一次
TIM5CH2_CAPTURE_VAL = 0XFFFF;
//xTaskNotifyGive(Handle_IC_Val_Task_Handle);
}
else
TIM5CH2_CAPTURE_STA ++;//正常记录溢出中断的次数
}
}
}
}
/********************************************************************************
*函数名称:IC_Task2
*函数句柄:IC_Task2_Handle
*输入参数:无
*返回值 :无
*函数功能:输入捕获实验中,触发捕获中断时,中断函数发送任务通知,此函数接受任务通知,并处理。
*备注 :此实验输入捕获功能,是实现高电平的捕获,并计时。
*日期 :2017/12/10
********************************************************************************/
void IC_Task2(void const * argument)
{
while(1)
{
ulTaskNotifyTake(pdTRUE,portMAX_DELAY);//等待任务通知
//能运行到此,说明已经触发了输入捕获中断
if((TIM5CH2_CAPTURE_STA&0X80)==0)//如果还未成功捕获
{
if(TIM5CH2_CAPTURE_STA&0X40)//如果之前已经标记了上升沿被捕获,说明此次捕获的是低电平
{
TIM5CH2_CAPTURE_STA |= 0X80; //标记成功捕获到一次高电平脉宽
TIM5CH2_CAPTURE_VAL = HAL_TIM_ReadCapturedValue(&htim5,TIM_CHANNEL_2);//获取当前的捕获值.
TIM_RESET_CAPTUREPOLARITY(&htim5,TIM_CHANNEL_2); //一定要先清除原来的设置!!
TIM_SET_CAPTUREPOLARITY(&htim5,TIM_CHANNEL_2,TIM_ICPOLARITY_RISING);//配置TIM5通道2上升沿捕获
//xTaskNotifyGive(Handle_IC_Val_Task_Handle);
}
else//还未开始捕获,这次是第一次捕获,捕获的是上升沿
{
TIM5CH2_CAPTURE_STA = 0; //清空模拟状态寄存器
TIM5CH2_CAPTURE_VAL = 0; //清空数据缓存变量
TIM5CH2_CAPTURE_STA |= 0X40; //标记捕获到了上升沿
__HAL_TIM_DISABLE(&htim5); //关闭定时器5
__HAL_TIM_SET_COUNTER(&htim5,0); //往定时器5的计数器里写0
TIM_RESET_CAPTUREPOLARITY(&htim5,TIM_CHANNEL_2); //一定要先清除原来的设置!!
TIM_SET_CAPTUREPOLARITY(&htim5,TIM_CHANNEL_2,TIM_ICPOLARITY_FALLING);//定时器5通道2设置为下降沿捕获
__HAL_TIM_ENABLE(&htim5); //使能定时器5
}
}
}
}
/********************************************************************************
*函数名称:High_Out_Task
*函数句柄:High_Out_Task_Handle
*输入参数:无
*返回值 :无
*函数功能:为输入捕获实验提供变化的高电平变化(GPIO翻转)
*备注 :
*日期 :2017/12/10
********************************************************************************/
void High_Out_Task(void const * argument)
{
// uint32_t C_time = 1;
// while(1)
// {
// //HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_12);
// PBout(12) = ~PBout(12);
// C_time += 1;
// if(C_time >= 300) C_time = 1;
// delay_ms(C_time);
// }
static uint32_t temp = 0;
while(1)
{
delay_ms(10);
TIM_SetTIM3Compare1(TIM_GetTIM3Capture1()+1);
if(TIM_GetTIM3Capture1()==499)TIM_SetTIM3Compare1(0);
if(TIM5CH2_CAPTURE_STA & 0X80) //成功捕获到了一次高电平
{
temp = TIM5CH2_CAPTURE_STA & 0X3F;
temp *= 65535; //溢出时间总和(这里需要看定时器的重装载值是多少)
temp += TIM5CH2_CAPTURE_VAL; //得到总的高电平时间
//temp /= 1000;
printf("检测到高电平脉冲:%d us.\r\n",temp);//打印总的高点平时间
TIM5CH2_CAPTURE_STA=0; //开启下一次捕获
}
}
}[/mw_shl_code]
四、我自己的代码C
最后我把更新中断和捕获中断的处理内容直接写到了回调函数里,FreeRTOS的任务只有一个改变占空比和输出结果的函数。
这一次的输出效果和原子哥的一样了。
如下图:
my3
代码C的主要内容如下:
[mw_shl_code=c,true]void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
//BaseType_t TIM5_pxHigher_2;
if(htim == &htim5)
{
// /* 任务通知模拟事件标志组 */
// /* 发送任务通知函数 */
// xTaskNotifyFromISR( (TaskHandle_t ) TIM_IC_Task_Handle, //任务通知句柄
// (uint32_t ) Event_1 , //任务通知值
// (eNotifyAction ) eSetBits, //任务通知更新方法,此处是更新指定的bit
// (BaseType_t * ) &TIM5_pxHigher_2); //标记退出函数后是否进行任务切换
//
// portYIELD_FROM_ISR(TIM5_pxHigher_2);//判断是否需要切换任务
//能运行到此,说明已经触发了输入捕获中断
if((TIM5CH2_CAPTURE_STA&0X80)==0)//如果还未成功捕获
{
if(TIM5CH2_CAPTURE_STA&0X40)//如果之前已经标记了上升沿被捕获,说明此次捕获的是低电平
{
TIM5CH2_CAPTURE_STA |= 0X80; //标记成功捕获到一次高电平脉宽
TIM5CH2_CAPTURE_VAL = HAL_TIM_ReadCapturedValue(&htim5,TIM_CHANNEL_2);//获取当前的捕获值.
TIM_RESET_CAPTUREPOLARITY(&htim5,TIM_CHANNEL_2); //一定要先清除原来的设置!!
TIM_SET_CAPTUREPOLARITY(&htim5,TIM_CHANNEL_2,TIM_ICPOLARITY_RISING);//配置TIM5通道2上升沿捕获
}
else//还未开始捕获,这次是第一次捕获,捕获的是上升沿
{
TIM5CH2_CAPTURE_STA = 0; //清空模拟状态寄存器
TIM5CH2_CAPTURE_VAL = 0; //清空数据缓存变量
TIM5CH2_CAPTURE_STA |= 0X40; //标记捕获到了上升沿
__HAL_TIM_DISABLE(&htim5); //关闭定时器5
__HAL_TIM_SET_COUNTER(&htim5,0); //往定时器5的计数器里写0
TIM_RESET_CAPTUREPOLARITY(&htim5,TIM_CHANNEL_2); //一定要先清除原来的设置!!
TIM_SET_CAPTUREPOLARITY(&htim5,TIM_CHANNEL_2,TIM_ICPOLARITY_FALLING);//定时器5通道2设置为下降沿捕获
__HAL_TIM_ENABLE(&htim5); //使能定时器5
}
}
}
}
/* USER CODE END 4 */
/**
* @brief Period elapsed callback in non blocking mode
* @note This function is called when TIM6 interrupt took place, inside
* HAL_TIM_IRQHandler(). It makes a direct call to HAL_IncTick() to increment
* a global variable "uwTick" used as application time base.
* @param htim : TIM handle
* @retval None
*/
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
/* USER CODE BEGIN Callback 0 */
//BaseType_t TIM5_pxHigher_1;
/* USER CODE END Callback 0 */
if (htim->Instance == TIM6) {
HAL_IncTick();
}
/* USER CODE BEGIN Callback 1 */
/*********************************************
定时器回调函数
*******************************************/
if(htim == &htim7)
{
RunTimeCounterValue++;
}
if(htim == &htim5)
{
// /* 任务通知模拟事件标志组 */
// /* 发送任务通知函数 */
// xTaskNotifyFromISR( (TaskHandle_t ) TIM_IC_Task_Handle, //任务通知句柄
// (uint32_t ) Event_2 , //任务通知值
// (eNotifyAction ) eSetBits, //任务通知更新方法,此处是更新指定的bit
// (BaseType_t * ) &TIM5_pxHigher_1); //标记退出函数后是否进行任务切换
//
// portYIELD_FROM_ISR(TIM5_pxHigher_1);//判断是否需要切换任务
//能运行到此,说明已经触发了定时器5的更新(溢出)中断
if((TIM5CH2_CAPTURE_STA & 0X80) == 0) //还未成功捕获
{
if(TIM5CH2_CAPTURE_STA & 0X40) //已经捕获到高电平了
{
if((TIM5CH2_CAPTURE_STA & 0X3F) == 0X3F) //高电平太长了
{
TIM5CH2_CAPTURE_STA |= 0X80; //标记成功捕获了一次
TIM5CH2_CAPTURE_VAL = 0XFFFF;
}
else
TIM5CH2_CAPTURE_STA ++;//正常记录溢出中断的次数
}
}
}
/* USER CODE END Callback 1 */
}[/mw_shl_code]
[mw_shl_code=c,true]/********************************************************************************
*函数名称:High_Out_Task
*函数句柄:High_Out_Task_Handle
*输入参数:无
*返回值 :无
*函数功能:为输入捕获实验提供变化的高电平变化(GPIO翻转)
*备注 :
*日期 :2017/12/10
********************************************************************************/
void High_Out_Task(void const * argument)
{
//uint32_t C_time = 1;
static uint32_t temp = 0;
while(1)
{
// //HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_12);
// PBout(12) = ~PBout(12);
// C_time += 1;
// if(C_time >= 500) C_time = 1;
// delay_ms(C_time);
vTaskDelay(10);
TIM_SetTIM3Compare1(TIM_GetTIM3Capture1()+1);
if(TIM_GetTIM3Capture1()==499)TIM_SetTIM3Compare1(0);
if(TIM5CH2_CAPTURE_STA & 0X80)//如果已经标记成功捕获到了一次高电平(完成捕获)
{
temp = TIM5CH2_CAPTURE_STA & 0X3F;
temp *= 50000; //溢出时间总和(这里需要看定时器的重装载值是多少)
temp += TIM5CH2_CAPTURE_VAL; //得到总的高电平时间
//temp /= 1000;
printf("检测到高电平脉冲:%d us.\r\n",temp);//打印总的高点平时间
TIM5CH2_CAPTURE_STA=0; //开启下一次捕获
}
}
}[/mw_shl_code]
五、我的疑问
问题1:我自己的代码A/B输出有问题,是因为我的代码组织思路有问题吗?还是说我的FreeRTOS的任务通知功能使用的思路有问题?
问题2:除了代码C把任务处理内容直接放在中断回调函数里的方法,还有其他什么方法能实现原子哥正常输出的结果吗?
希望有知道的前辈指点一下。谢谢。
我来回答