51单片机串口教程(原理+实例教学)

本文主要参考资料:

马淑华、高 军、蔡凌.单片机原理与接口技术(第三版).北京:北京邮电大学出版社,2018.6

一、串行通信的基本知识

1.串行通信的基本方式

全双工通信

        通信双方可同时进行数据收发的工作方式。51单片机的串行口是全双工传输方式。

②半双工通信

        通信双方交替进行双向数据传输,但两个方向的传输不能同时进行。

③单工通信

        数据只能单方向传输。

2.串行通信相关寄存器

①串行数据缓冲器——SBUF

        SBUF物理地址为99H,但实际是对应两个缓冲器(一收一发)

  • 发送内容:CPU写入SBUF
  • 接收内容:CPU读取SBUF

②串口控制寄存器——SCON(98H)

SM0和SM1:工作方式选择位,模式选择如下表            

SM2:多机通信选择。当SM2=0时,单机对单机通信;当SM2=1时,多机对多机通信。

REN:串行接收允许位,REN=1 允许接收数据手动控制

RB8 :在模式 2、3 时为第 9 个接收位;

TB8:在模式 2、3 时为第 9 个发送位;

RI:串口接收完成中断标志位(即RI=1),需要手动清0

TI:串口发送完成中断标志位(即TI=1),需要手动清0

③波特率与电源控制寄存器——PCON(不可位寻址,87H)

SMOD:波特率加倍控制位。在模式1、2、3下,当SMOD=0时,波特率不加倍;当SMOD=1时,波特率加倍。

其他位暂不做详解

④与串行口通信相关的中断寄存器——IE(中断允许寄存器,可位寻址,A8H)

 与串行中断相关的两个位:

EA:总中断控制位,EA=1时允许中断,EA=0时禁止中断

ES:串行口中断允许位,ES=1时允许串行口中断,ES=0时禁止串行口中断。

 3、串行通信工作模式详解

波特率的计算方法:

1 .模式 0 的波特率

   波特率固定:每个机器周期产生一个移位时钟,可发送或接收一位数据。且不受SMOD位的影响。即

           模式0的波特率={\color{Red} f_{osc} }/12 

2 .模式 2 的波特率

   由系统的振荡频率foscSMOD共同确定,即

          模式2的波特率={\color{Red} 2^{SMOD} }·{\color{Red} f_{osc} }/64 

3 .模式 1 和模式 3 的波特率

    模式1和模式3的移位时钟由定时器T1的溢出率决定,故波特率由T1的溢出率与SMOD值共同决定,即

       模式1和模式3的波特率= {\color{Red} 2^{SMOD} }·T1的溢出率/32

4. 波特率设计
        当定时器T1
做波特率发生器使用时,最典型的用法是使T1工作在模式2(初值自动加载),若计数初值为X,则每经过256X个机器周期。定时器T1就会产生一次溢出,溢出周期为:

        {\color{Red} \frac{12}{f_{osc}}(256-X) }

溢出率为溢出周期的倒数,所以波特率为:

\huge {\color{Red}\frac{2^{SMOD} }{32} \frac{f_{osc}}{12(256-X)} }

从上式解出定时器的初值X:

①模式0:同步移位寄存器

(1)设置方式

 当SM0=0,SM1=0,SM2=0时使用模式0

(2)模式特点

  • 波特率固定,波特率等于f_{osc}/12(即晶振频率的十二分之一),波特率不受SMOD的影响。
  • 数据        由RXD端发送或接收
  • 同步脉冲 由TXD端输出
  • 发送接收的是8位数据,低位在先

(3) 数据传输过程

发送过程:

        将数据写入SBUF时启动发送,串行口即将8位数据以f_{osc}/ 12的波特率从RxD输出(从低位到高位),发送完中断标志TI置"1”,TxD管脚输出同步移位脉冲。

        在再次发送数据前,必须用软件将TI清0。.

