【STC8A8K64S4A12开发板】—学习UART串口通信

版权声明:本文为博主原创文章,转载请附上原文出处链接。


前言

本次讲解STC8A8K64S4A12系列MCU串行口原理、4个UART串行口外设相关寄存器配置及程序设计。

一、硬件设计

1.开发板串口硬件电路

STC8A8K64S4A12开发板上设计了USB转TTL电路(CH340),其主要作用有3个:

  1. USB转串口通信,可用于开发板串口通信调试。
  2. USB接口有5V电源,可为开发板供电。
  3. 可使用短路帽选择J5端子,将USB转TTL电路的TTL信号连接到MCU的P3.0和P3.1引脚,这样可用于开发板程序下载。

USB转TTL电路如下图所示,串口接收和发送的引脚上均连接了LED指示灯,收发数据或程序下载时指示灯会闪烁,这样,更方便我们从硬件的角度观察串口有没有在进行数据通信。电路中的1000mA自恢复保险丝用于保护开发板和计算机USB口。

在这里插入图片描述

图1:开发板USB转TTL电路

STC8A8K64S4A12开发板上还设计了RS232电路(MAX3232)及DB9接口,因为有的客户电脑有DB9接口,那么开发板也有DB9接口就可以直接连接,方便调试。同样可使用短路帽选择J5端子,将该RS232电路的TTL信号连接到MCU的P3.0和P3.1引脚,这样也可用于开发板程序下载和串口通信。

在这里插入图片描述

图2:开发板RS232电路

USB转TTL(RS232电路)占用的单片机的引脚如下表:

UART功能描述对应IO口说明
RXD串口接收P3.0独立GPIO
TXD串口发送P3.1独立GPIO

☆注:独立GPIO表示开发板没有其他的电路使用这个GPIO。

2.STC8A8K64S4A12系列单片机UART介绍

STC8A8K64S4A12系列单片机有4个采用UART工作方式的全双工异步串行通信接口,每个串行口由2个数据缓存器、1个移位寄存器、1个串行控制寄存器和1个波特率发生器等组成。每个串行口的数据缓存器由2个相互独立的接收、发送缓冲器构成,因此可以同时发送和接收数据。

■ STC8A8K64S4A12系列单片机的4个串口引脚分配:

STC8A8K64S4A12系列单片机的4个UART是相互独立的,可以同时使用。但每个UART会有多组引脚与之对应(具体几组还取决于芯片封装引脚数),请注意同一个UART只能通过相关寄存器配置其中的一组使用,比如P3.0、P3.1是串口1,而P1.6、P1.7也是串口1,在使用串口1时必须选择一个来使用。STC8A8K64S4A12系列单片机串口的引脚分配如下表。

表2:单片机4个串口引脚分配

在这里插入图片描述

☆注:同一个串口各组之间切换是需要配置相关寄存器的相关位实现,如果没有对该部分寄存器配置,一般默认选择的都是第一组串口。

■ STC8A8K64S4A12系列单片机的4个串口用定时器:

STC8A8K64S4A12系列单片机UART用于波特率发生器的定时器也是可以选择的,但不是任意选择哪个定时器都可以的。针对不同UART可供选择的定时器如下表所示。

表3:单片机4个串口波特率发生器用定时器

在这里插入图片描述

☆注:同一个串口的波特率发生器使用的定时器是需要配置相关寄存器的相关位实现,注意对寄存器按位进行操作,没有使用的位不要去配置。

3.串行口UART工作模式

STC8A8K64S4A12系列单片机4个UART均有多种工作模式,串口1有4种工作模式,其中2种工作模式的波特率是可变的,另2种工作模式的波特率是固定的,以供不同应用场合选用。串口2、串口3和串口4都只用2种工作模式,这2种工作模式的波特率都是可变的。下面列表4个UART的工作模式。

表4:单片机4个串口工作模式

在这里插入图片描述

☆注:艾克姆提供例程是按照“8位UART,波特率可变”模式进行配置。

■ UART1工作模式1原理介绍:

“8位UART,波特率可变”的工作方式,其一帧信息为10位:1位起始位+8位数据位(低位在先)+1位停止位。

发送过程:串行通信模式发送时,数据由串行发送端TXD输出。当主机执行一条写SBUF的指令就启动串行通信的发送,写“SBUF”信号还把“1”装入发送移位寄存器的第9位,并通知TX控制单元开始发送。移位寄存器将数据不断右移送TXD端口发送,在数据的左边不断移入“0”作补充。当数据的最高位移到移位寄存器的输出位置,紧跟其后的是第9位“1”,在其左边各位全为“0”,这个状态条件,使TX控制单元作最后一次移位输出,然后使允许发送信号“SEND”失效,完成一帧信息的发送,并置位中断请求位TI,即TI=1,向主机请求中断处理。

在这里插入图片描述

图3:UART1工作方式1发送数据示意图

接收过程:当软件置位接收允许标志位REN,即REN=1时,接收器便对RXD端口的信号进行检测,当检测到RXD端口发送从“1”→“0”的下降沿跳变时就启动接收器准备接收数据,并立即复位波特率发生器的接收计数器,将1FFH装入移位寄存器。接收的数据从移位寄存器的右边移入,已装入的1FFH向左边移出,当起始位“0”移到移位寄存器的最左边时,使RX控制器作最后一次移位,完后一帧信息的接收。

接收数据有效需同时满足以下两个条件:

  1. RI = 0;
  2. SM2 = 0或接收到的停止位为1。

☆注:若上述两条件不能同时满足,则接收到的数据作废并丢失,无论条件满足与否,接收器重新检测RXD端口上的“1”→“0”的跳变,继续下一帧信息的接收。

在这里插入图片描述

图4:UART1工作方式1接收数据示意图

下面举例介绍下串口1在进行工作方式选择时,需要配置的是串口1控制寄存器SCON。该寄存器支持位寻址,该寄存器的B6和B7位便是用来选择串口1工作方式的,寄存器的B5、B3和B2位是9位UART时需要配置的位,寄存器的B4位是串行接收控制位,寄存器的B0、B1位是串口接收和发送中断请求标志位。

在这里插入图片描述

图5:串口1控制寄存器SCON

☆注:若上述两条件不能同时满足,则接收到的数据作废并丢失,无论条件满足与否,接收器重新检测RXD端口上的“1”→“0”的跳变,继续下一帧信息的接收。

4.串行口使用引脚切换选择

在使用STC8A8K64S4A12系列单片机的4个UART时,需要确定使用串口的哪一组引脚。这需要通过操作P_SW1或P_SW2等寄存器实现。

STC8A8K64S4A12系列单片机串口1有4组串口引脚可供选择,实现引脚切换选择需要P_SW1寄存器(即外设端口切换控制寄存器1)的B6和B7位,如下图所示。

在这里插入图片描述

图6:P_SW1外设端口切换控制寄存器1

☆注:STC15W4K32S4系列单片机串口1只有3组供选择,STC8A8K64S4A12系列单片机串口1有4组供选择。同一时间只能使能其中的一组串口作为串口1使用。

STC8A8K64S4A12系列单片机串口2、串口3和串口4均有2组串口引脚可供选择,实现引脚切换选择需要配置外设端口切换控制寄存器2的B0、B1和B2位,如下图所示。

在这里插入图片描述

图7:P_SW2外围设备功能切换控制寄存器2

☆注:因为串口2、串口3和串口4只有2组串口引脚供选择,所以寄存器使用1位即可控制切换。

5.串行口1工作模式1波特率计算公式

