木子剑
生命不熄,学习不止!

一不做,二不休,下面是事件的测试代码:

//
//-- main.c
//

#include <stdio.h>
#include <stdlib.h>
#include "stm32f10x.h"
#include "tn.h"

//-- 空闲任务栈的大小,以字为单位 17+32个字,即49个字
#define IDLE_TASK_STACK_SIZE          (TN_MIN_STACK_SIZE + 32)

//-- 中断堆栈大小,以字为单位
#define INTERRUPT_STACK_SIZE          (TN_MIN_STACK_SIZE + 32)

//-- 用户任务的堆栈大小
#define TASK_A_STK_SIZE               (TN_MIN_STACK_SIZE + 32)
#define TASK_B_STK_SIZE               (TN_MIN_STACK_SIZE + 32)

//-- 用户任务优先级,0最高
#define TASK_A_PRIORITY    2
#define TASK_B_PRIORITY    1

//-- 消费者信息队列中的项目数量
#define  CONS_QUE_BUF_SIZE    50
//-- 等待内存和空闲信息的最大超时时间
#define  WAIT_TIMEOUT         10


/*******************************************************************************
 *    DATA
 ******************************************************************************/

//-- Allocate arrays for stacks: stack for idle task
//   and for interrupts are the requirement of the kernel;
//   others are application-dependent.
//   不是创建任务时,会分配堆栈吗? 这里要做一次?
//   We use convenience macro TN_STACK_ARR_DEF() for that.

TN_STACK_ARR_DEF(idle_task_stack, IDLE_TASK_STACK_SIZE);
TN_STACK_ARR_DEF(interrupt_stack, INTERRUPT_STACK_SIZE);

TN_STACK_ARR_DEF(task_a_stack, TASK_A_STK_SIZE);
TN_STACK_ARR_DEF(task_b_stack, TASK_B_STK_SIZE);

//-- 任务结构, 当成C++公有的类,task_a b c 是类实列化的对像
static struct TN_Task task_a;
static struct TN_Task task_b;

//-- 为其定义队列和缓冲器
static struct TN_DQueue     cons_que;
void                       *cons_que_buf[ CONS_QUE_BUF_SIZE ];

//-- 事件组,基于队列的
static struct TN_EventGrp que_example_events;


//-- 队列状态的旗号
enum E_QueExampleFlag {
   /// 0000
   /// Flag indicating that consumer task is initialized 
   QUE_EXAMPLE_FLAG__TASK_CONSUMER_INIT = (1 << 0), //1
   ///
   /// Flag indicating that producer task is initialized
   QUE_EXAMPLE_FLAG__TASK_PRODUCER_INIT = (1 << 1), //2
   ///
   /// Flag indicating that consumer's queue A is not empty
   QUE_EXAMPLE_FLAG__MSG_A = (1 << 2), //4
   ///
   /// Flag indicating that consumer's queue B is not empty
   QUE_EXAMPLE_FLAG__MSG_B = (1 << 3), //8
};


/*******************************************************************************
 *    FUNCTIONS
 ******************************************************************************/

//-- to printf
int fputc(int c, FILE *stream)
{
   return ITM_SendChar(c);
}


//-- init system timer
void hw_init(void)
{
   SysTick_Config(SYS_FREQ / SYS_TMR_FREQ); // 配置SysTick
}


//-- system timer 中断
void SysTick_Handler(void)
{
   tn_tick_int_processing();  // 周期性执行
}