接收过程:

        接收时,将接收中断标志RI=0, 设置REN=1允许接收,启动串接收过程。启动接收过程后,RxD为串行输入端,TxD为同步脉冲输出端。串行接收的波特率为f_{osc}/12。

        当接收完成数据(8位)后,控制信号复位,中断标志RI被置”1”,呈中断申请状态。

        当再次接收时,必须通过软件将RI清0。

②模式1:8位UART,波特率可变(最常用)

(1)设置方式

当SM0=0,SM1=1时,使用模式1

(2)模式特点

  • 波特率可变,由定时器的溢出率和SMOD决定。
  • 数据通过TXD发送,RXD接收
  • 发送10位数据,1位起始位(0),8位数据位,1位停止位。

(3)数据传输过程

     发送过程:

        数据由串行发送端TXD输出。将数据写入SBUF时启动发送,写“SBUF”信号还把“1”装入发送移位寄存器的第9位,并通知TX控制单元开始发送。

        发送完成后,硬件置位中断请求位Tl,即TI=1,向主机请求中断处理。

        再次发送需要将TI置0。

接收过程

        当允许接收位REN=1时,启动接收。

        接收完成之后,置位中断请求位RI,即RI=1,向主机请求中断处理。

        再次接收需要手动将RI置0。

③模式2和模式3

模式2:    9位UART,波特率固定

模式3: 9位UART,波特率可变

(1)设置方式

 SM0=1,SM1=0设置为模式2;

 SM0=1,SM1=1设置为模式3;

(2)模式特点

  • 方式2和方式3都为11位数据的异步通信口,他们唯一的区别是传输速率不同。
  • 数据通过TXD发送,RXD接收
  • 发送11位数据,1位起始位(0),9位数据位,1位停止位。
  • 第9位的数据位发送时在SCON的TB8,接收时在RB8。

(3)数据传输过程

        发送过程:

                将数据写入SBUF后立即启动发送。

                发送完成后,硬件置位中断请求位Tl,即TI=1,向主机请求中断处理。

                再次发送需要手动将TI置0。

        接收过程:

                当允许接收位REN=1时,启动接收。

                接收完成之后,置位中断请求位RI,即RI=1,向主机请求中断处理。

                再次接收需要手动将RI置0。

二、串行通信的应用(实例分析)

模式1是串口最常用的方式,所以重点讲模式1的应用。

实例所使用的单片机是 亚博智能mini51开发板,编程开发环境为keil5

1.模式1的应用

①通过串口发送一个字符给计算机

步骤:

  • 初始化串口(设置工作模式、波特率等)
  • 编写发送字符的函数

1、初始化串口

设置工作模式:

        由于我们使用的是模式1,所以设置SM0=0,SM1=0,直接赋值SCON=0x40;

配置波特率:

        配置波特率要设置定时器的工作模式及计算定时器的初值。这里选择定时器T1模式2,8位自动重装方式,然后计算初值。波特率选择不加倍。

初值X的计算公式已经给出。

为了省事,可以直接使用下表经过计算后的值:

查表可知,9600波特率的TH1初值X为0XFD,波特率不加倍。

代码如下:

//串口初始化函数
void UartInit()		//9600bps@11.0592MHz
{
	SCON = 0x40;		//0100 0000 串口工作模式1
	TMOD &= 0x0F;		//清空TMOD中定时器1相关
	TMOD |= 0X20;       //设置定时器1工作模式2:8位自动重载
    TH1 = 0xFD;		//设定定时初值
	TL1 = 0XFD;
	TR1 = 1;		//启动定时器1
}

2、编写发送字符的函数

当寄存器SBUF被赋值时,启动发送。在这个发送字符的函数中,只需要写出被赋值语句即可。

void send_char(unsigned char m)
{
    SBUF=m;
}

   完整代码:

