搜索
bottom↓
回复: 12

能不能讲解下串口环形缓冲区的概念?

[复制链接]

出0入0汤圆

发表于 2011-6-7 11:03:46 | 显示全部楼层 |阅读模式
有段串口收发程序,看起来费劲,感觉可能是环形收发,能不能普及下思路?
/* 变量声明 */
INT8U * p_uart1SendBuf;                                                                /* 串口发送缓冲区指针 */
INT8U xdata uc_uart1RecBuf[UART1_RECBUF_LEN];                /* 串口接收缓冲区 */
INT8U xdata uc_uart1SendBuf[UART1_SENDBUF_LEN];                /* 串口发送缓冲区 */
volatile INT8U pdata uc_uart1RecBufIndex;                        /* 串口接收缓冲区索引 */
volatile INT8U pdata uc_uart1RecDealIndex;                        /* 串口接收处理索引 */
volatile INT8U pdata uc_uart1SendBufIndex;                        /* 串口发送缓冲区索引 */
volatile INT8U pdata uc_uart1SendDealIndex;                        /* 串口发送处理索引 */
volatile INT8U pdata uc_uart1SendFifoLen;                        /* 发送FIFO队列中数据个数 */
volatile INT16U pdata us_uart1RecFifoLen;                        /* 接收FIFO队列中数据个数 */
volatile BOOLEAN b_uart1SendFifoFlag;                                /* 发送FIFO队列有数据标志 */
volatile BOOLEAN b_uart1RecFifoFlag;                                /* 接收FIFO队列有数据标志 */


BOOLEAN b_uart1Cmd;                                /* 串口1命令标志 */
INT8U        uc_uart1Cmd;                        /* 串口1命令 */

BOOLEAN        b_uart1Sending;                        /* 正在发送标志 */



/* 函数声明 */
void         uart1_ram_init(void);        /* 串口1变量初始化 */
void         timer1_init(void);                /* 波特率发生器(定时器1)初始化 */
void         uart1_init(void);                /* 串口1初始化 */
INT8U *        put_to_uart1_send_fifo(INT8U *,INT8U); //reentrant;        /* 串口1写发送FIFO程序 */
void         put_to_uart1_rec_fifo(void);                        /* 串口1写接收FIFO程序 */


#if TEST_UART1_ISR_PROG == 1
        void uart1_isr_prog_test(INT8U);                /* 串口服务程序的测试代码(仅调试时用) */
#endif


/*
******************************************************************************************
** 函数名称 : uart1_ram_init()
** 函数功能 : 串口1使用的变量的初始化                          
** 入口参数 : 无
** 出口参数 : 无
** 函数说明 : 无            
******************************************************************************************
*/
void uart1_ram_init(void)
{
        uc_uart1RecBufIndex = 0x00;
        uc_uart1RecDealIndex = 0x00;
        uc_uart1SendBufIndex = 0x00;
        uc_uart1SendDealIndex = 0x00;
        uc_uart1SendFifoLen = 0x00;
        us_uart1RecFifoLen = 0x00;

        b_uart1SendFifoFlag = FALSE;
        b_uart1RecFifoFlag = FALSE;

        b_uart1Cmd = FALSE;
        uc_uart1Cmd = 0x00;

        b_uart1Sending = FALSE;
}


/*
******************************************************************************************
** 函数名称 : timer1_init()
** 函数功能 : 定时器1初始化程序                          
** 入口参数 : 无
** 出口参数 : 无
** 函数说明 : 定时器1作为串口1的波特率发生器
**             定时器1设置1T时钟周期,定时器1使用模式2,8位重载模式
**             同时禁止定时器1中断
******************************************************************************************
*/
void timer1_init(void)
{
       
        AUXR |= TMR1_1T;                        // 设置是1T还是12T(设置为12T)(复位后默认为12T)
        TMOD &= ~TMR1_CFG_BIT;                //清除定时器1模式位
        TMOD |= TMR1_TIMER | TMR1_8BIT_MODE ;                                //设定定时器1为8位自动重装方式
        TL1 = 0xFD;                //设定定时初值
        TH1 = 0xFD;                //设定定时器重装值
        ET1 = 0;                //禁止定时器1中断
        TR1 = 1;                //启动定时器1

}

