FreeRTOS_直接任务通知(Driect Task Notifications)

1、简介

我们使用队列、信号量、事件组时,都需要提前创建对应的结构体,双方通过中间的结构体进行通信。
在这里插入图片描述
使用任务通知时,允许一个任务直接通知另一个任务进行操作,不需要经过中间结构体。任务通知的结构体是由FreeRTOS内核在任务创建时自动创建的,并且会直接关联到相应的任务句柄上。(TCB结构体中包括两个成员:一个8位的通知状态变量;一个32位的通知值变量
在这里插入图片描述

  • ISR只能发送任务通知,无法接收任务通知。
  • 通知的数据只能由相应的任务独享。
  • 发送方无法进入堵塞状态,接收方可以堵塞等待。

2、TCB成员

TCB(Task Control Block)结构体中有两个成员:

  • uint8_t类型,用来表示通知状态
  • uint32_t类型,用来表示通知值
typedef struct tskTaskControlBlock
{
     ···
	#if( configUSE_TASK_NOTIFICATIONS == 1 )
		volatile uint32_t ulNotifiedValue;
		volatile uint8_t ucNotifyState;
	#endif
    ···
} tskTCB;

通知状态有以下三种取值:

  • taskNOT_WAITING_NOTIFICATION 表示接收任务未等待任何通知
  • taskWAITING_NOTIFICATION 表示接收任务当前正在等待通知
  • taskNOTIFICATION_RECEIVED 表示接收任务已经收到通知。

通知值可以有很多类型:

  • 计数值
  • 位(类似事件组)
  • 任意数值

所以任务通知可以取代信号量、事件组使用。

3、任务通知API

任务通知也有两套函数,一套简化版、一套专业版。

简化版本专业版本
发送通知xTaskNotifyGive/ vTaskNotifyGiveFromISRxTaskNotify /xTaskNotifyFromISR
等待通知ulTaskNotifyTakexTaskNotifyWait

3.1、简化版

3.1.1、xTaskNotifyGive()

发送通知

  • 使得通知值加一
  • 使对应任务的通知状态变为taskNOTIFICATION_RECEIVED ,表示接收到数据了。
/*任务用*/
BaseType_t xTaskNotifyGive( TaskHandle_t xTaskToNotify );
/*ISR用*/
void vTaskNotifyGiveFromISR( 
		TaskHandle_t xTaskHandle,
	    BaseType_t *pxHigherPriorityTaskWoken );

参数描述
xTaskToNotify任务句柄,表示给哪个任务发通知
pxHigherPriorityTaskWoken被通知的任务,可能正处于阻塞状态。 此函数发出通知后,会把它从阻塞状态切换为就绪态。 如果被唤醒的任务的优先级,高于当前任务的优先级, 则"*pxHigherPriorityTaskWoken"被设置为pdTRUE, 这表示在中断返回之前要进行任务切换。
返回值xTaskNotifyGive返回pdPASS ;vTaskNotifyGiveFromISR无返回值

3.1.2、ulTaskNotifyTake()

接收通知

  • 如果通知值等于0,可以进入阻塞;若通知值大于0,则进入就绪态
  • 在函数返回前,可以将通知值减一或清零
  • 相当于二进制、计数型信号量。
uint32_t ulTaskNotifyTake( 
		BaseType_t xClearCountOnExit, 
		TickType_t xTicksToWait );
参数描述
xClearCountOnExitpdTRUE:函数返回前将通知值清零;pdFALSE,若通知值大于0,将其减一
xTicksToWait如果无法获得通知,可以进入阻塞态,xTicksToWait 表示阻塞时间。若设为0,则会直接返回;若设为portMAX_DELAY,则一直等待到有通知。
返回值函数返回之前,在清零或减一之前的通知值。 如果xTicksToWait非0,则返回值有2种情况: 1. 大于0:在超时前,通知值被增加了 2. 等于0:一直没有其他任务增加通知值,最后超时返回0

3.2、专业版

专业版本的发送通知函数xTaskNotify可以实现更多的功能

  • 让接收任务的通知值加一:这时 xTaskNotify() 等同于 xTaskNotifyGive()
  • 设置接收任务的通知值的某一位、某些位,这就是一个轻量级的、更高效的事件组
  • 把一个新值写入接收任务的通知值:上一次的通知值被读走后,写入才成功。这就是轻量级的、长度为1的队列
  • 用一个新值覆盖接收任务的通知值:无论上一次的通知值是否被读走,覆盖都成功。类似 xQueueOverwrite() 函数,这就是轻量级的邮箱。

接收通知函数功能

  • 可以让任务等待(可以加上超时时间),等到任务状态为"pending"(也就是有数据)
  • 还可以在函数进入、退出时,清除通知值的指定位

3.2.1、xTaskNotify()

发送通知函数:

/*任务中使用*/
BaseType_t xTaskNotify( 
		TaskHandle_t xTaskToNotify,
		uint32_t ulValue, 
 		eNotifyAction eAction );
/*ISR中使用*/
BaseType_t xTaskNotifyFromISR( 
		TaskHandle_t xTaskToNotify,
		uint32_t ulValue, 
		eNotifyAction eAction, 
		BaseType_t *pxHigherPriorityTaskWoken );
参数描述
xTaskToNotify任务句柄,表示给哪个任务发通知
ulValue参数ulValue的使用由 eAction决定
eAction见下表
pxHigherPriorityTaskWoken一旦pxHigherPriorityTaskWoken 被设置为 pdTRUE,FreeRTOS 内核将根据被通知任务的优先级和其他任务的优先级进行上下文切换,将控制权交给被唤醒的高优先级任务。这样可以确保高优先级任务尽快运行,提高系统的响应性和实时性
返回值pdPASS:成功

eAction 参数说明:

eAction 取值描述
eNoAction仅仅是更新通知状态为"pending",未使用ulValue。 这个选项相当于轻量级的、更高效的二进制信号量。
eSetBits通知值 = 原来的通知值
eIncrement通知值 = 原来的通知值 + 1,未使用ulValue。 相当于轻量级的、更高效的二进制信号量、计数型信号量。 相当于**xTaskNotifyGive()**函数。
eSetValueWithoutOverwrite不覆盖。 如果通知状态为"pending"(表示有数据未读), 则此次调用xTaskNotify不做任何事,返回pdFAIL。 如果通知状态不是"pending"(表示没有新数据), 则:通知值 = ulValue。
eSetValueWithOverwrite覆盖。 无论如何,不管通知状态是否为"pendng", 通知值 = ulValue。

3.2.2、xTaskNotifyWait()

接收通知函数:

BaseType_t xTaskNotifyWait( 
		uint32_t ulBitsToClearOnEntry, 
		uint32_t ulBitsToClearOnExit, 
		uint32_t *pulNotificationValue, 
		TickType_t xTicksToWait );
参数描述
ulBitsToClearOnEntry在等待事件发生之前,需要清除旧通知值的特定位。只有当通知状态不是"pending"时,才会执行清零操作。 如果传入值是 0x01,表示要清除通知值的 bit0。如果传入值是 0xffffffff 或者 ULONG_MAX,表示要清除所有位,即将通知值设置为0
ulBitsToClearOnExit在函数退出前,如果不是因为超时推出,而是因为得到了数据而退出时: 通知值 = 通知值 & ~(ulBitsToClearOnExit)。 在清除某些位之前,通知值先被赋给"*pulNotificationValue"。 比如入0x03,表示清除通知值的bit0、bit1; 传入0xffffffff即ULONG_MAX,表示清除所有位,即把值设置为0
pulNotificationValue用来取出通知值。 在函数退出时,使用ulBitsToClearOnExit清除之前,把通知值赋给"*pulNotificationValue"。 如果不需要取出通知值,可以设为NULL
xTicksToWait任务进入阻塞态的超时时间,它在等待通知状态变为"pending"。 0:不等待,即刻返回; portMAX_DELAY:一直等待,直到通知状态变为"pending"; 其他值:Tick Count,可以用*pdMS_TO_TICKS()*把ms转换为Tick Count
返回值pdPASS:成功 这表示xTaskNotifyWait成功获得了通知: pdFAIL:没有得到通知。
### FreeRTOS Socket 使用方法及示例 #### 一、FreeRTOS中的Socket API概述 在嵌入式系统开发中,网络通信是不可或缺的一部分。FreeRTOS提供了丰富的API用于TCP/IP协议栈操作,其中包括Socket接口。这些接口允许开发者创建基于TCP或UDP的应用程序[^1]。 对于FreeRTOS环境下的socket编程来说,主要涉及如下几个核心函数: - `FreeRTOS_socket()`:创建一个新的套接字实例。 - `FreeRTOS_bind()` :绑定本地地址到已创建的套接字上。 - `FreeRTOS_listen()`: 对于服务器端而言,在给定监听队列长度的情况下使套接字进入监听状态等待连接请求到来。 - `FreeRTOS_accept()` : 接受来自客户端的新连接并返回新的连接描述符。 - `FreeRTOS_connect()` : 客户端调用此函数发起与远程主机之间的连接建立过程。 - `FreeRTOS_sendto()/FreeRTOS_write()` 和 `FreeRTOS_recvfrom()/FreeRTOS_read()` 分别用来发送/接收数据报文或者流形式的数据包。 - `FreeRTOS_closesocket()` : 关闭不再使用的套接字资源释放相关联的所有内存空间。 #### 二、简单TCP Server实现案例 下面给出一段简单的TCP服务端代码片段作为例子展示如何利用上述提到的一些基本API完成一个可以接受外部设备连接的服务端应用逻辑: ```c #include "FreeRTOS.h" #include "task.h" #include "semphr.h" #include "lwip/api_shell.h" #define SERVER_PORT (80) void prvTCPServerTask(void *pvParameters) { struct freertos_sockaddr server_addr; int listen_fd, new_conn_fd; /* 创建监听套接字 */ if ((listen_fd = FreeRTOS_socket(FREERTOS_AF_INET, FREERTOS_SOCK_STREAM, FREERTOS_IPPROTO_TCP)) != FREERTOS_INVALID_SOCKET) { memset(&server_addr, 0, sizeof(server_addr)); server_addr.sin_family = FREERTOS_AF_INET; server_addr.sin_port = FreeRTOS_htons(SERVER_PORT); /* 绑定地址信息至套接字 */ if (FreeRTOS_bind(listen_fd, &server_addr, sizeof(server_addr)) == 0) { /* 开始监听传入连接 */ if (FreeRTOS_listen(listen_fd, 5) == 0) { while (1) { /* 阻塞直到有新连接到达 */ new_conn_fd = FreeRTOS_accept(listen_fd, NULL, 0); if (new_conn_fd >= 0) { char buffer[64]; /* 处理接收到的消息 */ size_t received_bytes = FreeRTOS_recv(new_conn_fd, buffer, sizeof(buffer), 0); if(received_bytes>0){ //处理消息... /* 发送响应 */ const char* response="HTTP/1.1 200 OK\r\nContent-Type:text/html;charset=UTF-8\r\nConnection:close\r\n\r\n<html><body>Hello from FreeRTOS TCP Server!</body></html>"; FreeRTOS_send(new_conn_fd,response,strlen(response),0); } /* 关闭当前会话对应的套接字 */ FreeRTOS_closesocket(new_conn_fd); } } } /* 清理工作 */ FreeRTOS_closesocket(listen_fd); } } } ``` 这段代码展示了怎样通过FreeRTOS提供的API构建一个能够回应HTTP GET请求的基础版Web服务器。当浏览器访问运行着该任务的目标板卡IP地址加上指定端口号时(例如http://192.168.x.y:80),将会看到一条欢迎语句显示出来。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值