//-- a task 生产者
void task_a_body(void *par)
{
   printf("New task a is OK!\n");

   tn_eventgrp_wait( //-- 等待消费者队列初始化完成,有消费才进行生产,附合正常思维
       &que_example_events,
       QUE_EXAMPLE_FLAG__TASK_CONSUMER_INIT, 
       TN_EVENTGRP_WMODE_AND,
       TN_NULL,
       TN_WAIT_INFINITE
       );

   tn_eventgrp_modify( //-- 调整事件状态,生产者已初始化OK
       &que_example_events,
       TN_EVENTGRP_OP_SET,
       QUE_EXAMPLE_FLAG__TASK_PRODUCER_INIT
       );

   enum TN_RCode tn_rc;
   static int p_msg=0x55;

   for(;;)
   {    
     tn_rc = tn_is_task_context() //-- 将其发送给消费者任务
       ? tn_queue_send(&cons_que, (void *)&p_msg, WAIT_TIMEOUT)
       : tn_queue_isend_polling(&cons_que, (void *)&p_msg);

      if (tn_rc == TN_RC_OK){
         printf("task_a_queue_send_success\n");
         p_msg++;
      }
        tn_task_sleep(60);
     }
}

 
//-- b task  消费者
void task_b_body(void *par)
{
   printf("New task b is OK!\n");
   //-- 消费者任务已初始化,调整事件状态
   tn_eventgrp_modify(
         &que_example_events,     //-- 要调整的事件
         TN_EVENTGRP_OP_SET,      //-- 事件调整为SET
         QUE_EXAMPLE_FLAG__TASK_CONSUMER_INIT//-- 状态的旗号__消费者已经初始化OK
         );

   enum TN_RCode rc; 
   char* dat;
   TN_UWord flags_pattern = 0;
   int *p_msg=0;

   for(;;)
   {
      //-- 等待消息(即连接标志),可能永远等待
      enum TN_RCode rc = tn_eventgrp_wait(
               &que_example_events,        // 取事件
               (QUE_EXAMPLE_FLAG__MSG_A),  // 队列旗号QUE_EXAMPLE_FLAG__MSG_A
               TN_EVENTGRP_WMODE_OR,  // OR模式
               &flags_pattern,        // 指向实际事件模式的 `TN_UWord` 变量的指针
               TN_WAIT_INFINITE       // 超时
               );


      if (rc == TN_RC_OK){
         if (flags_pattern & QUE_EXAMPLE_FLAG__MSG_B){

            rc = tn_queue_receive(&cons_que, (void *)&p_msg, 0);
            if (rc == TN_RC_OK){
                 printf("task_b_msg = 0x%X\n", (int)*p_msg);
                 p_msg = NULL;
            }
         }
      }
   }
}



//-- 空闲回调,从空闲任务中定期调用
void idle_task_callback (void)
{
  ;
}


//-- 创建应用任务, 
void init_task_create(void)
{

   //-- 初始化各种板载外围设备,如闪存、显示器,等等。
   //-- 初始化各种程序模块,----- 免去再建一个APP_init函数,没必要

   //-- 创建队列cons_que
   tn_queue_create(&cons_que, (void *)cons_que_buf, CONS_QUE_BUF_SIZE);
  
   //-- 创建事件que_example_events
   tn_eventgrp_create(&que_example_events, (0));


   //-- 将应用程序的公共事件组与队列连接起来,并加上状态的旗号
   tn_queue_eventgrp_connect(  
         &cons_que,               //-- 要连接的队列
         &que_example_events,     //-- 要连接的事件
         (QUE_EXAMPLE_FLAG__MSG_A) //-- 状态的旗号,处理时的依据
         );


   //-- 以下是任务建立,推介只激活一个任务,然后在该任务依次激活所有任务.
   tn_task_create(
         &task_a,                   //-- task structure  任务a对像的地址
         task_a_body,               //-- task body function  任务A的回调函数
         TASK_A_PRIORITY,           //-- task priority  任务A的优先级
         task_a_stack,              //-- task stack   任务A的堆栈地址,类似数组
         TASK_A_STK_SIZE,           //-- task stack size (in words) 任务A的堆栈长度 
         NULL,                      //-- task function parameter  函数参数
         TN_TASK_CREATE_OPT_START   //-- creation option  任务创建后是否应立即激活
         );

   tn_task_create(
         &task_b,
         task_b_body,
         TASK_B_PRIORITY,
         task_b_stack,
         TASK_B_STK_SIZE,
         NULL,
         TN_TASK_CREATE_OPT_START  // 激活
         );
}


/*******************************************************************************
 *    主程序入口
 ******************************************************************************/
int main(void)
{
   //-- 无条件关中断,初始化OS心跳
   tn_arch_int_dis(); 
   hw_init();


   //-- call to tn_sys_start() never returns  对tn_sys_start()的调用从不返回
   tn_sys_start(
         idle_task_stack,       // 空闲堆栈地址   
         IDLE_TASK_STACK_SIZE,  // 空闲堆栈大小
         interrupt_stack,       // 中断栈
         INTERRUPT_STACK_SIZE,  // 中断栈大小
         init_task_create,      // 用户任务回调
         idle_task_callback     // 空闲任务回调
         );


   //-- unreachable  无法到达
   return 1;
}

 

