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

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

 1 #include "reg51.h"
 2 #include "intrins.h"
 3 
 4 #define FOSC        11059200UL
 5 #define BRT         (65536 - FOSC / 115200 / 4)
 6 
 7 
 8 bit busy;
 9 char wptr;
10 char rptr;
11 char buffer[16];
12 
13 void UartIsr() interrupt 4
14 {
15     if (TI)
16     {
17         TI = 0;
18         busy = 0;
19     }
20     if (RI)
21     {
22         RI = 0;
23         buffer[wptr++] = SBUF;
24         wptr &= 0x0f;
25     }
26 }
27 
28 
29 void UartSend(char dat)
30 {
31     while (busy);
32     busy = 1;
33     SBUF = dat;
34 }
35 
36 void UartSendStr(char *p)
37 {
38     while (*p)
39     {
40         UartSend(*p++);
41     }
42 }
43 
44 void main()
45 {
46     UartInit();
47     ES = 1;
48     EA = 1;
49     UartSendStr("Uart Test !\r\n");
50 
51     while (1)
52     {
53         if (rptr != wptr)
54         {
55             UartSend(buffer[rptr++]);
56             rptr &= 0x0f;
57         }
58     }
59 }

 

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

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

 1 #include "stc8g.h"
 2 #include "intrins.h"
 3 
 4 
 5 
 6 #define uint8_t unsigned char
 7     //串口发送缓存定义
 8 typedef struct uartstruct{
 9     unsigned int tx_fifo[3][64];    //3个缓冲区 每个缓冲区64字节  缓存区数据加载满,继续写入会覆盖未发送的
10     unsigned int tx_fifo_p_w;            //缓存区写指针
11     unsigned int tx_fifo_p_r;            //缓存区写指针
12     unsigned int tx_fifo_t;            //缓存发送指针
13     unsigned int tx_fifo_w;            //缓存写入指针
14 } uart_fofi_typedef;
15 uart_fofi_typedef uart_fofi={{0},0,0,0,0};
16 
17 
18 void UART1_Isr() interrupt 4
19 {
20     if (TI)
21     {
22             if(uart_fofi.tx_fifo_w)                                                        //缓存不为空,FIFO写指针当前值为已经装入个数
23             {
24                 SBUF = uart_fofi.tx_fifo[0][uart_fofi.tx_fifo_t];                        //从FIFO的起始地址发送    
25                 uart_fofi.tx_fifo_t++;                                                    //发送指针指向FIFO下个地址
26                 if(uart_fofi.tx_fifo_t==uart_fofi.tx_fifo_w)                            //发送指针等于写指针,代表发送完成
27                     uart_fofi.tx_fifo_t=uart_fofi.tx_fifo_w=0;                            //指针全部归零 一帧数据结束
28             }
29         TI = 0;                                 //清中断标志
30     }
31     if (RI)
32     {
33         RI = 0;                                 //清中断标志
34 //        P11 = !P11;                             //测试端口
35     }
36 }
37 
38 void UartSendStr(uint8_t *str)
39 {
40     while(uart_fofi.tx_fifo_w);                            //缓冲器空闲  否则等等,后期加FIFO 深度
41 
42         while(*str)
43     {
44         uart_fofi.tx_fifo[0][uart_fofi.tx_fifo_w]=(*str);//装入缓存
45         str++;                                        //指向字符串下个字节
46         uart_fofi.tx_fifo_w++;                            //指向缓存下个字节
47     }
48     TI=1;                                                //进中断开始发送
49 }
50 
51 void main()
52 {
53         UartInit();
54 
55     while (1)
56         {
57             UartSendStr("uartstr 1\r\n");    
58         }
59 }

 

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

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

 

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

 1 void UartSendStr(uint8_t *str)
 2 {
 3     while(uart_fofi.tx_fifo_w);                            //缓冲器空闲  否则等等,后期加FIFO 深度
 4 
 5         while(*str)
 6     {
 7         uart_fofi.tx_fifo[0][uart_fofi.tx_fifo_w]=(*str);//装入缓存
 8         str++;                                        //指向字符串下个字节
 9         uart_fofi.tx_fifo_w++;                            //指向缓存下个字节
10     }
11     TI=1;                                                //进中断开始发送
12 }

 

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

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

 

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

posted @ 2022-05-09 11:10  山东徐大侠  阅读(936)  评论(0)    收藏  举报