/*
******************************************************************************************
** 函数名称 : uart1_init()
** 函数功能 : 串口1的初始化程序                          
** 入口参数 : 无
** 出口参数 : 无
** 函数说明 : 串口1以定时器1作为波特率发生器
**             串口1设置为115200bps(默认),8位数据,无效验位
******************************************************************************************
*/
void uart1_init(void)                //115200bps@11.0592MHz
{
        uart1_ram_init();        /* 串口1变量初始化 */

        PCON &= 0x7f;                /* 波特率不倍速 */
        SCON = 0x50;                /* 8位数据,可变波特率 */
        AUXR &= 0xfe;                /* 串口1选择定时器1为波特率发生器 */

        timer1_init();                /* 作为波特率发生器的定时器1的初始化 */

        /* 设置串口1中断优先级 */
        if (UART1_PRIO & 0x02)
        {
                IPH |= PSH;
        }
        else
        {
                IPH &= ~PSH;
        }
        if (UART1_PRIO & 0x01)
        {
                PS = 1;                       
        }
        else
        {
                PS = 0;
        }

        ES = 1;                /* 打开串口1中断 */
}

/*
******************************************************************************************
** 函数名称 : uart1_isr_prog_test()
** 函数功能 : 串口1中断服务程序的测试代码                          
** 入口参数 : 无
** 出口参数 : 无
** 函数说明 : 无            
******************************************************************************************
*/
#if TEST_UART1_ISR_PROG == 1
        void uart1_isr_prog_test(INT8U cmd)
        {
                b_uart1Cmd = TRUE;
                uc_uart1Cmd = cmd;
        }
#endif

/*
******************************************************************************************
** 函数名称 : uart1_isr()
** 函数功能 : 串口1中断服务程序                          
** 入口参数 : 无
** 出口参数 : 无
** 函数说明 : 串口1的中断号为4            
******************************************************************************************
*/
void uart1_isr(void) interrupt 4
{
        if (RI)
        {
                RI = 0;        /* 清除中断 */

                /* 把接收的数据写入FIFO缓冲区 */
                if ( (uc_uart1RecDealIndex + UART1_RECBUF_LEN) != uc_uart1RecBufIndex )
                {
                        uc_uart1RecBuf[uc_uart1RecBufIndex++ & (UART1_RECBUF_LEN - 1)] = SBUF;                /* 数据写入接收FIFO队列 */
                       
                        us_uart1RecFifoLen++;                /* 接收FIFO数量增加 */
                        b_uart1RecFifoFlag = TRUE;        /* 接收FIFO非空标志 */
       
                        /* 如果FIFO缓冲区超过设计值时则产生中断 */
                        if (us_uart1RecFifoLen >= UART1_REC_INT_LEN)
                        {
                                /* 人工触发定时器中断 */
                                #if UART2_DEAL_PROG_TIMER_INDEX == 0
                                        TF0 = TRUE;
                                #else
                                        TF1 = TRUE;
                                #endif
                        }               
                }

                #if TEST_UART1_ISR_PROG == 1
                        uart1_isr_prog_test(SBUF);
                #endif
        }

        if(TI)
        {
                TI = 0;

                /* 判断发送FIFO是否为空 */
                if (uc_uart1SendFifoLen != 0)
                {
                        SBUF = uc_uart1SendBuf[uc_uart1SendDealIndex++ & (UART1_SENDBUF_LEN - 1)];        /* 发送FIFO中的数据 */
                        //SBUF = p_uart1SendBuf++;

                        uc_uart1SendFifoLen--;        /* 发送FIFO数目减1 */
                       
                        if (!uc_uart1SendFifoLen )        /* 判断发送FIFO是否为空 */
                        {
                                b_uart1SendFifoFlag = FALSE;        /* 清除发送FIFO非空标志 */                               
                        }
                }
                else
                {
                        b_uart1Sending = FALSE;
                }
        }
}

