IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> 51单片机非阻塞串口中断收发数据 -> 正文阅读

[嵌入式]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单片机效率得到了提升!

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

  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2022-05-10 12:04:54  更:2022-05-10 12:05:15 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 3:47:41-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码