#include <REGX52.H>
//串口初始化函数
void UartInit()		//9600bps@11.0592MHz
{
	SCON = 0x40;		//0100 0000 串口工作模式1
	TMOD &= 0x0F;		//清空TMOD中定时器1相关
	TMOD |= 0X20;       //设置定时器1工作模式2:8位自动重载
  TH1 = 0xFD;		//设定定时初值
	TL1 = 0XFD;
	TR1 = 1;		//启动定时器1
}

//字符发送函数
void send_char(unsigned char m)
{
    SBUF=m;
}
//延时约1s的程序
void delay()
{
	unsigned char i, j, k;
	i = 8;
	j = 1;
	k = 243;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}	
//主函数
void main()
{
	UartInit();//调用串口初始化函数
	while(1)
	{
		send_char('p');//调用发送一个字符的函数
		delay();//调用延时1S的函数
	}	
}

将程序烧录到单片机后,打开串口助手即可查看串口收到的单片机信息。

(串口调试助手操作)

 (串口接受到了字符)

②通过串口发送字符串给计算机

类似于发送单个字符的步骤

步骤:

  • 初始化串口(设置工作模式、波特率等)
  • 编写发送字符串的函数

第一步已经介绍过,下面介绍编写发送字符串的函数。

编写发送字符串的函数:

void send_string(unsigned char str[])
{
		unsigned char i=0;
		while(str[i]!='\0')//判断是否到字符串尾
		{
			SBUF = str[i];
			while(TI==0);//等待发送完成
			TI=0;//下次发送前,要手动将TI置0
			i++;//下次发送
		}	
}

 完整代码:

#include <REGX52.H>
//串口初始化函数
void UartInit()		//9600bps@11.0592MHz
{
	SCON = 0x40;		//0100 0000 串口工作模式1
	TMOD &= 0x0F;		//清空TMOD中定时器1相关
	TMOD |= 0X20;       //设置定时器1工作模式2:8位自动重载
  TH1 = 0xFD;		//设定定时初值
	TL1 = 0XFD;
	TR1 = 1;		//启动定时器1
}
void send_string(unsigned char str[])
{
		unsigned char i=0;
		while(str[i]!='\0')//判断是否到字符串尾
		{
			SBUF = str[i];
			while(TI==0);//等待发送完成
			TI=0;//下次发送前,要手动将TI置0
			i++;//下次发送
		}	
}
//延时1sd函数
void delay()
{
	unsigned char i, j, k;i = 8;j = 1;k = 243;	
	do
	{do
		{
			while (--k);
		} while (--j);
	} while (--i);
}
	
void main()
{
	UartInit();
	while(1)
	{
		send_string("Hello World!\n\r");
		delay();
	}
}

打开串口助手即可看到效果(每隔1s发送“Hello World!”):

③单片机接收一个字符

        当SCON寄存器接收位REN=1时,启动接收。

        步骤:

  • 编写串口初始化函数(配置寄存器模式)
  • 编写接收函数

根据串口接收的原理:

        当串口接收到数据时,RI自动硬件置1,所以我们判断RI是否是1即可判断串口是否接受到数据,再将SBUF的内容读出即可获得数据

        为了验证串口收到的数据是否是我们所发送的数据,可以对单片机定义一些操作来进行确认。

        比如我们把串口接受到的数据在数码管/LCD等显示模块显示出来进行验证,当然也可以采用比较简单的方法,比如点灯验证法。

点灯验证法:

         比如我们要发送一个字符‘O’,规定字符‘O’可以点亮LED1。我们只需观察发送后LED1是否点亮即可判断我们串口接收到了正确的字符。

        同理,我们也可以发送一个字符'C',规定字符C可以熄灭LED1,我们只需观察发送后LED是否熄灭即可判断我们串口接收到了正确的字符。

完整代码(扫描判断法):

#include <REGX52.H>
sbit led=P1^0;

//串口初始化函数
void UartInit()		//9600bps@11.0592MHz
{
	SCON = 0x50;		//0101 0000 串口工作模式1
	TMOD &= 0x0F;		//清空TMOD中定时器1相关
	TMOD |= 0X20;       //设置定时器1工作模式2:8位自动重载
  TH1 = 0xFD;		//设定定时初值
	TL1 = 0XFD;
	TR1 = 1;		//启动定时器1
}

