一、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; //清除发送忙标志
}
}