ESP32 S3 基于开发框架(Arduino)使用FreeRTOS通知

324 阅读15分钟

1 二值化

/*
//  多线程基于FreeRTOS,可以多个任务并行处理;
//  ESP32具有两个32位Tensilica Xtensa LX6微处理器;
//  实际上我们用Arduino进行编程时只使用到了第一个核(大核),第0核并没有使用
//  多线程可以指定在那个核运行;
 */

#include <Arduino.h>
#define USE_MULTCORE 0
hw_timer_t * timer = NULL;

bool state = 0;

static TaskHandle_t TaskBlink_Handle = NULL;

void TaskBlink(void *pvParameters)  // This is a task.
{
  (void) pvParameters;
  bool ON = true;
  pinMode(45, OUTPUT);
  for (;;) // A Task shall never return or exit.
  {
    // 函数 ulTaskNotifyTake 第一个参数说明:
    //    1. 此参数设置为 pdFALSE,任务 vTaskMsgPro 的 TCB(任务控制块)中的变量 ulNotifiedValue 减一
    //    2. 此参数设置为 pdTRUE,任务 vTaskMsgPro 的 TCB(任务控制块)中的变量 ulNotifiedValue 清零
    // 第 2 个参数是没有任务信号量可用时,等待信号量可用的最大等待时间,单位系统时钟节拍。
    USBSerial.println("TaskNotify Ask !!\r\n");
    ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
    if (state == true)
    {
        digitalWrite(45, LOW);
        USBSerial.println("TaskBlink OFF ! \r\n");
    }
    else
    {
        USBSerial.println("TaskBlink ON ! \r\n");
        digitalWrite(45, HIGH);
    }
    state = !state;
    //vTaskDelay(pdMS_TO_TICKS(1000));
  }
}


 //测试用的主函数
 void app_main(void)
 {
    // Now set up two tasks to run independently.
    /**
        pvTaskCode: 指向任务输入函数的指针。 任务必须实现永不返回(即连续循环),或者应该使用vTaskDelete函数终止。
        pcName 任务的描述性名称。 这主要是为了方便调试。 最大长度由configMAX_TASK_NAME_LEN定义 - 默认是16。
        usStackDepth: 任务栈的大小,以字节数表示。字节数。注意,这与vanilla FreeRTOS不同。
        pvParameters: 指针将被用作任务的参数。正在创建。
        uxPriority: 任务运行的优先级。 系统包括MPU支持的系统可以选择在特权(系统)下创建任务。通过设置优先级参数的位portPRIVILEGE_BIT来创建任务。 例如例如,要创建一个优先级为2的特权任务,uxPriority参数应该被设置为 ( 2 | portPRIVILEGE_BIT )。
        pvCreatedTask: 用于传回一个句柄,创建的任务可以通过它来引用。可以被引用。
        xCoreID: 如果该值为tskNO_AFFINITY,则创建的任务不被钉在任何CPU上。钉在任何CPU上,调度器可以在任何可用的核心上运行它。值为0或1时,表示该任务应该被钉在CPU上的索引号。被钉住。指定大于(portNUM_PROCESSORS - 1)的值将导致函数失败。导致该函数失败。
                  如果任务被成功创建并添加到准备好的列表中,返回pdPASS。列表中,否则会有一个错误代码,该代码在文件projdefs.h中定义。
    */
    xTaskCreatePinnedToCore(
    TaskBlink
        ,  "TaskBlink"   // A name just for humans
        ,  6000 // This stack size can be checked & adjusted by reading the Stack Highwater
        ,  NULL
        ,  2  // Priority, with 3 (configMAX_PRIORITIES - 1) being the highest, and 0 being the lowest.
        ,  (TaskHandle_t*) &TaskBlink_Handle
        ,  ARDUINO_RUNNING_CORE); 

    /**    
    xTaskCreate(
    TaskBlink
        ,  "TaskBlink"   // A name just for humans
        ,  6000 // This stack size can be checked & adjusted by reading the Stack Highwater
        ,  NULL
        ,  2  // Priority, with 3 (configMAX_PRIORITIES - 1) being the highest, and 0 being the lowest.
        ,  (TaskHandle_t*) &TaskBlink_Handle); 

    USBSerial.println("Tasks launched ...");
    */
 }

