51单片机之串口通信

    好记性不如烂笔头,以前总以为自己记性比较好,但事实总是一次一次的打我脸,刚开始学习单片机的时候在串口通信这一块发了一段时间,才将这一块弄懂了个七七八八,这几天回头想一下那一方面的知识,感觉忘得差不多了,现在重新理一遍,里面的程序大部分都是以前抄袭大佬的,但具体是哪一位大佬的博客现在也找不到了,希望大佬见谅。

    首先了解串口通信先要熟悉SCON,PCON,TMOD三个寄存器

    串口工作方式寄存器SCON,

D7 D6 D5 D4 D3 D2 D1 D0
功能 SM0 SM1 SM2 REN TB8 RB8 TI RI

RI:接收中断标志位,数据接收结束时,标志位会自动置1,需要通过程序将其置0

TI:发送中断标志位,数据发送结束时,标志位会自动置1,需要通过程序将其置0

RB8:存放发送数据的第9位

TB8:存放接收数据的第9位

REN:串行接收允许位,0允许串行接收,1禁止串行接收

SM2:多机控制位

SM1,SM0:串行工作方式

SM0 SM1 方式 说明 波特率
0 0 0 移位寄存器 fosc/12
0 1 1 10位异步收发器(8位数据) 可变
1 0 2 11位异步收发器(9位数据) fosc/64或fosc/32
1 1 3 11位异步收发器(9位数据) 可变

    PCON寄存器

D7 D6 D5 D4 D3 D2 D1 D0
功能 SMOD - - - - - - -

SOMD:波特率是否加倍选择位,0波特率不加倍,1波特率加倍

    定时器工作方式寄存器TMOD

D7 D6 D5 D4 D3 D2 D1 D0
功能 GATE C/T M1 M0 GATE C/T M1 M0

高四位为定时计数器1的设置,低四位是定时计数器0设置,串口通信波特率设置占用定时计数器1,这里主要说串口通信,不过多说定时计数器,只需要设置定时计数器1的工作方式即可

振荡周期:也称时钟周期(频率的倒数),单片机提供时钟信号的振荡源周期,频率一般有11.0592MHz,12MHz等

状态周期:是时钟周期的2倍,

机器周期:是包含6个状态周期,机器周期=1/单片机时钟频率

单片机时钟频率:是外部时钟的12分频,如果是12MHz的晶振,机器周期=1/单片机时钟频率=1/(12MHz/12)=12/12M=1us

这里一个机器周期为1us,若定时时间为1ms,则需要1000个机器周期,计算出初值;如果机器周期为2us,则只需要500个机器周期。

定时器初值计算:初值=(65536-机器周期数量)


波特率计算:

当串口工作在工作方式0和2是,波特率固定,方式0时fosc/12;方式2时fosc/32或fosc/64(根据SMOD判断)。

当串口工作在方式1时,波特率=(2^SMOD/32)*(单片机时钟频率/(256-X)),X是初值

C/T:定时器和计数器选择位,0为定时器,1为计数器

M1 M0 工作方式
0 0

工作方式0:为13位定时/计数器

0 1 工作方式1:为16位定时/计数器
1 0 工作方式2:8位初值自动重装定时/计数器
1 1 工作方式3:仅适用于T0,分成两个8位计数器,T1停止计数

下面是程序结构和代码:


mian.c

#include "main.h"
uchar UartRxBuffer[ 64 ] = { 0 };			//uart串口接收数据
uchar	TX_Cnt = 0;		//发送计数
uchar	RX_Cnt = 0;		//接收计数
bit TX_Busy = 0;	  //发送忙标志
void main()
{
	uart_init();
	while(1)
	{
		if((TX_Cnt!=RX_Cnt)&&(!TX_Busy))
		{
			//将数组中的数据放入到发送缓冲区
			SBUF=UartRxBuffer[TX_Cnt];			
			if(++TX_Cnt>=64)
			{
				TX_Cnt=0;
			}
			TX_Busy==1;
		}	
	}
}

/*************************************************
函数:串口程序
功能:将接收到的数据存入到UartRxBuffer
*************************************************/
void UART_INT (void) interrupt 4
{
	if(RI)
	{
		RI = 0;
		UartRxBuffer[RX_Cnt] = SBUF;//将数据写入数组
		//防止溢出,当计数=16时,将计数清零
		if(++RX_Cnt >= 64){
			RX_Cnt = 0;
		}
	}
	if(TI)
	{
		TI = 0;
		TX_Busy = 0;
	}
}

mcu_uart.c

#include "mcu_uart.h"

/*************************************************
函数:uart_init
功能:初始化串口
出口:void
入口:void
*************************************************/
void uart_init()
{
    SCON = 0x50;//设置串口工作方式1
    TMOD = 0x20;//设置计数器工作方式2
    PCON = 0x00;//即SMOD=1,波特率不加倍
    TH1 = 0xFD;//计数器初值,波特率是9600,晶振为11.0592MHz
    TL1 = 0xFD;
    ES = 1;//打开接收中断
    EA = 1;//打开总中断
    TR1 = 1;//打开计数器		
}

/*************************************************
函数:uart_tx_byte
功能:串口发送一个字节
出口:void
入口:一个字节
*************************************************/
void uart_tx_byte(uchar str)
{
	SBUF=str;
	while(!TI);
	TI==0;
}
/*************************************************
函数:uart_tx_string
功能:串口发送一个字符串
出口:void
入口:字符串数组
*************************************************/
void uart_tx_string(uchar *str)
{
	while(*str!='\0')
	{
		uart_tx_byte(*str++);
	}
}

/*************************************************
函数:uart_rx_string
功能:串口接收一个字符串
出口:字符串的长度
入口:字符串数组
*************************************************/
uchar uart_rx_string( uchar* RxBuffer )
{
	uchar rxLength = 0;
	uint uartRxTimOut = 0x7FFF;	
	while( uartRxTimOut-- )
	{
		if( 0 != RI )
		{
			RI = 0;	
			*RxBuffer = SBUF;
			RxBuffer++;
			rxLength++;
			uartRxTimOut = 0x7FFF;
		}
	}	
	return rxLength;
}

mian.h

#ifndef __MAIN_H__
#define __MAIN_H__

#include "mcu_uart.h"

#endif

mcu_uart.h

#ifndef __MCU_UART_H__
#define __MCU_UART_H__

#include "mcu_type.h"

void uart_init();
#endif

mcu_type.h

#ifndef __MCU_TYPE_H__
#define __MCU_TYPE_H__

#include <reg52.h>
/*无符号*/
typedef unsigned char 			uchar;
typedef unsigned int 				uint;

#endif

猜你喜欢

转载自blog.csdn.net/jx_lihuifu/article/details/80308364