[共享][版本更新][古董贴]C语言代码模板_环形队列
本帖最后由 Gorgon_Meducer 于 2013-3-12 12:50 编辑古董贴仅供考古,相同内容最新版本,请参考http://www.amobbs.com/thread-5515836-1-1.html
#ifndef _USE_TPL_QUEUE_H_
#define _USE_TPL_QUEUE_H_
/***********************************************************
* 模板库说明:环形队列模板 *
* 版本: v1.10 *
* 作者: 王卓然 *
* 创建日期:2008年1月19日 *
* -------------------------------------------------------- *
*[支 持 库] *
* 支持库名称:RD_MacroAndConst.h *
* 需要版本:v0.04 &abv *
* 支持库说明:系统常用宏定义库 *
* -------------------------------------------------------- *
*[版本更新] *
* 修改: 王卓然 *
* 修改日期:2008年3月8日 *
* 版本: v1.10 *
* -------------------------------------------------------- *
*[版本历史] *
* v1.00 提供了基本的代码模板和基本的队列操作函数。 *
* v1.10 增加了一个扩展的Peek函数,可以连续的向后预 *
* 读队列中的内容。并增加了队列清空的函数。 *
* -------------------------------------------------------- *
*[使用说明] *
***********************************************************/
/********************
* 头 文 件 配 置 区 *
********************/
# include "RD_MacroAndConst.h"
/********************
* 系 统 宏 定 义*
********************/
/*------------------*
* 常 数 宏 定 义*
*------------------*/
/*------------------*
* 动 作 宏 定 义*
*------------------*/
# define START_DEFINE_QUEUE(__NAME,__SIZE,__TYPE,__POS_TYPE)\
__TYPEs_Queue##__NAME##Buffer[(__SIZE)] = {0};\
__POS_TYPEs_Queue##__NAME##Tail = 0;\
__POS_TYPEs_Queue##__NAME##Head = 0;\
__POS_TYPEs_Queue##__NAME##Counter = 0;\
__POS_TYPEs_Queue##__NAME##PeekCounter = 0;\
\
BOOL Queue_##__NAME##_Add_Data(__TYPE Data)\
{\
SAFE_CODE_PERFORMANCE\
(\
if ((s_Queue##__NAME##Tail == s_Queue##__NAME##Head)\
&& (s_Queue##__NAME##Counter != 0))\
{\
EXIT_SAFE_CODE\
return FALSE;\
}\
\
s_Queue##__NAME##Buffer = Data;\
s_Queue##__NAME##Tail = (s_Queue##__NAME##Tail == (__SIZE - 1)) ? \
0 : (s_Queue##__NAME##Tail + 1);\
s_Queue##__NAME##Counter++;\
)\
\
return TRUE;\
}\
\
BOOL Queue_##__NAME##_Get_Data(__TYPE *pData)\
{\
SAFE_CODE_PERFORMANCE\
(\
if (((s_Queue##__NAME##Tail == s_Queue##__NAME##Head)\
&& (s_Queue##__NAME##Counter == 0))\
|| (pData == NULL))\
{\
EXIT_SAFE_CODE\
return FALSE;\
}\
\
(*pData) = s_Queue##__NAME##Buffer;\
s_Queue##__NAME##Head = (s_Queue##__NAME##Head == (__SIZE - 1)) ? \
0 : (s_Queue##__NAME##Head + 1);\
s_Queue##__NAME##Counter--;\
s_Queue##__NAME##PeekCounter = s_Queue##__NAME##Head;\
)\
\
return TRUE;\
}\
\
BOOL Queue_##__NAME##_Peek_Data(__TYPE *pData)\
{\
SAFE_CODE_PERFORMANCE\
(\
if (((s_Queue##__NAME##Tail == s_Queue##__NAME##Head)\
&& (s_Queue##__NAME##Counter == 0))\
|| (pData == NULL))\
{\
EXIT_SAFE_CODE\
return FALSE;\
}\
\
(*pData) = s_Queue##__NAME##Buffer;\
)\
\
return TRUE;\
}\
\
BOOL Queue_##__NAME##_Check_Empty(void)\
{\
SAFE_CODE_PERFORMANCE\
(\
if ((s_Queue##__NAME##Tail == s_Queue##__NAME##Head)\
&& (s_Queue##__NAME##Counter == 0))\
{\
EXIT_SAFE_CODE\
return TRUE;\
}\
)\
return FALSE;\
}\
\
__POS_TYPE Queue_##__NAME##_Get_Count(void)\
{\
return s_Queue##__NAME##Counter;\
}\
BOOL Queue_##__NAME##_Peek_Extend(__TYPE *pData)\
{\
SAFE_CODE_PERFORMANCE\
(\
if ((s_Queue##__NAME##PeekCounter == s_Queue##__NAME##Tail)\
|| (pData == NULL))\
{\
EXIT_SAFE_CODE\
return FALSE;\
}\
\
(*pData) = s_Queue##__NAME##Buffer;\
s_Queue##__NAME##PeekCounter++;\
s_Queue##__NAME##PeekCounter = (s_Queue##__NAME##PeekCounter == (__SIZE)) ? \
0 : (s_Queue##__NAME##PeekCounter);\
)\
\
return TRUE;\
}\
void Queue_##__NAME##_Reset_Peek_Counter(void)\
{\
SAFE_CODE_PERFORMANCE\
(\
s_Queue##__NAME##PeekCounter = s_Queue##__NAME##Head;\
)\
}\
void Queue_##__NAME##_Clear(void)\
{\
SAFE_CODE_PERFORMANCE\
(\
s_Queue##__NAME##Tail = s_Queue##__NAME##Head;\
s_Queue##__NAME##Counter = 0;\
s_Queue##__NAME##PeekCounter = s_Queue##__NAME##Head;\
)\
}
# define END_DEFINE_QUEUE
# define GET_QUEUE_DATA(__NAME,__ADDR) Queue_##__NAME##_Get_Data(__ADDR)
# define PEEK_QUEUE_DATA(__NAME,__ADDR) Queue_##__NAME##_Peek_Data(__ADDR)
# define ADD_QUEUE_DATA(__NAME,__VAR) Queue_##__NAME##_Add_Data(__VAR)
# define GET_QUEUE_COUNT(__NAME) Queue_##__NAME##_Get_Count()
# define CHECK_QUEUE_EMPTY(__NAME) Queue_##__NAME##_Check_Empty()
# define PEEK_QUEUE_DATA_EXTEND(__NAME,__ADDR)Queue_##__NAME##_Peek_Extend(__ADDR)
# define RESET_PEEK_COUNTER(__NAME) Queue_##__NAME##_Reset_Peek_Counter();
# define CLEAR_QUEUE(__NAME) Queue_##__NAME##_Clear();
# define EXTERN_REFERENCE_QUEUE(__NAME,__TYPE,__POS_TYPE)\
extern BOOL Queue_##__NAME##_Add_Data(__TYPE Data);\
extern BOOL Queue_##__NAME##_Get_Data(__TYPE *pData);\
extern BOOL Queue_##__NAME##_Peek_Data(__TYPE *pData);\
extern BOOL Queue_##__NAME##_Check_Empty(void);\
extern __POS_TYPE Queue_##__NAME##_Get_Count(void);\
extern BOOL Queue_##__NAME##_Peek_Extend(__TYPE *pData);\
extern void Queue_##__NAME##_Reset_Peek_Counter(void);\
extern void Queue_##__NAME##_Clear(void);
/********************
*用户变量类型定义 *
********************/
/********************
* 结构体定义区 *
********************/
/********************
* 函 数 引 用 区*
********************/
/********************
* 全局变量引用区*
********************/
#endif
本帖最后由 Gorgon_Meducer 于 2012-8-1 21:45 编辑
[使用方法]
假设我想建立一个按键缓冲区,我们需要首先声明一个环形队列,名叫PS2KeyBuffer,大小为8个字节START_DEFINE_QUEUE(PS2KeyBuffer,8,uint8,uint8)
END_DEFINE_QUEUE接下来,我们可以通过以下方法访问这个队列# define __PUT_DATA(n) ADD_QUEUE_DATA(PS2KeyBuffer,(n))
# define __GET_DATA(n) GET_QUEUE_DATA(PS2KeyBuffer,&(n))
# define __GET_QUEUE_COUNT GET_QUEUE_COUNT(PS2KeyBuffer)
# define __PEEK_DATA(n) PEEK_QUEUE_DATA(PS2KeyBuffer,&(n))
# define __CHECK_EMPTY CHECK_QUEUE_EMPTY(PS2KeyBuffer)
很方便哈?代码效率也很高哦 呵呵,好东西,先收着 记号 收藏,希望你的好东西都能在你的新书中找到,这样我们买书就很值了。以往买的很多书都是垃圾,相互抄的,期待
楼主的大作早日面世
我的大部分东西都在书中,在DEMO以外的章节中还有专题讲嵌入式相关的数据结构。以实践和实用为主,决不掉书袋。 RD_MacroAndConst.h
是什么头文件,没看过啊,LZ用的是什么编译器? 这个头文件是我自己写的,在我的专栏的顶楼有。
这个模板是C语言通用的,我自己用的是ICC编译器。 好东西,先来个记号 建议楼主共享时提供C文件,刚才试验了一下,错误一大堆,半天才查出来,原来是换行符后面多了空格!
宏定义的错误真不好找 楼上批评的是,我以后一定注意。谢谢您的测试! 中断调用的情况似乎没考虑。 楼主没处理考虑中断应该是为了考虑通用性
是不是可以增加两个开关中断的宏,插入到对队列指针操作得地方
这样在移植到不同平台时只要修改这两个宏就可以了
不用每次在调用的时候关开中断,毕竟只是在修改指针得时候才需要屏蔽中断 我考虑中断了。大家仔细看
SAFE_CODE_PERFORMANCE
(
……
)
这个宏就是用来屏蔽中断的。 不是说楼主忘设置临界段了,那个SAFE_CODE_PERFORMANCE我看到了。
我是指,因为楼主使用了SAFE_CODE_PERFORMANCE,如果在中断中调用某个读写函数就会引起中断嵌套错误。
要通用的话,那个SAFE_CODE_PERFORMANCE和那个退出临界段的宏也要是支持中断嵌套的。 to 【14楼】 gingin
用#undef 和#define 重新定义这个宏就可以根据需要来配置了。
如果可能,尽量不要在中断中使用对队列进行操作的函数。AVR的中断
系统有一个特点,除非同一个中断发生了两次,一般情况下,发生的中
断如果全局响应被关闭时,该中断只是被缓冲了,当全局响应开启时,
AVR会立即响应该中断。 mark~~ 记号 没仔细看,不知道和数据结构中的循环队列是不是一个东东?? to 【18楼】 edaworld 笨笨小熊
就是循环队列哈。 模板啊!!!模板不是不好,不过检错很麻烦,如果出问题,很难检测出来!而且编译器报错时有不能定位~~~ 环形队列挺常用,我也用宏写了一个比较简单的,有时间贴上来。
大家有兴趣的话研究研究这个(别人写的),没仔细看,虽不是为单片机写的,可以借鉴一下:
http://bbs.chinaunix.net/viewthread.php?tid=759781&highlight=%B7%B6%D0%CD 哇,Gorgon Meducer 傻孩子
我一看到上述包含编译开关代码就头痛。
#define QUEUE_MAX_LENGTH 50
typedef struct tagELEMENT
{
unsigned char bVal;
unsigned int Addr;
} ELEMENT;
struct EEPROM_QUEUE
{
ELEMENT element;
int rear;
int front;
};
struct EEPROM_QUEUE QUEUE;
void QUEUE_Init(void)
{
QUEUE.rear = 0;
QUEUE.front = 0;
}
BOOL QUEUE_EMPTY(void)
{
if (QUEUE.rear == QUEUE.front)
return (TRUE);
return (FALSE);
}
BOOL QUEUE_FULL(void)
{
if ((QUEUE.rear+1) % QUEUE_MAX_LENGTH == QUEUE.front)
return (TRUE);
return (FALSE);
}
BOOL QUEUE_In(ELEMENT element)
{
if (!QUEUE_FULL())
{
QUEUE.rear = (QUEUE.rear + 1) % QUEUE_MAX_LENGTH;
QUEUE.element.bVal = element.bVal;
QUEUE.element.Addr = element.Addr;
return (TRUE);
}
return (FALSE);
}
BOOL QUEUE_Out(ELEMENT *element)
{
if (!QUEUE_EMPTY())
{
QUEUE.front = (QUEUE.front + 1) % QUEUE_MAX_LENGTH;
element->bVal = QUEUE.element.bVal;
element->Addr = QUEUE.element.Addr;
return (TRUE);
}
return (FALSE);
} mark 楼主啊,你发的帖子,AVR端口位操作支持库 ICC,我下载了你的代码,但是现在我却没有,RD_UseBITs.h、RD_MacroAndConst.h和LIB_Config.h这三个头文件。不知道楼主能不能帮个忙,将这三个头文件相应版本的代码发给我,不胜感谢,我的邮箱xyzjacky@sina.com mark to 【24楼】 xyzjacky
就在我专栏的置顶贴里面。 记号 MARK mark mark mark ji 标记学习 mark 标记学习 mark mark 循环队列 MARK mark mark mark 多谢傻孩子共享好东东./emotion/em007.gif mark 环形队列 好东西 当然支持 楼主辛苦了,ba_wang_mao的也不错 mark mark,学习{:smile:} 学习学习,谢谢. 路过学习一下!!!!!! 经典的,回味! Gorgon_Meducer 发表于 2008-1-26 02:57 static/image/common/back.gif
[使用方法]
假设我想建立一个按键缓冲区,我们需要首先声明一个环形队列,名叫PS2KeyBuffer,大小为8个 ...
傻孩子老师您好:有一个关于串口发送中断的问题想请教你一下:
我最近做了一个关于发送中断的工程,关于串口发送中断有一些疑问,我的部分代码如下:
第一种情况:
void transmit_byte(uchar DAT)
{
uchar tmphead;
tmphead=(uart_txhead+1)&UART_TX_BUFFER_MASK;//calculate buffer index
while(tmphead==uart_txtail)// wait for free space in buffer
{
;
}
uart_txbuf=DAT;//store data in buffer
uart_txhead=tmphead;//store new index
UCSRB|=(1<<UDRIE);//enable UDRE interrupt
}
#pragma interrupt_handlerUsart_txc:14
void Usart_txc()
{
uchar tmptail;
if(uart_txhead!=uart_txtail)// check all data is transmitted
{
tmptail=(uart_txtail+1)&UART_TX_BUFFER_MASK;//calculate buffer index
uart_txtail=tmptail;//store new index
UDR=uart_txbuf;//start transmition
}
}
以上两个是发送中断函数,在主程序中是
void main()
{
while(1)
{
transmit_byte(0x00);
transmit_byte(0x01);
}
}
以上配置的波特率是9600
这是第一种按照发送缓冲区和发送中断函数结合使用的,目前在PC机上可以实现发送功能;
第二种情况:
voidsend(uchar DAT)//串口发送一个字节
{
while( !(UCSRA & (1<<UDRE)) );
UDR=DAT;
}
主函数位
void main()
{
while(1)
{
send(0x00);
send(0x01);
}
}
以上配置的波特率是9600
用示波器测上面两种情况 发现波形都是一样的,并没有出现您所说的:用中断发送函数比查询更加节约MCU的时间。有些不懂 请问是怎么回事?怎么可以测出用串口中断发送的数据包比用查询发送的数据包速度快??谢谢傻孩子老师! taocongrong 发表于 2012-10-6 13:51 static/image/common/back.gif
傻孩子老师您好:有一个关于串口发送中断的问题想请教你一下:
我最近做了一个关于发送中断的工程,关于 ...
这是很早之前的说法了,当时没有说清楚,其实所谓的节省时间其实非常笼统,更精确的说法是这样的
1、通过中断配合缓冲区的方法,可以使得系统在发送的时候只关注将数据送到缓冲区的过程,而不关注
发送是否成功这样的细节——也就是说至少不用让系统死等每一个字节都发送成功(也就是所谓的阻
塞式代码)
2、在同样使用非阻塞代码的情况下,也就是我们所说的多任务的情况下,使用缓冲区可以保证字节发送
的连续性,不使用缓冲区则字节与字节之间的间隔有可能会随着主循环中要处理的任务数量增加而增
加——这一现象是跟你用的芯片、跑的时钟频率、波特率以及任务的多少有关系的。要想验证,有两
种方法,用理论验证,也就是搞清楚轮询模式下while把所有任务跑一圈所需要的周期,这个周期就有
可能成为字节发送的间隔——这个你自己应该可以想得通;另外一种方法就是用一个较低频率的系统,
比如AVR跑1M,然后跑多个任务(每一个任务都是 none-block的),然后你应该可以很清晰的看到
任务的多少与字节与字节之间时间间隔的关系——当然,建议你用高一点的波特率,因为波特率越低
字节发送所需的时间就越长。
所有这些都是相对的概念,以前说的比较绝对,现在要纠正一下。 本帖最后由 taocongrong 于 2012-10-7 18:10 编辑
Gorgon_Meducer 发表于 2012-10-6 19:08 static/image/common/back.gif
这是很早之前的说法了,当时没有说清楚,其实所谓的节省时间其实非常笼统,更精确的说法是这样的
1、通过 ...
谢谢您,谢谢傻孩子老师,为什么叫你傻 孩子,你那么聪明,呵呵,不过还是要谢谢你 。{:smile:} taocongrong 发表于 2012-10-7 17:58 static/image/common/back.gif
谢谢您,谢谢傻孩子老师,为什么叫你傻 孩子,你那么聪明,呵呵,不过还是要谢谢你 。...
之前手机回帖不方便,现在在电脑上面回你。
好像你有个问题没有搞清楚。不管是中断+缓冲方式还是直接等待发送这种方式,这个效率不是你用示波器测波特率能够测出来的。
对于指定的波特率,难道你能够测出来两种波形???
好了,下面重点讲讲效率的问题。
假设系统中有一个主机, N个从机,通信方式为主机主动发起,从机应答。每个命令包长度为10个字节。
假定波特率9600 bps
则一个命令包长度发送完毕耗时约为 11 ms
采用查询方式发送:
//伪代码,下面函数发送指定 字节, 采用等待方式发送
SendCmdPacket(uint8 * pBuffer, uint8 u8Length)
//伪代码,下面函数发送指定 字节, 采用中断+缓冲方式
SendCmdPacketToBuffer(uint8 * pBuffer, uint8 u8Length)
则调用SendCmdPacket()发送时候,该函数会阻塞 11ms 直到发送完毕
相反,调用SendCmdPacketToBuffer()发送时候,该函数将数据包复制到缓冲区后,立即返回,耗时可能只有不到 0.1 ms 时间
加上中断处理时间 整个数据包发送完毕消耗CPU时间约为 1ms
相比查询方式,中断方式可以多出10ms 时间用来处理其它任务,这10 ms 就是效率!!!
而且通信数据包长度越长,优势越明显。
你的主机系统不可能只有通信任务,如果还有按键,显示等等其它任务,则用中断+缓冲能够提高整个系统的实时性!
明白否? mcu_lover 发表于 2012-10-8 20:25 static/image/common/back.gif
之前手机回帖不方便,现在在电脑上面回你。
好像你有个问题没有搞清楚。不管是中断+缓冲方式还是直接 ...
这个例子举得好。不过他有可能是观测字节与字节之间的时间间隔。在使用none-block代码且任务极少,
波特率不高或者CPU时钟很高的情况下,缓冲法和查询法的区别可能不是几千块钱的示波器能观察得到的。
在这种情况下,只有none-block的任务增多,波特率比较高,CPU时钟比较低的情况下才可能从示波器上
观测到字节与字节之间时间价格的显著差异——用中断+缓冲法比单纯的查询法字节间距离更小。 {:smile:}{:smile:} Gorgon_Meducer 发表于 2012-10-6 19:08 static/image/common/back.gif
这是很早之前的说法了,当时没有说清楚,其实所谓的节省时间其实非常笼统,更精确的说法是这样的
1、通过 ...
王老师 你说的 你的版块中的状态机的名字叫什么啊! taocongrong 发表于 2012-11-2 14:23 static/image/common/back.gif
王老师 你说的 你的版块中的状态机的名字叫什么啊!
状态机入门——程咬金只有三板斧厉害 太经典了,谢谢楼长! 能不能上传一个完整的程序Demo? lz强悍.... 看的有点吃力,楼主能不能先介绍点相关的书籍,先补补基础 好东西。。谢谢 feiante116 发表于 2013-2-7 10:44 static/image/common/back.gif
看的有点吃力,楼主能不能先介绍点相关的书籍,先补补基础
先看看数据结构吧。现在有很多书都是用C语言来介绍数据结构的。 第140行的 s_Queue##__NAME##PeekCounter++;是不是多余的? sbk100 发表于 2013-3-5 15:43 static/image/common/back.gif
第140行的 s_Queue##__NAME##PeekCounter++;是不是多余的?
141代码有点问题,我更新了。谢谢你。 在什么情况下用Peek_Extend()这个函数呢? 傻孩子大虾,我用了这个头文件,编译也是错误一堆啊~,现在不知道错在哪里了啊~ 有5个错误,都是类似这种:syntax error; found `Queue_PS2KeyBuffer_Add_Data' expecting `;' abcdzhy 发表于 2013-3-12 09:50 static/image/common/back.gif
有5个错误,都是类似这种:syntax error; found `Queue_PS2KeyBuffer_Add_Data' expecting `;' ...
用这个帖子里面我提供的模板http://www.amobbs.com/thread-5515836-1-1.html Gorgon_Meducer 发表于 2008-1-26 02:57 static/image/common/back.gif
[使用方法]
假设我想建立一个按键缓冲区,我们需要首先声明一个环形队列,名叫PS2KeyBuffer,大小为8个 ...
请问傻孩子,你这个模板相对与用纯c写的有何优势,效率高怎么体现的?感觉这样代码不容易读,倒是代码量少了很多,相对的也比较花心思。 多谢傻孩子大虾,我去研究下! 收藏一下 本帖最后由 abcdzhy 于 2013-3-12 15:29 编辑
找到错的原因了,是我没有定义BOOL,改成bool就没错了。你现在写的那个看着很高深啊,我粗看了一下,感觉少了这个原子操作的定义:__ATOM_ACCESS,不知道是我没找到还是怎么回事。还有就是老版的这个版本有这个定义# define END_DEFINE_QUEUE,没看到这个有什么作用啊! meirenai 发表于 2013-3-12 14:22 static/image/common/back.gif
请问傻孩子,你这个模板相对与用纯c写的有何优势,效率高怎么体现的?感觉这样代码不容易读,倒是代码量 ...
一次操心,终身受益。也就是,只要你一次获得了可靠的代码,以后你就可以稳定的获得同样的结果。 本帖最后由 Gorgon_Meducer 于 2013-3-12 18:56 编辑
abcdzhy 发表于 2013-3-12 15:16 static/image/common/back.gif
找到错的原因了,是我没有定义BOOL,改成bool就没错了。你现在写的那个看着很高深啊,我粗看了一下,感觉少 ...
关于原子操作的宏,看 DEF_SAFE_QUEUE 和 DEF_QUEUE 两个宏的定义。
另外一个问题,关于宏# define END_DEFINE_QUEUE,这是为了保证各式上的一致,同时也是为了以后能够扩展宏的功能或者结构留下伏笔。 多谢傻孩子大虾回复! 谢谢楼主学习了 mark。。。。。。。。。。。。。 mark…… 环形队列,高端
看不懂·····要学的好多···路要慢慢走··· Gorgon_Meducer 发表于 2013-3-5 17:03
141代码有点问题,我更新了。谢谢你。
傻孩子,每次关注你的版块,你都能够帮助一些热心的坛友解决问题,我个人感觉你在嵌入式C的算法方法,研究的比较透,是否可以考虑出一本这方面的书,我挺期待的,呵呵...... lpdpzc 发表于 2014-1-6 11:12
傻孩子,每次关注你的版块,你都能够帮助一些热心的坛友解决问题,我个人感觉你在嵌入式C的算法方法,研 ...
谢谢哈,我也有这个计划,看这个帖子
http://www.amobbs.com/thread-5506790-1-1.html
最近在忙一些大家伙,暂时缓了缓。不过2014年会是一个很有成果的年份。谢谢关注哈。不过与其等书,不如有什么
实际问题一起拿出来我们在这里交流交流更直接哈。 mark................... mark一 下,傻孩子 的环形队列
页:
[1]