运行效果如下:

 大概的工作原理:

这是用列队邦定到事件组的一个测试代码,即然有队列就有生产和消费关系,见A任务和B任务。在开始之前,除了正常的创建队列,创建任务之外。

我们还要使用tn_queue_eventgrp_connect的OS接口,对事件与队列进行邦定,还要建立对应的E_QueExampleFlag状态标示号。最后通过

tn_eventgrp_wait的OS接口等待事件的到来,只要其他任务往队列里放入信息时,就会产生事件,来源为队列的QUE_EXAMPLE_FLAG__MSG_A状态号。在TNeo中,

以STM32为例,就是32种事件,一个事件占用一个bit位。如果是16位单片机,就是有16种事件,也是一个BIT一个事件,这种模设计有助于提升速度,优先级也是这个套路。

 

有些问题:

通常来说队列用于任务的异步通讯的,而事件是用于任务的同步通讯的。所以上面的测试,到底测试了个啥??有没有丢掉队列单独测试事件方法??

 

代码流程:

从tn_eventgrp.h的文件来看,貌似要邦定队列用,可以一对多,多对一,多对多,应该是比较方便的,不能以常规的思维看待TNeo.

 

   //-- 创建队列cons_que
   tn_queue_create(&cons_que, (void *)cons_que_buf, CONS_QUE_BUF_SIZE);

   //-- 创建事件que_example_events
   tn_eventgrp_create(&que_example_events, (1<<31));  //-- 初始事件模式:(1<<31)bit位

   //-- 将应用程序的公共事件组与队列连接起来,并加上状态的旗号
   tn_queue_eventgrp_connect(  
         &cons_que,                //-- 要连接的队列
         &que_example_events,      //-- 要连接的事件
         QUE_EXAMPLE_FLAG__MSG_A   //-- 状态的旗号,处理时的依据
         );
tn_eventgrp_create(&que_example_events, (1<<31));     events是一个事件结构的变量,1<<31占一个bit位,在32bit单片机中,最多就是32个事件。
大意就是,第31bit位邦定事件为que_example_events,其他的XXbit位可以邦定XXX_events.这个要理解

tn_queue_eventgrp_connect这是事件组连接队列API函数,大意就是队列cons_que与事件que_example_events连接起来,cons_que产生的事件旗号为QUE_EXAMPLE_FLAG__MSG_A,
旗号QUE_EXAMPLE_FLAG__MSG_A为enum结构,其实可以bit位来表示,如(1<<2),(1<<3),都行,只是数字没有QUE_EXAMPLE_FLAG__MSG_A更能望文生义,便于人类理解


关于事件队列的消费者:
首先消费者的任务应当比生产者任务高,必竟是先有市场消费,才有工厂生产。这个思维要理解就OK
   tn_eventgrp_modify(
         &que_example_events,                //-- 要调整的事件
         TN_EVENTGRP_OP_SET,                 //-- 事件调整为SET
         QUE_EXAMPLE_FLAG__TASK_CONSUMER_INIT//-- 状态的旗号__消费者已经初始化OK
         );

modify函数为调整events事件状态为OP_SET,    OP_SET相当于使能events事件,OP_CLEAR为清除事件,当成中断源开启和关闭就可以。

QUE_EXAMPLE_FLAG__TASK_CONSUMER_INIT表示消费者任务已经初始化完成。

 

      //-- 等待消息,可能永远等待
      enum TN_RCode rc = tn_eventgrp_wait(
               &que_example_events,        //-- 取事件
               (QUE_EXAMPLE_FLAG__MSG_A),  //-- 队列旗号QUE_EXAMPLE_FLAG__MSG_A
               TN_EVENTGRP_WMODE_OR,       //-- OR模式
               &flags_pattern,             //-- 指向实际事件模式的 `TN_UWord` 变量的指针
               TN_WAIT_INFINITE            //-- 超时
               );

