有關FreeRTOS中的函數后面都會講到,前面章節部分主要是說一下原理,看的時候比較乏味,了解即可,沒必要較真。
1-簡介
FreeRTOS流和緩沖區是任務和任務之間、中斷和任務之間的通信原語。和其他多數FreeRTOS通信原語不同,他們針對讀和寫的單一性進行了方案的優化工作。例如將數據從中斷服務例程傳遞到任務,或在雙核cpu上從一個微控制器內核傳遞到另一個微控制器內核。數據通過拷貝傳遞——發送端將數據復制到緩沖區,讀取端將數據復制出緩沖區。
流緩沖區傳遞連續的字節流。消息緩沖區傳遞大小可變但離散的消息。消息緩沖區使用流緩沖區進行數據傳輸。
在FreeRTOS對象是惟一的,流緩沖區的實現(也是消息緩沖區的實現,因為消息緩沖區是建立在流緩沖區之上的)假設只有一個任務或中斷將寫入緩沖區(寫入器),并且僅有一個任務或中斷將從緩沖區讀取(讀取器)。對不同的任務或者中斷寫入或者讀取是安全的,但是有多個不同的寫入或者讀取是不安全的。若有多個不同的寫入器,那么程序寫入必須每次對寫入的API函數(例如xStreamBufferSend())的調用都放在臨界區中,并將發送阻塞時間設置為0。同樣,如果有多個不同的讀取器,那么應用程序編寫器必須將每次對讀取API函數(如xStreamBufferReceive())的調用都放在臨界區中,并使用0的接收塊時間。
2-中斷服務到任務流
2.1 介紹
流緩沖區允許一個字節流從中斷服務程序傳遞到一個任務,或者從一個任務傳遞到另一個任務。字節流可以是任意長度的,而且不一定有開頭或結尾。一次可以寫入任意數量的字節,一次可以讀取任意數量的字節。數據通過復制傳遞——發送端將數據復制到緩沖區,讀取端將數據復制出緩沖區。
與大多數其他FreeRTOS通信原語不同,流緩沖區是針對單讀單寫場景進行優化的,例如將數據從中斷服務程序傳遞到任務,或在雙核CPU上從一個微控制器內核傳遞到另一個微控制器內核。
在FreeRTOS/source/stream_buffer.c源文件來啟用流緩沖區功能。可自行查閱。
流緩沖區的實現使用了任務的通知。因此,調用將調用任務置于阻塞狀態的流緩沖區API函數可以更改調用任務的通知狀態和值。
2.2 快速入門
在reeRTOS/Demo/Common/Minimal/StreamBufferInterrupt.c源文件 提供了一個示例,說明從中斷服務程序到任務使用流緩沖區傳遞數據的例程。在這里我給大家復制過來了。
1/*
2* FreeRTOS V202112.00
3* Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
4*
5* Permission is hereby granted, free of charge, to any person obtaining a copy of
6* this software and associated documentation files (the "Software"), to deal in
7* the Software without restriction, including without limitation the rights to
8* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9* the Software, and to permit persons to whom the Software is furnished to do so,
10* subject to the following conditions:
11*
12* The above copyright notice and this permission notice shall be included in all
13* copies or substantial portions of the Software.
14*
15* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21*
22* http://www.FreeRTOS.org
23* http://aws.amazon.com/freertos
24*
25* 1 tab == 4 spaces!
26*/
27
28/*
29* A simple example that shows a stream buffer being used to pass data from an
30* interrupt to a task.
31*
32* There are two strings, pcStringToSend and pcStringToReceive, where
33* pcStringToReceive is a substring of pcStringToSend. The interrupt sends
34* a few bytes of pcStringToSend to a stream buffer ever few times that it
35* executes. A task reads the bytes from the stream buffer, looking for the
36* substring, and flagging an error if the received data is invalid.
37*/
38
39/* Standard includes. */
40#include "stdio.h"
41#include "string.h"
42
43/* FreeRTOS includes. */
44#include "FreeRTOS.h"
45#include "task.h"
46#include "stream_buffer.h"
47
48/* Demo app includes. */
49#include "StreamBufferInterrupt.h"
50
51#define sbiSTREAM_BUFFER_LENGTH_BYTES ( ( size_t ) 100 )
52#define sbiSTREAM_BUFFER_TRIGGER_LEVEL_10 ( ( BaseType_t ) 10 )
53
54/*-----------------------------------------------------------*/
55
56/* Implements the task that receives a stream of bytes from the interrupt. */
57static void prvReceivingTask( void * pvParameters );
58
59/*-----------------------------------------------------------*/
60
61/* The stream buffer that is used to send data from an interrupt to the task. */
62static StreamBufferHandle_t xStreamBuffer = NULL;
63
64/* The string that is sent from the interrupt to the task four bytes at a
65* time. Must be multiple of 4 bytes long as the ISR sends 4 bytes at a time*/
66static const char * pcStringToSend = "_____Hello FreeRTOS_____";
67
68/* The string to task is looking for, which must be a substring of
69* pcStringToSend. */
70static const char * pcStringToReceive = "Hello FreeRTOS";
71
72/* Set to pdFAIL if anything unexpected happens. */
73static BaseType_t xDemoStatus = pdPASS;
74
75/* Incremented each time pcStringToReceive is correctly received, provided no
76* errors have occurred. Used so the check task can check this task is still
77* running as expected. */
78static uint32_t ulCycleCount = 0;
79
80/*-----------------------------------------------------------*/
81
82void vStartStreamBufferInterruptDemo( void )
83{
84/* Create the stream buffer that sends data from the interrupt to the
85* task, and create the task. */
86xStreamBuffer = xStreamBufferCreate( /* The buffer length in bytes. */
87sbiSTREAM_BUFFER_LENGTH_BYTES,
88/* The stream buffer's trigger level. */
89sbiSTREAM_BUFFER_TRIGGER_LEVEL_10 );
90
91xTaskCreate( prvReceivingTask, /* The function that implements the task. */
92"StrIntRx", /* Human readable name for the task. */
93configMINIMAL_STACK_SIZE, /* Stack size (in words!). */
94NULL, /* Task parameter is not used. */
95tskIDLE_PRIORITY + 2, /* The priority at which the task is created. */
96NULL ); /* No use for the task handle. */
97}
98/*-----------------------------------------------------------*/
99
100static void prvReceivingTask( void * pvParameters )
101{
102char cRxBuffer[ 20 ];
103BaseType_t xNextByte = 0;
104
105/* Remove warning about unused parameters. */
106( void ) pvParameters;
107
108/* Make sure the string will fit in the Rx buffer, including the NULL
109* terminator. */
110configASSERT( sizeof( cRxBuffer ) > strlen( pcStringToReceive ) );
111
112/* Make sure the stream buffer has been created. */
113configASSERT( xStreamBuffer != NULL );
114
115/* Start with the Rx buffer in a known state. */
116memset( cRxBuffer, 0x00, sizeof( cRxBuffer ) );
117
118for( ; ; )
119{
120/* Keep receiving characters until the end of the string is received.
121* Note: An infinite block time is used to simplify the example. Infinite
122* block times are not recommended in production code as they do not allow
123* for error recovery. */
124xStreamBufferReceive( /* The stream buffer data is being received from. */
125xStreamBuffer,
126/* Where to place received data. */
127( void * ) &( cRxBuffer[ xNextByte ] ),
128/* The number of bytes to receive. */
129sizeof( char ),
130
131/* The time to wait for the next data if the buffer
132* is empty. */
133portMAX_DELAY );
134
135/* If xNextByte is 0 then this task is looking for the start of the
136* string, which is 'H'. */
137if( xNextByte == 0 )
138{
139if( cRxBuffer[ xNextByte ] == 'H' )
140{
141/* The start of the string has been found. Now receive
142* characters until the end of the string is found. */
143xNextByte++;
144}
145}
146else
147{
148/* Receiving characters while looking for the end of the string,
149* which is an 'S'. */
150if( cRxBuffer[ xNextByte ] == 'S' )
151{
152/* The string has now been received. Check its validity. */
153if( strcmp( cRxBuffer, pcStringToReceive ) != 0 )
154{
155xDemoStatus = pdFAIL;
156}
157
158/* Return to start looking for the beginning of the string
159* again. */
160memset( cRxBuffer, 0x00, sizeof( cRxBuffer ) );
161xNextByte = 0;
162
163/* Increment the cycle count as an indication to the check task
164* that this demo is still running. */
165if( xDemoStatus == pdPASS )
166{
167ulCycleCount++;
168}
169}
170else
171{
172/* Receive the next character the next time around, while
173* continuing to look for the end of the string. */
174xNextByte++;
175
176configASSERT( ( size_t ) xNextByte < sizeof( cRxBuffer ) );
177}
178}
179}
180}
181/*-----------------------------------------------------------*/
182
183void vBasicStreamBufferSendFromISR( void )
184{
185static size_t xNextByteToSend = 0;
186const BaseType_t xCallsBetweenSends = 100, xBytesToSend = 4;
187static BaseType_t xCallCount = 0;
188
189/* Is it time to write to the stream buffer again? */
190xCallCount++;
191
192if( xCallCount > xCallsBetweenSends )
193{
194xCallCount = 0;
195
196/* Send the next four bytes to the stream buffer. */
197xStreamBufferSendFromISR( xStreamBuffer,
198( const void * ) ( pcStringToSend + xNextByteToSend ),
199xBytesToSend,
200NULL );
201
202/* Send the next four bytes the next time around, wrapping to the start
203* of the string if necessary. */
204xNextByteToSend += xBytesToSend;
205
206if( xNextByteToSend >= strlen( pcStringToSend ) )
207{
208xNextByteToSend = 0;
209}
210}
211}
212/*-----------------------------------------------------------*/
213
214BaseType_t xIsInterruptStreamBufferDemoStillRunning( void )
215{
216uint32_t ulLastCycleCount = 0;
217
218/* Check the demo is still running. */
219if( ulLastCycleCount == ulCycleCount )
220{
221xDemoStatus = pdFAIL;
222}
223else
224{
225ulLastCycleCount = ulCycleCount;
226}
227
228return xDemoStatus;
229}
2.2 阻塞讀取和觸發電平
xStreamBufferReceive()用于讀取來自 RTOS 任務的流緩沖區的數據。xStreamBufferReceiveFromISR())是 用于從中斷服務例程 (ISR) 從流緩沖區中讀取數據。
下面是這兩個函數的用法舉例:
1//1-xStreamBufferReceive()
2void vAFunction( StreamBuffer_t xStreamBuffer )
3{
4uint8_t ucRxData[ 20 ];
5size_t xReceivedBytes;
6const TickType_t xBlockTime = pdMS_TO_TICKS( 20 );
7
8/* Receive up to another sizeof( ucRxData ) bytes from the stream buffer.
9Wait in the Blocked state (so not using any CPU processing time) for a
10maximum of 100ms for the full sizeof( ucRxData ) number of bytes to be
11available. */
12xReceivedBytes = xStreamBufferReceive( xStreamBuffer,
13( void * ) ucRxData,
14sizeof( ucRxData ),
15xBlockTime );
16
17if( xReceivedBytes > 0 )
18{
19/* A ucRxData contains another xRecievedBytes bytes of data, which can
20be processed here.... */
21}
22}
23
242--xStreamBufferReceiveFromISR()
25
26/* A stream buffer that has already been created. */
27StreamBuffer_t xStreamBuffer;
28
29void vAnInterruptServiceRoutine( void )
30{
31uint8_t ucRxData[ 20 ];
32size_t xReceivedBytes;
33BaseType_t xHigherPriorityTaskWoken = pdFALSE; /* Initialised to pdFALSE. */
34
35/* Receive the next stream from the stream buffer. */
36xReceivedBytes = xStreamBufferReceiveFromISR( xStreamBuffer,
37( void * ) ucRxData,
38sizeof( ucRxData ),
39&xHigherPriorityTaskWoken );
40
41if( xReceivedBytes > 0 )
42{
43/* ucRxData contains xReceivedBytes read from the stream buffer.
44Process the stream here.... */
45}
46
47/* If xHigherPriorityTaskWoken was set to pdTRUE inside
48xStreamBufferReceiveFromISR() then a task that has a priority above the
49priority of the currently executing task was unblocked and a context
50switch should be performed to ensure the ISR returns to the unblocked
51task. In most FreeRTOS ports this is done by simply passing
52xHigherPriorityTaskWoken into taskYIELD_FROM_ISR(), which will test the
53variables value, and perform the context switch if necessary. Check the
54documentation for the port in use for port specific instructions. */
55taskYIELD_FROM_ISR( xHigherPriorityTaskWoken );
56}
xStreamBufferReceive()允許指定阻塞時間如果任務使用xStreamBufferReceive()從一個空的流緩沖區中讀取數據時指定了一個非零的阻塞時間,那么任務將被放置到阻塞狀態(因此它不會消耗任何CPU時間,其他任務可以運行),直到流緩沖區中有指定數量的數據可用,或者塊時間到期。在等待數據的任務從阻塞狀態移除之前,流緩沖區中必須存在的數據量稱為流緩沖區的觸發級別。例如:
如果一個任務在讀取觸發級別為1的空流緩沖區時被阻塞,那么當向緩沖區寫入一個字節或任務的阻塞時間到期時,該任務將被解除阻塞。
如果一個任務在讀取觸發級別為10的空流緩沖區時被阻塞,那么該任務將不會被解除阻塞,直到流緩沖區包含至少10個字節或任務的阻塞時間到期。
如果讀取任務的阻塞時間在觸發級別到達之前超時,那么無論實際可用的字節數是多少,該任務仍然會接收到。
2.3 阻塞寫入
xStreamBufferSend())用于從RTOS任務向流緩沖區發送數據。xStreamBufferSendFromISR())用于從中斷服務例程(ISR)向流緩沖區發送數據。
如果任務使用xStreamBufferSend()向流緩沖區寫入數據時指定了一個非零的阻塞時間,那么該任務將被放置到阻塞狀態(因此它不會消耗任何CPU時間,其他任務可以運行),直到流緩沖區中有空間可用,或者阻塞時間到達。
2.4 發送和接收的回調
流和消息緩沖區在每次發送和接收操作完成后都會執行一個回調:
使用xStreamBufferCreate()和xMessageBufferCreate() API函數(以及它們靜態分配的等價函數)創建的流和消息緩沖區共享相同的回調函數,這些回調函數是使用sbSEND_COMPLETED()和sbRECEIVE_COMPLETED()宏定義的。
使用xStreamBufferCreateWithCallback()和xMessageBufferCreateWithCallback() API函數(以及它們靜態分配的等價函數)創建的流緩沖區和消息緩沖區都可以有自己唯一的回調函數。
sbSEND_COMPLETED() (and sbSEND_COMPLETED_FROM_ISR())
sbSEND_COMPLETED()是一個宏,當數據寫入使用xStreamBufferCreate()或xStreamBufferCreateStatic() API創建的流緩沖區時,會調用該宏(在FreeRTOS API函數內部)。它接受一個參數,即更新后的流緩沖區句柄。
默認情況下(如果應用程序編寫者沒有提供自己的宏實現),sbSEND_COMPLETED()會檢查是否有任務阻塞在流緩沖區上等待數據,如果有,則從阻塞狀態中刪除該任務。
應用程序編寫人員可以通過在FreeRTOSConfig.h中提供他們自己的sbSEND_COMPLETED()實現來改變這種默認行為。當使用流緩沖區在多核處理器的核之間傳遞數據時,這很有用。在這種情況下,可以實現sbSEND_COMPLETED()在另一個CPU核心中生成一個中斷,然后中斷的服務例程可以使用xStreamBufferSendCompletedFromISR() API函數來檢查(如果必要的話)等待數據的任務。
在FreeRTOS/Demo/Common/Minimal/MessageBufferAMP.c文件中有更詳細的應用過程。有時間到時候會單獨的把FreeRTOS所有函數給介紹一下以及應用。
sbRECEIVE_COMPLETED() (and sbRECEIVE_COMPLETED_FROM_ISR())
sbRECEIVE_COMPLETED()是與sbSEND_COMPLETED()等價的接收方。當從流緩沖區讀取數據時,它會被調用(在FreeRTOS API函數內部)。默認情況下(如果應用程序編寫人員沒有提供自己的宏實現),宏會檢查流緩沖區上是否有任務阻塞,以等待緩沖區內的空間可用,如果有,則將該任務從阻塞狀態中移除。
與bSEND_COMPLETED()一樣,應用程序編寫人員可以通過在FreeRTOSConfig.h(后續會單獨講述這個文件)中提供替代實現來更改sbRECEIVE_COMPLETED()的默認行為。如果你需要每個流緩沖區都有自己的“接收完成”行為,可以使用xStreamBufferCreateWithCallback()或xStreamBufferCreateStaticWithCallback() API函數來創建流緩沖區。
3-內核到內核
3.1 介紹
消息緩沖區允許不同字節的離散消息從中斷服務程序傳遞到一個進程,或從一個進程傳遞到另一個進程。例如,長度為10、20和123字節的消息都可以寫入和讀取到同一個消息緩沖區。與使用流緩沖區不同的是,10字節的消息只能作為10字節消息讀取,而不能作為單個字節讀取。消息緩沖區建立在流緩沖區之上(也就是說,它們使用流緩沖區的實現)。
數據通過復制的方式通過消息緩沖區——發送方將數據復制到緩沖區中,讀取方將數據復制出緩沖區。
在文件目錄FreeRTOS/Demo/Common/Minimal/MessageBufferAMP.c下提供了一個從一個MCU到另一個MCU的數據,可以看看。
3.2消息緩沖區大小
為了能讓消息緩沖區能夠處理不同字節的消息,每個消息的字節大小在消息之前寫入消息緩沖區(這在FreeRTOS API函數內部發生)。字節長度保存在一個變量中,其類型由FreeRTOSConfig.h中的configMESSAGE_BUFFER_LENGTH_TYPE常數設置。
如果沒有額外的定義,configMESSAGE_BUFFER_LENGTH_TYPE默認為size_t類型,,size_t在32位體系結構上通常是4字節。所以呢,當configMESSAGE_BUFFER_LENGTH_TYPE為4字節時,向緩沖區寫入一個10字節的消息,但是實際上占用的字節是超過10個字節的,實際上是占用了14個字節。同樣,把100個字節的消息寫入到緩沖區,實際上占用104個字節空間。
3.3 阻塞的讀取和寫入
xMessageBufferReceive())是用來讀取任務消息緩沖區的數據的,xMessageBufferReceiveFromISR())是 用于從中斷服務程序 (ISR) 的消息緩沖區讀取數據。
用于從RTOS任務的消息緩沖區中讀取數據。用于從中斷服務例程(ISR)的消息緩沖區中讀取數據xMessageBufferSend()用于發送 數據從 RTOS 任務發送到消息緩沖區。xMessageBufferSendFromISR()是用于將數據從中斷服務程序 (ISR) 發送到消息緩沖區。
如果在任務中使用xMessageBufferReceive()說明了阻塞時間,從消息緩沖區,而這個緩沖區剛好是空的,則這個函數置于阻塞狀態(因此它不會消耗任何CPU時間,其他任務可以運行),直到數據在消息緩沖區中可用,或阻塞時間到達。)
如果在任務中使用xMessageBufferSend()函數寫入到消息緩沖區,這個緩沖區已經滿了,則任務將會被置位到阻塞狀態(因此它不消耗任何 CPU 時間,并且可以運行其他任務) 直到消息緩沖區中有空間可用或阻塞時間結束。)
-
緩沖區
+關注
關注
0文章
33瀏覽量
9173 -
函數
+關注
關注
3文章
4346瀏覽量
62971 -
FreeRTOS
+關注
關注
12文章
484瀏覽量
62395
發布評論請先 登錄
相關推薦
基于宏高效實現環形緩沖區教程
![基于宏高效<b class='flag-5'>實現</b>環形<b class='flag-5'>緩沖區</b>教程](https://file.elecfans.com/web2/M00/13/13/pYYBAGEwKRyAJkisAAAaP8rq95Y943.png)
環形緩沖區簡介
緩沖區溢出的危害及避免緩沖區溢出的三種方法
緩沖區溢出的危害及避免緩沖區溢出的三種方法
IOS NFC-TAP為什么不讀取FTM緩沖區?
環形緩沖區讀寫操作的分析與實現
緩沖區是啥意思 STM32串口數據接收之環形緩沖區
STM32串口數據接收 --環形緩沖區
![STM32串口數據接收 --環形<b class='flag-5'>緩沖區</b>](https://file.elecfans.com/web1/M00/D9/4E/pIYBAF_1ac2Ac0EEAABDkS1IP1s689.png)
評論