发新帖我要提问
12
返回列表
打印
[应用相关]

FreeRTOS 消息队列

[复制链接]
楼主: goodluck09876
手机看帖
扫描二维码
随时随地手机跟帖
21
goodluck09876|  楼主 | 2018-9-12 21:15 | 只看该作者 |只看大图 回帖奖励 |倒序浏览
函数描述:
函数 xEventGroupSetBitsFromISR,用于设置指定的事件标志位为 1。(中断方式)
 第 1 个参数是事件标志组句柄。
 第 2 个参数表示 24 个可设置的事件标志位,EventBits_t 是定义的 32 位变量(详解 18.1.2 小节说
明),低 24 位用于事件标志设置。变量 uxBitsToSet 的低 24 位的某个位设置为 1,那么被设置的
事件标志组的相应位就设置为 1。 变量 uxBitsToSet 设置为 0 的位对事件标志相应位没有影响。比
如设置变量 uxBitsToSet = 0x0003 就表示将事件标志的位 0 和位 1 设置为 1,其余位没有变化。
 第 3 个参数用于保存是否有高优先级任务准备就绪。如果函数执行完毕后,此参数的数值是 pdTRUE,
说明有高优先级任务要执行,否则没有。
 返回值,如果消息成功发送给 daemon 任务(就是 FreeRTOS 的定时器任务)返回 pdPASS,否则
返回 pdFAIL,另外 daemon 任务中的消息队列满了也会返回 pdFAIL。

使用特权

评论回复
22
goodluck09876|  楼主 | 2018-9-12 21:15 | 只看该作者
使用这个函数要注意以下问题:
1. 使用前一定要保证事件标志已经通过函数 xEventGroupCreate 创建了。同时要在 FreeRTOSConfig.h
文件中使能如下三个宏定义:
#define INCLUDE_xEventGroupSetBitFromISR 1
#define configUSE_TIMERS 1
#define INCLUDE_xTimerPendFunctionCall 1

使用特权

评论回复
23
goodluck09876|  楼主 | 2018-9-12 21:15 | 只看该作者
2. 函数 xEventGroupSetBitsFromISR 是用于中断服务程序中调用的,故不可以在任务代码中调用此函
数,任务代码中使用的是 xEventGroupSetBits。
3. 函数 xEventGroupSetBitsFromISR 对事件标志组的操作是不确定性操作,因为不知道当前有多少个
任务在等待此事件标志。而 FreeRTOS 不允许在中断服务程序和临界段中执行不确定性操作。 为了不
在中断服务程序中执行,就通过此函数给 FreeRTOS 的 daemon 任务(就是 FreeRTOS 的定时器任
务)发送消息,在 daemon 任务中执行事件标志的置位操作。 同时也为了不在临界段中执行此不确定
操作,将临界段改成由调度锁来完成。这样不确定性操作在中断服务程序和临界段中执行的问题就都
得到解决了。

使用特权

评论回复
24
goodluck09876|  楼主 | 2018-9-12 21:16 | 只看该作者
4. 由于函数 xEventGroupSetBitsFromISR 对事件标志的置位操作是在 daemon 任务里面执行的,如果
想让置位操作立即生效,即让等此事件标志的任务能够得到及时执行,需要设置 daemon 任务的优先
级高于使用此事件标志组的所有其它任务。

使用特权

评论回复
25
goodluck09876|  楼主 | 2018-9-12 21:16 | 只看该作者
5. 通过下面的使用举例重点看一下函数 xEventGroupSetBitsFromISR 第三个参数的规范用法,初学者务
必要注意。

使用特权

评论回复
26
goodluck09876|  楼主 | 2018-9-12 21:17 | 只看该作者

使用特权

评论回复
27
goodluck09876|  楼主 | 2018-9-12 21:17 | 只看该作者
函数 xEventGroupWaitBits
函数原型:
EventBits_t xEventGroupWaitBits(
const EventGroupHandle_t xEventGroup, /* 事件标志组句柄 */
const EventBits_t uxBitsToWaitFor, /* 等待被设置的事件标志位 */
const BaseType_t xClearOnExit, /* 选择是否清零被置位的事件标志位 */
const BaseType_t xWaitForAllBits, /* 选择是否等待所有标志位都被设置 */
TickType_t xTicksToWait ); /* 设置等待时间 */
函数描述:
函数 xEventGroupWaitBits 等待事件标志被设置。
 第 1 个参数是事件标志组句柄。
 第 2 个参数表示等待 24 个事件标志位中的指定标志,EventBits_t 是定义的 32 位变量(详解 18.1.2
小节说明),低 24 位用于事件标志设置。比如设置变量 uxBitsToWaitFor = 0x0003 就表示等待事
件标志的位 0 和位 1 设置为 1。 此参数切不可设置为 0。
 第 3 个参数选择是否清除已经被置位的事件标志,如果这个参数设置为 pdTRUE,且函数
