搜索
bottom↓
回复: 17

开源PLC学习笔记05(再从51开始 通讯 UART)——2013_11_10

[复制链接]

出0入0汤圆

发表于 2013-11-10 15:28:52 | 显示全部楼层 |阅读模式
本帖最后由 oldbeginner 于 2013-11-10 19:12 编辑

在笔记03和04中,通过研究Read code代码的功能,完成了对FX1NProcessing函数的研究,这个函数是和三菱上位机软件通讯的核心。FX1NProcessing实现了对协议的翻译,但是传送数据等是通过uart来实现的。现在就可以来理解uart是如何实现数据传送的。
******************************************************************************
uart相关的函数都定义在uart.c中,但是最关键的UartInit函数(初始化)是定义在PLC51x.c文件中,为什么?

//   串口初始化 函数  UartInit();                                                      //
//-------------------------------------------------------------------------------------//

void UartInit(void)
{
    SCON  = 0x50;
    //PCON |= 0x80;
    //AUXR2|= 0x40;
    //Timer1Init();
    TR1=0;                //停止定时器
//    TCON=0x00;            //定时器控制寄存器 注意:TCON只需操作一次
    TMOD |= 0x20;        //定时器1
    TL1 = -(SYSCLK/12/32/baud);//0xfa;    // -(SYSCLK/12/32/baud);    //注意波特率加倍位
       TH1 = TL1;
    TR1=1;                //启动定时器1
//    ET1=1;                //打开定时器1中断
    UartReceiveCounter=0;
    UartRxTimerStartFlag=0;
}
*****************************************
看得很难受,先把注释的行都去掉
void UartInit(void)
{
    SCON  = 0x50;
    //停止定时器
    TR1=0;              
   
   //定时器1
    TMOD |= 0x20;      
   
  //0xfa;    // -(SYSCLK/12/32/baud);    //注意波特率加倍位
   TL1 = -(SYSCLK/12/32/baud);
       TH1 = TL1;
  //启动定时器1   
   TR1=1;               

    UartReceiveCounter=0;
    UartRxTimerStartFlag=0;
}
***********************************************
在uart.h中定义
#define SYSCLK            11059200
#define baud            9600        // 注意修改Timer1 TH1

看着还是不舒服,重新写一下,添加了注释(基于外部接11.592Mhz晶振,并把倍频开通)
void UartInit(void)
{
      //停止定时器
    TR1=0;  
           
/******电源管理PCON寄存器************
位        7            6            5        4        3        2        1        0
寄存器    SMOD    X             X        X        X        X        X        X
        ————————————————————   
取值        1        0              0        0        0        0        0        0
说明    波特率加倍
************************************/
    PCON=0x80;
   
/*****定时器TMOD寄存器**********
位            7         6            5            4                3        2            1        0
寄存器    GATE    C/T          M1        M0        GATE         C/T        M1        M0
        ——————————————----         |———————————   
                定时器1的设置                    定时器0的设置
取值    0                0            1           0               0         0        0            0
说明        使用定时器1,工作方式2            |            没用使用            
**********************************/
   TMOD=TMOD | 0x20;

//定时器1工作方式2
//在下面的PCON设置上增加了倍频,所以晶振位22.1184Mhz
/**********波特率设置表(22.1184Mhz)************
波特率    2400    4800    9600    19200
TH1        E8        F4        FA        FD
TL1        E8        F4        FA        FD
说明,因为选取波特率9600,所以TH1和TL1都取FA
*************************************************/
    TH1=0xFA;
    TL1=0xFA;


//串口工作方式1
/*******串口SCON寄存器***************
位        7              6                5                    4                3             2                    1                          0
寄存器    SM0        SM1          SM2                REN            TB8            RB8                TI                        RI
        ——————————————————————————————————————
             工作方式控制|         多机通信|       接受标志位|  发第9位|     收第9位|          发送中断标志位  接受中断标志位
取值     0            1                 0                     1                0                0                     0                            0
说明        工作方式1            开始接受
**************************************/
SCON=0x50;   


//启动定时器1   
   TR1=1;               

    UartReceiveCounter=0;
    UartRxTimerStartFlag=0;
}

源程序中没有开启中断,还有为什么注释掉几行?目前无法判断原因,继续下去找答案。
附录:波特率与定时器


