巧奪天工 的 KFIFO ,用STM32實現。
實現源文件如下:
/********************************************************** * * 文件名: kfifo.c * * 文件描述: 該文件包含的kfifo的處理函數 * * 創建人: GXP * * 創建日期: 2016年8月9日16:13:06 * * 版本號: 1.0 * * 修改記錄: 無 * * 本文參考博客地址: http://blog.csdn.net/linyt/article/details/5764312 * http://blog.csdn.net/chen19870707/article/details/39899743 * ***********************************************************/ #include "kfifo.h" #include "FreeRTOS.h" #include "task.h" #define min(a, b) (((a) < (b)) ? (a) : (b)) //找出最接近 最大2的指數次冪 unsigned int roundup_pow_of_two(unsigned int date_roundup_pow_of_two ) { /* 這里采用 STM32 硬件提供的計算前導零指令 CLZ * 舉個例子,假如變量date_roundup_pow_of_two 0x09 *(二進制為:0000 0000 0000 0000 0000 0000 0000 1001), 即bit3和bit0為1 * 則__clz( (date_roundup_pow_of_two)的值為28,即最高位1 前面有28個0,32-28 =3 代表最高位1 的 位置 * 31UL 表示 無符號 int 數字 31,否則默認為 有符號 int 數字 31 * 這里參考 FreeRTOS 的 尋找高級優先級任務 的寫法,詳細解釋到朱工博客 * 博客地址: http://blog.csdn.net/zhzht19861011/article/details/51418383 */ return ( 1UL << ( 32UL - ( unsigned int ) __clz( (date_roundup_pow_of_two) ) ) ); } /* * 每次調用這個函數都會產生 兩個內存塊,一個內存塊指向struct KFIFO,一個指向 KFIFO.buff * 因此 如果這兩個內存塊不在使用請釋放掉!GXP,2016年8月17日12:55:54 */ struct KFIFO *kfifo_alloc(unsigned int size) { unsigned char *buffer; struct KFIFO *ret; ret=(struct KFIFO *) pvPortMalloc(sizeof (struct KFIFO)); /* * round up to the next power of 2, since our 'let the indices * wrap' tachnique works only in this case. * 如果size 是2的 次冪圓整,則 size & (size - 1) =0 */ if (size & (size - 1)) { // BUG_ON(size > 0x80000000); //如果你要申請的buffer 不是 2的 次冪圓整,就要把 size 變成 2的次冪圓整 ,方便下面計算 size = roundup_pow_of_two(size); } //這里使用 FreeRTOS的 分配內存的 API buffer = (unsigned char*) pvPortMalloc(size); if (!buffer) //如果返回的值為NULL,這說明分配內存失敗 return 0UL; // ret = kfifo_init(buffer, size, lock); ret->buffer=buffer; ret->size =size; ret->in = 0; ret->out = 0; if (!ret) //如果ret的值為NULL,這說明分配內存失敗 vPortFree(buffer); //釋放之前分配的 內存空間 return ret; } unsigned int __kfifo_put(struct KFIFO *fifo, unsigned char *buffer, unsigned int len) { unsigned int L; //環形緩沖區的剩余容量為fifo->size - fifo->in + fifo->out,讓寫入的長度取len和剩余容量中較小的,避免寫越界; len = min( len , fifo->size - fifo->in + fifo->out ); /* * Ensure that we sample the fifo->out index -before- we * start putting bytes into the kfifo. */ //多處理器 處理內存 的 屏障,STM32不需要這個 // smp_mb(); /* first put the data starting from fifo->in to buffer end */ /* 首先將數據從fifo.in 所在的位置開始寫,寫之前,首先要看一下fifo->in到 buffer 末尾的大小 是不是 比 len 大*/ /* * 前面講到fifo->size已經2的次冪圓整,主要是方便這里計算,提升效率 * 在對10進行求余的時候,我們發現,余數總是整數中的個位上的數字,而不用管其他位是什么; * 所以,kfifo->in % kfifo->size 可以轉化為 kfifo->in & (kfifo->size – 1),效率會提升 * 所以fifo->size - (fifo->in & (fifo->size - L)) 即位 fifo->in 到 buffer末尾所剩余的長度, * L取len和剩余長度的最小值,即為需要拷貝L 字節到fifo->buffer + fifo->in的位置上。 */ L = min(len, fifo->size - (fifo->in & (fifo->size - 1))); memcpy(fifo->buffer + (fifo->in & (fifo->size - 1)), buffer, L); /* then put the rest (if any) at the beginning of the buffer */ memcpy(fifo->buffer, buffer + L, len - L); /* * Ensure that we add the bytes to the kfifo -before- * we update the fifo->in index. */ // smp_wmb(); //多處理器 處理內存 的 屏障,STM32不需要這個 /* * 注意這里 只是用了 fifo->in += len而未取模, * 這就是kfifo的設計精妙之處,這里用到了unsigned int的溢出性質, * 當in 持續增加到溢出時又會被置為0,這樣就節省了每次in向前增加都要取模的性能, * 錙銖必較,精益求精,讓人不得不佩服。 */ fifo->in += len; /*返回值 代表 寫入數據的個數 ,這樣 就可以根據返回值 判斷緩沖區是否寫滿*/ return len; } unsigned int __kfifo_get(struct KFIFO *fifo, unsigned char *buffer, unsigned int len) { unsigned int L; len = min(len, fifo->in - fifo->out); /* * Ensure that we sample the fifo->in index -before- we * start removing bytes from the kfifo. */ //smp_rmb(); //多處理器 處理內存 的 屏障,STM32不需要這個 /* first get the data from fifo->out until the end of the buffer */ L = min(len, fifo->size - (fifo->out & (fifo->size - 1))); memcpy(buffer, fifo->buffer + (fifo->out & (fifo->size - 1)), L); /* then get the rest (if any) from the beginning of the buffer */ memcpy(buffer + L, fifo->buffer, len - L); /* * Ensure that we remove the bytes from the kfifo -before- * we update the fifo->out index. */ //smp_mb(); //多處理器 處理內存 的 屏障,STM32不需要這個 /* * 注意這里 只是用了 fifo->out += len 也未取模運算, * 同樣unsigned int的溢出性質,當out 持續增加到溢出時又會被置為0, * 如果in先溢出,出現 in < out 的情況,那么 in – out 為負數(又將溢出), * in – out 的值還是為buffer中數據的長度。 */ fifo->out += len; return len; }
頭文件如下:
#ifndef _KFIFO_H_ #define _KFIFO_H //聲明 一個 結構體 kfifo struct KFIFO { unsigned char *buffer; /* the buffer holding the data */ unsigned int size; /* the size of the allocated buffer */ unsigned int in; /* data is added at offset (in % size) */ unsigned int out; /* data is extracted from off. (out % size) */ /*STM32 只有一個核心,同一時刻只能寫或者讀,因此不需要*/ // volatile unsigned int *lock; /* protects concurrent modifications */ }; unsigned int roundup_pow_of_two( unsigned int date_roundup_pow_of_two ); struct KFIFO *kfifo_alloc(unsigned int size); unsigned int __kfifo_put(struct KFIFO *fifo, unsigned char *buffer, unsigned int len); unsigned int __kfifo_get(struct KFIFO *fifo, unsigned char *buffer, unsigned int len); #endif
上面 實現 找出 最接近 最大2的指數次冪 的是通過 STM32 一個特殊的 寄存器實現的 ,可以百度 找 C 語言 實現的方式。
以及內存 的 分配 是由 FreeRTOS提供 的 內存分配和釋放 實現的 。
使用 方式如下:
//創建一個 KFIFO 的結構體 指針 struct KFIFO *test_kifo_buffer=NULL;
//kfifo 測試 第一步: 創建 一個 1024字節的 fifo buff //首先定義一個 在全局變量中定義一個 KFIFO 結構體 test_kifo_buffer //接着 給這個 KFIFO 結構體 test_kifo_buffer 申請 一個 1024字節的 內存空間 //你如果分配468,會分配512字節的空間,你如果寫 668,就會分配1024字節的 空間,1025就分配 2048 //因為這樣 會方便計算 test_kifo_buffer=kfifo_alloc( 668);//這里寫入668,也分配的是 1024 字節的 空間 if( !test_kifo_buffer ) printf("\r\n KFIFO 結構體 test_kifo_buffer 沒有創建成功!\r\n");
//kfifo 測試 第二步: 發送10個字節寫入到 KFIFO 結構體 test_kifo_buffer 中 write_counter=__kfifo_put(test_kifo_buffer, test_fifo_write_buff, 10 ); if(write_counter!=10) printf("\r\n發送10個字節寫入到KFIFO結構體test_kifo_buffer失敗,寫入個數是:%d.\r\n",write_counter);
read_counter=read_counter=__kfifo_get(test_kifo_buffer, test_fifo_read_buff,5); printf("從test_kifo_buffer緩沖區讀取的數據是%s,讀出的個數是%d.\r\n",test_fifo_read_buff,read_counter); printf("test_kifo_buffer->size:%d,test_kifo_buffer->out:%d,test_kifo_buffer->in:%d.\r\n",test_kifo_buffer->size,test_kifo_buffer->out,test_kifo_buffer->in);
