Bootstrap

STC15系列单片机学习4:串口通讯

一、STC15系列单片机的串口个数

在使用单片机的串口前,得先知道所使用的单片机有几个串口,再结合你的硬件电路图来使用哪个串口。

以下是STC15各系列单片机的串口数量,STC15W4K32S4系列有4个串口

二、串口的工作模式

工作模式0:同步移位寄存器(官方建议初学者不学)

工作模式1:8位串口,波特率可变

工作模式2:9位串口,波特率固定(官方建议不学习)

工作模式3:9位串口,波特率可变

三、与串口工作相关的寄存器

以串口1为例,与串口1相关的寄存器

1、串行控制寄存器

STC15系列单片机串口1的控制相关的寄存器有SCON和PCON;

通过SCON寄存器中的SM0和SM1位来设置工作方式,设置如下表格所示,最常用的是工作方式1 

REN:串口接收使能位,REN = 1,启动串口接收,开始接收信息,REN = 0,禁止接收数据;

TB8:工作在9位UART模式才有效,也就是方式2和3,表示要发送的第9位;也就是奇偶校验位,8位UART不需要用这个位;

RB9:工作在9位UART模式才有效,也就是方式2和3,表示要接收的第9位;也就是奇偶校验位,8位UART不需要用这个位;

TI:发送中断请求标志位,在方式1,2,3中,在停止位开始发送的时候,单片机将TI位,置1,也就是TI = 1,向CPU发出中断申请,响应中断后,TI必须由软件清零,也就是用户要在代码中写上TI = 0;

RI:接收中断请求标志位,在方式1,2,3中,串行接收到停止位的终点时刻,单片机将RI位,置1,也就是RI = 1,向CPU发出中断申请,响应中断后,RI必须由软件清零,也就是用户要在代码中写上RI = 0;

电源控制寄存器PCON用来设置串口1的波特率是否加倍,PCON中与串口1相关的位只有SMOD和SMOD0

SMOD:波特率选择位,当SMOD = 1,串口波特率加倍;当SMOD = 0,波特率不加倍,默认是SMOD = 0;

SMOD0:帧错误检测有效控制位。这个位能决定SCON寄存器中SM0/FE位的功能,SMOD0 = 1时,SM0/FE位的功能是FE(帧错误检测),SMOD0 = 0时,SM0/FE位的功能是SM0功能,复位时SMOD0 = 0;

2、串行口数据缓冲寄存器SBUF

数据缓冲寄存器器,分为数据接收缓冲器数据发送缓冲器,它们可以同时工作,也就是说,串口可以同时发送数据和接收数据。

数据接收缓冲器只能读出,不能写入;

数据发送缓冲器只能写入,不能读出;

数据接收缓冲器与数据发送缓冲器的地址是一样的,比如

串口1的数据接收与发送缓冲器的地址都是99H,在官方头文件中定义的名字都是SBUF;

串口发送数据:

单片机只要执行了写SBUF命令,(汇编指令:MOV    SBUF, A)或者 (C语言指令SBUF = XX),要发送的数据(一个字节)就会装入相同的9位移位寄存器,前面8位是数据字节,第9位可以根据工作模式来设置,9位串口设置为TB的值,8位串口设置为1;

串口接收数据:

串口的接收急促器本身就是一个输入移位寄存器,串口在工作模式1,2,3,移位寄存器为9位,当一帧数据接收完毕,移位寄存器中的数据字节(前8位)装入数据接收缓冲器SBUF,第9位装入SCON寄存器的RB8位。当一帧数据从移位寄存器装入SBUF后,可立即开始接收下一帧数据,用户应该在下一帧数据接收结束前将数据从SBUF中取走,否则数据就被覆盖了。

3、辅助寄存器AUXR

官方推荐用定时器T2作为串口1的波特率发生器,那就从了他吧,相关的AUXR寄存器位是T2x12和S1ST2.

T2x12:定时器2速度控制位,T2x12 = 0,12T 模式,T2x12 = 1,1T 模式,

S1ST2:串口1选择哪个定时器作为波特率发生器控制位,S1ST2 = 0,选择定时1作为串口 1的波特率发生器,S1ST2 = 1,选择定时2作为串口 1的波特率发生器。默认S1ST2 = 1;

4、定时器2的寄存器T2H,T2L

定时器2的寄存器T2H和寄存器T2L是定时器用来保存重装时间。

5、与串口1中断相关的寄存器位ES,PS