STC8A8K64S4A12系列单片机4个UART在不同的工作模式下,选择不同的定时器作为波特率发生器时,波特率计算公式都是不同的。下面举例给出UART1工作模式1时的波特率计算公式。

表5:UART1工作模式1波特率计算

在这里插入图片描述

☆注:SYSclk为系统工作频率,SMOD是PCON寄存器最高位(用于波特率加倍选择),定时器1模式0为16位自动重装载模式,定时器1模式2为8位自动重装载模式(详见定时器部分介绍)。

举例,系统时钟频率为11.0592MHZ,配置定时器1为1T,工作模式为模式2, PCON寄存器SMOD位置为0,波特率预设置为9600bps,计算下定时器1重装载值。

  1. SMOD = 0,则2SMOD * SYSclk =11059200。
  2. 11059200/(32*9600) =36。
  3. 256 – 36 = 220。十进制220对应十六进制是DC。
  4. 所以对定时器1的高8位寄存器初始装载值和低8位寄存器初始装载值赋值0xDC。
  5. 如果已知定时器重装载值,计算串口波特率,则是反推过来即可(建议使用软件STC-ISP的波特率计算器)。

6.串行口中断配置步骤

针对STC8A8K64S4A12系列单片机4个串行口外设,软件的配置过程如下:

在这里插入图片描述

图8:串行口中断软件配置步骤

☆注:实验例程即是按照上述配置步骤操作寄存器相关位实现,后有详述

二、软件设计

1.串行口寄存器汇集

STC8A8K64S4A12系列单片机操作串行口时会用到18个寄存器,如下表所示:

表6:STC8A8K64S4A12系列串行口使用寄存器汇总

在这里插入图片描述

☆注:串口波特率发生器需要用到定时器,定时器相关的寄存器没有在上述表格中列举。串口3和串口4是没有中断高优先级的,所以在中断优先级控制寄存器中没有对串口3和串口4中断优先级的配置位。

2.寄存器解析

2.1.中断允许寄存器IE

外部中断允许寄存器IE支持位寻址,该寄存器的B4位是串口1的中断允许位。

在这里插入图片描述

图9:中断允许寄存器

2.2.中断允许寄存器IE2

中断允许寄存器IE2不支持位寻址,该寄存器的B0、B3和B4位是串口2、串口3和串口4的中断允许位。因为IE2寄存器不支持位寻址,所以举例操作该寄存器B0位时,不可以直接“ES2=0;”进行操作,参考下图。

在这里插入图片描述

图10:中断允许寄存器2

2.3.电源控制寄存器PCON

电源控制寄存器PCON不支持位寻址,该寄存器的B6位和B7位是UART1的帧错误检测有效控制位和波特率选择位,具体含义如下图。

在这里插入图片描述

图11:电源控制寄存器

☆注:PCON寄存器的SMOD位和SMOD0位是用于串口1的,换句话说,串口2、串口3和串口4没有与之对应的控制位。

2.4.辅助寄存器AUXR

辅助寄存器AUXR不支持位寻址,该寄存器的B0位是串口1的波特率发生器定时器选择控制位,寄存器的B5位是串口1模式0的通信速度设置位。

在这里插入图片描述

图12:辅助寄存器

☆注:AUXR寄存器的其他位用于定时器配置,操作串口时也会有对定时器的配置部分,请注意按位操作。

2.5.中断优先级控制寄存器IP

中断优先级控制寄存器IP支持位寻址,该寄存器的B4位是串口1中断优先级控制位。

在这里插入图片描述

图13:中断优先级控制寄存器

☆注:艾克姆例程没有对串口1中断优先级进行配置,可根据项目需要配置PS位。

2.6.中断优先级控制寄存器IP2

中断优先级控制寄存器2不支持位寻址,该寄存器的B0位是串口2中断优先级控制位。

在这里插入图片描述

图14:中断优先级控制寄存器2

2.7.串行口2控制寄存器S2CON

串行口2控制寄存器S2CON不支持位寻址,该寄存器的B7位是用来选择串口2工作模式的,寄存器的B5、B3和B2位是9位UART时需要配置的位,寄存器的B4位是串行接收控制位,寄存器的B0、B1位是串口接收和发送中断请求标志位。

在这里插入图片描述

图15:串行口2控制寄存器

2.8.串行口3控制寄存器S3CON

串行口3控制寄存器S3CON不支持位寻址,该寄存器的B7位是用来选择串口3工作模式的,寄存器的B5、B3和B2位是9位UART时需要配置的位,寄存器的B4位是串行接收控制位,寄存器的B0、B1位是串口接收和发送中断请求标志位。

在这里插入图片描述

图16:串行口3控制寄存器

2.9.串行口4控制寄存器S4CON

串行口4控制寄存器S4CON不支持位寻址,该寄存器的B7位是用来选择串口4工作模式的,寄存器的B5、B3和B2位是9位UART时需要配置的位,寄存器的B4位是串行接收控制位,寄存器的B0、B1位是串口接收和发送中断请求标志位。

在这里插入图片描述

图17:串行口4控制寄存器

3.串口1收发实验(P3.0和P3.1)

3.1.工程需要用到的c文件

本例需要用到的c文件如下表所示,工程需要添加下表中的c文件。

表7:实验需要用到的c文件
序号文件名后缀功能描述
1uart.c外部串行口有关的用户自定义函数。
2delay.c包含用户自定义延时函数。

3.2.头文件引用和路径设置

■ 需要引用的头文件

#include "delay.h"  
#include "uart.h"  

■ 需要包含的头文件路径

本例需要包含的头文件路径如下表:

表8:头文件包含路径
序号路径描述
1…\ Sourceuart.h和delay.h头文件在该路径,所以要包含。
2…\UserSTC8.h头文件在该路径,所以要包含。

MDK中点击魔术棒,打开工程配置窗口,按照下图所示添加头文件包含路径。

在这里插入图片描述

图18:添加头文件包含路径

3.3.编写代码

首先,在uart.c文件中编写串口1的初始化函数Uart1_Init,代码如下。

程序清单:串口1初始化函数

/*************************************************************************** 
 * 描  述 : 串口1初始化函数 
 * 入  参 : 无 
 * 返回值 : 无 
备注:波特率9600bps   晶振11.0592MHz 
 **************************************************************************/  
void Uart1_Init(void)  
{     
    PCON &= 0x3f;       //波特率不倍速,串行口工作方式由SM0、SM1决定  
    SCON = 0x50;        //8位数据,可变波特率,启动串行接收器  
    AUXR |= 0x40;       //定时器1时钟为Fosc,即1T  
    AUXR &= 0xfe;       //串口1选择定时器1为波特率发生器  
    TMOD &= 0x0f;       //清除定时器1模式位  
    TMOD |= 0x20;       //设定定时器1为8位自动重装方式  
    TL1 = 0xDC;         //设定定时初值  
    TH1 = 0xDC;         //设定定时器重装值  
    ET1 = 0;            //禁止定时器1中断  
    TR1 = 1;            //启动定时器1  
    ES = 1;             // 串口1中断打开  
} 

然后,编写串口1发送数据函数,把要发送的字节存放于数据缓存寄存器中,直到数据发送完成,代码如下。

程序清单:数据发送函数函数

/*************************************************************************** 
 * 描  述 : 串口1发送数据函数 
 * 入  参 : uint8 数据 
 * 返回值 : 无 
 **************************************************************************/  
void SendDataByUart1(uint8 dat)  
{  
    SBUF = dat;                 //写数据到UART数据寄存器  
    while(TI == 0);             //在停止位没有发送时,TI为0即一直等待  
    TI = 0;                     //清除TI位(该位必须软件清零)  
} 

