方式0
方式 0 时,串行口为同步移位寄存器的输入输出方式。主要用于扩展并行输 入或输出口。
数据由 RXD(P3.0)引脚输入或输出,同步移位脉冲由 TXD(P3.1) 引脚输出。发送和接收均为 8 位数据,低位在先,高位在后。波特率固定为 fosc/12
方式0输出
方式0输入
方式1
方式 1 是 10 位数据的异步通信口。TXD 为数据发送引脚,RXD 为数据接收引 脚,传送一帧数据的格式如下所示。其中 1 位起始位,8 位数据位,1 位停止位。
方式1输出
方式1输入
用软件置 REN 为 1 时,接收器以所选择波特率的 16 倍速率采样 RXD 引脚电 平,检测到 RXD 引脚输入电平发生负跳变时,则说明起始位有效,将其移入输入 移位寄存器,并开始接收这一帧信息的其余位。接收过程中,数据从输入移位寄 存器右边移入,起始位移至输入移位寄存器最左边时,控制电路进行最后一次移 位。当 RI=0,且 SM2=0(或接收到的停止位为 1)时,将接收到的 9 位数据的前 8 位数据装入接收 SBUF,第 9 位(停止位)进入 RB8,并置 RI=1,向 CPU 请求中断。
方式2或方式3
输出
方式 2 或方式 3 时为 11 位数据的异步通信口。TXD 为数据发送引脚,RXD 为 数据接收引脚。
发送开始时,先把起始位 0 输出到 TXD 引脚,然后发送移位寄存器的输出位 (D0)到 TXD 引脚。每一个移位脉冲都使输出移位寄存器的各位右移一位,并由 TXD 引脚输出。第一次移位时,停止位“1”移入输出移位寄存器的第 9 位上, 以后每次移位,左边都移入 0。当停止位移至输出位时,左边其余位全为 0,检 测电路检测到这一条件时,使控制电路进行最后一次移位,并置 TI=1,向 CPU 请求中断。
输入
接收时,数据从右边移入输入移位寄存器,在起始位 0 移到最左边时,控制 电路进行最后一次移位。当 RI=0,且 SM2=0(或接收到的第 9 位数据为 1)时, 接收到的数据装入接收缓冲器 SBUF 和 RB8(接收数据的第 9 位),置 RI=1,向 CPU 请求中断。如果条件不满足,则数据丢失,且不置位 RI,继续搜索 RXD 引脚 的负跳变。
串口使用方法
如何计算波特率
(256 - TH1) = 11.0592M/12169600,所以TH1=TL1=250;
串口初始化步骤
如何使用串口,大家可以按照以下几个步骤配置。
- ①确定 T1 的工作方式(TMOD 寄存器);
- ②确定串口工作方式(SCON 寄存器);
- ③计算 T1 的初值(设定波特率),装载 TH1、TL1;
- ④启动 T1(TCON 中的 TR1 位);
- ⑤如果使用中断,需开启串口中断控制位(IE 寄存器)。
串口回传实验
//功能:串口回传实验,PC通过串口通讯工具,发送字符a或字符串hello,MCU回复相同内容
#include "reg52.h"
typedef unsigned int u16;
typedef unsigned char u8;
typedef unsigned long u32;
//以毫秒为单位的延时
void delay_ms(unsigned int ms){
unsigned int i,j;
for(i=ms;i>0;i--)
for(j=123;j>0;j--);
}
//以10微秒为单位的延时
void delay_10us(u16 ten_us)
{
while(ten_us--);
}
void Uart1_Init(void); //串口1的初始化
void main()
{
Uart1_Init();
while(1) //保持应用程序不退出
{
}
}
void Uart1_Init(void)//配置串口为工作方式1,定时器1,工作方式为2,8位自重载
{
//串口模块寄存器
PCON |= 0x80; //使能波特率倍速位SMOD
SCON |= 0x50; //配置串口工作方式1,允许接收数据
//定时器1相关寄存器
TMOD &= 0x0F; //使用定时器1
TMOD |= 0x20; //配置工作方式2,8位自重载
TL1 = 0xFA; //设置定时计数器的低8位
TH1 = 0xFA; //设置定时计数器的高8位
TR1 = 1; //开启定时器1
//中断相关寄存器
ES = 1; //串行口中断允许位
EA = 1; //CPU 中断允许(总允许)位
}
void Uart1_Isp(void) interrupt 4
{
u8 u8RecData = 0;
if(RI) //检测串口接收完成中断
{
u8RecData = SBUF; //将串口模块缓存寄存器中的数据读到用户内存
SBUF = u8RecData; //将用户数据传输到串口缓存寄存器中,准备发送
while(TI==0); //用户在等待串口发送单元发送完成
RI = 0; //用户清除接收数据完成标志
TI = 0; //用户清除发送数据完成标志
}
}
模拟printf实验
//功能:实现printf的输出重定向
#include "reg52.h"
#include "stdio.h"
typedef unsigned int u16;
typedef unsigned char u8;
typedef unsigned long u32;
//定义LED的引脚
sbit LED1 = P2^0;
//以毫秒为单位的延时
void delay_ms(unsigned int ms){
unsigned int i,j;
for(i=ms;i>0;i--)
for(j=123;j>0;j--);
}
//以10微秒为单位的延时
void delay_10us(u16 ten_us)
{
while(ten_us--);
}
void Uart1_Init(void); //串口1的初始化
void Uart1_SendData(u8 u8data); //串口1的发送数据
void main()
{
u16 i = 0;
Uart1_Init();
while(1) //保持应用程序不退出
{
i++;
delay_ms(1000);
printf("i = %03d,hello world!\r\n",i);
}
}
void Uart1_Init(void)//配置串口为工作方式1,定时器1,工作方式为2,8位自重载
{
//串口模块寄存器
PCON |= 0x80; //使能波特率倍速位SMOD
SCON |= 0x50; //配置串口工作方式1,允许接收数据
//定时器1相关寄存器
TMOD &= 0x0F; //使用定时器1
TMOD |= 0x20; //配置工作方式2,8位自重载
TL1 = 0xFA; //设置定时计数器的低8位
TH1 = 0xFA; //设置定时计数器的高8位
TR1 = 1; //开启定时器1
//中断相关寄存器
ES = 1; //串行口中断允许位
EA = 1; //CPU 中断允许(总允许)位
}
void Uart1_Isp(void) interrupt 4
{
u8 u8RecData = 0;
if(RI) //检测串口接收完成中断
{
u8RecData = SBUF; //将串口模块缓存寄存器中的数据读到用户内存
switch(u8RecData)
{
case 0x10: LED1 = 1;break;
case 0x11: LED1 = 0;break;
default:break;
}
RI = 0; //用户清除接收数据完成标志
}
}
char putchar (char dat) //标准C的输出重定向
{
Uart1_SendData(dat);
return dat;
}
void Uart1_SendData(u8 u8data)
{
SBUF = u8data; //串口输出单元开始工作
while(TI == 0) //等TI为高电平,输出完成
{
}
TI = 0; //清除发送标志
}
串口接收数据不丢失实验
//功能:串口接收长字符(50以内)不丢失数据,通过printf发送给PC
#include "reg52.h"
#include "stdio.h"
typedef unsigned int u16;
typedef unsigned char u8;
typedef unsigned long u32;
u8 u8Uart_Buffer[50]={0}; //串口缓存池
u8 u8Uart_Rx_STA = 0; //串口接收状态标志
//bit7 0:表示没有接收完成, 1反之
//bit6 0:表示没有接收到'\r', 1反之
//bit5--bit0 表示接收到的有效数据长度
//以毫秒为单位的延时
void delay_ms(unsigned int ms){
unsigned int i,j;
for(i=ms;i>0;i--)
for(j=123;j>0;j--);
}
//以10微秒为单位的延时
void delay_10us(u16 ten_us)
{
while(ten_us--);
}
void Uart1_Init(void); //串口1的初始化
void Uart1_SendData(u8 u8data); //串口1的发送数据
void Uart1_RecvData(void);
void main()
{
u8 len = 0;
Uart1_Init();
while(1) //保持应用程序不退出
{
if(u8Uart_Rx_STA & 0x80) //串口数据接收完成
{
len = u8Uart_Rx_STA & 0x3f;
u8Uart_Buffer[len] = 0;
printf("%s\r\n",u8Uart_Buffer);
u8Uart_Rx_STA = 0; //清除状态标志
}
}
}
void Uart1_Init(void)//配置串口为工作方式1,定时器1,工作方式为2,8位自重载
{
//串口模块寄存器
PCON |= 0x80; //使能波特率倍速位SMOD
SCON |= 0x50; //配置串口工作方式1,允许接收数据
//定时器1相关寄存器
TMOD &= 0x0F; //使用定时器1
TMOD |= 0x20; //配置工作方式2,8位自重载
TL1 = 0xFA; //设置定时计数器的低8位
TH1 = 0xFA; //设置定时计数器的高8位
TR1 = 1; //开启定时器1
//中断相关寄存器
ES = 1; //串行口中断允许位
EA = 1; //CPU 中断允许(总允许)位
}
void Uart1_Isp(void) interrupt 4
{
u8 u8RecData = 0;
if(RI) //检测串口接收完成中断
{
Uart1_RecvData();
RI = 0; //用户清除接收数据完成标志
}
}
char putchar (char dat) //标准C的输出重定向
{
Uart1_SendData(dat);
return dat;
}
void Uart1_SendData(u8 u8data)
{
SBUF = u8data; //串口输出单元开始工作
while(!TI); //等TI为高电平,输出完成
TI = 0; //清除发送标志
}
void Uart1_RecvData(void)
{
u8 r = SBUF;
if((u8Uart_Rx_STA & 0x80) == 0)//没有接收完成
{
if(u8Uart_Rx_STA & 0x40)
{
if(r!='\n')
{
u8Uart_Rx_STA = 0; //将状态清0
}
else
{
u8Uart_Rx_STA |= 0x80; //标记数据接收完成
}
}
else //还没有接收过'\r'
{
if(r == '\r')
{
u8Uart_Rx_STA |= 0x40; //标记数据接收到\r
}
else
{
if((u8Uart_Rx_STA & 0x3f)<48)
{
u8Uart_Buffer[u8Uart_Rx_STA++] = r;
}
else
{
u8Uart_Rx_STA = 0;
}
}
}
}
}