// this function gets called by the interrupt at <sampleRate>Hertz
void TC4_Handler(void)
{
    BaseType_t xHigherPriorityTaskWoken = pdFALSE;
    //第2个参数用于保存是否有高优先级任务准备就绪。如果函数执行完毕后,此参数的数值是 pdTRUE,说明有高优先级任务要执行,否则没有。
    vTaskNotifyGiveFromISR(TaskBlink_Handle, &xHigherPriorityTaskWoken);
    portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}

void ARDUINO_ISR_ATTR onTimer(){

  TC4_Handler();
}


void setup()
{
    USBSerial.begin(115200);
    // Use 1st timer of 4 (counted from zero).
    // Set 80 divider for prescaler (see ESP32 Technical Reference Manual for more
    // info).
    timer = timerBegin(0, 80, true);
    // Attach onTimer function to our timer.
    timerAttachInterrupt(timer, &onTimer, true);
    // Set alarm to call onTimer function every second (value in microseconds).
    // Repeat the alarm (third parameter)
    timerAlarmWrite(timer, 1000000, true);
    // Start an alarm
    timerAlarmEnable(timer);

    delay(10);
    app_main();
}

void loop()
{

}

2 计数

/*
//  多线程基于FreeRTOS,可以多个任务并行处理;
//  ESP32具有两个32位Tensilica Xtensa LX6微处理器;
//  实际上我们用Arduino进行编程时只使用到了第一个核(大核),第0核并没有使用
//  多线程可以指定在那个核运行;
 */

#include <Arduino.h>
#define USE_MULTCORE 0
hw_timer_t * timer = NULL;

SemaphoreHandle_t semaphoreHandle;
static TaskHandle_t TaskBlink_Handle = NULL;
static TaskHandle_t CarInTask_Handle = NULL;

bool state = 0;

void carInTask(void *pvParam) 
{
    uint32_t task_num = pdTRUE;
	while (1) 
    {
        // 函数 ulTaskNotifyTake 第一个参数说明:
        //    1. 此参数设置为 pdFALSE,任务 vTaskMsgPro 的 TCB(任务控制块)中的变量 ulNotifiedValue 减一
        //    2. 此参数设置为 pdTRUE,任务 vTaskMsgPro 的 TCB(任务控制块)中的变量 ulNotifiedValue 清零
        // 第 2 个参数是没有任务信号量可用时,等待信号量可用的最大等待时间,单位系统时钟节拍。
        // ulTaskNotifyTake 返回值当前任务值,即:减一之前,或者清0之前
        task_num = ulTaskNotifyTake(pdFALSE, 0);
        if(task_num>0)
        {
            USBSerial.printf("成功申请到车位,剩余车位为 %d !!\r\n",task_num-1);
        }
        else
        {
            USBSerial.printf("无剩余车位 !!\r\n");
        }
        vTaskDelay(pdMS_TO_TICKS(5000));
    }
}


void carOutTask(void *pvParam) {
	while (1) {
		vTaskDelay(pdMS_TO_TICKS(6000));
		xSemaphoreGive(semaphoreHandle);
		USBSerial.printf("one car out\n");
	}
}


