本文最后更新于20 天前,其中的信息可能已经过时,如有错误请发送邮件到big_fw@foxmail.com
串口通信
STC89C52(下面简称C51)系列单片机内部集成了一个功能很强的全双工串行通信口,与传统8051单片机的串口完全兼容。设有两个互相独立的接收、发送缓存器,可以同时发送和接收数据。发送缓存器只能写入不能读出,接收缓存器只能读出不能写入,两个缓存器共用一个地址吗(99H)。
串行通信外设有4种工作方式,其中两种方式的波特率是可变的,另两种是固定的,以供不同应用场合选用。波特率由内部定时器/计数器产生,用软件设置不同的波特率和选择不同的工作方式。
C51系列单片机串行口对应的引脚是P3.0(RxD)和P3.1(TxD)。
串行口控制寄存器
C51系列单片机的串行口设有两个控制寄存器:SCON(串行控制寄存器)和PCON(波特率选择特殊功能寄存器)
SCON
SM0和SM1共同确定串行的工作方式:
SM2:允许方式2或方式3多机通信控制位 REN:允许/禁止串口接收控制位,REN=1为允许接收 TB8: 在方式2或方式3,为要发送的第九位数据,按需要由软件置位或清0 RB8: 在方式2或方式3,是接收到的第九位数据 TI: 发送中断请求标志位 RI: 接收中断请求标志位
串行通信的中断请求:当一帧发送完成,内部硬件自动置位TI,即TI=1,请求中断处理;当接收完一帧信息时,内部硬件自动置位RI,即RI=1,请求中断处理
PCON
PCON只有高2位(B6、B7)是属于串口的
SMOD: 波特率选择位 SNOD0: 帧错误检测有效控制位
串行口工作模式1:8位UART,波特率可变
实验
- P32和P33各接一个按键S3、S4。按下S3发送字符“1”到PC机,按下S4发送字符“2”到PC机的按键。-
- PC机用串口助手软件将接收数据显示。
- 晶振11.0592MHz,串行口用方式1,波特率9600bps
- 接收PC发来的数据,在P1口上显示
- 用LCD1602液晶屏显示接收到的数据
代码
#include <reg52.h>
sbit E = P2^6;
sbit RS = P2^4;
sbit RW = P2^5;
sbit S1 = P3^0;
sbit S3 = P3^2;
sbit S4 = P3^3;
#define LCD_Data P0
unsigned char flag1 = 1;
unsigned int time0 = 0;
unsigned int time1 = 0;
unsigned char S1_flag0 = 1;
unsigned char S1_flag1 = 1;
unsigned char S3_flag0 = 1;
unsigned char S3_flag1 = 1;
unsigned char S4_flag0 = 1;
unsigned char S4_flag1 = 1;
unsigned char clean_flag0 = 0;
unsigned char usart_send_flag1 = 0;
unsigned char usart_send_flag2 = 0;
unsigned char Addr = 1;
unsigned char LCD_flag0 = 0;
unsigned char Num = 1;
void LCD_Delay(unsigned char time)
{
while(time--)
{
unsigned char i = 0;
for(i = 0; i<123;i++);
}
}
void LCD_WriteCom(unsigned char com)
{
RS = 0;
RW = 0;
LCD_Data = com;
E = 1;
LCD_Delay(1);
E = 0;
LCD_Delay(1);
}
void LCD_WriteData(unsigned char Data)
{
RS = 1;
RW = 0;
LCD_Data = Data;
E = 1;
LCD_Delay(1);
E = 0;
LCD_Delay(1);
}
void LCD_Init(void)
{
LCD_WriteCom(0x38); //设置16x2显示,5x7点阵,8位数据接口
LCD_WriteCom(0x0C); // 0000 1100
LCD_WriteCom(0x06); //0000 0110
LCD_WriteCom(0x01); //清除 复位
}
void LCD_SetCursor(unsigned Line, unsigned char Column)
{
if(Line == 1)
{
LCD_WriteCom(0x80|(Column - 1));
}
else
{
LCD_WriteCom(0x80|(Column) + 0x40);
}
}
void LCD_ShowNum(unsigned char Line, unsigned char Column, unsigned char Num)
{
LCD_SetCursor(Line, Column);
LCD_WriteData('0' + Num);
}
void Uart_SendByte(unsigned char Byte)
{
SBUF = Byte;
while(TI == 0);
TI = 1;
}
//11.0592Mhz
void Uart_Init(void)
{
SCON = 0x50; //8位数据,可变波特率
PCON &= 0x7F; //不使能波特率倍速位
TMOD &= 0x0F; //清除定时器1模式
TMOD |= 0x20; //设定定时器1为8位自动重装
TL1 = 0xFD; //设定定时初值
TH1 = 0xFD; //设定定时器重装值
ET1 = 0; //不需要进中断
TR1 = 1; //启动定时器1
ES = 1; //串口中断
}
void Time_Init(void)
{
EA = 1;
TMOD = 0x01;
ET0 = 1;
TR0 = 1;
TH0 = 64535 / 256;
TL0 = 64535 % 256;
}
void main(void)
{
LCD_Init();
Time_Init();
Uart_Init();
while(1)
{
if(Addr == 17)
{
Addr = 0x41;
}
if(usart_send_flag1 == 1)
{
Uart_SendByte('1');
LCD_ShowNum(1, Addr++, 1);
usart_send_flag1 = 0;
}
if(usart_send_flag2 == 1)
{
Uart_SendByte('2');
LCD_ShowNum(1, Addr++, 2);
usart_send_flag2 = 0;
}
if(LCD_flag0 == 1)
{
LCD_ShowNum(1, Addr++, Num - '0');
LCD_flag0 = 0;
}
if(clean_flag0 == 1)
{
Addr = 1;
LCD_WriteCom(0x01);
clean_flag0 = 0;
}
}
}
void Time0_IT(void) interrupt 1
{
time0 ++;
time1 ++;
TH0 = 45535/256;
TL0 = 45535%256;
if(time0 == 25)
{
if(flag1 == 0)
{
flag1 = 1;
}
else if(flag1 == 1)
{
flag1 = 0;
}
time0 =0;
}
if(time1 == 1) //非阻塞按键扫描
{
S1_flag1 = S1_flag0; //保存上一次的值
S1_flag0 = S1; //获取新的值
S3_flag1 = S3_flag0; //保存上一次的值
S3_flag0 = S3; //获取新的值
S4_flag1 = S4_flag0; //保存上一次的值
S4_flag0 = S4; //获取新的值
if(S1_flag1 == 1 && S1_flag0 == 0)
{
clean_flag0 = 1;
}
if(S3_flag1 == 1 && S3_flag0 == 0)
{
usart_send_flag1 = 1;
}
if(S4_flag1 == 1 && S4_flag0 == 0)
{
usart_send_flag2 = 1;
}
time1 = 0;
}
}
void Uart_Read() interrupt 4
{
if(RI == 1)
{
Num = SBUF;
LCD_flag0 = 1;
RI = 0;
}
}