之后,编写串口1的中断服务函数,将接收的数据存放到用户自定义变量uart1temp中,代码如下。

程序清单:中断服务函数

/*************************************************************************** 
 * 描  述 : 串口1中断服务函数 
 * 入  参 : 无 
 * 返回值 : 无 
 **************************************************************************/  
void Uart1() interrupt 4 using 1  
{  
    ES = 0;                       // 串口1中断关闭  
    Flag=TRUE;                    //接收到数据,接收标识符有效  
    if (RI)                       //串行接收到停止位的中间时刻时,该位置1  
  {  
      RI = 0;                 //清除RI位 (该位必须软件清零)  
      uart1temp = SBUF;         
   }  
   if (TI)                    //在停止位开始发送时,该位置1  
   {  
      TI = 0;                 //清除TI位(该位必须软件清零)  
   }  
     ES =  1;                   // 串口1中断打开  
} 

最后,用户定义一个自定义函数UART1_Tx_Puts,该函数将接收的数据原样返回去并加上回车符。主函数main在主循环中调用该函数。具体代码如下。

代码清单:用户函数UART1_Tx_Puts

/*************************************************************************** 
 * 描  述 : 串口1接收到数据后发送回去 
 * 入  参 : 无 
 * 返回值 : 无 
 ***************************************************************************/  
void UART1_Tx_Puts(void)  
{  
    if(Flag)                               //有新数据通过串口被接收到  
    {     
        ES = 0;                           //串口1中断关闭                     
        SendDataByUart1(uart1temp);       //发送字符   
        SendDataByUart1(0x0D);            //发送换行符  
        SendDataByUart1(0x0A);            //发送换行符                         
        ES = 1;                           //串口1中断打开           
        Flag=FALSE;                       //清除接收标识符  
     }  
} 

代码清单:主函数

int main()  
{  
    P3M1 &= 0xFE;   P3M0 &= 0xFE;                     //设置P3.0为准双向口  
    P3M1 &= 0xFD;   P3M0 |= 0x02;                     //设置P3.1为推挽输出  
      
    Uart1_Init();         //串口1初始化  
    EA = 1;               //总中断打开  
      
    while(1)  
    {  
        UART1_Tx_Puts();   //串口接收到一个字符后返回该字符  
    }  
} 

3.4.硬件连接

本实验需要将USB线连接至PC,因为开发板板载CH340电路连接的就是单片机P3.0和P3.1口,所以无需外接USB转TTL模块做该实验。

在这里插入图片描述

图19:串口1实验连接图

4.串口2收发实验(P1.0和P1.1)

4.1.编写代码

首先,在uart.c文件中编写串口2的初始化函数Uart2_Init,代码如下。

程序清单:串口2初始化函数

/*************************************************************************** 
 * 描  述 : 串口2初始化函数 
 * 入  参 : 无 
 * 返回值 : 无 
备注:波特率9600bps   晶振11.0592MHz 
 **************************************************************************/  
void Uart2_Init(void)  
{     
    S2CON = 0x50;       //8位数据,可变波特率,启动串行接收器      
    AUXR |= 0x04;       //定时器2时钟为Fosc,即1T  
    T2L = 0xE0;       //设定定时初值  
    T2H = 0xFE;     //设定定时初值  
    AUXR |= 0x10;   //启动定时器2  
    IE2 |= 0x01;    //串口2中断打开  
} 

然后,编写串口2发送数据函数,把要发送的字节存放于数据缓存寄存器中,直到数据发送完成,代码如下。

程序清单:数据发送函数函数

/*************************************************************************** 
 * 描  述 : 串口2发送数据函数 
 * 入  参 : uint8 数据 
 * 返回值 : 无 
 **************************************************************************/  
void SendDataByUart2(uint8 dat)  
{  
    S2BUF = dat;                 //写数据到UART数据寄存器  
    while(!(S2CON&S2TI));        //在停止位没有发送时,S2TI为0即一直等待  
    S2CON&=~S2TI;                //清除S2CON寄存器对应S2TI位(该位必须软件清零)  
} 

之后,编写串口2的中断服务函数,将接收的数据存放到用户自定义变量uart2temp中,代码如下。

程序清单:中断服务函数

/*************************************************************************** 
 * 描  述 : 串口2中断服务函数 
 * 入  参 : 无 
 * 返回值 : 无 
 **************************************************************************/  
void Uart2() interrupt 8 using 1  
{  
    IE2 &= 0xFE;                           // 串口2中断关闭  
    Flag=TRUE;                       //接收到数据,接收标识符有效  
    if (S2CON & S2RI)                //串行接收到停止位的中间时刻时,该位置1  
    {  
      S2CON &= ~S2RI;                //清除S2CON寄存器对应S2RI位(该位必须软件清零)
      uart2temp = S2BUF;         
    }  
    if (S2CON & S2TI)                //在停止位开始发送时,该位置1  
    {  
      S2CON &= ~S2TI;               //清除S2CON寄存器对应S2TI位(该位必须软件清零) 
     }  
     IE2 |= 0x01;                    // 串口2中断打开   
} 

最后,用户定义一个自定义函数UART2_Tx_Puts,该函数将接收的数据原样返回去并加上回车符。主函数main在主循环中调用该函数。具体代码如下。

代码清单:用户函数UART2_Tx_Puts

/*************************************************************************** 
 * 描  述 : 串口2接收到数据后发送出去 
 * 入  参 : 无 
 * 返回值 : 无 
 ***************************************************************************/  
void UART2_Tx_Puts(void)  
{  
  if(Flag)      //有新数据通过串口被接收到  
  {  
      IE2 &= 0xFE;                            // 串口2中断关闭   
      SendDataByUart2(uart2temp);            //发送字符   
      SendDataByUart2(0x0D);                 //发送换行符  
      SendDataByUart2(0x0A);                 //发送换行符                        
      IE2 |= 0x01;                           // 串口2中断打开
      Flag=FALSE;                            //清除接收标识符  
  }  
} 

代码清单:主函数

int main()  
{  
    P1M1 &= 0xFE;   P1M0 &= 0xFE;                     //设置P1.0为准双向口  
    P1M1 &= 0xFD;   P1M0 |= 0x02;                     //设置P1.1为推挽输出  
      
    Uart2_Init();         //串口2初始化  
    EA = 1;               //总中断打开  
      
    while(1)  
    {  
        UART2_Tx_Puts();   //串口接收到一个字符后返回该字符  
    }  
}   

4.2.硬件连接

本实验需要外接USB转TTL模块连接到开发板串口2上,该实验串口2选择的P1.0和P1.1,具体接线图如下。

在这里插入图片描述

图20:串口2实验连接图

5.串口3收发实验(P0.0和P0.1)

5.1.编写代码

首先,在uart.c文件中编写串口3的初始化函数Uart3_Init,代码如下。

程序清单:串口3初始化函数

/*************************************************************************** 
 * 描  述 : 串口3初始化函数 
 * 入  参 : 无 
 * 返回值 : 无 
备注:波特率9600bps   晶振11.0592MHz 
 **************************************************************************/  
void Uart3_Init(void)  
{     
    S3CON |= 0x10;        //启动串行接收器     
    S3CON &= 0x30;        //8位数据,可变波特率,串口3选择定时器2为波特率发生器  
    AUXR |= 0x04;         //定时器2时钟为Fosc,即1T  
    T2L = 0xE0;           //设定定时初值  
    T2H = 0xFE;           //设定定时初值  
    AUXR |= 0x10;         //启动定时器2  
    IE2 |= 0x08;           // 串口3中断打开  
} 

