基于Linux的kfifo移植到STM32(支持os的互斥访问)
关于kfifo
kfifo是内核里面的一个First In First Out数据结构,它采用环形循环队列的数据结构来实现;它提供一个无边界的字节流服务,最重要的一点是,它使用并行无锁编程技术,即当它用于只有一个入队线程和一个出队线程的场情时,两个线程可以并发操作,而不需要任何加锁行为,就可以保证kfifo的线程安全。
具体什么是环形缓冲区,请看我以前的文章
说明
关于kfifo的相关概念我不会介绍,有兴趣可以看他的相关文档,我只将其实现过程移植重写,移植到适用stm32开发板上,并且按照我个人习惯重新命名,RingBuff->意为环形缓冲区
往期关于环形缓冲区的文章:
杰杰带你解读【机智云】环形缓冲区源码
STM32进阶之串口环形缓冲区实现
RingBuff_t
环形缓冲区的结构体成员变量,具体含义看注释。
buffer: 用于存放数据的缓存
size: buffer空间的大小
in, out: 和buffer一起构成一个循环队列。 in指向buffer中队头,而且out指向buffer中的队尾
typedefstructringbuff
{
uint8_t*buffer;/*数据区域*/
uint32_tsize;/*环形缓冲区大小*/
uint32_tin;/*数据入队指针(in%size)*/
uint32_tout;/*数据出队指针(out%size)*/
#ifUSE_MUTEX
MUTEX_T*mutex;/*支持rtos的互斥*/
#endif
}RingBuff_t;
Create_RingBuff
创建一个环形缓冲区,为了适应后续对缓冲区入队出队的高效操作,环形缓冲区的大小应为2^n字节,
如果不是这个大小,则系统默认裁剪以对应缓冲区字节。
当然还可以优化,不过我目前并未做,思路如下:如果系统支持动态分配内存,则向上对齐,避免浪费内存空间,否则就按照我默认的向下对齐,当内存越大,对齐导致内存泄漏则会越多。对齐采用的函数是roundup_pow_of_two。如果系统支持互斥量,那么还将创建一个互斥量用来做互斥访问,防止多线程同时使用导致数据丢失。
/************************************************************
*@briefCreate_RingBuff
*@paramrb:环形缓冲区句柄
*buffer:环形缓冲区的数据区域
*size:环形缓冲区的大小,缓冲区大小要为2^n
*@returnerr_t:ERR_OK表示创建成功,其他表示失败
*@authorjiejie
*@githubhttp://github.com/jiejieTop
*@date2018-xx-xx
*@versionv1.0
*@note用于创建一个环形缓冲区
***********************************************************/
err_tCreate_RingBuff(RingBuff_t*rb,
uint8_t*buffer,
uint32_tsize
)
{
if((rb==NULL)||(buffer==NULL)||(size==0))
{
PRINT_ERR("dataisnull!");
returnERR_NULL;
}
PRINT_DEBUG("ringbuffsizeis%d!",size);
/*缓冲区大小必须为2^n字节,系统会强制转换,
否则可能会导致指针访问非法地址。
空间大小越大,强转时丢失内存越多*/
if(size&(size-1))
{
size=roundup_pow_of_two(size);
PRINT_DEBUG("changeringbuffsizeis%d!",size);
}
rb->buffer=buffer;
rb->size=size;
rb->in=rb->out=0;
#ifUSE_MUTEX
/*创建信号量不成功*/
if(!create_mutex(rb->mutex))
{
PRINT_ERR("createmutexfail!");
ASSERT(ASSERT_ERR);
returnERR_NOK;
}
#endif
PRINT_DEBUG("createringBuffok!");
returnERR_OK;
}
roundup_pow_of_two
/************************************************************
*@briefroundup_pow_of_two
*@paramsize:传递进来的数据长度
*@returnsize:返回处理之后的数据长度
*@authorjiejie
*@githubhttp://github.com/jiejieTop
*@date2018-xx-xx
*@versionv1.0
*@note用于处理数据,使数据长度必须为2^n
*如果不是,则转换,丢弃多余部分,如
*roundup_pow_of_two(66)->返回64
***********************************************************/
staticunsignedlongroundup_pow_of_two(unsignedlongx)
{
return(1out=0;
#ifUSE_MUTEX
if(!deleta_mutex(rb->mutex))
{
PRINT_DEBUG("deletamutexisfail!");
returnERR_NOK;
}
#endif
returnERR_OK;
}
Write_RingBuff
向环形缓冲区写入指定数据,支持线程互斥访问。用户想要写入缓冲区的数据长度不一定是真正入队的长度,在完成的时候还要看看返回值是否与用户需要的长度一致~
这个函数很有意思,也是比较高效的入队操作,将指定区域的数据拷贝到指定的缓冲区中,过程看注释即可
/************************************************************
*@briefWrite_RingBuff
*@paramrb:环形缓冲区句柄
*@paramwbuff:写入的数据起始地址
*@paramlen:写入数据的长度(字节)
*@returnlen:实际写入数据的长度(字节)
*@authorjiejie
*@githubhttp://github.com/jiejieTop
*@date2018-xx-xx
*@versionv1.0
*@note这个函数会从buff空间拷贝len字节长度的数据到
rb环形缓冲区中的空闲空间。
***********************************************************/
uint32_tWrite_RingBuff(RingBuff_t*rb,
uint8_t*wbuff,
uint32_tlen)
{
uint32_tl;
#ifUSE_MUTEX
/*请求互斥量,成功才能进行ringbuff的访问*/
if(!request_mutex(rb->mutex))
{
PRINT_DEBUG("requestmutexfail!");
return0;
}
else/*获取互斥量成功*/
{
#endif
len=min(len,rb->size-rb->in+rb->out);
/*第一部分的拷贝:从环形缓冲区写入数据直至缓冲区最后一个地址*/
l=min(len,rb->size-(rb->in&(rb->size-1)));
memcpy(rb->buffer+(rb->in&(rb->size-1)),wbuff,l);
/*如果溢出则在缓冲区头写入剩余的部分
如果没溢出这句代码相当于无效*/
memcpy(rb->buffer,wbuff+l,len-l);
rb->in+=len;
PRINT_DEBUG("writeringBufflenis%d!",len);
#ifUSE_MUTEX
}
/*释放互斥量*/
release_mutex(rb->mutex);
#endif
returnlen;
}
Read_RingBuff
读取缓冲区数据到指定区域,用户指定读取长度,用户想要读取的长度不一定是真正读取的长度,在读取完成的时候还要看看返回值是否与用户需要的长度一致~也支持多线程互斥访问。
也是缓冲区出队的高效操作。过程看代码注释即可
/************************************************************
*@briefRead_RingBuff
*@paramrb:环形缓冲区句柄
*@paramwbuff:读取数据保存的起始地址
*@paramlen:想要读取数据的长度(字节)
*@returnlen:实际读取数据的长度(字节)
*@authorjiejie
*@githubhttp://github.com/jiejieTop
*@date2018-xx-xx
*@versionv1.0
*@note这个函数会从rb环形缓冲区中的数据区域拷贝len字节
长度的数据到rbuff空间。
***********************************************************/
uint32_tRead_RingBuff(RingBuff_t*rb,
uint8_t*rbuff,
uint32_tlen)
{
uint32_tl;
#ifUSE_MUTEX
/*请求互斥量,成功才能进行ringbuff的访问*/
if(!request_mutex(rb->mutex))
{
PRINT_DEBUG("requestmutexfail!");
return0;
}
else
{
#endif
len=min(len,rb->in-rb->out);
/*第一部分的拷贝:从环形缓冲区读取数据直至缓冲区最后一个*/
l=min(len,rb->size-(rb->out&(rb->size-1)));
memcpy(rbuff,rb->buffer+(rb->out&(rb->size-1)),l);
/*如果溢出则在缓冲区头读取剩余的部分
如果没溢出这句代码相当于无效*/
memcpy(rbuff+l,rb->buffer,len-l);
rb->out+=len;
PRINT_DEBUG("readringBufflenis%d!",len);
#ifUSE_MUTEX
}
/*释放互斥量*/
release_mutex(rb->mutex);
#endif
returnlen;
}
获取缓冲区信息
这些就比较简单了,看看缓冲区可读可写的数据有多少
/************************************************************
*@briefCanRead_RingBuff
*@paramrb:环形缓冲区句柄
*@returnuint32:可读数据长度0/len
*@authorjiejie
*@githubhttp://github.com/jiejieTop
*@date2018-xx-xx
*@versionv1.0
*@note可读数据长度
***********************************************************/
uint32_tCanRead_RingBuff(RingBuff_t*rb)
{
if(NULL==rb)
{
PRINT_ERR("ringbuffisnull!");
return0;
}
if(rb->in==rb->out)
return0;
if(rb->in>rb->out)
return(rb->in-rb->out);
return(rb->size-(rb->out-rb->in));
}
/************************************************************
*@briefCanRead_RingBuff
*@paramrb:环形缓冲区句柄
*@returnuint32:可写数据长度0/len
*@authorjiejie
*@githubhttp://github.com/jiejieTop
*@date2018-xx-xx
*@versionv1.0
*@note可写数据长度
***********************************************************/
uint32_tCanWrite_RingBuff(RingBuff_t*rb)
{
if(NULL==rb)
{
PRINT_ERR("ringbuffisnull!");
return0;
}
return(rb->size-CanRead_RingBuff(rb));
}
附带
这里的代码我是用于测试的,随便写的
RingBuff_tringbuff_handle;
uint8_trb[64];
uint8_tres[64];
Create_RingBuff(&ringbuff_handle,
rb,
sizeof(rb));
Write_RingBuff(&ringbuff_handle,
res,
datapack.data_length);
PRINT_DEBUG("CanRead_RingBuff=%d!",CanRead_RingBuff(&ringbuff_handle));
PRINT_DEBUG("CanWrite_RingBuff=%d!",CanWrite_RingBuff(&ringbuff_handle));
Read_RingBuff(&ringbuff_handle,
res,
datapack.data_length);
支持多个os的互斥量操作
此处模仿了文件系统的互斥操作
#ifUSE_MUTEX
#defineMUTEX_TIMEOUT1000/*超时时间*/
#defineMUTEX_Tmutex_t/*互斥量控制块*/
#endif
/***********************************mutex**************************************************/
#ifUSE_MUTEX
/************************************************************
*@briefcreate_mutex
*@parammutex:创建信号量句柄
*@return创建成功为1,0为不成功。
*@authorjiejie
*@githubhttp://github.com/jiejieTop
*@date2018-xx-xx
*@versionv1.0
*@note创建一个互斥量,用户在os中互斥使用ringbuff,
*支持的os有rtt、win32、ucos、FreeRTOS、LiteOS
***********************************************************/
staticerr_tcreate_mutex(MUTEX_T*mutex)
{
err_tret=0;
//*mutex=rt_mutex_create("test_mux",RT_IPC_FLAG_PRIO);/*rtt*/
//ret=(err_t)(*mutex!=RT_NULL);
//*mutex=CreateMutex(NULL,FALSE,NULL);/*Win32*/
//ret=(err_t)(*mutex!=INVALID_HANDLE_VALUE);
//*mutex=OSMutexCreate(0,&err);/*uC/OS-II*/
//ret=(err_t)(err==OS_NO_ERR);
//*mutex=xSemaphoreCreateMutex();/*FreeRTOS*/
//ret=(err_t)(*mutex!=NULL);
//ret=LOS_MuxCreate(&mutex);/*LiteOS*/
//ret=(err_t)(ret!=LOS_OK);
returnret;
}
/************************************************************
*@briefdeleta_mutex
*@parammutex:互斥量句柄
*@returnNULL
*@authorjiejie
*@githubhttp://github.com/jiejieTop
*@date2018-xx-xx
*@versionv1.0
*@note删除一个互斥量,支持的os有rtt、win32、ucos、FreeRTOS、LiteOS
***********************************************************/
staticerr_tdeleta_mutex(MUTEX_T*mutex)
{
err_tret;
//ret=rt_mutex_delete(mutex);/*rtt*/
//ret=CloseHandle(mutex);/*Win32*/
//OSMutexDel(mutex,OS_DEL_ALWAYS,&err);/*uC/OS-II*/
//ret=(err_t)(err==OS_NO_ERR);
//vSemaphoreDelete(mutex);/*FreeRTOS*/
//ret=1;
//ret=LOS_MuxDelete(&mutex);/*LiteOS*/
//ret=(err_t)(ret!=LOS_OK);
returnret;
}
/************************************************************
*@briefrequest_mutex
*@parammutex:互斥量句柄
*@returnNULL
*@authorjiejie
*@githubhttp://github.com/jiejieTop
*@date2018-xx-xx
*@versionv1.0
*@note请求一个互斥量,得到互斥量的线程才允许进行访问缓冲区
*支持的os有rtt、win32、ucos、FreeRTOS、LiteOS
***********************************************************/
staticerr_trequest_mutex(MUTEX_T*mutex)
{
err_tret;
//ret=(err_t)(rt_mutex_take(mutex,MUTEX_TIMEOUT)==RT_EOK);/*rtt*/
//ret=(err_t)(WaitForSingleObject(mutex,MUTEX_TIMEOUT)==WAIT_OBJECT_0);/*Win32*/
//OSMutexPend(mutex,MUTEX_TIMEOUT,&err));/*uC/OS-II*/
//ret=(err_t)(err==OS_NO_ERR);
//ret=(err_t)(xSemaphoreTake(mutex,MUTEX_TIMEOUT)==pdTRUE);/*FreeRTOS*/
//ret=(err_t)(LOS_MuxPend(mutex,MUTEX_TIMEOUT)==LOS_OK);/*LiteOS*/
returnret;
}
/************************************************************
*@briefrelease_mutex
*@parammutex:互斥量句柄
*@returnNULL
*@authorjiejie
*@githubhttp://github.com/jiejieTop
*@date2018-xx-xx
*@versionv1.0
*@note释放互斥量,当线程使用完资源必须释放互斥量
*支持的os有rtt、win32、ucos、FreeRTOS、LiteOS
***********************************************************/
staticvoidrelease_mutex(MUTEX_T*mutex)
{
//rt_mutex_release(mutex);/*rtt*/
//ReleaseMutex(mutex);/*Win32*/
//OSMutexPost(mutex);/*uC/OS-II*/
//xSemaphoreGive(mutex);/*FreeRTOS*/
//LOS_MuxPost(mutex);/*LiteOS*/
}
#endif
/***********************************mutex**************************************************/
debug.h
最后送一份debug的简便操作源码,因为前文很多时候会调用
PRINT_ERR
PRINT_DEBUG
#ifndef_DEBUG_H
#define_DEBUG_H
/************************************************************
*@briefdebug.h
*@authorjiejie
*@githubhttp://github.com/jiejieTop
*@date2018-xx-xx
*@versionv1.0
*@note此文件用于打印日志信息
***********************************************************/
/**
*@nameDebugprint
*@{
*/
#definePRINT_DEBUG_ENABLE1/*打印调试信息*/
#definePRINT_ERR_ENABLE1/*打印错误信息*/
#definePRINT_INFO_ENABLE0/*打印个人信息*/
#ifPRINT_DEBUG_ENABLE
#definePRINT_DEBUG(fmt,args...)do{(printf("\n[DEBUG]>>"),printf(fmt,##args));}while(0)
#else
#definePRINT_DEBUG(fmt,args...)
#endif
#ifPRINT_ERR_ENABLE
#definePRINT_ERR(fmt,args...)do{(printf("\n[ERR]>>"),printf(fmt,##args));}while(0)
#else
#definePRINT_ERR(fmt,args...)
#endif
#ifPRINT_INFO_ENABLE
#definePRINT_INFO(fmt,args...)do{(printf("\n[INFO]>>"),printf(fmt,##args));}while(0)
#else
#definePRINT_INFO(fmt,args...)
#endif
/**@}*/
//针对不同的编译器调用不同的stdint.h文件
#ifdefined(__ICCARM__)||defined(__CC_ARM)||defined(__GNUC__)
#include
#endif
/*断言Assert*/
#defineAssertCalled(char,int)printf("\nError:%s,%d\r\n",char,int)
#defineASSERT(x)if((x)==0)AssertCalled(__FILE__,__LINE__)
typedefenum
{
ASSERT_ERR=0,/*错误*/
ASSERT_SUCCESS=!ASSERT_ERR/*正确*/
}Assert_ErrorStatus;
typedefenum
{
FALSE=0,/*假*/
TRUE=!FALSE/*真*/
}ResultStatus;
#endif/*__DEBUG_H*/
源码在后台回复“ringbuff”即可下载。
本文是杰杰原创,转载请说明出处。
往期精彩回顾【连载】从单片机到操作系统③——走进FreeRTOS
【连载】从单片机到操作系统④——FreeRTOS任务详解
【连载】从单片机到操作系统⑤——FreeRTOS列表&列表项的源码解读
【连载】从单片机到操作系统⑥——FreeRTOS任务切换机制详解
RT-Thread内核实现的思维导图
RTOS临界段知识详解
STM32进阶之串口环形缓冲区实现
杰杰带你解读【机智云】环形缓冲区源码
分享一些我自己写的数据处理的源码
STM32之串口DMA接收不定长数据
【书籍分享】程序员的数学系列丛书
【书籍分享】C语言进阶必看《C和指针》
欢迎大家一起来讨论操作系统的知识
我们的群号是:783234154
杰杰:
创客飞梦空间是开源公众号
如果喜欢我的文章,那就关注我吧
也欢迎大家投稿
|