void TaskBlink(void *pvParameters)  // This is a task.
{
  (void) pvParameters;
  bool ON = true;
  pinMode(48, OUTPUT);
  for (;;) // A Task shall never return or exit.
  {
    if (state == true)
    {
        digitalWrite(48, LOW);
    }
    else
    {
        digitalWrite(48, HIGH);

    }
    state = !state;

    vTaskDelay(pdMS_TO_TICKS(1000));
  }
}

 //测试用的主函数
 void app_main(void)
 {
    int i;

    // Now set up two tasks to run independently.
    /**
        pvTaskCode: 指向任务输入函数的指针。 任务必须实现永不返回(即连续循环),或者应该使用vTaskDelete函数终止。
        pcName 任务的描述性名称。 这主要是为了方便调试。 最大长度由configMAX_TASK_NAME_LEN定义 - 默认是16。
        usStackDepth: 任务栈的大小,以字节数表示。字节数。注意,这与vanilla FreeRTOS不同。
        pvParameters: 指针将被用作任务的参数。正在创建。
        uxPriority: 任务运行的优先级。 系统包括MPU支持的系统可以选择在特权(系统)下创建任务。通过设置优先级参数的位portPRIVILEGE_BIT来创建任务。 例如例如,要创建一个优先级为2的特权任务,uxPriority参数应该被设置为 ( 2 | portPRIVILEGE_BIT )。
        pvCreatedTask: 用于传回一个句柄,创建的任务可以通过它来引用。可以被引用。
        xCoreID: 如果该值为tskNO_AFFINITY,则创建的任务不被钉在任何CPU上。钉在任何CPU上,调度器可以在任何可用的核心上运行它。值为0或1时,表示该任务应该被钉在CPU上的索引号。被钉住。指定大于(portNUM_PROCESSORS - 1)的值将导致函数失败。导致该函数失败。
                  如果任务被成功创建并添加到准备好的列表中,返回pdPASS。列表中,否则会有一个错误代码,该代码在文件projdefs.h中定义。
    */
    xTaskCreatePinnedToCore(
           TaskBlink
        ,  "TaskBlink"   // A name just for humans
        ,  1024  // This stack size can be checked & adjusted by reading the Stack Highwater
        ,  NULL
        ,  2  // Priority, with 3 (configMAX_PRIORITIES - 1) being the highest, and 0 being the lowest.
        ,   (TaskHandle_t*) &TaskBlink_Handle
        ,  ARDUINO_RUNNING_CORE); 


    xTaskCreate(
        carInTask,  /* Task function. */
        "carInTask", /* String with name of task. */
        4096,      /* Stack size in bytes. */
        NULL,      /* Parameter passed as input of the task */
        3,         /* Priority of the task.(configMAX_PRIORITIES - 1 being the highest, and 0 being the lowest.) */
        (TaskHandle_t*) &CarInTask_Handle);     /* Task handle. */

	//xTaskCreate(carOutTask, "carOutTask", 1024 * 5, NULL, 1, NULL);
    USBSerial.println("Tasks launched ...");
 }

// this function gets called by the interrupt at <sampleRate>Hertz
void TC4_Handler(void)
{
    BaseType_t xHigherPriorityTaskWoken = pdFALSE;
    //第2个参数用于保存是否有高优先级任务准备就绪。如果函数执行完毕后,此参数的数值是 pdTRUE,说明有高优先级任务要执行,否则没有。
    vTaskNotifyGiveFromISR(CarInTask_Handle, &xHigherPriorityTaskWoken);
    portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}

void ARDUINO_ISR_ATTR onTimer(){

  TC4_Handler();
}


void setup()
{
    USBSerial.begin(115200);
    // Use 1st timer of 4 (counted from zero).
    // Set 80 divider for prescaler (see ESP32 Technical Reference Manual for more
    // info).
    timer = timerBegin(0, 80, true);
    // Attach onTimer function to our timer.
    timerAttachInterrupt(timer, &onTimer, true);
    // Set alarm to call onTimer function every second (value in microseconds).
    // Repeat the alarm (third parameter)
    timerAlarmWrite(timer, 1000000, true);
    // Start an alarm
    timerAlarmEnable(timer);

    delay(10);
    app_main();
}

void loop()
{

}

3 MailNotify

/*
//  多线程基于FreeRTOS,可以多个任务并行处理;
//  ESP32具有两个32位Tensilica Xtensa LX6微处理器;
//  实际上我们用Arduino进行编程时只使用到了第一个核(大核),第0核并没有使用
//  多线程可以指定在那个核运行;
 */
#include <Arduino.h>
#define USE_MULTCORE 0
hw_timer_t * timer = NULL;