然后,编写串口3发送数据函数,把要发送的字节存放于数据缓存寄存器中,直到数据发送完成,代码如下。

程序清单:数据发送函数函数

/*************************************************************************** 
 * 描  述 : 串口3发送数据函数 
 * 入  参 : uint8 数据 
 * 返回值 : 无 
 **************************************************************************/  
void SendDataByUart3(uint8 dat)  
{  
    S3BUF = dat;                 //写数据到UART数据寄存器  
    while(!(S3CON&S3TI));        //在停止位没有发送时,S3TI为0即一直等待  
    S3CON&=~S3TI;                //清除S3CON寄存器对应S3TI位(该位必须软件清零)  
} 

之后,编写串口3的中断服务函数,将接收的数据存放到用户自定义变量uart3temp中,代码如下。

程序清单:中断服务函数

/*************************************************************************** 
 * 描  述 : 串口3中断服务函数 
 * 入  参 : 无 
 * 返回值 : 无 
 **************************************************************************/  
void Uart3() interrupt 17 using 1  
{  
    IE2 &= 0xF7;                      // 串口3中断关闭  
    Flag=TRUE;                       //接收到数据,接收标识符有效  
    if (S3CON & S3RI)                //串行接收到停止位的中间时刻时,该位置1  
  {  
      S3CON &= ~S3RI;              //清除S3CON寄存器对应S3RI位(该位必须软件清零)  
      uart3temp = S3BUF;         
   }  
  if (S3CON & S3TI)                //在停止位开始发送时,该位置1  
   {  
      S3CON &= ~S3TI;              //清除S3CON寄存器对应S3TI位(该位必须软件清零)  
   }  
     IE2 |= 0x08;                  // 串口3中断打开  
} 

最后,用户定义一个自定义函数UART3_Tx_Puts,该函数将接收的数据原样返回去并加上回车符。主函数main在主循环中调用该函数。具体代码如下。

代码清单:用户函数UART3_Tx_Puts

/************************************************************************** 
 * 描  述 : 串口3接收到数据后发送出去 
 * 入  参 : 无 
 * 返回值 : 无 
 *************************************************************************/  
void UART3_Tx_Puts(void)  
{  
  if(Flag)      //有新数据通过串口被接收到  
 {  
    IE2 &= 0xF7;                                 //串口3中断关闭  
    SendDataByUart3(uart3temp);            //发送字符   
    SendDataByUart3(0x0D);                 //发送换行符  
    SendDataByUart3(0x0A);                 //发送换行符                        
    IE2 |= 0x08;                           //串口3中断打开
    Flag=FALSE;                            //清除接收标识符  
  }  
} 

代码清单:主函数

int main()  
{  
    P0M1 &= 0xFE;   P0M0 &= 0xFE;                     //设置P0.0为准双向口  
    P0M1 &= 0xFD;   P0M0 |= 0x02;                     //设置P0.1为推挽输出  
      
    Uart3_Init();         //串口3初始化  
    EA = 1;               //总中断打开  
      
    while(1)  
    {  
        UART3_Tx_Puts();   //串口接收到一个字符后返回该字符  
    }  
} 

5.2.硬件连接

本实验需要外接USB转TTL模块连接到开发板串口3上,该实验串口3选择的P0.0和P0.1,具体接线图如下。

在这里插入图片描述

图21:串口3实验连接图

6.串口4收发实验(P0.2和P0.3)

6.1.编写代码

首先,在uart.c文件中编写串口4的初始化函数Uart4_Init,代码如下。

程序清单:串口4初始化函数

/*************************************************************************** 
 * 描  述 : 串口4初始化函数 
 * 入  参 : 无 
 * 返回值 : 无 
备注:波特率9600bps   晶振11.0592MHz 
 **************************************************************************/  
void Uart4_Init(void)  
{     
    S4CON |= 0x10;      //启动串行接收器     
    S4CON &= 0x30;      //8位数据,可变波特率,串口4选择定时器2为波特率发生器  
    AUXR |= 0x04;         //定时器2时钟为Fosc,即1T  
    T2L = 0xE0;         //设定定时初值  
    T2H = 0xFE;         //设定定时初值  
    AUXR |= 0x10;         //启动定时器2  
    IE2 |= 0x10;      // 串口4中断打开  
} 

然后,编写串口4发送数据函数,把要发送的字节存放于数据缓存寄存器中,直到数据发送完成,代码如下。

程序清单:数据发送函数函数

/*************************************************************************** 
 * 描  述 : 串口4发送数据函数 
 * 入  参 : uint8 数据 
 * 返回值 : 无 
 **************************************************************************/  
void SendDataByUart4(uint8 dat)  
{  
    S4BUF = dat;                 //写数据到UART数据寄存器  
    while(!(S4CON&S4TI));        //在停止位没有发送时,S4TI为0即一直等待  
    S4CON&=~S4TI;                //清除S4CON寄存器对应S4TI位(该位必须软件清零)  
} 

之后,编写串口4的中断服务函数,将接收的数据存放到用户自定义变量uart4temp中,代码如下。

程序清单:中断服务函数

/*************************************************************************** 
 * 描  述 : 串口4中断服务函数 
 * 入  参 : 无 
 * 返回值 : 无 
 **************************************************************************/  
void Uart4() interrupt 18 using 1  
{  
    IE2 &= 0xEF;                      // 串口4中断关闭  
    Flag=TRUE;                       //接收到数据,接收标识符有效  
    if (S4CON & S4RI)                //串行接收到停止位的中间时刻时,该位置1  
  {  
      S4CON &= ~S4RI;              //清除S4CON寄存器对应S4RI位(该位必须软件清零)  
      uart4temp = S4BUF;         
   }  
  if (S4CON & S4TI)                //在停止位开始发送时,该位置1  
   {  
      S4CON &= ~S4TI;                //清除S4CON寄存器对应S4TI位(该位必须软件清零)
   }  
     IE2 |= 0x10;                     // 串口4中断打开  
}

最后,用户定义一个自定义函数UART4_Tx_Puts,该函数将接收的数据原样返回去并加上回车符。主函数main在主循环中调用该函数。具体代码如下。

代码清单:用户函数UART4_Tx_Puts

/************************************************************************* 
 * 描  述 : 串口4接收到数据后发送出去 
 * 入  参 : 无 
 * 返回值 : 无 
 **************************************************************************/  
void UART4_Tx_Puts(void)  
{  
  if(Flag)      //有新数据通过串口被接收到  
    {  
        IE2 &= 0xEF;                                 // 串口4中断关闭  
        SendDataByUart4(uart4temp);            //发送字符   
        SendDataByUart4(0x0D);                 //发送换行符  
        SendDataByUart4(0x0A);                 //发送换行符
        IE2 |= 0x10;                           // 串口4中断打开
        Flag=FALSE;                            //清除接收标识符  
  }  
} 

代码清单:主函数

int main()  
{  
    P0M1 &= 0xFB;   P0M0 &= 0xFB;                     //设置P0.2为准双向口  
    P0M1 &= 0xF7;   P0M0 |= 0x08;                     //设置P0.3为推挽输出  
      
    Uart4_Init();         //串口4初始化  
    EA = 1;               //总中断打开  
      
    while(1)  
    {  
        UART4_Tx_Puts();   //串口接收到一个字符后返回该字符  
    }  
} 

6.2.硬件连接

本实验需要外接USB转TTL模块连接到开发板串口4上,该实验串口4选择的P0.2和P0.3,具体接线图如下。

在这里插入图片描述

图22:串口4实验连接图

7.串口1串口2同时收发字符串实验

7.1.工程需要用到的c文件

