版权声明:本文为博主原创文章,转载请附上原文出处链接。
STC8A8K64S4A12开发板上设计了USB转TTL电路(CH340),其主要作用有3个:
USB转TTL电路如下图所示,串口接收和发送的引脚上均连接了LED指示灯,收发数据或程序下载时指示灯会闪烁,这样,更方便我们从硬件的角度观察串口有没有在进行数据通信。电路中的1000mA自恢复保险丝用于保护开发板和计算机USB口。
STC8A8K64S4A12开发板上还设计了RS232电路(MAX3232)及DB9接口,因为有的客户电脑有DB9接口,那么开发板也有DB9接口就可以直接连接,方便调试。同样可使用短路帽选择J5端子,将该RS232电路的TTL信号连接到MCU的P3.0和P3.1引脚,这样也可用于开发板程序下载和串口通信。
USB转TTL(RS232电路)占用的单片机的引脚如下表:
UART | 功能描述 | 对应IO口 | 说明 |
---|---|---|---|
RXD | 串口接收 | P3.0 | 独立GPIO |
TXD | 串口发送 | P3.1 | 独立GPIO |
☆注:独立GPIO表示开发板没有其他的电路使用这个GPIO。
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系列单片机串口的引脚分配如下表。
☆注:同一个串口各组之间切换是需要配置相关寄存器的相关位实现,如果没有对该部分寄存器配置,一般默认选择的都是第一组串口。
■ STC8A8K64S4A12系列单片机的4个串口用定时器:
STC8A8K64S4A12系列单片机UART用于波特率发生器的定时器也是可以选择的,但不是任意选择哪个定时器都可以的。针对不同UART可供选择的定时器如下表所示。
☆注:同一个串口的波特率发生器使用的定时器是需要配置相关寄存器的相关位实现,注意对寄存器按位进行操作,没有使用的位不要去配置。
STC8A8K64S4A12系列单片机4个UART均有多种工作模式,串口1有4种工作模式,其中2种工作模式的波特率是可变的,另2种工作模式的波特率是固定的,以供不同应用场合选用。串口2、串口3和串口4都只用2种工作模式,这2种工作模式的波特率都是可变的。下面列表4个UART的工作模式。
☆注:艾克姆提供例程是按照“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,向主机请求中断处理。
接收过程:当软件置位接收允许标志位REN,即REN=1时,接收器便对RXD端口的信号进行检测,当检测到RXD端口发送从“1”→“0”的下降沿跳变时就启动接收器准备接收数据,并立即复位波特率发生器的接收计数器,将1FFH装入移位寄存器。接收的数据从移位寄存器的右边移入,已装入的1FFH向左边移出,当起始位“0”移到移位寄存器的最左边时,使RX控制器作最后一次移位,完后一帧信息的接收。
接收数据有效需同时满足以下两个条件:
☆注:若上述两条件不能同时满足,则接收到的数据作废并丢失,无论条件满足与否,接收器重新检测RXD端口上的“1”→“0”的跳变,继续下一帧信息的接收。
下面举例介绍下串口1在进行工作方式选择时,需要配置的是串口1控制寄存器SCON。该寄存器支持位寻址,该寄存器的B6和B7位便是用来选择串口1工作方式的,寄存器的B5、B3和B2位是9位UART时需要配置的位,寄存器的B4位是串行接收控制位,寄存器的B0、B1位是串口接收和发送中断请求标志位。
☆注:若上述两条件不能同时满足,则接收到的数据作废并丢失,无论条件满足与否,接收器重新检测RXD端口上的“1”→“0”的跳变,继续下一帧信息的接收。
在使用STC8A8K64S4A12系列单片机的4个UART时,需要确定使用串口的哪一组引脚。这需要通过操作P_SW1或P_SW2等寄存器实现。
STC8A8K64S4A12系列单片机串口1有4组串口引脚可供选择,实现引脚切换选择需要P_SW1寄存器(即外设端口切换控制寄存器1)的B6和B7位,如下图所示。
☆注:STC15W4K32S4系列单片机串口1只有3组供选择,STC8A8K64S4A12系列单片机串口1有4组供选择。同一时间只能使能其中的一组串口作为串口1使用。
STC8A8K64S4A12系列单片机串口2、串口3和串口4均有2组串口引脚可供选择,实现引脚切换选择需要配置外设端口切换控制寄存器2的B0、B1和B2位,如下图所示。
☆注:因为串口2、串口3和串口4只有2组串口引脚供选择,所以寄存器使用1位即可控制切换。
STC8A8K64S4A12系列单片机4个UART在不同的工作模式下,选择不同的定时器作为波特率发生器时,波特率计算公式都是不同的。下面举例给出UART1工作模式1时的波特率计算公式。
☆注:SYSclk为系统工作频率,SMOD是PCON寄存器最高位(用于波特率加倍选择),定时器1模式0为16位自动重装载模式,定时器1模式2为8位自动重装载模式(详见定时器部分介绍)。
举例,系统时钟频率为11.0592MHZ,配置定时器1为1T,工作模式为模式2, PCON寄存器SMOD位置为0,波特率预设置为9600bps,计算下定时器1重装载值。
针对STC8A8K64S4A12系列单片机4个串行口外设,软件的配置过程如下:
☆注:实验例程即是按照上述配置步骤操作寄存器相关位实现,后有详述
STC8A8K64S4A12系列单片机操作串行口时会用到18个寄存器,如下表所示:
☆注:串口波特率发生器需要用到定时器,定时器相关的寄存器没有在上述表格中列举。串口3和串口4是没有中断高优先级的,所以在中断优先级控制寄存器中没有对串口3和串口4中断优先级的配置位。
外部中断允许寄存器IE支持位寻址,该寄存器的B4位是串口1的中断允许位。
中断允许寄存器IE2不支持位寻址,该寄存器的B0、B3和B4位是串口2、串口3和串口4的中断允许位。因为IE2寄存器不支持位寻址,所以举例操作该寄存器B0位时,不可以直接“ES2=0;”进行操作,参考下图。
电源控制寄存器PCON不支持位寻址,该寄存器的B6位和B7位是UART1的帧错误检测有效控制位和波特率选择位,具体含义如下图。
☆注:PCON寄存器的SMOD位和SMOD0位是用于串口1的,换句话说,串口2、串口3和串口4没有与之对应的控制位。
辅助寄存器AUXR不支持位寻址,该寄存器的B0位是串口1的波特率发生器定时器选择控制位,寄存器的B5位是串口1模式0的通信速度设置位。
☆注:AUXR寄存器的其他位用于定时器配置,操作串口时也会有对定时器的配置部分,请注意按位操作。
中断优先级控制寄存器IP支持位寻址,该寄存器的B4位是串口1中断优先级控制位。
☆注:艾克姆例程没有对串口1中断优先级进行配置,可根据项目需要配置PS位。
中断优先级控制寄存器2不支持位寻址,该寄存器的B0位是串口2中断优先级控制位。
串行口2控制寄存器S2CON不支持位寻址,该寄存器的B7位是用来选择串口2工作模式的,寄存器的B5、B3和B2位是9位UART时需要配置的位,寄存器的B4位是串行接收控制位,寄存器的B0、B1位是串口接收和发送中断请求标志位。
串行口3控制寄存器S3CON不支持位寻址,该寄存器的B7位是用来选择串口3工作模式的,寄存器的B5、B3和B2位是9位UART时需要配置的位,寄存器的B4位是串行接收控制位,寄存器的B0、B1位是串口接收和发送中断请求标志位。
串行口4控制寄存器S4CON不支持位寻址,该寄存器的B7位是用来选择串口4工作模式的,寄存器的B5、B3和B2位是9位UART时需要配置的位,寄存器的B4位是串行接收控制位,寄存器的B0、B1位是串口接收和发送中断请求标志位。
本例需要用到的c文件如下表所示,工程需要添加下表中的c文件。
序号 | 文件名 | 后缀 | 功能描述 |
---|---|---|---|
1 | uart | .c | 外部串行口有关的用户自定义函数。 |
2 | delay | .c | 包含用户自定义延时函数。 |
■ 需要引用的头文件
#include "delay.h"
#include "uart.h"
■ 需要包含的头文件路径
本例需要包含的头文件路径如下表:
序号 | 路径 | 描述 |
---|---|---|
1 | …\ Source | uart.h和delay.h头文件在该路径,所以要包含。 |
2 | …\User | STC8.h头文件在该路径,所以要包含。 |
MDK中点击魔术棒,打开工程配置窗口,按照下图所示添加头文件包含路径。
首先,在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(); //串口接收到一个字符后返回该字符
}
}
本实验需要将USB线连接至PC,因为开发板板载CH340电路连接的就是单片机P3.0和P3.1口,所以无需外接USB转TTL模块做该实验。
首先,在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(); //串口接收到一个字符后返回该字符
}
}
本实验需要外接USB转TTL模块连接到开发板串口2上,该实验串口2选择的P1.0和P1.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(); //串口接收到一个字符后返回该字符
}
}
本实验需要外接USB转TTL模块连接到开发板串口3上,该实验串口3选择的P0.0和P0.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(); //串口接收到一个字符后返回该字符
}
}
本实验需要外接USB转TTL模块连接到开发板串口4上,该实验串口4选择的P0.2和P0.3,具体接线图如下。
本例需要用到的c文件如下表所示,工程需要添加下表中的c文件。
序号 | 文件名 | 后缀 | 功能描述 |
---|---|---|---|
1 | uart | .c | 外部串行口有关的用户自定义函数。 |
2 | delay | .c | 包含用户自定义延时函数。 |
3 | led | .c | 包含与用户led控制有关的用户自定义函数。 |
■ 需要引用的头文件
#include "delay.h"
#include "uart.h"
■ 需要包含的头文件路径
本例需要包含的头文件路径如下表:
序号 | 路径 | 描述 |
---|---|---|
1 | …\ Source | Led.h、uart.h和delay.h头文件在该路径,所以要包含。 |
2 | …\User | STC8.h头文件在该路径,所以要包含。 |
MDK中点击魔术棒,打开工程配置窗口,按照下图所示添加头文件包含路径。
首先,在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接收到字符串后发送出去
}
}
首先,在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接收到字符串后发送出去
}
}
首先,在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中断打开
}
}
}