附录2:串口相关函数

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

发表于 2013-11-10 16:00:01 | 显示全部楼层
顶楼主,我是个外行,这个是什么协议,与modbusb比如何

出0入0汤圆

 楼主| 发表于 2013-11-10 16:33:15 | 显示全部楼层

初始化后,继续。

再返回到uart.c中,先看一下变量
volatile unsigned char UartSendBuffer[OutLEN];            //发送缓冲

volatile unsigned char UartReceiveBuffer[InLEN];        //接收数据缓冲

volatile unsigned char *outlast=UartSendBuffer;            //最后由中断传输出去的字节位置

volatile unsigned char *putlast=UartSendBuffer;            //最后放入发送缓冲区的字节位置

volatile unsigned char UartSendBufferemptyFlag=1;        //缓冲区数据发完标志   发完=1

volatile unsigned char UartSendBufferHaveDataFlag=0;    //发送缓冲区非空标志   有=1

volatile unsigned char UartReceiveCounter=0;            //接收计数器

volatile unsigned char UartRxTimerStartFlag=0;            //接收超时计数器启动标志

volatile unsigned char UartWaitForCounter=0;            //接收超时计数器

volatile unsigned char UartDataReadyFlag=0;                //接收完成标志
********************************************************************
上面UartReceiveBuffer在FX1NProcessing函数多次使用,为了简洁,当时记为Buffer变量名很多又太长,没有必要记,暂时只看一下变量名的末尾(红色),了解大概功能即可,等在具体代码中遇到再返回来确认。

// 函数名称: UartSendchar
//uart.c
// 功能描述:放入一个字节到发送缓冲区
//
// 输 入:   unsigned char ucdata
//         
// 输 出:   void
void UartSendchar(unsigned char ucdata)
{
    ES=0;                                     // 暂停串行中断,以免数据比较时出错
    while((((outlast-putlast)==2)&&(outlast > putlast ))||((outlast < putlast)&&(OutLEN-(putlast-outlast)==2)))
    {
        ES=1;
        ucdata++;
        ucdata--;
        ES=0;
    }
    *putlast=ucdata;                        // 放字节进入缓冲区
    putlast++;                              // 发送缓冲区指针加1
    if (putlast==UartSendBuffer+OutLEN) putlast=UartSendBuffer;        // 指针到了顶部换到底部
    UartSendBufferHaveDataFlag=1;
    if (UartSendBufferemptyFlag)             // 缓冲区无数据
    {
        UartSendBufferemptyFlag =0;
        SBUF=*outlast;                        // 未发送完继续发送
        outlast++;                            // 最后传出去的字节位置加1
        if (outlast==UartSendBuffer+OutLEN)outlast=UartSendBuffer;    // 地址到顶部回到底部
        if (putlast==outlast)UartSendBufferHaveDataFlag=0;            // 数据发送完置发送缓冲区空标志
    }                                        // 缓冲区开始为空,置为有,启动发送
    ES=1;
}
看来 UartSendchar是uart相关最复杂的函数,但要实现的功能从字面上就很好理解。
********************************************************************
按顺序理解,
ES=0;                                     // 暂停串行中断,以免数据比较时出错
什么是ES?
// (bits in IE,定义在REG_MPC82G516.h中)
sbit EA       = IE^7;
sbit ET2      = IE^5;
sbit ES       = IE^4;
sbit ET1      = IE^3;
sbit EX1      = IE^2;
sbit ET0      = IE^1;
sbit EX0      = IE^0;
从定义上,和REG52.h中的定义一致,就是断开串口中断。下图左下角的开关。


while((((outlast-putlast)==2)&&(outlast > putlast ))||((outlast < putlast)&&(OutLEN-(putlast-outlast)==2)))
看来也许要再定义一个宏了,不过先理解一下,

最后由中断传输出去的字节位置  outlast

最后放入发送缓冲区的字节位置 putlast
要理解还需要看中断函数,因为outlast和putlast都是由中断函数赋值的。所以要理解一下中断函数后再返回。


本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

 楼主| 发表于 2013-11-10 18:50:55 | 显示全部楼层
oldbeginner 发表于 2013-11-10 16:33
初始化后,继续。

再返回到uart.c中,先看一下变量