本例需要用到的c文件如下表所示,工程需要添加下表中的c文件。

表9:实验需要用到的c文件
序号文件名后缀功能描述
1uart.c外部串行口有关的用户自定义函数。
2delay.c包含用户自定义延时函数。
3led.c包含与用户led控制有关的用户自定义函数。

7.2.头文件引用和路径设置

■ 需要引用的头文件

#include "delay.h"  
#include "uart.h"  

■ 需要包含的头文件路径

本例需要包含的头文件路径如下表:

表10:头文件包含路径
序号路径描述
1…\ SourceLed.h、uart.h和delay.h头文件在该路径,所以要包含。
2…\UserSTC8.h头文件在该路径,所以要包含。

MDK中点击魔术棒,打开工程配置窗口,按照下图所示添加头文件包含路径。

在这里插入图片描述

图23:添加头文件包含路径

7.3.编写代码

首先,在uart.c文件中编写串口1的初始化函数Uart1_Init和串口1发送单个字符函数SendDataByUart1,这两个函数不介绍了。下面介绍下串口1清缓存函数CLR_Buf1和发送字符串函数SendStringByUart1,代码如下。

程序清单:串口1发送字符串函数

/*************************************************************************** 
 * 描  述 : 串口1发送字符串函数 
 * 入  参 : 字符串 
 * 返回值 : 无 
 **************************************************************************/  
void SendStringByUart1(uint8 *s)  
{  
    while(*s)  
    {  
        SendDataByUart1(*s++);       //将字符串中的字符一个一个发送  
    }  
} 

程序清单:串口1清缓存函数

/***************************************************************************** 
功能描述:清除串口1缓存内容函数 
入口参数:无 
返回值:无 
******************************************************************************/ void CLR_Buf1(void)  
{  
    uint8 k;  
    for(k=0;k<Buf1_Max;k++)      //将串口1缓存数组的值都清为零  
    {  
        Rec_Buf1[k] = 0;  
    }  
    i = 0;                      //清零串口1接收数据个数变量  
}

然后,串口2的初始化函数Uart2_Init和串口2发送单个字符函数SendDataByUart2,这两个函数不介绍了。下面介绍下串口2清缓存函数CLR_Buf2和发送字符串函数SendStringByUart2,代码如下。

程序清单:串口2发送字符串函数

/*************************************************************************** 
 * 描  述 : 串口2发送字符串函数 
 * 入  参 : 字符串 
 * 返回值 : 无 
 **************************************************************************/  
void SendStringByUart2(uint8 *s)  
{  
    while (*s)                  //检测字符串结束标志  
  {  
    SendDataByUart2(*s++);         //发送当前字符  
  }  
} 

程序清单:串口2清缓存函数

/************************************************************************** 
功能描述:清除串口2缓存内容函数 
入口参数:无 
返回值:无 
***************************************************************************/  
void CLR_Buf2(void)  
{  
    uint8 k;  
  for(k=0;k<Buf2_Max;k++)      //将串口2缓存数组的值都清为零  
    {  
         Rec_Buf2[k] = 0;  
  }  
  j = 0;                          //清零串口2接收数据个数变量               
} 

之后,编写串口1和串口2的中断服务函数,将接收的数据存放到用户自定义数组Rec_Buf1和Rec_Buf2中,代码如下。

程序清单:串口1中断服务函数

/*************************************************************************** 
 * 描  述 : 串口1中断服务函数 
 * 入  参 : 无 
 * 返回值 : 无 
 **************************************************************************/  
void Uart1() interrupt 4 using 1  
{  
    uint8 temp;  
    ES = 0;                        // 串口1中断关闭  
    if (RI)                      //串行接收到停止位的中间时刻时,该位置1  
  {  
      RI = 0;                 //清除RI位 (该位必须软件清零)  
          temp = SBUF;            //接收到的数赋值给临时变量  
          if(temp !='\n')         //没有接收到换行符  
            {  
                Rec_Buf1[i] = temp;     //接收到的数存到接收数组中  
                i++;                    //串口1接收数据个数变量累加  
            }  
            else                    //接收到结束符  
            {  
                Buf1_Length = i;      //接收数据长度赋值  
                i = 0;                //清零串口1接收数据个数变量  
                Buf1_Flag=TRUE;       //接收完数据,接收标识符有效  
                led_toggle(LED_3);    //翻转用户指示灯D3,方便观察实验现象  
            }             
   }  
   if (TI)                    //在停止位开始发送时,该位置1  
   {  
      TI = 0;                 //清除TI位(该位必须软件清零)  
   }  
     ES =  1;                   // 串口1中断打开  
} 

程序清单:串口2中断服务函数

/*************************************************************************** 
 * 描  述 : 串口2中断服务函数 
 * 入  参 : 无 
 * 返回值 : 无 
 **************************************************************************/  
void Uart2() interrupt 8 using 1  
{  
    uint8 temp;  
    IE2 &= 0xFE;                             //串口2中断关闭  
    if (S2CON & S2RI)                       //串行接收到停止位的中间时刻时,该位置1 
    {  
      S2CON &= ~S2RI;               //清除S2CON寄存器对应S2RI位(该位必须软件清零)
      temp = S2BUF;                           //接收到的数赋值给临时变量  
      if(temp !='\n')                         //没有接收到换行符  
      {  
         Rec_Buf2[j] = temp;                   //接收到的数存到接收数组中  
         j++;                                  //串口2接收数据个数变量累加  
      }  
      else                                    //接收到结束符  
      {  
         Buf2_Length = j;                      //接收数据长度赋值  
         j = 0;                                //清零串口2接收数据个数变量  
         Buf2_Flag=TRUE;                       //接收完数据,接收标识符有效  
         led_toggle(LED_4);                //翻转用户指示灯D4,方便观察实验现象  
      }  
   }             
   if (S2CON & S2TI)                          //在停止位开始发送时,该位置1  
   {  
      S2CON &= ~S2TI;            //清除S2CON寄存器对应S2TI位(该位必须软件清零)  
   }  
   IE2 |= 0x01;                               //串口2中断打开     
}

最后,用户定义一个自定义函数UART1_2_Tx_Puts,该函数将接收的字符串原样返回去并加上回车符。主函数main在主循环中调用该函数。具体代码如下。

代码清单:用户函数UART1_2_Tx_Puts

/************************************************************************* 
 * 描  述 : 串口1和串口2接收到字符串后发送出去 
 * 入  参 : 无 
 * 返回值 : 无 
 ************************************************************************/  
void UART1_2_Tx_Puts(void)  
{  
  if(Buf1_Flag)                //串口1接收一组字符串完成  
    {     
        ES = 0;                                 //串口1中断关闭     
         SendStringByUart1(Rec_Buf1);      //发送字符   
         SendDataByUart1(0x0D);            //发送换行符  
         SendDataByUart1(0x0A);            //发送换行符                         
        ES = 1;                           //串口1中断打开   
        CLR_Buf1();                         //清除串口1缓存         
        Buf1_Flag=FALSE;                  //清除接收标识符  
  }  
    if(Buf2_Flag)                //串口2接收一组字符串完成  
    {     
        IE2 &= 0xFE;                            //串口2中断关闭  
        SendStringByUart2(Rec_Buf2);      //发送字符   
        SendDataByUart2(0x0D);            //发送换行符  
        SendDataByUart2(0x0A);            //发送换行符                         
        IE2 |= 0x01;                      //串口2中断打开   
        CLR_Buf2();                       //清除串口2缓存         
        Buf2_Flag=FALSE;                  //清除接收标识符  
  }  
} 

代码清单:主函数