/*
******************************************************************************************
** 函数名称 : put_to_uart1_send_fifo()
** 函数功能 : 串口发送FIFO数据队列程序                          
** 入口参数 : 需要写入发送FIFO队列的数据指针pBuf,写入数目length
** 出口参数 : 返回剩余的未写入发送FIFO队列的数据的指针
** 返回值定义
**  0 全部数据均写入FIFO
**  其它值 下一个未写入的数据的指针
******************************************************************************************
*/
INT8U *        put_to_uart1_send_fifo(INT8U * pBuf,INT8U length)  //reentrant
{       
        INT8U        len;
        INT8U        cnt;
       
        while(1)
        {
                len = length;
                cnt = 0;
                       
                // 数据加入缓冲区       
                while(length)
                {
                        if ( (uc_uart1SendDealIndex + UART1_SENDBUF_LEN) != uc_uart1SendBufIndex )
                        {
                                uc_uart1SendBuf[uc_uart1SendBufIndex++ & (UART1_SENDBUF_LEN - 1)] = *pBuf;
                                pBuf++;
                                       
                                cnt++;
                                length--;
                        }
                        else
                        {
                                break;
                        }                       
                }
                         
                if (len != length)
                {
                        ES = 0;                /* 关串口1中断 */
       
                        uc_uart1SendFifoLen += cnt;                        /* 把保存数目叠加进去 */
                        b_uart1SendFifoFlag = TRUE;                        /* 设置缓冲区有数据标志 */

                        /* 如果没有正在发送中,则要启动发送 */
                        if (!b_uart1Sending)
                        {
                                start_uart1_send();
                        }
       
                        ES = 1;        /* 开串口1中断 */               
                }
                               
                if (length == 0)
                {
                        break;
                }                       
        }

        if (!length)
        {
                return        length;                /* 返回未加入发送FIFO缓冲区的数目 */
        }
        else
        {
                return pBuf;
        }
}


/* 启动串口1发送 */
void        start_uart1_send(void)
{
        if (uc_uart1SendFifoLen != 0)
        {
                SBUF = uc_uart1SendBuf[uc_uart1SendDealIndex++ & (UART1_SENDBUF_LEN - 1)];        /* 发送FIFO中的数据 */
                uc_uart1SendFifoLen--;        /* 发送FIFO数目减1 */

                b_uart1Sending = TRUE;
                       
                if (!uc_uart1SendFifoLen )        /* 判断发送FIFO是否为空 */
                {
                        b_uart1SendFifoFlag = FALSE;        /* 清除发送FIFO非空标志 */
                }
        }
}

阿莫论坛20周年了!感谢大家的支持与爱护!!

一只鸟敢站在脆弱的枝条上歇脚,它依仗的不是枝条不会断,而是自己有翅膀,会飞。

出0入0汤圆

发表于 2011-6-7 13:35:16 | 显示全部楼层
我没有看程序,不过你应该先找本《数据结构》的书来看看,里面有介绍“队列”,所谓的环形缓冲,其实就是环形队列。

出0入0汤圆

发表于 2011-6-7 16:39:30 | 显示全部楼层
先记下,回头看评论学习

出0入0汤圆

发表于 2011-6-7 16:43:27 | 显示全部楼层
一个接收队列
接收入队,取数据处理时出队

一个发送队列
要发送的数据入队,取数据发送出队

环形,且取余的方法实现环形,也就是入队递增时地址超过队列最大时,再卷回到0

出0入0汤圆

发表于 2011-7-5 17:14:20 | 显示全部楼层
确实是环形缓冲区。。。。。。。。也不懂

出0入0汤圆

发表于 2011-9-29 14:59:17 | 显示全部楼层
mark

出0入0汤圆

发表于 2012-8-8 13:13:29 | 显示全部楼层
缓冲区就是一个数组,头尾两个索引(指针都不用,安全)。
其实要理解这个环形缓冲区,首先要理解生产者-消费者模型。

出0入0汤圆

发表于 2012-8-8 16:20:11 | 显示全部楼层
学了两年单片机   现在还要回来学习数据结构

出0入0汤圆

发表于 2012-8-8 16:42:02 | 显示全部楼层
简单点说就是利用队列的机制,先进先出。
环形是防止数组越界访问……

出0入0汤圆

发表于 2013-3-29 11:00:18 | 显示全部楼层
赞一下 请问能不能用于上位机串口指令控制下位机?

出0入0汤圆

发表于 2014-3-22 13:35:05 | 显示全部楼层
看看,好好看看

出0入0汤圆

发表于 2015-1-12 17:31:46 | 显示全部楼层
串口环形缓冲区可以保护数组不越界,挺好用的

出0入0汤圆

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

本版积分规则

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

GMT+8, 2024-5-16 17:36

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

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