xEventGroupWaitBits 在参数 xTicksToWait 设置的溢出时间内返回,那么相应被设置的事件标志
位会被清零。 如果这个参数设置为 pdFALSE,对已经被设置的事件标志位没有影响。
 第 4 个参数选择是否等待所有的标志位都被设置,如果这个参数设置为 pdTRUE,要等待第 2 个参
数 uxBitsToWaitFor 所指定的标志位全部被置 1,函数才可以返回。当然,超出了在参数
xTicksToWait 设置的溢出时间也是会返回的。如果这个参数设置为 pdFALSE,第 2 个参数
uxBitsToWaitFor 所指定的任何标志位被置 1,函数都会返回,超出溢出时间也会返回。
 第 5 个参数设置等待时间,单位时钟节拍周期。 如果设置为 portMAX_DELAY,表示永久等待。
 返回值,由于设置的时间超时或者指定的事件标志位被置 1,导致函数退出时返回的事件标志组数值。

使用特权

评论回复
28
goodluck09876|  楼主 | 2018-9-12 21:17 | 只看该作者
使用这个函数要注意以下问题:
1. 此函数切不可在中断服务程序中调用。
2. 这里再着重说明下这个函数的返回值,通过返回值用户可以检测是哪个事件标志位被置 1 了。
 如果由于设置的等待时间超时,函数的返回值可能会有部分事件标志位被置 1。
 如果由于指定的事件标志位被置1而返回, 并且设置了这个函数的参数xClearOnExit为pdTRUE,
那么此函数的返回值是清零前的事件标志组数值。
另外,调用此函数的任务在离开阻塞状态到退出函数 xEventGroupWaitBits 之间这段时间,如果一个
高优先级的任务抢占执行了,并且修改了事件标志位,那么此函数的返回值会跟当前的事件标志组数
值不同 。

使用特权

评论回复
29
goodluck09876|  楼主 | 2018-9-12 21:18 | 只看该作者
static void AppTaskCreate(void)
{

        xTaskCreate(vTaskWork,       /* 任务函数  */
                 "vTaskWork",         /* 任务名    */
                  512,                   /* 任务栈大小,单位word,也就是4字节 */
                  NULL,                  /* 任务参数  */
                  1,                     /* 任务优先级*/
                  &xHandleTaskWork );  /* 任务句柄  */
   
   
    xTaskCreate( vTaskLed1,            /* 任务函数  */
                 "vTaskLed1",          /* 任务名    */
                 512,                 /* 任务栈大小,单位word,也就是4字节 */
                 NULL,                /* 任务参数  */
                 2,                   /* 任务优先级*/
                 &xHandleTaskLED1); /* 任务句柄  */
   
    xTaskCreate( vTaskBeep,             /* 任务函数  */
                 "vTaskBeep",           /* 任务名    */
                 512,                     /* 任务栈大小,单位word,也就是4字节 */
                 NULL,                   /* 任务参数  */
                 3,                       /* 任务优先级*/
                 &xHandleTaskBeep );  /* 任务句柄  */
   
   
}

使用特权

评论回复
30
goodluck09876|  楼主 | 2018-9-12 21:18 | 只看该作者
/*********************************************************************************
  * @ 函数名  : vTaskBeep
  * @ 功能说明: Beep 任务
  * @ 参数    : pvParameters,当任务创建的时候传进来,可以没有  
  * @ 返回值  : 无
  ********************************************************************************/
void vTaskBeep(void *pvParameters)
{
        EventBits_t uxBits;
        const TickType_t xTicksToWait = 5000; /* 最大延迟100ms */
   
    while(1)
    {
        /* 等K1按键按下设置bit0和K2按键按下设置bit1 */
        uxBits = xEventGroupWaitBits(xCreatedEventGroup, /* 事件标志组句柄 */
                                     BIT_ALL,            /* 等待bit0和bit1被设置 */
                                     pdTRUE,             /* 退出前bit0和bit1被清除,这里是bit0和bit1都被设置才表示“退出”*/
                                     pdTRUE,             /* 设置为pdTRUE表示等待bit1和bit0都被设置*/
                                     xTicksToWait);      /* 等待延迟时间 */
        
        if((uxBits & BIT_ALL) == BIT_ALL)
        {
            /* 接收到bit1和bit0都被设置的消息 */
            printf("接收到bit0和bit1都被设置的消息\r\n");
        }
        else
        {
            /* 超时,另外注意仅接收到一个按键按下的消息时,变量uxBits的相应bit也是被设置的 */
            BEEP_TOGGLE;
        }
    }
}

使用特权

评论回复
31
goodluck09876|  楼主 | 2018-9-12 21:19 | 只看该作者
/***按键处理任务***/