int main()  
{  
    P3M1 &= 0xFE;   P3M0 &= 0xFE;                     //设置P3.0为准双向口  
    P3M1 &= 0xFD;   P3M0 |= 0x02;                     //设置P3.1为推挽输出  
    P1M1 &= 0xFE;   P1M0 &= 0xFE;                     //设置P1.0为准双向口  
    P1M1 &= 0xFD;   P1M0 |= 0x02;                     //设置P1.1为推挽输出  
      
    Uart1_Init();                   //串口1初始化  
    Uart2_Init();                   //串口2初始化  
    EA = 1;                         //总中断打开  
      
    CLR_Buf1();                     //清除串口1缓存         
    CLR_Buf2();                     //清除串口2缓存         
      
    while(1)  
    {  
        UART1_2_Tx_Puts();           //UART1和UART2接收到字符串后发送出去  
    }  
}  

7.4.硬件连接

在这里插入图片描述

图24:串口1串口2实验连接图

8.串口3串口4同时收发字符串实验

8.1.编写代码

首先,在uart.c文件中编写串口3的初始化函数Uart3_Init和串口3发送单个字符函数SendDataByUart3,这两个函数不介绍了。下面介绍下串口3清缓存函数CLR_Buf3和发送字符串函数SendStringByUart3,代码如下。

程序清单:串口3发送字符串函数

/*************************************************************************** 
 * 描  述 : 串口3发送字符串函数 
 * 入  参 : 字符串 
 * 返回值 : 无 
 **************************************************************************/  
void SendStringByUart3(uint8 *s)  
{  
      while (*s)                       //检测字符串结束标志  
    {  
      SendDataByUart3(*s++);         //发送当前字符  
    }  
} 

程序清单:串口3清缓存函数

/***************************************************************************** 
功能描述:清除串口3缓存内容函数 
入口参数:无 
返回值:无 
****************************************************************************/  
void CLR_Buf3(void)  
{  
    uint8 k;  
  for(k=0;k<Buf3_Max;k++)      //将串口3缓存数组的值都清为零  
    {  
        Rec_Buf3[k] = 0;  
    }  
  i = 0;                      //清零串口3接收数据个数变量  
} 

然后,串口4的初始化函数Uart4_Init和串口4发送单个字符函数SendDataByUart4,这两个函数不介绍了。下面介绍下串口4清缓存函数CLR_Buf4和发送字符串函数SendStringByUart4,代码如下。

程序清单:串口4发送字符串函数

/*************************************************************************** 
 * 描  述 : 串口4发送字符串函数 
 * 入  参 : 字符串 
 * 返回值 : 无 
 **************************************************************************/  
void SendStringByUart4(char *s)  
{  
    while (*s)                       //检测字符串结束标志  
  {  
    SendDataByUart4(*s++);         //发送当前字符  
  }  
} 

程序清单:串口4清缓存函数

/************************************************************************** 
功能描述:清除串口4缓存内容函数 
入口参数:无 
返回值:无 
***************************************************************************/  
void CLR_Buf4(void)  
{  
    uint8 k;  
  for(k=0;k<Buf4_Max;k++)      //将串口4缓存数组的值都清为零  
    {  
         Rec_Buf4[k] = 0;  
  }  
  j = 0;                          //清零串口4接收数据个数变量               
} 

之后,编写串口3和串口4的中断服务函数,将接收的数据存放到用户自定义数组Rec_Buf3和Rec_Buf4中,代码如下。

程序清单:串口3中断服务函数

/*************************************************************************** 
 * 描  述 : 串口3中断服务函数 
 * 入  参 : 无 
 * 返回值 : 无 
 **************************************************************************/  
void Uart3() interrupt 17 using 1  
{  
    uint8 temp;  
    IE2 &= 0xF7;                           //串口3中断关闭  
    if (S3CON & S3RI)                //串行接收到停止位的中间时刻时,该位置1  
    {  
      S3CON &= ~S3RI;              //清除S3CON寄存器对应S3RI位(该位必须软件清零)  
          temp = S3BUF;                //接收到的数赋值给临时变量  
          if(temp !='\n')              //没有接收到换行符  
            {  
                Rec_Buf3[i] = temp;        //接收到的数存到接收数组中  
                i++;                       //串口3接收数据个数变量累加  
            }  
            else                         //接收到结束符  
            {  
                Buf3_Length = i;           //接收数据长度赋值  
                i = 0;                     //清零串口3接收数据个数变量  
                Buf3_Flag=TRUE;            //接收完数据,接收标识符有效  
                led_toggle(LED_3);         //翻转用户指示灯D3,方便观察实验现象  
            }             
   }  
   if (S3CON & S3TI)                //在停止位开始发送时,该位置1  
   {  
      S3CON &= ~S3TI;               //清除S3CON寄存器对应S3TI位(该位必须软件清零) 
   }  
   IE2 |= 0x08;                    //串口3中断打开  
} 

程序清单:串口2中断服务函数

/*************************************************************************** 
 * 描  述 : 串口4中断服务函数 
 * 入  参 : 无 
 * 返回值 : 无 
 **************************************************************************/  
void Uart4() interrupt 18 using 1  
{  
    uint8 temp;  
    IE2 &= 0xEF;                                    //串口4中断关闭    
    if(S4CON & S4RI)                     //串行接收到停止位的中间时刻时,该位置1  
    {  
        S4CON &= ~S4RI;            //清除S4CON寄存器对应S4RI位(该位必须软件清零)
        temp = S4BUF;                           //接收到的数赋值给临时变量  
        if(temp !='\n')                         //没有接收到换行符  
        {  
            Rec_Buf4[j] = temp;                   //接收到的数存到接收数组中  
            j++;                                  //串口4接收数据个数变量累加  
         }  
         else                                    //接收到结束符  
         {  
             Buf4_Length = j;                      //接收数据长度赋值  
             j = 0;                                //清零串口4接收数据个数变量  
             Buf4_Flag=TRUE;                       //接收完数据,接收标识符有效  
             led_toggle(LED_4);             //翻转用户指示灯D4,方便观察实验现象  
          }  
    }             
    if(S4CON & S4TI)                            //在停止位开始发送时,该位置1  
    {  
        S4CON &= ~S4TI;            //清除S4CON寄存器对应S4TI位(该位必须软件清零) 
    }  
    IE2 |= 0x10;                                //串口4中断打开  
}

最后,用户定义一个自定义函数UART3_4_Tx_Puts,该函数将接收的字符串原样返回去并加上回车符。主函数main在主循环中调用该函数。具体代码如下。

代码清单:用户函数UART3_4_Tx_Puts

/************************************************************************* 
 * 描  述 : 串口3和串口4接收到字符串后发送出去 
 * 入  参 : 无 
 * 返回值 : 无 
 ***********************************************************************/  
void UART3_4_Tx_Puts(void)  
{  
  if(Buf3_Flag)                //串口3接收一组字符串完成  
  {     
      IE2 &= 0xF7;                      //串口3中断关闭    
      SendStringByUart3(Rec_Buf3);      //发送字符   
      SendDataByUart3(0x0D);            //发送换行符  
      SendDataByUart3(0x0A);            //发送换行符                         
      IE2 |= 0x08;                      //串口3中断打开  
      CLR_Buf3();                       //清除串口3缓存         
      Buf3_Flag=FALSE;                  //清除接收标识符  
  }  
  if(Buf4_Flag)                //串口4接收一组字符串完成  
  {     
      IE2 &= 0xEF;                      //串口4中断关闭    
      SendStringByUart4(Rec_Buf4);      //发送字符   
      SendDataByUart4(0x0D);            //发送换行符  
      SendDataByUart4(0x0A);            //发送换行符                         
      IE2 |= 0x10;                      //串口4中断打开   
      CLR_Buf4();                         //清除串口4缓存         
      Buf4_Flag=FALSE;                  //清除接收标识符  
  }  
} 

