即日起在codingBlog上分享您的技术经验即可获得积分,积分可兑换现金哦。

CMSIS里的FreeRTOS第一篇之任务通知:osSignalSet与osSignalWait

编程语言 lh2008xp 11℃ 0评论

FreeRTOS从V8.2.0版本开始提供任务通知这个功能,每个任务多有一个32位的通知值,任务通知比二进制信号量方式解除阻塞任务速率提升45%,并且无需创建队列更加省内存。

osSignalSetosSignalWait是CMSIS中的任务通知,在文档cmsis_os.c中定义;


osSignalSet是对FreeRTOS中的xTaskNotifyFromISR和xTaskNotify的封装,该函数对函数所处环境进行了判断,当处于中断回调函数中时使用xTaskNotifyFromISR,否则使用xTaskNotify,这样使用者无需关心通知的来源直接使用;

osSignalWait是对xTaskNotifyWait的封装,该函数不能用于中断中否则返回osErrorISR;

xTaskNotifyFromISR/xTaskNotify和xTaskNotifyWait是FreeRTOS中任务通知,更准确的说是任务通知的一部分用法,在文档task.c中定义;想进一步了解其他任务通知函数用法比如ulTaskNotifyTake和xTaskNotifyGive等,请看源码task.c。


osSignalSet发送通知时发送通知并带通知值,osSignalWait等待通知(接收通知值时清零和等待超时时长);


任务通知的使用限制条件:

1.只能有一个任务接收通知事件。

2.接收通知的任务可以因为等待通知而进入阻塞状态,但是发送通知的任务即便不能立即完成通知发送也不能进入阻塞状态。

通知的发送可以从多个任务或者多个中断中发出,但是等待任务通知函数只能有一个。这个地方可能有的人会理解错误,以为只能有一个等待任务通知,其实这里说的是一个任务中只能有一个等待任务通知函数,其他任务中还可以有等待任务通知函数;


发送通知

函数描述:


/***************************  Signal Management ********************************/
/**
* @brief  Set the specified Signal Flags of an active thread.
* @param  thread_id     thread ID obtained by \ref osThreadCreate or \ref osThreadGetId.
* @param  signals       specifies the signal flags of the thread that should be set.
* @retval  osOK if successful, osErrorOS if failed .
* @note   MUST REMAIN UNCHANGED: \b osSignalSet shall be consistent in every CMSIS-RTOS.
*/
int32_t osSignalSet (osThreadId thread_id, int32_t signal)

参数描述:

thread_id是任务句柄,任务通知的目的地任务;

signal任务通知值,一般按位操作数字,一个事件用一个类似这样的值表示(即0x0001,0x0002,0x0004,0x0008,0x0010……);

因为函数中使用的是xTaskNotify( thread_id, (uint32_t)signal, eSetBits),eSetBits表示更新通知的方法为被通知的任务的通知值按位或上signal。

返回值:

发送成功时返回osOK,失败时返回osErrorOS

用法举例:

void ComponentCtrlAreaHandle(uint16_t CtrlAreaReg)
{
 uint16_t reg_offset = CtrlAreaReg-COMPONENT_CTRL_AREA_START_ADDR;
 static uint32_t CtrlValidFlag,legHandleValidFlag=0;
 if(reg_offset>=COMPONENT_CTRL_AREA_SIZE) return;
 uint16_t reg_val=componentCtrlArea[reg_offset];
 switch(CtrlAreaReg)
 {

  case COMPONENT_CTRL_REGISTER_MOTOR_HAND_LEFT_HIGH://0x1016
   CtrlValidFlag|=0x0400;
   break;
  case COMPONENT_CTRL_REGISTER_MOTOR_HAND_LEFT_LOW://0x1017
   if(CtrlValidFlag&0x0400) 
   {
    CtrlValidFlag&=~0x0400;
    osSignalSet(HandMotorControHandle,0x01); //发送通知到任务到HandMotorControHandle,信号为0x01
   }
   break;
  case COMPONENT_CTRL_REGISTER_MOTOR_HAND_RIGHT_HIGH://0x1018
   CtrlValidFlag|=0x0800;
   break;
  case COMPONENT_CTRL_REGISTER_MOTOR_HAND_RIGHT_LOW://0x1019
   if(CtrlValidFlag&0x0800) 
   {
    CtrlValidFlag&=~0x0800;
    osSignalSet(HandMotorControHandle,0x02);//发送通知到任务HandMotorContorHandle,信号为0x02

   }
   break;
  default:
   break;
 }
}


接收通知

参数描述:

/**
* @brief  Wait for one or more Signal Flags to become signaled for the current \b RUNNING thread.
* @param  signals   wait until all specified signal flags set or 0 for any single signal flag.
* @param  millisec  timeout value or 0 in case of no time-out.
* @retval  event flag information or error code.
* @note   MUST REMAIN UNCHANGED: \b osSignalWait shall be consistent in every CMSIS-RTOS.
*/
osEvent osSignalWait (int32_t signals, uint32_t millisec)

参数描述:

signals接收完成后等待被清零的数据位(原函数是ulBitsToClearOnExit),也是等待被接收的数据置位的数据位!

比如一个任务状态切换,由四个不同的事件分别影响着,通知osSignalSet中的signal分别为0x0001,0x0002,0x0004,0x0008,那么接收端osSignalWait 中的signals应该为0x000F(0x0001|0x0002|0x0004|0x0008)

millisec为等待超时时间,单位为ms,已将系统节拍时钟转换成毫秒;