入门时很少见到串口中断,搜一下,只要给单片机发送数据,单片机就会自动接收数据,并把它放在数据缓冲器SBUF中,如果你之前有允许串行口中断,RI就会置1,向单片机CPU申请中断,并进入中断服务程序,做完中断函数后就会自动返回断点。RI和TI中有一个为1就会马上进入中断服务子程序。
http://bbs.elecfans.com/forum.php?mod=viewthread&tid=206923

void Uart(void) interrupt 4 using 2
{
     中断处理;
}

interrupt 4 using 2是什么?再搜一下,
interrupt 4  指明是串行口中断;
using 2时设置 RS1=1,RS0 =0,用第2组寄存器,R0--R7的在数据存储区里的实际地址是08H-0FH。http://zhidao.baidu.com/link?url ... nY1Q5SLezt7k2Rpt8Lq

为什么制定用第2组?先保留这个问题,以后解决。
void Uart(void) interrupt 4 using 2
{
     if(TI)
     发送中断处理;
   
     if(RI)
     接收中断处理;
}

******************************
先看发送中断处理,
        TI=0;
        if (UartSendBufferHaveDataFlag)
        {
            SBUF=*outlast;                                                 // 未发送完继续发送
            outlast++;                                                     // 最后传出去的字节位置加1
           
            if (outlast==UartSendBuffer+OutLEN)
                         outlast=UartSendBuffer;    // 地址到顶部回到底部
            
            if (putlast==outlast)
                         UartSendBufferHaveDataFlag=0;            // 数据发送完置发送缓冲区空标志
        }
        else UartSendBufferemptyFlag =1;

1、TI=0,好理解,先复位。

2、要判断一个标志位,UartSendBufferHaveDataFlag,这个标志位就是一个句子,根据句子字面意思就是,串口发送缓冲区有了数据。
  确认一下定义volatile unsigned char UartSendBufferHaveDataFlag=0;    //发送缓冲区非空标志   有=1

3、如果DataFlag(暂时简洁一下)为0,则UartSendBufferemptyFlag =1,根据字面意思,缓冲区是空的。
   确认一下定义volatile unsigned char UartSendBufferemptyFlag=1;        //缓冲区数据发完标志   发完=1