代码清单:主函数

int main()  
{  
    P0M1 &= 0xFA;   P0M0 &= 0xFA;                 //设置P0.0  P0.2为准双向口  
    P0M1 &= 0xF5;   P0M0 |= 0x0A;                 //设置P0.1  P0.3为推挽输出  
  
    Uart3_Init();                  //串口3初始化  
    Uart4_Init();                  //串口4初始化  
    EA = 1;                        //总中断打开  
      
    CLR_Buf3();                    //清除串口3缓存          
    CLR_Buf4();                    //清除串口4缓存          
      
    while(1)  
    {  
        UART3_4_Tx_Puts();   //UART3和UART4接收到字符串后发送出去  
    }  
}  

8.2.硬件连接

在这里插入图片描述

图26:串口3串口4实验连接图

9.串口1串口2串口3串口4同时收发实验

9.1.编写代码

首先,在uart.c文件中编写串口1、串口2、串口3和串口4的初始化函数Uart1234_Init,代码如下。

程序清单:串口初始化函数

/*************************************************************************** 
 * 描  述 : 串口1/2/3/4初始化函数 
 * 入  参 : 无 
 * 返回值 : 无 
备注:波特率9600bps   晶振11.0592MHz 
 **************************************************************************/  
void Uart1234_Init(void)  
{     
    //串口1配置   
    PCON &= 0x3f;           //串口1波特率不倍速,串行口工作方式由SM0、SM1决定  
    SCON = 0x50;            //串口1的8位数据,可变波特率,启动串行接收器  
    AUXR |= 0x01;           //串口1选择定时器2为波特率发生器  
    //串口2配置   
    S2CON = 0x50;           //串口2的8位数据,可变波特率      
    //串口3配置     
    S3CON |= 0x10;        //串口3启动串行接收器  
    S3CON &= 0x30;        //串口3选择定时器2为波特率发生器,8位数据,可变波特率  
    //串口4配置   
    S4CON |= 0x10;        //启动串行接收器   
    S4CON &= 0x30;        //8位数据,可变波特率,串口4选择定时器2为波特率发生器  
      
    //定时器2配置  
    AUXR |= 0x04;           //定时器2时钟为Fosc,即1T  
    T2L = 0xE0;           //设定定时初值  
    T2H = 0xFE;         //设定定时初值  
    AUXR |= 0x10;       //启动定时器2  
      
    //打开串口中断  
    ES = 1;                                       //串口1中断打开 
    IE2 |= 0x01;                                  //串口2中断打开  
    IE2 |= 0x08;                                  //串口3中断打开  
    IE2 |= 0x10;                                  //串口4中断打开  
} 

然后,介绍下4个串口的握手函数,代码如下。(串口1、串口2、串口3和串口4的发送函数、清缓存函数不作介绍)

程序清单:串口1握手函数

/****************************************************************************** 
功能描述:握手成功与否函数 
入口参数:uint8 *a 
返回值:位 
****************************************************************************/  
bit Hand1(uint8 *a)  
{   
    if(strstr(Rec_Buf1,a)!=NULL)     //判断字符串a是否是字符串Rec_Buf1的子串  
        return 1;                      //如果字符串a是字符串Rec_Buf1的子串  
    else  
        return 0;                      //如果字符串a不是字符串Rec_Buf1的子串  
}  

程序清单:串口2握手函数

/*************************************************************************** 
功能描述:握手成功与否函数 
入口参数:uint8 *a 
返回值:位 
****************************************************************************/  
bit Hand2(uint8 *a)  
{   
  if(strstr(Rec_Buf2,a)!=NULL)      //判断字符串a是否是字符串Rec_Buf2的子串  
       return 1;                      //如果字符串a是字符串Rec_Buf2的子串  
  else  
         return 0;                      //如果字符串a不是字符串Rec_Buf2的子串  
} 

程序清单:串口3握手函数

/***************************************************************************** 
功能描述:握手成功与否函数 
入口参数:uint8 *a 
返回值:位 
************************************************************************/  
bit Hand3(uint8 *a)  
{   
    if(strstr(Rec_Buf3,a)!=NULL)     //判断字符串a是否是字符串Rec_Buf3的子串  
        return 1;                      //如果字符串a是字符串Rec_Buf3的子串  
    else  
        return 0;                      //如果字符串a不是字符串Rec_Buf3的子串  
} 

程序清单:串口4握手函数

/*************************************************************************** 
功能描述:握手成功与否函数 
入口参数:uint8 *a 
返回值:位 
****************************************************************************/  
bit Hand4(uint8 *a)  
{   
  if(strstr(Rec_Buf4,a)!=NULL)       //判断字符串a是否是字符串Rec_Buf4的子串  
        return 1;                      //如果字符串a是字符串Rec_Buf4的子串  
  else  
        return 0;                      //如果字符串a不是字符串Rec_Buf4的子串  
} 

之后,编写串口1、串口2、串口3和串口4的中断服务函数,将接收的数据存放到用户自定义数组Rec_Buf1、Rec_Buf2、Rec_Buf3和Rec_Buf4中,代码如下。

程序清单:串口1中断服务函数

/*************************************************************************** 
 * 描  述 : 串口1中断服务函数 
 * 入  参 : 无 
 * 返回值 : 无 
 **************************************************************************/  
void Uart1() interrupt 4 using 1  
{  
    ES = 0;                        // 串口1中断关闭  
    if (RI)                      //串行接收到停止位的中间时刻时,该位置1  
  {  
      RI = 0;                 //清除RI位 (该位必须软件清零)  
      Rec_Buf1[i] = SBUF;     //接收到的数存到接收数组中  
      i++;                    //串口1接收数据个数变量累加  
      if(i>Buf_Max)           //判断接收数据个数是否超限  
      {  
          i = 0;                //清零接收数据个数  
       }             
   }  
   if (TI)                    //在停止位开始发送时,该位置1  
   {  
      TI = 0;                 //清除TI位(该位必须软件清零)  
   }  
   ES =  1;                   // 串口1中断打开  
} 

程序清单:串口2中断服务函数

/*************************************************************************** 
 * 描  述 : 串口2中断服务函数 
 * 入  参 : 无 
 * 返回值 : 无 
 **************************************************************************/  
void Uart2() interrupt 8 using 1  
{  
    IE2 &= 0xFE;                           // 串口2中断关闭  
    if (S2CON & S2RI)                      //串行接收到停止位的中间时刻时,该位置1  
    {  
      S2CON &= ~S2RI;            //清除S2CON寄存器对应S2RI位(该位必须软件清零)  
      Rec_Buf2[j] = S2BUF;    //把串口2缓存SBUF寄存器数据依次存放到数组Rec_Buf2中
      j++;                     //串口2接收数据个数变量累加 
      if(j>Buf_Max)          //接收数大于定义接收数组最大个数时,覆盖接收数组之前值  
      {  
          j = 0;             //清零串口2接收数据个数变量  
       }             
     }  
     if (S2CON & S2TI)                       //在停止位开始发送时,该位置1  
     {  
        S2CON &= ~S2TI;    //清除S2CON寄存器对应S2TI位(该位必须软件清零)  
      }  
     IE2 |= 0x01;                               //串口2中断打开     
} 

程序清单:串口3中断服务函数

/*************************************************************************** 
 * 描  述 : 串口3中断服务函数 
 * 入  参 : 无 
 * 返回值 : 无 
 **************************************************************************/  
