OpenEdv-开源电子网

 找回密码
 立即注册

扫一扫,访问微社区

正点原子新作:阿波罗STM32F767&F429&探索者STM32F4开发板&赶快来下载资料哦。

查看: 356|回复: 2

输入捕获实验的疑问(FreeRTOS + HAL库)

[复制链接]

  离线 

4

主题

7

帖子

0

精华

初级会员

Rank: 2

积分
145
金钱
145
注册时间
2017-5-21
在线时间
75 小时
发表于 2017-12-18 16:50:18 | 显示全部楼层 |阅读模式
1金钱
本帖最后由 tanjing2017 于 2017-12-18 16:50 编辑

大家好,最近学了原子哥的输入捕获实验,然后自己用FreeRTOS + HAL库做了一遍,有一些疑问请教一下。
一、原子哥的案例简介。
我看的是原子哥HAL库的案例,大体思路是一个定时器输出PWM,每10ms改一次占空比,然后另一个定时器捕获高电平,每10ms输出一次结果。
主要的代码及输出效果图如下:
[C] 纯文本查看 复制代码
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;          //开启下一次捕获
                }
    }


2017-12-12_210508.png

二、我自己的代码A
我仿照原子哥的思路,自己做了下代码。
代码编辑方式是:用FreeRTOS的任务通知模拟事件标志组,在定时器5的更新中断和捕获中断里分别发送任务通知到TIM_IC_Task函数,在这个函数里处理相应的操作。然后在另外一个函数High_Out_Task里每10ms改变一个PWM的占空比,然后输出对应的捕获结果。
但是这种代码的写法输出有点问题,输出的结果比价乱,如下图:

my1

my1


方案A的主要代码如下:
[C] 纯文本查看 复制代码
/********************************************************************************
*函数名称: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;          //开启下一次捕获
                        }
        }
}


三、我自己的代码B
方案B的思路是把方案A里的两个中断处理部分的函数分开,用两个二值信号量函数来处理。其余部分都一样。
结果是输出的结果比A好了一点,但是输出结果仍然有点乱。如下图:

my2

my2

my22

my22


方案B的主要代码如下:
[C] 纯文本查看 复制代码
/********************************************************************************
*函数名称: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;          //开启下一次捕获
                }
        }
}



四、我自己的代码C
最后我把更新中断和捕获中断的处理内容直接写到了回调函数里,FreeRTOS的任务只有一个改变占空比和输出结果的函数。
这一次的输出效果和原子哥的一样了。
如下图:

my3

my3


代码C的主要内容如下:
[C] 纯文本查看 复制代码
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
  * [url=home.php?mod=space&uid=60778]@note[/url]   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 */
}

[C] 纯文本查看 复制代码
/********************************************************************************
*函数名称: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;          //开启下一次捕获
                        }
        }
}



五、我的疑问
问题1:我自己的代码A/B输出有问题,是因为我的代码组织思路有问题吗?还是说我的FreeRTOS的任务通知功能使用的思路有问题?
问题2:除了代码C把任务处理内容直接放在中断回调函数里的方法,还有其他什么方法能实现原子哥正常输出的结果吗?

希望有知道的前辈指点一下。谢谢。

回复

使用道具 举报

  离线 

4

主题

7

帖子

0

精华

初级会员

Rank: 2

积分
145
金钱
145
注册时间
2017-5-21
在线时间
75 小时
 楼主| 发表于 2017-12-18 16:50:19 | 显示全部楼层
回复

使用道具 举报

  离线 

485

主题

9万

帖子

30

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
120540
金钱
120540
注册时间
2010-12-1
在线时间
965 小时
发表于 2017-12-19 00:35:29 | 显示全部楼层
帮顶
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则




QQ|联系我们|手机版|官方淘宝店|新浪微博|微信公众平台|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2018-1-22 12:13

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表
/* */