static TaskHandle_t TaskBlink_Handle = NULL;
static TaskHandle_t MailTask_Handle = NULL;
#define USE_CHAR 0   /* 测试字符串的时候配置为 1 ,测试变量配置为 0 */
uint8_t ucCount = 0;
bool state = 0;

/*
任务通知也可用来向任务发送数据,但是相对于用队列发送消息,任务通知向任务发送消息会受到很多限制!
        1)只能发送 32 位的数据值。
        2)消息被保存为任务的任务通知值,而且一次只能保存一个任务通知值,相当于队列长度为1。
因此说任务通知可以模拟一个轻量级的消息邮箱而不是轻量级的消息队列。

任务通知值就是消息邮箱的值
*/

void MailTask(void *pvParam) 
{
    uint32_t task_num = pdTRUE;
    BaseType_t xReturn=pdTRUE;
    const TickType_t xMaxBlockTime = pdMS_TO_TICKS(500); /* 设置最大等待时间为 500ms */
    uint32_t r_event=0; //接收事件
	while (1) 
    {
        /*
			第一个参数 ulBitsToClearOnEntry 的作用(函数执行前):
				ulNotifiedValue &= ~ulBitsToClearOnEntry
				简单的说就是参数 ulBitsToClearOnEntry 那个位是 1,那么 ulNotifiedValue的那个位就会被清零。
				这里 ulBitsToClearOnEntry = 0x00000000 就是函数执行前保留所有位。
		
			第二个参数 ulBitsToClearOnExit 的作用(函数退出前):
					ulNotifiedValue &= ~ulBitsToClearOnExit
				简单的说就是参数 ulBitsToClearOnEntry 那个位是 1,那么 ulNotifiedValue的那个位就会被清零。
				这里 ulBitsToClearOnExit = 0xFFFFFFFF 就是函数退出前清楚所有位。
			注: ulNotifiedValue 表示任务 LED_Task 的任务控制块里面的变量,用来做消息邮箱数据的存取。
		*/
		/*获取一个任务通知,没有获取到一直等待 */
		xReturn=xTaskNotifyWait(0x0,//进入函数的时候不清除任务 bit
								0xFFFFFFFF,//退出函数的时候清除所有的 bitR
								&r_event,// 保存 ulNotifiedValue 到变量  
								xMaxBlockTime);/* 最大允许延迟时间 */
		/* 此函数只会返回 pdPASS */
		if(pdTRUE ==xReturn)
		{
			 USBSerial.printf("接收到消息邮箱数据 ulValue = %d\r\n", r_event);
		
		}			
    }
}