4、如果DataFlag为1,SBUF=*outlast;  ( // 未发送完继续发送 ),然后outlast++; ( // 最后传出去的字节位置加1 )。基本能猜出outlast用来定位字符数组要传的字节,确认一下定义volatile unsigned char *outlast=UartSendBuffer;            //最后由中断传输出去的字节位置

5、首先是一个判断,if (outlast==UartSendBuffer+OutLEN),如果成立然后outlast=UartSendBuffer;    (// 地址到顶部回到底部),基本能看出来,当outlast定位到数组末尾后,即所有数组成员都被遍历后,重新回到开头。OutLen也能猜出什么意思来,确认一下,#define OutLEN            30            //    FIFO 发送缓冲区长度。数组长度是一个固定值。

6、首先是一个判断, if (putlast==outlast),查看一下定义volatile unsigned char *putlast=UartSendBuffer;            //最后放入发送缓冲区的字节位置 。如果成立,然后UartSendBufferHaveDataFlag=0; putlast就是指向数组的首地址,等outlast遍历数组成员后,再回到首地址,这时两者就相等,然后声明发送缓冲区是空的。这里有个疑问,putlast是指向首地址的,在程序运行期间不应该发生变化,为什么起名叫put last,字面上无法理解。

这样,发送中断处理有了一个了解。
********************************************

出0入0汤圆

 楼主| 发表于 2013-11-10 18:52:09 | 显示全部楼层
semonpic 发表于 2013-11-10 16:00
顶楼主,我是个外行,这个是什么协议,与modbusb比如何

我也是外行,正在学习,你说的哪个协议我见到过,但是不会。帖子里讲的是入门的串口通讯。

出0入0汤圆

发表于 2013-11-10 19:24:19 | 显示全部楼层
oldbeginner 发表于 2013-11-10 18:52
我也是外行,正在学习,你说的哪个协议我见到过,但是不会。帖子里讲的是入门的串口通讯。 ...

modbus 是工控设备 基本支持的一种基于485硬件的协议,

三菱那年头的老家伙,似乎是不支持,,

出0入0汤圆

发表于 2013-11-10 20:14:44 | 显示全部楼层
MARK马克

出0入0汤圆

发表于 2013-11-10 20:44:07 | 显示全部楼层
好。。。
串口:环形队列+中断。
以前C51BBS上有一个所子写的,我觉得不错。

出0入0汤圆

 楼主| 发表于 2013-11-11 12:58:04 | 显示全部楼层
本帖最后由 oldbeginner 于 2013-11-11 13:16 编辑
oldbeginner 发表于 2013-11-10 18:50
入门时很少见到串口中断,搜一下,只要给单片机发送数据,单片机就会自动接收数据,并把它放在数据缓冲器 ...


再继续接收中断处理,
        RI = 0;
        //==========================================================
        //      若有必要 有待于加入 偶校验算法 .数据位的bit7位为校验位
        //==========================================================
        Buffer[UartReceiveCounter++]=SBUF&0x7f;

        UartRxTimerStartFlag=1;                // 启动超时计数器

          UartWaitForCounter=0;                // 清超时计数器    // 10ms

        if (UartReceiveCounter>=InLEN)
        {
            UartDataReadyFlag=1;
            //UartReceiveCounter=0;
            REN=0;
        }
******************************************************************

1、RI=0;好理解,复位。

2、SBUF是接收到的数据,然后&0111 1111。为什么要&0x7F呢?搜了一下,同一论坛上用STM32也不知道为什么,
有谁做过STM32串口通信使用7位数据位的?
不过网上找到一个比较可行的理解是,51单片机的确只能做到8个数据位。所问的“7位数据位串行通信”估计是“1个起始位+7个数据位+1个偶校验位+1个停止位”这种方式。http://zhidao.baidu.com/link?url ... eC9Tcjw2O5HCzdIhXeq(评论中)
按照下图来理解,把高位屏蔽(本例)或作为校验位。



3、启动超时计数器, UartRxTimerStartFlag=1;查看一下定义volatile unsigned char UartRxTimerStartFlag=0;            //接收超时计数器启动标志,可以理解,继续

4、 清超时计数器,UartWaitForCounter=0;查看一下定义volatile unsigned char UartWaitForCounter=0;            //接收超时计数器,应该在后面的uart相关函数会遇到,到时再返回来确认。

5、首先是一个判断,UartReceiveCounter>=InLEN,InLen曾经遇到过,再复习一下,#define InLEN            142            //    超时收 接收缓冲区长度。这是一个判断是否超时的语句,当UartReceiveCounter>=142时,就认为超时。

6、如果超时,则 UartDataReadyFlag=1;并让REN复位。查看volatile unsigned char UartDataReadyFlag=0;                //接收完成标志
把REN和RI搞混了,搜一下:
REN:允许接收位。   REN用于控制数据接收的允许和禁止,REN=1时,允许接收,REN=0时,禁止接收。

中断处理函数,
void Uart(void) interrupt 4 using 2
{
     if(TI)
     发送中断处理;
   
     if(RI)
     接收中断处理;
}
这样串口中断函数第一遍理解就完成了,在它的帮助下,再次去理解UartSendChar函数。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

 楼主| 发表于 2013-11-11 14:49:24 | 显示全部楼层
本帖最后由 oldbeginner 于 2013-11-11 15:07 编辑
oldbeginner 发表于 2013-11-11 12:58
再继续接收中断处理,
        RI = 0;
        //=================================================== ...

返回上次被卡住的地方
***************************
while((((outlast-putlast)==2)&&(outlast > putlast ))||((outlast < putlast)&&(OutLEN-(putlast-outlast)==2)))
看来也许要再定义一个宏了,不过先理解一下,

最后由中断传输出去的字节位置  outlast

最后放入发送缓冲区的字节位置 putlast
****************************
在中断函数理解中,我认为outlast的作用是遍历数组每个成员,而putlast是指向首地址,用来判断outlast是否回到了首地址。
不过看到(outlast < putlast),我认为自己的理解有问题。

要理解串口中断函数,还必须了解一下环形队列。
找到了一个还算详细的资料,http://wenku.baidu.com/view/3117421303d8ce2f006623aa.html

自己在理解发送中断时有个疑问,现在可以浮出了,
6、首先是一个判断, if (putlast==outlast),查看一下定义volatile unsigned char *putlast=UartSendBuffer;            //最后放入发送缓冲区的字节位置 。如果成立,然后UartSendBufferHaveDataFlag=0; putlast就是指向数组的首地址,等outlast遍历数组成员后,再回到首地址,这时两者就相等,然后声明发送缓冲区是空的。这里有个疑问,putlast是指向首地址的,在程序运行期间不应该发生变化,为什么起名叫put last,字面上无法理解。

当然,理解错了。还需要重新返回中断处理函数,首先看发送中断。


putlast和outlast都是移动的,当发送数据时,outlast移动,这也让我误认为putlast是不动的。

环形队列的特点是,不需要进行动态的内存释放和分配,使用固定大小的内存空间(本例中OutLEN就是一个常数)反复使用。在实际的队列插入和弹出操作中,是不断交叉进行的。                                           http://blog.csdn.net/billow_zhang/article/details/4420789

            SBUF=*outlast;                                                 // 未发送完继续发送
            outlast++;                                                     // 最后传出去的字节位置加1


然后,直到


所以在第2遍理解时加入了环形队列,把outlast当作head,putlast当作tail。

*******************************

小结,
串口中断处理函数,中断发送函数利用了一个环形队列UartSendBuffer[30]。

接收函数则没这样复制,只是利用另外一个队列,Buffer(UartReceiveBuffer)。 核心是Buffer[UartReceiveCounter++]=SBUF&0x7f。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

 楼主| 发表于 2013-11-11 16:00:59 | 显示全部楼层
本帖最后由 oldbeginner 于 2013-11-11 16:26 编辑
oldbeginner 发表于 2013-11-11 14:49
返回上次被卡住的地方
***************************
while((((outlast-putlast)==2)&&(outlast > putlast  ...

第3次去理解UartSendChar函数,
while((((outlast-putlast)==2)&&(outlast > putlast ))||((outlast < putlast)&&(OutLEN-(putlast-outlast)==2)))
{
    等待处理;
}

感觉条件判断内部的函数是为了等待某个条件,先看一下什么条件(左右两条件或)


基本上就是队列马上就要满了,只差一个。
出现上面两种情况后,就
        ES=1;
        ucdata++;
        ucdata--;
        ES=0;
不是很理解。

继续向下,走出while语句后,
    *putlast=ucdata;                        // 放字节进入缓冲区
    putlast++;                              // 发送缓冲区指针加1

putlast开始移动了。

然后一个判断语句,
  if (putlast==UartSendBuffer+OutLEN)
                   putlast=UartSendBuffer;        // 指针到了顶部换到底部
从上面的环形图中也可以看出,如果putlast又移动到位置0,这时需要人工重新赋一下地址。

然后,数据标志位置1,
UartSendBufferHaveDataFlag=1;  同样可以从上图中可以看出,ucdata已经读入了队列中,所以标志有数据了。

然后又是一个判断,
    if (UartSendBufferemptyFlag)             // 缓冲区无数据
    {
        缓冲区无数据处理
    }         
查看定义,volatile unsigned char UartSendBufferemptyFlag=1;        //缓冲区数据发完标志   发完=1
emptyFlag初值是1,那么什么时候会置1呢?复习一下串口发送中断里的一部分,
************(串口中断函数部分)***************
        if (UartSendBufferHaveDataFlag)
        {
            SBUF=*outlast;           // 未发送完继续发送
            outlast++;                 // 最后传出去的字节位置加1
            。。。。。。
        }
        else
             UartSendBufferemptyFlag =1;
**************************************

理解了emptyFlag,如果为1,则缓冲区无数据处理
        UartSendBufferemptyFlag =0;
        SBUF=*outlast;                        // 未发送完继续发送
        outlast++;                            // 最后传出去的字节位置加1
        if (outlast==UartSendBuffer+OutLEN)
                   outlast=UartSendBuffer;    // 地址到顶部回到底部
        if (putlast==outlast)
                   UartSendBufferHaveDataFlag=0;            // 数据发送完置发送缓冲区空标志

1、复位emptyFlag,好理解。

2、利用outlast发送,环形队列中解释比较清楚了。

3、第一个判断,outlast是否到位置0了,如果是,人工重新赋地址;
     第二个判断,outlast是否追上putlast,如果是则队列空,复位DataFlag

第1遍理解差不多完成,
********************************************************
void UartSendchar(unsigned char ucdata)
{
    ES=0;                                     // 暂停串行中断,以免数据比较时出错
    while(队列马上就要满了)
    {
        不是很理解的代码;
    }

    *putlast=ucdata;                        // 放字节进入缓冲区
    putlast++;                              // 发送缓冲区指针加1

    if (putlast==UartSendBuffer+OutLEN)
                  putlast=UartSendBuffer;        // 指针到了顶部换到底部

    UartSendBufferHaveDataFlag=1;

    if (UartSendBufferemptyFlag)             // 缓冲区无数据
    {
        缓冲区无数据处理
    }
                                 
    ES=1;
}
***************************************************

因为UartSendChar和串口中断函数互相调用环形队列,所以必须熟悉环形队列的模型(有点像太极图)。


有了上面的模型,在利用UartSendBufferHaveDataFlag和UartSendBufferemptyFlag这两个标志位来判断状态。

另外,这里没有使用队列已经满了的标志位,我猜测和那段我不太理解的代码有关。


本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

 楼主| 发表于 2013-11-11 17:09:02 | 显示全部楼层
oldbeginner 发表于 2013-11-11 16:00
第3次去理解UartSendChar函数,
while((((outlast-putlast)==2)&&(outlast > putlast ))||((outlast < put ...

还剩下两个比较简单的函数
// 函数名称: UartSendString
//
// 功能描述:发送字符串
//
// 输 入:   unsigned char *str
//         
// 输 出:   void
void UartSendString(unsigned char *str)
{
    while(*str)                                // 遇到停止符0结束
    {
        UartSendchar(*str++);
        //while(UartSendBufferHaveDataFlag);
    }
}

不停调用UartSendChar,
为什么写while(UartSendBufferHaveDataFlag),然后又注释掉。当UartSendBufferHaveDataFlag=1时,表示队列写入了字符,
然后在中断处理函数中,
           if (putlast==outlast)
                         UartSendBufferHaveDataFlag=0;            // 数据发送完置发送缓冲区空标志
这里可能是为了确认写入后并被发送后,再发送下一条字符,为什么又注释掉了呢?

感觉应该是while(队列满标志位);但是这个程序中没有设置队列满标志位。

*********************************
// 函数名称: UartSendByte
//
// 功能描述:发送一串数据
//
// 输 入:   unsigned char *Startaddr,unsigned char SendByte
//         
// 输 出:   void
void UartSendByte(unsigned char *Startaddr,unsigned char SendByte)
{   
    while(SendByte--)
    {
        UartSendchar(*Startaddr++);
    }
}
发送长度确定。




出0入0汤圆

 楼主| 发表于 2013-11-11 18:21:34 | 显示全部楼层
oldbeginner 发表于 2013-11-11 17:09
还剩下两个比较简单的函数
// 函数名称: UartSendString
//

理解了uart是如何工作后,就要进入我还没接触过IAP了。

http://www.amobbs.com/thread-5558672-1-1.html

出0入0汤圆

发表于 2013-11-11 22:49:05 | 显示全部楼层
oldbeginner 发表于 2013-11-11 18:21
理解了uart是如何工作后,就要进入我还没接触过IAP了。

http://www.amobbs.com/thread-5558672-1-1.html ...

while((((outlast-putlast)==2)&&(outlast > putlast ))||((outlast < putlast)&&(OutLEN-(putlast-outlast)==2)))
    {
        ES=1;
        ucdata++;
        ucdata--;
        ES=0;
    }
这个ucdata++;然后ucdata--是什么意思?

出0入0汤圆

 楼主| 发表于 2013-11-12 13:03:06 | 显示全部楼层
秋原之风 发表于 2013-11-11 22:49
while((((outlast-putlast)==2)&&(outlast > putlast ))||((outlast < putlast)&&(OutLEN-(putlast-outl ...

感觉这段代码没有意义,此时tail就要追上head了,只差1格队列就满了,只是为了让tail停一下。

出0入0汤圆

发表于 2015-7-3 22:08:54 | 显示全部楼层
克鲁克各国

出0入0汤圆

发表于 2015-10-21 15:07:34 | 显示全部楼层
太感谢楼主了  帮了我大忙

出0入0汤圆

发表于 2015-11-25 15:07:21 | 显示全部楼层
好东西啊
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-4-20 13:58

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表