void main()
{
	char receive;//用于接收串口数据的变量
	led=1;
	UartInit();
	while(1)
    {
        if(RI==1)//RI=1说明串口接收到了数据
        {
			RI=0;//RI置0保证下次接收
			receive=SBUF;//将从串口接收到的数据报存到变量中

            //判断接收的数据,作出相应的操作
			if(receive=='O')
				led=0;
			
			if(receive=='C')
				led=1;	
		}
    }
}

中断判断法:   

#include <REGX52.H>
sbit led=P1^0;

//串口初始化函数
void UartInit()		//9600bps@11.0592MHz
{
	SCON = 0x50;		//0101 0000 串口工作模式1
	TMOD &= 0x0F;		//清空TMOD中定时器1相关
	TMOD |= 0X20;       //设置定时器1工作模式2:8位自动重载
    TH1 = 0xFD;		//设定定时初值
	TL1 = 0XFD;
	TR1 = 1;		//启动定时器1
	ES=1;//打开串行通信中断
	EA=1;//打开总中断
}
//串行中断函数
void Uart_receive() interrupt 4
{
	
	if(RI==1)//RI=1说明串口接收到了数据
    {		char receive;
			RI=0;//RI置0保证下次接收
			receive=SBUF;//将从串口接收到的数据报存到变量中

            //判断接收的数据,作出相应的操作
			if(receive=='O')
				led=0;
			
			if(receive=='C')
				led=1;	
	}
}
//主函数
void main()
{
	led=1;
	UartInit();
	while(1)
	{
        //do any other things what you want;
    }

}

打开串口助手,按照步骤操作给单片机发送数据:

  • 39
    点赞
  • 234
    收藏
    觉得还不错? 一键收藏
  • 15
    评论
51单片机串口接收数据的步骤如下: 1. 配置串口:设置波特率、校验位、数据位、停止位等参数,使单片机的串口能够正确接收外部设备发送的数据。 2. 等待接收:使用串口接收中断或查询方式等待数据的到来。 3. 接收数据:当接收到数据时,将数据存储到缓冲区中,等待后续处理。 4. 处理数据:根据应用需求,对接收到的数据进行处理,如解析数据、进行逻辑判断等。 下面是一个简单的串口接收数据的示例代码: ``` #include <reg52.h> // 定义串口参数 #define BAUDRATE 9600 // 波特率为9600 #define TIMERVAL 65536L - FOSC / 12 / BAUDRATE // 计时器初值 // 定义全局变量 unsigned char rx_buf[16]; // 定义接收缓冲区 unsigned char rx_count = 0; // 接收计数器 // 初始化串口 void init_uart() { TMOD = 0x20; // 定时器1工作在模式2,自动重装载 TH1 = (unsigned char)(TIMERVAL >> 8); // 设置计时器初值高位 TL1 = (unsigned char)TIMERVAL; // 设置计时器初值低位 TR1 = 1; // 启动计时器1 SM0 = 0; // 设置串口工作在模式1 SM1 = 1; REN = 1; // 启用串口接收 EA = 1; // 全局中断使能 ES = 1; // 串口中断使能 } // 串口中断服务函数 void uart_isr() interrupt 4 { if (RI == 1) { // 接收中断 RI = 0; // 清除接收中断标志 rx_buf[rx_count++] = SBUF; // 读取接收缓冲区 if (rx_count >= 16) { // 接收缓冲区已满 rx_count = 0; // 复位接收计数器 } } } // 主函数 void main() { init_uart(); // 初始化串口 while (1) { // 等待接收数据 } } ``` 在上述代码中,我们通过`init_uart()`函数初始化了串口,并在串口中断服务函数`uart_isr()`中读取了接收到的数据,并存储到了接收缓冲区中。在主函数中,我们可以通过轮询等待接收数据的方式来处理接收到的数据。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值