51单片机非阻塞串口中断收发数据

        51单片机是指8051内核的8位单片机,因其内部结构相对简单,成本低廉,所以应用非常广泛!串口作为单片机最基本的通信接口,无论是开发调试,日常使用都是用得比较频繁的一个基本外设!但是很多教程包括官方提供的资料都是使用查询法发送数据!基本流程就是等待忙闲标志归0,忙闲标志置1,写SBUF寄存器,等待发送完成进入中断,忙闲标志清零。发送下一字节。

源代码已经打包上传,大家可以免费下载使用!如有问题,欢迎留言讨论!​​​​​​​

STC官方示例代码如下:

#include "reg51.h"            //这是官方例程  没有耐心认真看的使劲往下划
#include "intrins.h"

#define FOSC        11059200UL
#define BRT         (65536 - FOSC / 115200 / 4)


bit busy;
char wptr;
char rptr;
char buffer[16];

void UartIsr() interrupt 4
{
    if (TI)
    {
        TI = 0;
        busy = 0;
    }
    if (RI)
    {
        RI = 0;
        buffer[wptr++] = SBUF;
        wptr &= 0x0f;
    }
}


void UartSend(char dat)
{
    while (busy);        //这是官方例程  没有耐心认真看的使劲往下划
    busy = 1;
    SBUF = dat;
}

void UartSendStr(char *p)
{
    while (*p)
    {
        UartSend(*p++);
    }
}

void main()
{
    UartInit();
    ES = 1;
    EA = 1;
    UartSendStr("Uart Test !\r\n");

    while (1)
    {
        if (rptr != wptr)
        {
            UartSend(buffer[rptr++]);
            rptr &= 0x0f;
        }
    }
}

        这种方法对于通信而言一般情况不会有什么问题,但是在发送完第一个字节,准备发送第二个字节这段时间,单片机会停下来等待标志位归0.尤其是低波特率情况,发送一个字符串可能会浪费比较长的时间!而且会被别的中断打断,导致偶发性通信错误或数据丢失!

        一般32单片机提供的库会使用DMA,中断封装成一个非阻塞式发送函数,51单片机虽然没有DMA,但是中断还是有的。我们可以利用串口中断封装一个非阻塞式发送函数!代码如下:

#include "stc8g.h"
#include "intrins.h"



#define uint8_t unsigned char
	//串口发送缓存定义
typedef struct uartstruct{
	unsigned int tx_fifo[3][64];	//3个缓冲区 每个缓冲区64字节  缓存区数据加载满,继续写入会覆盖未发送的
	unsigned int tx_fifo_p_w;			//缓存区写指针
	unsigned int tx_fifo_p_r;			//缓存区写指针
	unsigned int tx_fifo_t;			//缓存发送指针
	unsigned int tx_fifo_w;			//缓存写入指针
} uart_fofi_typedef;
uart_fofi_typedef uart_fofi={{0},0,0,0,0};


void UART1_Isr() interrupt 4
{
    if (TI)
    {
			if(uart_fofi.tx_fifo_w)														//缓存不为空,FIFO写指针当前值为已经装入个数
			{
				SBUF = uart_fofi.tx_fifo[0][uart_fofi.tx_fifo_t];						//从FIFO的起始地址发送	
				uart_fofi.tx_fifo_t++;													//发送指针指向FIFO下个地址
				if(uart_fofi.tx_fifo_t==uart_fofi.tx_fifo_w)							//发送指针等于写指针,代表发送完成
					uart_fofi.tx_fifo_t=uart_fofi.tx_fifo_w=0;							//指针全部归零 一帧数据结束
			}
        TI = 0;                                 //清中断标志
    }
    if (RI)
    {
        RI = 0;                                 //清中断标志
//        P11 = !P11;                             //测试端口
    }
}