返回值:

系统正常运行,没有错误或没有事件发生,则返回osOK;

等待通知超时,则返回osEventTimeout;

在不该使用的地方调用了函数,即中断调用了该函数,则返回osErrorISR;

接收值超过范围,则返回osErrorValue;

接收到任务通知,则返回osEventSignal;

系统故障,则返回osErrorOS;

用法举例:

osThreadId HandMotorControHandle;
/* StartHandMotorControl function */
void StartHandMotorControl(void const * argument)
{
  /* USER CODE BEGIN StartHandMotorControl */
  /* Infinite loop */
 osEvent event;
 uint16_t *LeftHandCtrlAngle = &componentCtrlArea[COMPONENT_CTRL_REGISTER_MOTOR_HAND_LEFT_HIGH-COMPONENT_CTRL_AREA_START_ADDR];
 uint16_t *RightHandCtrlAngle = LeftHandCtrlAngle+2;
 for(;;)
 {
  event=osSignalWait(0x03,100); //待接收的数据位是0x01和0x10,等待时间为100ms
  if(event.status==osEventSignal) //如果接收到通知
  {
   if(event.value.signals&0x01) //接收的通知为0x01
   {
    Left_Hand_Control((*LeftHandCtrlAngle)&0xff);
   }
   if(event.value.signals&0x02) //接收的通知为0x02
   {
    Right_Hand_Control((*RightHandCtrlAngle)&0xff);
   }
  }
 }
  /* USER CODE END StartHandMotorControl */
}


以上就是CMSIS中的FreeRTOS任务通知的基本用法介绍;

cmsis_os.c是对task.c阉割和移植,用户也可以根据自己的需求调用task.c中的函数进行自定义;


cmsis_os.c中的任务通知对比FreeRTOS也有扩展了一些功能,比如超时判断和故障判断等等,这些状态可以用来作一些应用;

以下为扩展利用CMSIS中任务通知的超时时间osEventTimeout实现:正常接收时响应操作1,超时接收是响应操作2;
例如机器人方案中需求:机器热接收到握手指令时,抬起手臂等待握手;若规定时间内无握手信号,则手臂恢复原位;若有效时间内有握手信号,则执行握手命令,等待握手信号消失后手机恢复原位;
方案1:采用定时器和中断实现,这样浪费一个硬件定时器;
方案2:新建全局变量采用systick系统节拍时间实现计时,这样占用更多的内存,且全局变量存在不稳定性和实时性较差的问题;
方案3:任务通知中的等待时间作计时,通过修改超时时间修改任务的运行状态,这个方案较前两个方案部件资源占用少,代码实现也比较容易;
/* StartHandMotorControl function */

void StartHandMotorControl(void const * argument)
{
  /* USER CODE BEGIN StartHandMotorControl */
  /* Infinite loop */
 osEvent event; 
 uint32_t flag_right_hand_shake = 0; //接收到握手指令标志位
 uint32_t flag_right_hand_shake_done = 0; //握手握手动作执行完成标志位
 uint32_t taskDealy = osWaitForever;
 uint16_t *LeftHandCtrlAngle = &componentCtrlArea[COMPONENT_CTRL_REGISTER_MOTOR_HAND_LEFT_HIGH-COMPONENT_CTRL_AREA_START_ADDR];
 uint16_t *RightHandCtrlAngle = LeftHandCtrlAngle+2;
 uint16_t *RightHandCtrlAction = LeftHandCtrlAngle+3;
 for(;;)
 {
  event=osSignalWait(0x0F,taskDealy);
  if(event.status==osEventSignal)
  {
   if(event.value.signals&0x01)  //左手控制
   {
    Left_Hand_Control((*LeftHandCtrlAngle)&0xff);
   }
   if(event.value.signals&0x02)  //右手控制
   {
    if((*RightHandCtrlAction) == 0) //无手部联动控制
    Right_Hand_Control((*RightHandCtrlAngle)&0xff);//进行角度控制
    else if ((*RightHandCtrlAction) == 0x01) //寄存器中手部联动控制为0x01即握手动作
    {
     flag_right_hand_shake = 1;
     Right_Hand_Control(90); //抬起右手,等待握手信号
     taskDealy = 6000; //修改阻塞时间为6s
    }                          
   }
   if((event.value.signals&0x04)&&flag_right_hand_shake)  //接收到握手信号
   {
    Right_Hand_Shake(); //握手动作
    taskDealy = osWaitForever; //修改阻塞时间为永远
    flag_right_hand_shake = 0; //清标志位  
    flag_right_hand_shake_done = 1;置位握手动作完成标志位
   }
   if((event.value.signals&0x08)&&flag_right_hand_shake_done)  //接收到握手释放信号
   {
    Right_Hand_Control(0); //手臂复位
    taskDealy = osWaitForever; //阻塞时间改为永远
    flag_right_hand_shake_done = 0;//清握手完成标志位
   }
  }
  else if(event.status==osEventTimeout) //超时判断
  {
   if(flag_right_hand_shake == 1) //且处于等待握手信号的状态
   {
    Right_Hand_Control(0);//手臂复位
    taskDealy = osWaitForever;//阻塞时间改为永远
    flag_right_hand_shake =0;//清标志位
   }  
  }
 }
  /* USER CODE END StartHandMotorControl */
}






转载请注明:CodingBlog » CMSIS里的FreeRTOS第一篇之任务通知:osSignalSet与osSignalWait

喜欢 (0)or分享 (0)
发表我的评论
取消评论

*

表情