_MSG_A前面指定的队列旗号,_OR模式不是很理解,flags_pattern就是事件创建时的(1<<31)bit位。 大意就是tn_eventgrp_wait函数,一直等待_events的产生。

一旦产生了_events,再进一步确认事件源是不是QUE_EXAMPLE_FLAG__MSG_A。也可以邦定了三个队列,可能像这样:

(QUE_EXAMPLE_FLAG__MSG_A)|(QUE_EXAMPLE_FLAG__MSG_B)|(QUE_EXAMPLE_FLAG__MSG_B), //-- 队列旗号QUE_EXAMPLE_FLAG__MSG_A  _B  _C


关于事件队列的生产者:
   tn_eventgrp_wait( //-- 等待消费者队列初始化完成,有消费才进行生产,附合正常思维
       &que_example_events,
       QUE_EXAMPLE_FLAG__TASK_CONSUMER_INIT, 
       TN_EVENTGRP_WMODE_AND,
       TN_NULL,
       TN_WAIT_INFINITE
       );

生产者任务,应先等待消费者任务的初始化完成。tn_eventgrp_wait上文讲过了,回访阅读便可。_AND还是不理解其作用,等待模式有AND    OR    AUTOCLR,是对多个旗帜号进行运算??

 

   tn_eventgrp_modify( //-- 调整事件状态,生产者已初始化OK
       &que_example_events,
       TN_EVENTGRP_OP_SET,
       QUE_EXAMPLE_FLAG__TASK_PRODUCER_INIT
       );

上文也讲过modify,表示生产者初化OK!!

 

     tn_rc = tn_is_task_context() //-- 将其发送给消费者任务
       ? tn_queue_send(&cons_que, (void *)&p_msg, WAIT_TIMEOUT)
       : tn_queue_isend_polling(&cons_que, (void *)&p_msg);

发送队列信息,这样消费者可以收到事件。

 

 

时间:20220404

事件组绝对是强大的存在,此前的测试例程只是一个队列事件,还有很多没有测试。

事件由TN_UWord进行描述,正如之前的(1<<31)bit作为一个事件,这个事件把队列联接在一起,所以只要向队列丢数据就会产生对应的事件

 

 

很多OS对像可以产生事件,如下图:

 

 因此,事件组是最后一道工序,也就是说其他的队列互斥等等都初始化OK后,最后进行事件链接,而且还可以无限嵌套。

 

 时间:20220405

最简单事件测试,这次没用到队列,看下面代码:

//-- 事件组
static struct TN_EventGrp example_events;

tn_eventgrp_create(&example_events, (1<<30));  //-- 初始事件模式TN_UWord:30bit


//-- c task  优先级--低
void task_c_body(void *par)
{
   printf("New task c is OK!\n");

        tn_eventgrp_modify(
           &example_events,                //-- 事件30bit
           TN_EVENTGRP_OP_SET,                 //-- 事件调整为SET,就是打开可以上下文转换
           0X0100//-- 状态的号_0x0100
           );

   for(;;)
   { 
      tn_task_sleep(100);
   }
}


//-- d task 待用任务 优先级--高
void task_d_body(void *par)
  {
   printf("New task d is OK!\n");
   TN_UWord flags_pattern = 0;

   for(;;)
   {
      enum TN_RCode rc = tn_eventgrp_wait(
               &example_events,        //-- 取事件
               0x0100,  //-- 状态号
               TN_EVENTGRP_WMODE_OR,       //-- OR模式
               &flags_pattern,             //-- 指向实际事件模式的 `TN_UWord` 变量的指针
               TN_WAIT_INFINITE            //-- 超时
               );

      if (rc == TN_RC_OK){
         if (flags_pattern & 0x0100){ // 如果是tim_example_events事件,又是0x0100状态
             printf("example_events TEST  OK!!\n");// 打印测试信息
         }
      }
   }
  }

简单原理就是(1<<30)bit事件,等待0x0100状态,你可以在任意时间和地方通过tn_eventgrp_modify进行设置状态号,

对方通过tn_eventgrp_wait等待该事件的对应状态。确实可达到任务同步的目的

 

posted on 2022-04-04 21:06  木子剑  阅读(44)  评论(0编辑  收藏  举报