void UartSendStr(uint8_t *str)
{
	while(uart_fofi.tx_fifo_w);							//缓冲器空闲  否则等等,后期加FIFO 深度

		while(*str)
	{
		uart_fofi.tx_fifo[0][uart_fofi.tx_fifo_w]=(*str);//装入缓存
		str++;										//指向字符串下个字节
		uart_fofi.tx_fifo_w++;							//指向缓存下个字节
	}
	TI=1;												//进中断开始发送
}

void main()
{
		UartInit();

    while (1)
		{
			UartSendStr("uartstr 1\r\n");	
		}
}

以上代码的基本工作流程:首先定义一个缓存区,这里定义的是一个二维数组,为的是避免数据发送完之前,又来了新的数据把缓存区覆盖掉。此处暂不讨论,姑且当作1维数组使用。为了方便使用,同时把缓存区的控制指针(单缓冲区用不到),缓存的写入,发送指针定义为一个结构。

//串口发送缓存定义
typedef struct uartstruct{
	unsigned int tx_fifo[3][64];	    //3个缓冲区 每个缓冲区64字节  缓存区数据加载满,继续写入会覆盖未发送的
	unsigned int tx_fifo_p_w;			//缓存区写指针
	unsigned int tx_fifo_p_r;			//缓存区写指针
	unsigned int tx_fifo_t;			    //缓存发送指针
	unsigned int tx_fifo_w;			    //缓存写入指针
} uart_fofi_typedef;
uart_fofi_typedef uart_fofi={{0},0,0,0,0};

第一步把字符串装入缓存数组,完成之后触发串口发送中断。然后主循环就可以去忙别的事情了!

void UartSendStr(uint8_t *str)
{
	while(uart_fofi.tx_fifo_w);							//缓冲器空闲  否则等等,后期加FIFO 深度

		while(*str)
	{
		uart_fofi.tx_fifo[0][uart_fofi.tx_fifo_w]=(*str);//装入缓存
		str++;										//指向字符串下个字节
		uart_fofi.tx_fifo_w++;							//指向缓存下个字节
	}
	TI=1;												//进中断开始发送
}

第二步进入中断后判断写指针是不是不为0,如果不为0发送指针就开始从缓存区的起始地址发送数据。因为每次写SBUF硬件都会在发送完成时自动触发中断,所以后边的字节就不需要再去手动触发。直到发送指针=写缓存指针,说明数据都发送完了。此时两个指针归零,下次进入中断后仅清理发送完成标志TI,完成一帧数据发送,不再进入中断,直到下次调用UartSendStr函数。

void UART1_Isr() interrupt 4
{
    if (TI)
    {
			if(uart_fofi.tx_fifo_w)														//缓存不为空,FIFO写指针当前值为已经装入个数
			{
				SBUF = uart_fofi.tx_fifo[0][uart_fofi.tx_fifo_t];						//从FIFO的起始地址发送	
				uart_fofi.tx_fifo_t++;													//发送指针指向FIFO下个地址
				if(uart_fofi.tx_fifo_t==uart_fofi.tx_fifo_w)							//发送指针等于写指针,代表发送完成
					uart_fofi.tx_fifo_t=uart_fofi.tx_fifo_w=0;							//指针全部归零 一帧数据结束
			}
        TI = 0;                                 //清中断标志
    }
    if (RI)
    {
        RI = 0;                                 //清中断标志
    }
}

至此我们就通过串口中断,完成了数据的非阻塞式发送。让本来就处理数据比较慢的51单片机效率得到了提升!

        我来这里写文章的目的不是为了舞文弄墨,主要是分享一些我觉得还不错的小知识,还有就是遇到问题记录的笔记。如果您有不同看法,欢迎评论区留言,如果能帮到您,更是荣幸之至!

微信扫码订阅
UP更新不错过~
关注
  • 6
    点赞
  • 25
    收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:数字20 设计师:CSDN官方博客 返回首页
评论 2

打赏作者

山东徐大侠

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值