void Uart3() interrupt 17 using 1  
{  
    IE2 &= 0xF7;                           // 串口3中断关闭  
    if (S3CON & S3RI)                //串行接收到停止位的中间时刻时,该位置1  
    {  
      S3CON &= ~S3RI;              //清除S3CON寄存器对应S3RI位(该位必须软件清零)  
      Rec_Buf3[m] = S3BUF;   //把串口3缓存SBUF寄存器数据依次存放到数组Rec_Buf3中  
      m++;                         //串口3接收数据个数变量累加             
      if(m>Buf_Max)          //接收数大于定义接收数组最大个数时,覆盖接收数组之前值  
      {  
           m = 0;                   //清零串口3接收数据个数变量  
      }       
   }  
  if (S3CON & S3TI)                //在停止位开始发送时,该位置1  
   {  
      S3CON &= ~S3TI;      //清除S3CON寄存器对应S3TI位(该位必须软件清零)  
   }  
   IE2 |= 0x08;                    //串口3中断打开  
} 

程序清单:串口4中断服务函数

/*************************************************************************** 
 * 描  述 : 串口4中断服务函数 
 * 入  参 : 无 
 * 返回值 : 无 
 **************************************************************************/  
void Uart4() interrupt 18  using 1 
{             
    IE2 &= 0xEF;                   //串口4中断关闭    
    if(S4CON & S4RI)           //串行接收到停止位的中间时刻时,该位置1  
    {  
        S4CON &= ~S4RI;          //清除S4CON寄存器对应S4RI位(该位必须软件清零)  
        Rec_Buf4[n] = S4BUF;  //把串口4缓存SBUF寄存器数据依次存放到数组Rec_Buf4中 
        n++;                  //串口4接收数据个数变量累加  
      if(n>Buf_Max)           //接收数大于定义接收数组最大个数时,覆盖接收数组之前值  
        {  
                n = 0;               //清零串口4接收数据个数变量  
        }  
    }  
    if(S4CON & S4TI)           //在停止位开始发送时,该位置1  
    {  
        S4CON &= ~S4TI;          //清除S4CON寄存器对应S4TI位(该位必须软件清零)  
    }  
     IE2 |= 0x10;              //串口4中断打开  
}

最后,主函数main在主循环中判断对应串口接收到的字符串是不是规定的字符串,然后再发送另一串字符串。具体代码如下。

代码清单:主函数

int main()  
{  
    P3M1 &= 0xFE;   P3M0 &= 0xFE;                     //设置P3.0为准双向口  
    P3M1 &= 0xFD;   P3M0 |= 0x02;                     //设置P3.1为推挽输出  
    P1M1 &= 0xFE;   P1M0 &= 0xFE;                     //设置P1.0为准双向口  
    P1M1 &= 0xFD;   P1M0 |= 0x02;                     //设置P1.1为推挽输出  
    P0M1 &= 0xFA;   P0M0 &= 0xFA;                   //设置P0.0  P0.2为准双向口  
    P0M1 &= 0xF5;   P0M0 |= 0x0A;                   //设置P0.1  P0.3为推挽输出  
      
    Uart1234_Init();                                //串口1/2/3/4初始化  
    EA = 1;                                         //总中断打开  
    CLR_Buf1();                                     //清除串口1缓存         
    CLR_Buf2();                                     //清除串口2缓存         
    CLR_Buf3();                                     //清除串口3缓存         
    CLR_Buf4();                                     //清除串口4缓存         
      
    while(1)  
    {  
        if(Hand1("UART1"))                            //串口1收到字符串UART1  
        {  
            CLR_Buf1();                        //将串口1缓存数组的值都清为零  
            ES = 0;                                           //串口1中断关闭  
            SendStringByUart1("UART1 CHECK OK!\r\n");       //串口1发送字符串UART1 CHECK OK!  
            ES = 1;                                        //串口1中断打开     
        }     
        if(Hand2("UART2"))                            //串口2收到字符串UART2  
        {  
            CLR_Buf2();                         //将串口2缓存数组的值都清为零  
            IE2 &= 0xFE;                                      //串口2中断关闭  
            SendStringByUart2("UART2 CHECK OK!\r\n");       //串口2发送字符串UART2 CHECK OK!  
            IE2 |= 0x01;                           //串口2中断打开             
        }     
        if(Hand3("UART3"))                            //串口3收到字符串UART3  
        {  
            CLR_Buf3();                          //将串口3缓存数组的值都清为零  
            IE2 &= 0xF7;                               //串口3中断关闭           
            SendStringByUart3("UART3 CHECK OK!\r\n");       //串口3发送字符串UART3 CHECK OK!  
            IE2 |= 0x08;                          //串口3中断打开  
        }  
        if(Hand4("UART4"))                            //串口4收到字符串UART4  
        {  
            CLR_Buf4();                   //将串口4缓存数组的值都清为零  
            IE2 &= 0xEF;                        //串口4中断关闭       
            SendStringByUart4("UART4 CHECK OK!\r\n");       //串口4发送字符串UART4 CHECK OK!  
            IE2 |= 0x10;                        //串口4中断打开                 
        }   
    }  
}

9.2.硬件连接

在这里插入图片描述

图28:4个串口同时通信实验连接图

总结

以上就是今天要讲的内容,本文仅仅简单介绍了:1、编写程序实现单个串行口收发通信的程序设计;2、编写程序实现多个串行口收发通信的程序设计。
STC8A8K64S4A12单片机开发板软硬件技术资料+软件DEMO例程源码: 01参考程序 02原理图与结构图纸 STC8 使用手册.pdf STC8系列单片机技术参考手册-20170517.pdf 文件资料 01-P33口LED灯运行 02-P2口跑马灯实验 03- 定时器0 模式0 04- 定时器1 模式0 05-定时器2 06- 看门狗实验 06-定时器0 P35模拟10位或16位PWM输出程序 08-串口1发送 8位自动重装载 09-串口1收发 8位自动重装载 10-串口1仅发 16位重装载 11- 串口1收发 16位重装载 12-外部中断 1 13 模拟I2C读写24C 串口监测 14-外部FLASH读写 串口监测 15-中景园电子0.96OLED显示屏IIC_例程 15-中景园电子0.96OLED显示屏IIC_例程.zip 16-中景园电子0.96OLED显示屏SPI_例程 16-中景园电子0.96OLED显示屏SPI_例程.zip 17-中景园电子0.96OLED显示屏0.96OLED带字库 17-中景园电子0.96OLED显示屏0.96OLED带字库.zip 18-中景园电子1.44寸LCD-51(图片显示) 19-中景园电子1.8寸LCD-51(模拟SPI中文显示) 20-中景园电子2.4寸不带触摸51单片机测试(要求大于16K ROM的单片机) 21-中景园电子3.5寸FTFT_LCD_SPI接口 12864详细中文资料.pdf 1602中文资料.doc 1838红外接收头.pdf 24c0系列.pdf AMS1117.pdf C数组、BIN、BMP图片制作 elh.pdf DS18B20.pdf FAT及FATFS资料 I2C.doc ILI9325DS_ID9325.pdf MMA7361.pdf MMA7361L.pdf NRF24l01模块说明书.pdf PCB设计资料.pdf sd卡读写.pdf SD卡资料.pdf UCOS+UCGUI学习资料 UG-2864HSWEG01 user guide.pdf W25X16中文手册.pdf XPT2046中文资料.pdf 关于舵机的死区.txt 红外遥控器编码大全.pdf
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

电子友人张

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

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

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

打赏作者

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

抵扣说明:

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

余额充值