一种简单的FIFO实现
The FIFO library provides a simple circular buffer implementation for storing bytes. The FIFO uses the size and buffer memory provided by the application on initialization.
Initializing FIFO
// Create a FIFO structure app_fifo_t my_fifo; // Create a buffer for the FIFO uint16_t buffer_size = 8; uint8_t buffer[buffer_size]; // Initialize FIFO structure uint32_t err_code = app_fifo_init(&my_fifo, buffer, (uint16_t)sizeof(buffer));
The application must provide a FIFO structure and a memory buffer to the initialization function.
The FIFO structure (app_fifo_t) is passed by reference and will be initialized by the FIFO module. It ensures correct indexing when reading and writing data to the FIFO. The library module itself will not store state information, thus the application must ensure that the memory used by app_fifo_t is not reclaimed as long as the FIFO is in use.
The size of the buffer must be a power of two. As seen in the code example, the application must ensure that the buffer has a reserved memory space for the lifetime of the FIFO.
FIFO instance
The app_fifo_t structure that is used to create a FIFO instance contains 4 members:
- p_buf - copy of the address to the provided buffer.
- read_pos - counter for the next byte to be read in the provided buffer.
- write_pos - counter for the next byte to be written in the provided buffer.
- buf_size_mask - calculated based on the buffer size. The size of the buffer is always a power of two, therefore it is possible to get a filter mask by subtracting 1.
The FIFO is implemented as a circular buffer of size n. Whenever the nth element is added to the buffer, the index wraps over and counts from index zero. This can be summarized to i = count mod n
, where:
- n: buffer size
- i: calculated read/write index
- count: read/write counter
To simplify the index handling, the FIFO is restricted to require the buffer size n to be a power of two, because this reduces the modulus operation to become a simple AND operation with n-1. For example, if the buffer size n=8:
n = 8 = 0b00001000 n - 1 = 7 = 0b00000111
If n is a power of two, ANDing any number with n-1 is identical to the modulus operation of n. For example:
11 mod 8 = 3 11 AND 7 = 0b00001011 & 0b00000111 = 0b00000011 = 3
During initialization of the FIFO, a check is performed to ensure that the size n is a power of two.
The following image shows how the FIFO instance looks like when it is initialized. The two arrows on the top illustrate the read and write indexes. They both start at index 0, and all bytes in the buffer are free to be used.
- Warning
- The FIFO implementation is not re-entrant safe. Thus data must be added or fetched from the FIFO from the same context level. However, adding in one context level and fetching from another context is supported.
Adding an element
To add an element to the FIFO, the application must use the app_fifo_put function. This is done by passing the FIFO structure and the data to app_fifo_put.
Example of adding an element to the FIFO:
uint8_t data = 88; uint32_t err_code; // Add an element to the FIFO err_code = app_fifo_put(&my_fifo, data);
When the new element is added to the buffer, the value supplied as parameter to the put function will be copied over to the current index of the write_pos member of the FIFO instance. When an element has been added to the FIFO, the write_pos member of the instance will be incremented by 1 to point to the next free byte. This is illustrated in Figure 2.
For each element added, the write_pos counter increments. This is illustrated in Figure 3.
If the FIFO is full, NRF_ERROR_NO_MEM will be returned.
Fetching an element
To get an element from the FIFO, the application must use the app_fifo_get function. This is done by passing the FIFO structure and a reference, return_val
, for returning the data from app_fifo_get.
Example of fetching an element from the FIFO:
// Consume one element from FIFO uint8_t return_val; uint32_t err_code; err_code = app_fifo_get(&my_fifo, &return_val);
When app_fifo_get is called, the memory of the supplied by-reference parameter is filled with the value stored in the memory buffer. Then, the read_pos member of the FIFO instance is incremented to free the byte that was read. Therefore, each byte can be fetched only once. An example of a FIFO where two elements have been consumed is shown in Figure 4.
Empty buffer
The read_pos and write_pos indexes have the same value when the FIFO is empty. This is the case when the FIFO is initialized, because both read_pos and write_pos are initialized to 0, and when all elements in the FIFO have been fetched. This is illustrated in Figure 5.
The FIFO can also be emptied by executing app_fifo_flush.
When the FIFO is empty, all calls to app_fifo_get will return NRF_ERROR_NOT_FOUND.
Full buffer
When write_pos == read_pos + n
, the buffer is full. All subsequent calls to app_fifo_put will return NRF_ERROR_NO_MEM.
Multi-byte operations
It is possible to write and read multiple bytes at the same time. Writing to the FIFO is equivalent to adding multiple bytes, one at a time. Reading from the FIFO is equivalent to fetching multiple bytes, one at a time.
Example of a write operation:
uint32_t err_code; uint32_t data_len = 5; uint8_t write_data = {1, 2, 3, 4, 5}; // Write 5 elements to the FIFO return_val = app_fifo_write(&my_fifo, &write_data, &data_len); // Check if write was successful if (return_val == NRF_SUCCESS) { // Check if write was partial if (data_len < 5) { // Attempt another write operation using data_len value as offset } } else if (return_val == NRF_ERROR_NO_MEM) { // FIFO is full } else { // API parameters incorrect, should not happen } Example of a read operation: uint32_t return_val; uint32_t data_len = 5; uint8_t read_data[5]; // Read 5 elements to the FIFO return_val = app_fifo_read(&my_fifo, &read_data, &data_len); // Check if write was successful if (return_val == NRF_SUCCESS) { // Check if read was partial if (data_len < 5) { // Not as many elements in array as requested } } else if (return_val == NRF_ERROR_NOT_FOUND) { // FIFO is empty } else { // API parameters incorrect, should not happen }
FIFO readable/writeable size operations
To query how many elements are available in the FIFO, the application can use the app_fifo_read function. To query how many elements can be written to the FIFO, the application can use the app_fifo_write function.
Example for querying how many bytes can be written to the FIFO:
uint32_t err_code; uint32_t data_len; // Request number of elements that can be written to the FIFO err_code = app_fifo_write(&my_fifo, NULL, &data_len); // Check if request was successful if (err_code == NRF_SUCCESS) { // data_len contains the number of bytes that can be written } else if (err_code == NRF_ERROR_NO_MEM) { // FIFO is full } else { // API parameters incorrect, should not happen } Example for querying how many bytes can be read from the FIFO: uint32_t err_code; uint32_t data_len; // Request number of elements in the FIFO err_code = app_fifo_read(&my_fifo, NULL, &data_len); // Check if request was successful if (err_code == NRF_SUCCESS) { // data_len contains the number of elements that can be read } else if (err_code == NRF_ERROR_NOT_FOUND) { // FIFO is empty } else { // API parameters incorrect, should not happen }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!