static void vTaskWork(void *pvParameters)
{

    EventBits_t uxBits;

    while(1)
    {
        
        
        if (key1_flag==1)
        {
            key1_flag=0;
                        /* 设置事件标志组的bit0 */
                    uxBits = xEventGroupSetBits(xCreatedEventGroup, BIT_0);
                    if((uxBits & BIT_0) != 0)
                    {
                        printf("K1键按下,事件标志的bit0被设置\r\n");
                    }
                    else
                    {
                        printf("K1键按下,事件标志的bit0被清除,说明任务vTaskBeep已经接受到bit0和bit1被设置的情况\r\n");
                    }
        }
        if(key2_flag==1)
        {
                    key2_flag=0;
                    uxBits = xEventGroupSetBits(xCreatedEventGroup, BIT_1);
                    if((uxBits & BIT_1) != 0)
                    {
                        printf("K2键按下,事件标志的bit1被设置\r\n");
                    }
                    else
                    {
                        printf("K2键按下,事件标志的bit1被清除,说明任务vTaskBeep已经接受到bit0和bit1被设置的情况\r\n");
                    }
            
        }
   
        
        vTaskDelay(20);
    }
}

使用特权

评论回复
32
goodluck09876|  楼主 | 2018-9-12 21:19 | 只看该作者
创建的任务,按键处理优先级低于事件等待的Beep任务,先按下K1,再按K2,打印如下:

使用特权

评论回复
33
goodluck09876|  楼主 | 2018-9-12 21:19 | 只看该作者
第一个输出毫无疑问,第二行,由于事件等待Beep优先级大于按键处理,所以当K2按下之后,调度器首先回到高优先级的任务Beep,打印出此时K1,K2都被按下以致bit0和bit1被置位的消息,在Beep任务中调用xEventGroupWaitBits函数后,这两个置为1的位bit1和bit0会被清零,此时,调度器再次回到低优先级的按键处理任务时,xEventGroupSetBits的返回值已经更新成清零值,故第三行打印清除的消息。

现在,我们把按键处理的优先级设置成为高于Beep任务的,打印输出如下:

使用特权

评论回复
34
goodluck09876|  楼主 | 2018-9-12 21:20 | 只看该作者
第一个输出也毫无疑问,按下K1,bit0被置位,当我按下K2的时候,此时调度器 不会马上返回低优先级的Beep任务,而会继续执行自身(此实验设置按键处理最高优先级)直到被阻塞,所以会有第二行的打印,但是,注意,第二行按下K2的打印却依旧显示的是被清除了,因为在Beep任务中使用了事件等待,而K2按下的时候,freertos操作系统会知道等待两个按键按下的事件已经触发了,此时,在按键处理任务中,xEventGroupSetBits的返回值,也不是当前获取的置位值了,而是经过xEventGroupSetBits函数自动清零之后的值,所以第二行打印的是清零消息,第三行打印都被置位,为什么不是清零?因为此时的xEventGroupWaitBits返回值是清零前的事件标志组数值。

使用特权

评论回复
35
goodluck09876|  楼主 | 2018-9-12 21:20 | 只看该作者
能你觉得有点奇怪, xEventGroupSetBits函数本就是置位信息的功能,居然还要受xEventGroupWaitBits函数和调用形式影响,哪怕调用xEventGroupWaitBits函数的任务优先级还是低于我们的按键任务的(但是事件标志的设置让低优先级的任务离开了阻塞态(只要离开了阻塞态,返回值就会更新),在就绪态,只是被高优先级任务抢占了),但是,正是因为这样,我们真正实时传递了事件信息啊。试想,要是我的两个按键事件都已经触发了,而我在按键处理任务中还不能立即知道,这样的实时性显然是不满足需求的。就连裸机中,我们通过中断改变一个元素的值,一定是中断改变之后,这个值在被任何使用的时候都已经更新,所以,作为实时操作系统,freertos这样的行为也就可以理解了。

使用特权

评论回复
36
goodluck09876|  楼主 | 2018-9-12 21:20 | 只看该作者
中断方式不再演示,只是需要在中断服务函数中使用xEventGroupSetBitsFromISR,使用方式如下:

/*
*********************************************************************************************************
*    函 数 名: TIM_CallBack1和TIM_CallBack2
*    功能说明: 定时器中断的回调函数,此函数被bsp_StartHardTimer所调用。                        
*    形    参: 无
*    返 回 值: 无
*********************************************************************************************************
*/
static void TIM_CallBack1(void)
{
    BaseType_t xResult;
    BaseType_t xHigherPriorityTaskWoken = pdFALSE;
   
    /* 向任务vTaskBeep发送事件标志 */
    xResult = xEventGroupSetBitsFromISR(xCreatedEventGroup, /* 事件标志组句柄 */
                                        BIT_0 ,             /* 设置bit0 */
                                        &xHigherPriorityTaskWoken );

    /* 消息被成功发出 */
    if( xResult != pdFAIL )
    {
        /* 如果xHigherPriorityTaskWoken = pdTRUE,那么退出中断后切到当前最高优先级任务执行 */
        portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
    }
}

使用特权

评论回复
37
晓伍| | 2018-9-13 07:23 | 只看该作者
感谢楼主 很详细

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则