有關(guān)FreeRTOS中的函數(shù)后面都會講到,前面章節(jié)部分主要是說一下原理,看的時候比較乏味,了解即可,沒必要較真。
1-簡介
FreeRTOS流和緩沖區(qū)是任務(wù)和任務(wù)之間、中斷和任務(wù)之間的通信原語。和其他多數(shù)FreeRTOS通信原語不同,他們針對讀和寫的單一性進行了方案的優(yōu)化工作。例如將數(shù)據(jù)從中斷服務(wù)例程傳遞到任務(wù),或在雙核cpu上從一個微控制器內(nèi)核傳遞到另一個微控制器內(nèi)核。數(shù)據(jù)通過拷貝傳遞——發(fā)送端將數(shù)據(jù)復(fù)制到緩沖區(qū),讀取端將數(shù)據(jù)復(fù)制出緩沖區(qū)。
流緩沖區(qū)傳遞連續(xù)的字節(jié)流。消息緩沖區(qū)傳遞大小可變但離散的消息。消息緩沖區(qū)使用流緩沖區(qū)進行數(shù)據(jù)傳輸。
在FreeRTOS對象是惟一的,流緩沖區(qū)的實現(xiàn)(也是消息緩沖區(qū)的實現(xiàn),因為消息緩沖區(qū)是建立在流緩沖區(qū)之上的)假設(shè)只有一個任務(wù)或中斷將寫入緩沖區(qū)(寫入器),并且僅有一個任務(wù)或中斷將從緩沖區(qū)讀取(讀取器)。對不同的任務(wù)或者中斷寫入或者讀取是安全的,但是有多個不同的寫入或者讀取是不安全的。若有多個不同的寫入器,那么程序?qū)懭氡仨毭看螌懭氲腁PI函數(shù)(例如xStreamBufferSend())的調(diào)用都放在臨界區(qū)中,并將發(fā)送阻塞時間設(shè)置為0。同樣,如果有多個不同的讀取器,那么應(yīng)用程序編寫器必須將每次對讀取API函數(shù)(如xStreamBufferReceive())的調(diào)用都放在臨界區(qū)中,并使用0的接收塊時間。
2-中斷服務(wù)到任務(wù)流
2.1 介紹
流緩沖區(qū)允許一個字節(jié)流從中斷服務(wù)程序傳遞到一個任務(wù),或者從一個任務(wù)傳遞到另一個任務(wù)。字節(jié)流可以是任意長度的,而且不一定有開頭或結(jié)尾。一次可以寫入任意數(shù)量的字節(jié),一次可以讀取任意數(shù)量的字節(jié)。數(shù)據(jù)通過復(fù)制傳遞——發(fā)送端將數(shù)據(jù)復(fù)制到緩沖區(qū),讀取端將數(shù)據(jù)復(fù)制出緩沖區(qū)。
與大多數(shù)其他FreeRTOS通信原語不同,流緩沖區(qū)是針對單讀單寫場景進行優(yōu)化的,例如將數(shù)據(jù)從中斷服務(wù)程序傳遞到任務(wù),或在雙核CPU上從一個微控制器內(nèi)核傳遞到另一個微控制器內(nèi)核。
在FreeRTOS/source/stream_buffer.c源文件來啟用流緩沖區(qū)功能。可自行查閱。
流緩沖區(qū)的實現(xiàn)使用了任務(wù)的通知。因此,調(diào)用將調(diào)用任務(wù)置于阻塞狀態(tài)的流緩沖區(qū)API函數(shù)可以更改調(diào)用任務(wù)的通知狀態(tài)和值。
2.2 快速入門
在reeRTOS/Demo/Common/Minimal/StreamBufferInterrupt.c源文件 提供了一個示例,說明從中斷服務(wù)程序到任務(wù)使用流緩沖區(qū)傳遞數(shù)據(jù)的例程。在這里我給大家復(fù)制過來了。
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 阻塞讀取和觸發(fā)電平
xStreamBufferReceive()用于讀取來自 RTOS 任務(wù)的流緩沖區(qū)的數(shù)據(jù)。xStreamBufferReceiveFromISR())是 用于從中斷服務(wù)例程 (ISR) 從流緩沖區(qū)中讀取數(shù)據(jù)。
下面是這兩個函數(shù)的用法舉例:
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()允許指定阻塞時間如果任務(wù)使用xStreamBufferReceive()從一個空的流緩沖區(qū)中讀取數(shù)據(jù)時指定了一個非零的阻塞時間,那么任務(wù)將被放置到阻塞狀態(tài)(因此它不會消耗任何CPU時間,其他任務(wù)可以運行),直到流緩沖區(qū)中有指定數(shù)量的數(shù)據(jù)可用,或者塊時間到期。在等待數(shù)據(jù)的任務(wù)從阻塞狀態(tài)移除之前,流緩沖區(qū)中必須存在的數(shù)據(jù)量稱為流緩沖區(qū)的觸發(fā)級別。例如:
如果一個任務(wù)在讀取觸發(fā)級別為1的空流緩沖區(qū)時被阻塞,那么當向緩沖區(qū)寫入一個字節(jié)或任務(wù)的阻塞時間到期時,該任務(wù)將被解除阻塞。
如果一個任務(wù)在讀取觸發(fā)級別為10的空流緩沖區(qū)時被阻塞,那么該任務(wù)將不會被解除阻塞,直到流緩沖區(qū)包含至少10個字節(jié)或任務(wù)的阻塞時間到期。
如果讀取任務(wù)的阻塞時間在觸發(fā)級別到達之前超時,那么無論實際可用的字節(jié)數(shù)是多少,該任務(wù)仍然會接收到。
2.3 阻塞寫入
xStreamBufferSend())用于從RTOS任務(wù)向流緩沖區(qū)發(fā)送數(shù)據(jù)。xStreamBufferSendFromISR())用于從中斷服務(wù)例程(ISR)向流緩沖區(qū)發(fā)送數(shù)據(jù)。
如果任務(wù)使用xStreamBufferSend()向流緩沖區(qū)寫入數(shù)據(jù)時指定了一個非零的阻塞時間,那么該任務(wù)將被放置到阻塞狀態(tài)(因此它不會消耗任何CPU時間,其他任務(wù)可以運行),直到流緩沖區(qū)中有空間可用,或者阻塞時間到達。
2.4 發(fā)送和接收的回調(diào)
流和消息緩沖區(qū)在每次發(fā)送和接收操作完成后都會執(zhí)行一個回調(diào):
使用xStreamBufferCreate()和xMessageBufferCreate() API函數(shù)(以及它們靜態(tài)分配的等價函數(shù))創(chuàng)建的流和消息緩沖區(qū)共享相同的回調(diào)函數(shù),這些回調(diào)函數(shù)是使用sbSEND_COMPLETED()和sbRECEIVE_COMPLETED()宏定義的。
使用xStreamBufferCreateWithCallback()和xMessageBufferCreateWithCallback() API函數(shù)(以及它們靜態(tài)分配的等價函數(shù))創(chuàng)建的流緩沖區(qū)和消息緩沖區(qū)都可以有自己唯一的回調(diào)函數(shù)。
sbSEND_COMPLETED() (and sbSEND_COMPLETED_FROM_ISR())
sbSEND_COMPLETED()是一個宏,當數(shù)據(jù)寫入使用xStreamBufferCreate()或xStreamBufferCreateStatic() API創(chuàng)建的流緩沖區(qū)時,會調(diào)用該宏(在FreeRTOS API函數(shù)內(nèi)部)。它接受一個參數(shù),即更新后的流緩沖區(qū)句柄。
默認情況下(如果應(yīng)用程序編寫者沒有提供自己的宏實現(xiàn)),sbSEND_COMPLETED()會檢查是否有任務(wù)阻塞在流緩沖區(qū)上等待數(shù)據(jù),如果有,則從阻塞狀態(tài)中刪除該任務(wù)。
應(yīng)用程序編寫人員可以通過在FreeRTOSConfig.h中提供他們自己的sbSEND_COMPLETED()實現(xiàn)來改變這種默認行為。當使用流緩沖區(qū)在多核處理器的核之間傳遞數(shù)據(jù)時,這很有用。在這種情況下,可以實現(xiàn)sbSEND_COMPLETED()在另一個CPU核心中生成一個中斷,然后中斷的服務(wù)例程可以使用xStreamBufferSendCompletedFromISR() API函數(shù)來檢查(如果必要的話)等待數(shù)據(jù)的任務(wù)。
在FreeRTOS/Demo/Common/Minimal/MessageBufferAMP.c文件中有更詳細的應(yīng)用過程。有時間到時候會單獨的把FreeRTOS所有函數(shù)給介紹一下以及應(yīng)用。
sbRECEIVE_COMPLETED() (and sbRECEIVE_COMPLETED_FROM_ISR())
sbRECEIVE_COMPLETED()是與sbSEND_COMPLETED()等價的接收方。當從流緩沖區(qū)讀取數(shù)據(jù)時,它會被調(diào)用(在FreeRTOS API函數(shù)內(nèi)部)。默認情況下(如果應(yīng)用程序編寫人員沒有提供自己的宏實現(xiàn)),宏會檢查流緩沖區(qū)上是否有任務(wù)阻塞,以等待緩沖區(qū)內(nèi)的空間可用,如果有,則將該任務(wù)從阻塞狀態(tài)中移除。
與bSEND_COMPLETED()一樣,應(yīng)用程序編寫人員可以通過在FreeRTOSConfig.h(后續(xù)會單獨講述這個文件)中提供替代實現(xiàn)來更改sbRECEIVE_COMPLETED()的默認行為。如果你需要每個流緩沖區(qū)都有自己的“接收完成”行為,可以使用xStreamBufferCreateWithCallback()或xStreamBufferCreateStaticWithCallback() API函數(shù)來創(chuàng)建流緩沖區(qū)。
3-內(nèi)核到內(nèi)核
3.1 介紹
消息緩沖區(qū)允許不同字節(jié)的離散消息從中斷服務(wù)程序傳遞到一個進程,或從一個進程傳遞到另一個進程。例如,長度為10、20和123字節(jié)的消息都可以寫入和讀取到同一個消息緩沖區(qū)。與使用流緩沖區(qū)不同的是,10字節(jié)的消息只能作為10字節(jié)消息讀取,而不能作為單個字節(jié)讀取。消息緩沖區(qū)建立在流緩沖區(qū)之上(也就是說,它們使用流緩沖區(qū)的實現(xiàn))。
數(shù)據(jù)通過復(fù)制的方式通過消息緩沖區(qū)——發(fā)送方將數(shù)據(jù)復(fù)制到緩沖區(qū)中,讀取方將數(shù)據(jù)復(fù)制出緩沖區(qū)。
在文件目錄FreeRTOS/Demo/Common/Minimal/MessageBufferAMP.c下提供了一個從一個MCU到另一個MCU的數(shù)據(jù),可以看看。
3.2消息緩沖區(qū)大小
為了能讓消息緩沖區(qū)能夠處理不同字節(jié)的消息,每個消息的字節(jié)大小在消息之前寫入消息緩沖區(qū)(這在FreeRTOS API函數(shù)內(nèi)部發(fā)生)。字節(jié)長度保存在一個變量中,其類型由FreeRTOSConfig.h中的configMESSAGE_BUFFER_LENGTH_TYPE常數(shù)設(shè)置。
如果沒有額外的定義,configMESSAGE_BUFFER_LENGTH_TYPE默認為size_t類型,,size_t在32位體系結(jié)構(gòu)上通常是4字節(jié)。所以呢,當configMESSAGE_BUFFER_LENGTH_TYPE為4字節(jié)時,向緩沖區(qū)寫入一個10字節(jié)的消息,但是實際上占用的字節(jié)是超過10個字節(jié)的,實際上是占用了14個字節(jié)。同樣,把100個字節(jié)的消息寫入到緩沖區(qū),實際上占用104個字節(jié)空間。
3.3 阻塞的讀取和寫入
xMessageBufferReceive())是用來讀取任務(wù)消息緩沖區(qū)的數(shù)據(jù)的,xMessageBufferReceiveFromISR())是 用于從中斷服務(wù)程序 (ISR) 的消息緩沖區(qū)讀取數(shù)據(jù)。
用于從RTOS任務(wù)的消息緩沖區(qū)中讀取數(shù)據(jù)。用于從中斷服務(wù)例程(ISR)的消息緩沖區(qū)中讀取數(shù)據(jù)xMessageBufferSend()用于發(fā)送 數(shù)據(jù)從 RTOS 任務(wù)發(fā)送到消息緩沖區(qū)。xMessageBufferSendFromISR()是用于將數(shù)據(jù)從中斷服務(wù)程序 (ISR) 發(fā)送到消息緩沖區(qū)。
如果在任務(wù)中使用xMessageBufferReceive()說明了阻塞時間,從消息緩沖區(qū),而這個緩沖區(qū)剛好是空的,則這個函數(shù)置于阻塞狀態(tài)(因此它不會消耗任何CPU時間,其他任務(wù)可以運行),直到數(shù)據(jù)在消息緩沖區(qū)中可用,或阻塞時間到達。)
如果在任務(wù)中使用xMessageBufferSend()函數(shù)寫入到消息緩沖區(qū),這個緩沖區(qū)已經(jīng)滿了,則任務(wù)將會被置位到阻塞狀態(tài)(因此它不消耗任何 CPU 時間,并且可以運行其他任務(wù)) 直到消息緩沖區(qū)中有空間可用或阻塞時間結(jié)束。)
-
緩沖區(qū)
+關(guān)注
關(guān)注
0文章
33瀏覽量
9107 -
函數(shù)
+關(guān)注
關(guān)注
3文章
4327瀏覽量
62573 -
FreeRTOS
+關(guān)注
關(guān)注
12文章
484瀏覽量
62144
發(fā)布評論請先 登錄
相關(guān)推薦
評論