void TaskBlink(void *pvParameters)  // This is a task.
{
  (void) pvParameters;
  bool ON = true;
  pinMode(48, OUTPUT);
  for (;;) // A Task shall never return or exit.
  {
    if (state == true)
    {
        digitalWrite(48, LOW);
    }
    else
    {
        digitalWrite(48, HIGH);

    }
    state = !state;

    vTaskDelay(pdMS_TO_TICKS(1000));
  }
}

 //测试用的主函数
 void app_main(void)
 {
    int i;

    // Now set up two tasks to run independently.
    /**
        pvTaskCode: 指向任务输入函数的指针。 任务必须实现永不返回(即连续循环),或者应该使用vTaskDelete函数终止。
        pcName 任务的描述性名称。 这主要是为了方便调试。 最大长度由configMAX_TASK_NAME_LEN定义 - 默认是16。
        usStackDepth: 任务栈的大小,以字节数表示。字节数。注意,这与vanilla FreeRTOS不同。
        pvParameters: 指针将被用作任务的参数。正在创建。
        uxPriority: 任务运行的优先级。 系统包括MPU支持的系统可以选择在特权(系统)下创建任务。通过设置优先级参数的位portPRIVILEGE_BIT来创建任务。 例如例如,要创建一个优先级为2的特权任务,uxPriority参数应该被设置为 ( 2 | portPRIVILEGE_BIT )。
        pvCreatedTask: 用于传回一个句柄,创建的任务可以通过它来引用。可以被引用。
        xCoreID: 如果该值为tskNO_AFFINITY,则创建的任务不被钉在任何CPU上。钉在任何CPU上,调度器可以在任何可用的核心上运行它。值为0或1时,表示该任务应该被钉在CPU上的索引号。被钉住。指定大于(portNUM_PROCESSORS - 1)的值将导致函数失败。导致该函数失败。
                  如果任务被成功创建并添加到准备好的列表中,返回pdPASS。列表中,否则会有一个错误代码,该代码在文件projdefs.h中定义。
    */
    xTaskCreatePinnedToCore(
           TaskBlink
        ,  "TaskBlink"   // A name just for humans
        ,  1024  // This stack size can be checked & adjusted by reading the Stack Highwater
        ,  NULL
        ,  2  // Priority, with 3 (configMAX_PRIORITIES - 1) being the highest, and 0 being the lowest.
        ,   (TaskHandle_t*) &TaskBlink_Handle
        ,  ARDUINO_RUNNING_CORE); 

    xTaskCreate(
        MailTask,  /* Task function. */
        "MailTask", /* String with name of task. */
        4096,      /* Stack size in bytes. */
        NULL,      /* Parameter passed as input of the task */
        3,         /* Priority of the task.(configMAX_PRIORITIES - 1 being the highest, and 0 being the lowest.) */
        (TaskHandle_t*) &MailTask_Handle);     /* Task handle. */

	//xTaskCreate(carOutTask, "carOutTask", 1024 * 5, NULL, 1, NULL);
    USBSerial.println("Tasks launched ...");
 }

static uint32_t g_uiCount = 0; /* 设置为静态变量,方便查看数据更新 */

void TC4_Handler(void)
{
	BaseType_t xHigherPriorityTaskWoken = pdFALSE;

	xTaskNotifyFromISR(MailTask_Handle,        /* 目标任务 *///这里是句柄,不是函数名,不然会进入硬件错误
			           g_uiCount++,             /* 发送数据 */
			           eSetValueWithOverwrite,  /* 如果目标任务上次的数据还没有处理,上次的数据会被覆盖 */
	                   &xHigherPriorityTaskWoken);

	/* 如果 xHigherPriorityTaskWoken = pdTRUE,那么退出中断后切到当前最高优先级任务执行 */
	if(pdTRUE ==xHigherPriorityTaskWoken)
		portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}


void ARDUINO_ISR_ATTR onTimer(){

  TC4_Handler();
}

void setup()
{
    USBSerial.begin(115200);
    // Use 1st timer of 4 (counted from zero).
    // Set 80 divider for prescaler (see ESP32 Technical Reference Manual for more
    // info).
    timer = timerBegin(0, 80, true);
    // Attach onTimer function to our timer.
    timerAttachInterrupt(timer, &onTimer, true);
    // Set alarm to call onTimer function every second (value in microseconds).
    // Repeat the alarm (third parameter)
    timerAlarmWrite(timer, 1000000, true);
    // Start an alarm
    timerAlarmEnable(timer);

    delay(10);
    app_main();
}

void loop()
{

}

4 事件组

/*
//  多线程基于FreeRTOS,可以多个任务并行处理;
//  ESP32具有两个32位Tensilica Xtensa LX6微处理器;
//  实际上我们用Arduino进行编程时只使用到了第一个核(大核),第0核并没有使用
//  多线程可以指定在那个核运行;
 */
#include <Arduino.h>
#define USE_MULTCORE 0
hw_timer_t * timer = NULL;

static TaskHandle_t TaskBlink_Handle = NULL;
static TaskHandle_t MailTask_Handle = NULL;
#define USE_CHAR 1   /* 测试字符串的时候配置为 1 ,测试变量配置为 0 */
#define KEY1_EVENT (0x01 << 0)//设置事件掩码的位 0
#define KEY2_EVENT (0x01 << 1)//设置事件掩码的位 1