EA:CPU的中断总开关控制位,EA= 1,使能中断,EA= 0,禁止所有中断;

ES:串行口中断允许控制位,ES = 1,允许串口中断;ES= 0,禁止串口中断;

 PS:串口1的中断优先级控制位,PS=0,串口1的中断优先级为0,最低优先级;PS= 1,串口1的中断优先级为1,最高优先级

6、控制串口1相关引脚的寄存器AUXR1

四、用户在程序中如何使用串口

以串口1为例,使用串口的步骤如下

1、设置串口1的工作模式,使用串行控制寄存器SCON中的SM0和SM1这两位来设置工作模式;

2、设置串口1的波特率,使用定时器2的寄存器T2H和T2L;

3、设置定时器2的速度1T还是12T,使用辅助寄存器AUXR中的T2x12位;

4、启动定时器2,通过设置T2R位为1,定时器2就开始计数;

5、设置串口1的中断优先级,并打开中断,打开中断需要打开PS、ES、EA这些控制位;

6、如果串口1是接收数据,将REN位设置为1就可以;如果串口1是发送数据,将数据送入SBUF就可以;

7、数据接收完成,标志位RI自动置1;数据发送完成,标志位TI自动置1;RI和TI都需要软件清0;

波特率的计算

串口1的波特率 = 定时器T2的溢出率 / 4;

STC15系列单片机我们一般用它工作在1T模式,这个通过AUXR来设置,

定时器2的溢出率 = SYSclk / (65536-[RL_TH2, RL+TL2]);

则串口1的波特率 = SYSclk / (65536-[RL_TH2, RL+TL2])/4

五、串口1工作模式1:8位UART,波特率可变

六、串口1的工作模式3: 9位UART,波特率可变

 

七、串口应用代码示例

程序示例2: 

#include <STC15.H> 
#include <intrins.h>

#define FOSC    11059200L

#define BAUD    115200
#define NONE_PARITY         0
#define ODD_PARTITY         1
#define EVEN_PARTITY        2
#define MARK_PARTITY        3
#define SPACE_PARTITY       4
#define PARITYBIT           EVEN_PARTITY

typedef unsigned char BYTE;
typedef unsigned int WORD;

bit busy;

BYTE flag = 0;

void SendDate(BYTE dat);
void SendString(char *s);



void main(void)
{
#if(PARITYBIT == NONE_PARITY)
    SCON = 0x50;//8位可变波特率
#elif(PARITYBIT == ODD_PARTITY)||(PARITYBIT == EVEN_PARTITY)||(PARITYBIT == MARK_PARTITY)
    SCON = 0xDA;//9位可变波特率,校验初始位为1
#elif(PARITYBIT == SPACE_PARTITY)
    SCON = 0xDa;//9位可变波特率,校验初始位为0
#endif
    
    T2L = (65536-(FOSC/4/BAUD));
    T2H = (65536-(FOSC/4/BAUD)) >> 8;
    AUXR = 0x14;//T2为1T模式,并启动定时器2
    AUXR |= 0x01;//选择T2作为串口1的波特率发生器
    
    ES = 1;//使能串口1中断
    EA = 1; //打开中断总开关
    
    while(1)
    {
        if(flag == 1)
        {
            SendString("STC15W4K\r\n");
            flag = 0;
        
        }  
    }
}

void Uart_ISR() interrupt 4
{
    if(RI)
    {
        RI = 0;//清除RI位
        flag = 1;      
    }
    if(TI)
    {
        TI = 0; //清除TI
        busy = 0; //清除忙标志位
    }
}


void SendData(BYTE dat)
{
    while(busy);//等待前面的数据发送完成
    ACC = dat;//获取校验位P,PSW.0
    if(P)
    {
    #if(PARITYBIT == ODD_PARTITY)
        TB8 = 0;//设置校验位为0
    #elif(PARITYBIT == EVEN_PARTITY)
        TB8 = 1;//设置校验位为1
    #endif       
    }
    else
    {
    #if(PARITYBIT == ODD_PARTITY)
        TB8 = 1;//设置校验位为1
    #elif(PARITYBIT == EVEN_PARTITY)
        TB8 = 0;//设置校验位为0
    #endif    
    }
    
    busy = 1;
    SBUF = ACC; //写数据到UART数据寄存器
}

void SendString(char *s)
{
    while(*s) //检查字符串结束标志
    {
        SendData(*s++); //发送当前字符
    }
}

程序实例2:

#include <STC15.H> //包含头文件,一般情况不需要改动,头文件包含特殊功能寄存器的定义
#include <intrins.h>

/*------------------------------------------------
                   函数声明
------------------------------------------------*/


/*************	功能说明	**************

串口1全双工中断方式收发通讯程序。本例程使用11.0592MHZ时钟,如要改变,请修改下面的"定义主时钟"的值并重新编译。

串口设置为:115200,8,n,1.

通过PC向MCU发送数据, MCU收到后通过串口把收到的数据原样返回.

******************************************/

/*************	本地常量声明	**************/
//#define MAIN_Fosc		22118400L	//定义主时钟
#define MAIN_Fosc		11059200L	//定义主时钟
#define	RX1_Lenth		32			//串口接收缓冲长度
#define	BaudRate1		115200UL	//选择波特率


#define	Timer1_Reload	(65536UL -(MAIN_Fosc / 4 / BaudRate1))		//Timer 1 重装值, 对应300KHZ
#define	Timer2_Reload	(65536UL -(MAIN_Fosc / 4 / BaudRate1))		//Timer 2 重装值, 对应300KHZ

#define		S1_DoubleRate()		PCON |= 0x80
#define		S1_SHIFT()			SCON &= 0x3f
#define		S1_8bit()			SCON  = (SCON & 0x3f) | 0x40
#define		S1_9bit()			SCON  = (SCON & 0x3f) | 0xc0
#define		S1_RX_Enable()		SCON |= 0x10
#define		S1_USE_P30P31()		P_SW1 &= ~0xc0					//UART1 使用P30 P31口	默认
#define		S1_USE_P36P37()		P_SW1 = (P_SW1 & ~0xc0) | 0x40	//UART1 使用P36 P37口
#define		S1_USE_P16P17()		P_SW1 = (P_SW1 & ~0xc0) | 0x80	//UART1 使用P16 P17口
#define		S1_TXD_RXD_SHORT()	PCON2 |=  (1<<4)	//将TXD与RXD连接中继输出
#define		S1_TXD_RXD_OPEN()	PCON2 &= ~(1<<4)	//将TXD与RXD连接中继断开	默认
#define 	S1_BRT_UseTimer2()	AUXR |=  1
#define 	S1_BRT_UseTimer1()	AUXR &= ~1



/*************	本地变量声明	**************/
unsigned char	RX1_Buffer[RX1_Lenth];	//接收缓冲
unsigned char	TX1_Cnt;	//发送计数
unsigned char	RX1_Cnt;	//接收计数
bit	B_TX1_Busy;	//发送忙标志


/*************	本地函数声明	**************/



/**********************************************/
void main(void)
{
	B_TX1_Busy = 0;
	RX1_Cnt = 0;
	TX1_Cnt = 0;

	//S1_8bit();				//8位数据
    SCON  = (SCON & 0x3f) | 0x40;
      
	//S1_USE_P30P31();		//UART1 使用P30 P31口	默认
    P_SW1 &= ~0xc0;	

	AUXR &= ~(1<<4);	//Timer stop  波特率使用Timer2产生
	AUXR |= 0x01;		//串口1的波特率发生器使用Timer2
	AUXR |=  (1<<2);	//Timer2 set as 1T mode
	T2H = Timer2_Reload >> 8;
	T2L = Timer2_Reload;
	AUXR |=  (1<<4);	//Timer run enable

	REN = 1;	//允许接收
	ES  = 1;	//允许中断

	EA = 1;		//允许全局中断
	
	while (1)
	{
		if(TX1_Cnt != RX1_Cnt) //收到过数据
		{
			if(!B_TX1_Busy)	//发送空闲
			{
				B_TX1_Busy = 1;		//标志发送忙
				SBUF = RX1_Buffer[TX1_Cnt];	//发一个字节
				if(++TX1_Cnt >= RX1_Lenth)	TX1_Cnt = 0;	//避免溢出处理
			}
		}
	}
}



/********************* UART1中断函数************************/
void UART1_interrupt (void) interrupt 4
{
	if(RI)
	{
		RI = 0;
		RX1_Buffer[RX1_Cnt] = SBUF;		//保存一个字节
		if((++RX1_Cnt) >= RX1_Lenth)
        {
            RX1_Cnt = 0;	//避免溢出处理
        }            
	}

	if(TI)
	{
		TI = 0;
		B_TX1_Busy = 0;		//清除发送忙标志
	}
}
 

;