uint8_t ucCount = 0;
bool state = 0;
bool flg = 0;
/*
任务通知也可用来向任务发送数据,但是相对于用队列发送消息,任务通知向任务发送消息会受到很多限制!
        1)只能发送 32 位的数据值。
        2)消息被保存为任务的任务通知值,而且一次只能保存一个任务通知值,相当于队列长度为1。
因此说任务通知可以模拟一个轻量级的消息邮箱而不是轻量级的消息队列。

任务通知值就是消息邮箱的值
*/

void MailTask(void *pvParam) 
{
    uint32_t task_num = pdTRUE;
    BaseType_t xReturn = pdTRUE;
    const TickType_t xMaxBlockTime = pdMS_TO_TICKS(500); /* 设置最大等待时间为 500ms */
    uint32_t r_event=0;//接收事件
    uint32_t last_event=0;//保存事件
    while (1) 
    {
        /*
        第一个参数 ulBitsToClearOnEntry 的作用(函数执行前):
            ulNotifiedValue &= ~ulBitsToClearOnEntry
            简单的说就是参数 ulBitsToClearOnEntry 那个位是 1,那么 ulNotifiedValue的那个位就会被清零。
            这里 ulBitsToClearOnEntry = 0x00000000 就是函数执行前保留所有位。
    
        第二个参数 ulBitsToClearOnExit 的作用(函数退出前):
                ulNotifiedValue &= ~ulBitsToClearOnExit
            简单的说就是参数 ulBitsToClearOnEntry 那个位是 1,那么 ulNotifiedValue的那个位就会被清零。
            这里 ulBitsToClearOnExit = 0xFFFFFFFF 就是函数退出前清楚所有位。
        注: ulNotifiedValue 表示任务 LED_Task 的任务控制块里面的变量,用来做消息邮箱数据的存取。
		*/
		/*获取一个任务通知,没有获取到一直等待 */
		xReturn=xTaskNotifyWait(0x0,//进入函数的时候不清除任务 bit
								0xFFFFF,//退出函数的时候清除所有的 bitR
								&r_event,//保存任务通知值
								portMAX_DELAY);//没获取到则一直等待
		/* 此函数只会返回 pdPASS */
		if(pdTRUE ==xReturn)
		{
			last_event |= r_event;
			if(last_event ==(KEY1_EVENT|KEY2_EVENT) )
			{
				last_event = 0; /* 上一次的事件清零 */
				USBSerial.printf ( "Key1 与 Key2 都按下\n");			
			}
			else /* 否则就更新事件 */
				last_event = r_event; /* 更新上一次触发的事件 */
                USBSerial.printf ("接收事件数据r_event = %d\r\n", r_event);	
		}			
    }
}

void TaskBlink(void *pvParameters)  // This is a task.
{
  (void) pvParameters;
  bool ON = true;
  pinMode(48, OUTPUT);
  for (;;) // A Task shall never return or exit.
  {
    if (state == true)
    {
        digitalWrite(48, LOW);
    }
    else
    {
        digitalWrite(48, HIGH);

    }
    state = !state;
    vTaskDelay(pdMS_TO_TICKS(5000));
  }
}

 //测试用的主函数
 void app_main(void)
 {
    int i;

    // Now set up two tasks to run independently.
    /**
        pvTaskCode: 指向任务输入函数的指针。 任务必须实现永不返回(即连续循环),或者应该使用vTaskDelete函数终止。
        pcName 任务的描述性名称。 这主要是为了方便调试。 最大长度由configMAX_TASK_NAME_LEN定义 - 默认是16。
        usStackDepth: 任务栈的大小,以字节数表示。字节数。注意,这与vanilla FreeRTOS不同。
        pvParameters: 指针将被用作任务的参数。正在创建。
        uxPriority: 任务运行的优先级。 系统包括MPU支持的系统可以选择在特权(系统)下创建任务。通过设置优先级参数的位portPRIVILEGE_BIT来创建任务。 例如例如,要创建一个优先级为2的特权任务,uxPriority参数应该被设置为 ( 2 | portPRIVILEGE_BIT )。
        pvCreatedTask: 用于传回一个句柄,创建的任务可以通过它来引用。可以被引用。
        xCoreID: 如果该值为tskNO_AFFINITY,则创建的任务不被钉在任何CPU上。钉在任何CPU上,调度器可以在任何可用的核心上运行它。值为0或1时,表示该任务应该被钉在CPU上的索引号。被钉住。指定大于(portNUM_PROCESSORS - 1)的值将导致函数失败。导致该函数失败。
                  如果任务被成功创建并添加到准备好的列表中,返回pdPASS。列表中,否则会有一个错误代码,该代码在文件projdefs.h中定义。
    */
    xTaskCreatePinnedToCore(
           TaskBlink
        ,  "TaskBlink"   // A name just for humans
        ,  4096  // This stack size can be checked & adjusted by reading the Stack Highwater
        ,  NULL
        ,  2  // Priority, with 3 (configMAX_PRIORITIES - 1) being the highest, and 0 being the lowest.
        ,   (TaskHandle_t*) &TaskBlink_Handle
        ,  ARDUINO_RUNNING_CORE); 

    xTaskCreate(
        MailTask,  /* Task function. */
        "MailTask", /* String with name of task. */
        4096,      /* Stack size in bytes. */
        NULL,      /* Parameter passed as input of the task */
        3,         /* Priority of the task.(configMAX_PRIORITIES - 1 being the highest, and 0 being the lowest.) */
        (TaskHandle_t*) &MailTask_Handle);     /* Task handle. */

	//xTaskCreate(carOutTask, "carOutTask", 1024 * 5, NULL, 1, NULL);
    USBSerial.println("Tasks launched ...");
 }

static uint32_t g_uiCount = 0; /* 设置为静态变量,方便查看数据更新 */

void TC4_Handler(void)
{
    BaseType_t xHigherPriorityTaskWoken = pdFALSE;
    char test_str[]="1S定时间隔测试邮箱通知";
    
    if (flg == true)
    {
        xTaskNotifyFromISR( (xTaskHandle)MailTask_Handle,
        (uint32_t)KEY1_EVENT, //要触发的事件
        (eNotifyAction)eSetBits,//设置任务通知值中的位
        /*eNoAction = 0,通知任务而不更新其通知值。
        * eSetBits, 设置任务通知值中的位。
        * eIncrement, 增加任务的通知值。
        * eSetvaluewithoverwrite,覆盖当前通知
        * eSetValueWithoutoverwrite 不覆盖当前通知
        */
        &xHigherPriorityTaskWoken 
        ); 
    }
    else
    {
        xTaskNotifyFromISR( (xTaskHandle)MailTask_Handle,
        (uint32_t)KEY2_EVENT, //要触发的事件
        (eNotifyAction)eSetBits,//设置任务通知值中的位
        /*eNoAction = 0,通知任务而不更新其通知值。
        * eSetBits, 设置任务通知值中的位。
        * eIncrement, 增加任务的通知值。
        * eSetvaluewithoverwrite,覆盖当前通知
        * eSetValueWithoutoverwrite 不覆盖当前通知
        */
        &xHigherPriorityTaskWoken 
        ); 
    }
    flg = !flg;

	/* 如果 xHigherPriorityTaskWoken = pdTRUE,那么退出中断后切到当前最高优先级任务执行 */
	if(pdTRUE ==xHigherPriorityTaskWoken)
		portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}


void ARDUINO_ISR_ATTR onTimer(){
  TC4_Handler();
}

void setup()
{
    USBSerial.begin(115200);
    // Use 1st timer of 4 (counted from zero).
    // Set 80 divider for prescaler (see ESP32 Technical Reference Manual for more
    // info).
    timer = timerBegin(0, 80, true);
    // Attach onTimer function to our timer.
    timerAttachInterrupt(timer, &onTimer, true);
    // Set alarm to call onTimer function every second (value in microseconds).
    // Repeat the alarm (third parameter)
    timerAlarmWrite(timer, 1000000, true);
    // Start an alarm
    timerAlarmEnable(timer);

    delay(10);
    app_main();
}

void loop()
{

}