单片机蓝桥杯——串口通信
1、什么是串行、并行、单工、全双工、半双工、同步、异步
通讯的方式分类:并行通信 串行通信
并行通信:数据的各位同时在多根数据线上发送或接收。
串行通信:数据的各位在同一根数据线上逐位发送和接收7
并行通信的特点:控制简单,传输速度快;由于传输线较多,适用于短距离通信。
串行通信的特点:控制复杂,传输速度慢;只需要一根数据线,适用于远距离通信。
根据串行通信中对数据流的分界、定时以及同步方案方法不同,可分为和同步和异步。
同步方式:发送端和接收端必须使用同一时钟,是一种连续传送数据的通信方式,一次通讯传送多个字符数据(一帧数据)
异步方式:发送和接收端使用的是各自的时钟,是一种不连续传送数据的通信方式,一次通信只能传输一个字符数据(字符帧)。字符帧之间的间隙可以是任意的
根据串行数据的传输方向,我们可以将通信分为单工,半双工,双工。
单工:信道是单向的,数据只能单方面传输,发送端只能发送数据,不能接收;接收端只能接收数据,不能发送;
半双工:数据可以进行双向传输,但不能在两个方向上同时进行。
全双工:数据可以同时进行双向传输。
---------------------------------------------------------------------------------------------------------------------------------
2、串口通信中需要用到的寄存器SCON——串行通信控制寄存器(一般设置为0x50)
B7 | B6 | B5 | B4 | B3 | B2 | B1 | B0 |
SM0 | SM1 | SM2 | REN | TB8 | RB8 | TI | RI |
SM0、SM1 工作方式:
SM2:多机通信控制位。方式2、方式3的时候才使用。
工作于方式2和方式3时,当SM2=1时,只有当接收到第9位数据(RB8)为1时,才把接收到的前8位数据送入SBUF,发出中断申请,否则会将接受到的数据放弃。当SM2=0时,就不管第位数据是0还是1,都将数据送入SBUF,并发出中断申请。
REN=1允许接收;REN=0禁止接收。
TB8:接收数据的第8位
RB8:接收数据的第8位
TI:串口发送完成中断标志位。由硬件自动置1,需要软件清“0”。
RI:串口接收完成中断标志位。由硬件自动置1,需要软件清“0”。
---------------------------------------------------------------------------------------------------------------------------------
3、代码思路
串口初始化设置
(1)设置串口工作方式,一般为异步8位UART并且允许接收(REN=1),即SCON=0x50
(2)设置定时器T1工作在方式2,8位自动重装,可用作波特率发生器
(3)通过给TH1、TL1赋不同值,设置波特率,其数值可查表找到
(4)打开中断总开关 EA=1;
(5)允许串口中断 ES=1;
(6)启动T1 TR1=1;
(7)此外还需要设置AUXR,用于设置T1时钟,为1T还是12T。注意如果用定时器2作为波特率发生器,则有关T2的设置在AUXR寄存器
比赛中,有关AUXR的设置都可以在STC-ISP中自动生成。如用定时器2作为波特率发生器,波特率为4800bps,数据位为8位,无校验位:
中断服务子程序
通过SBUF寄存器发送接收数据,发送数据时,将所要发送的数据给SBUF;接收数据时,直接从SBUF中读取数据
4、例题
功能1:上位机发送数据到单片机,单片机再将数据传送给上位机
代码实现:
#include"reg51.h"
#include"intrins.h"
sfr AUXR=0x8e;
unsigned char Rdat;
//***************串口初始化设置******************************************
void Init_Uart()
{
AUXR=0x00;
SCON=0x50; //0101 0000;工作在方式1,异步8位UART并且允许接收 即REN=1
TMOD=0x20; //设置定时器T1工作在方式2,8位自动重装,可用作波特率发生器
TH1=0xfd; //设置波特率为9600kbps,数值可查表找到
TL1=0xfd; //
EA=1; //打开中断总开关
ES=1; // 允许串口中断
TR1=1; //启动T0
}
//***************发送一个字节******************************************
void SendByte(unsigned char dat)
{
SBUF=dat;
while(TI==0);
TI=0;
}
//***************串口中断服务子程序******************************************
void ServiceUart() interrupt 4
{
if(RI==1)//如果接收完成
{
Rdat=SBUF;//Rdat为从上位机接收到的数据
RI=0;
SendByte(Rdat);//再将收到的数据再发送到上位机
}
}
//***************主函数**************************************************
void main()
{
Init_Uart();//调用串口初始化设置函数
while(1);
}
现象:
功能二:通过上位机发送数据到单片机,进而控制led,低电平点亮
代码实现:
#include"reg51.h"
#include"intrins.h"
sfr AUXR=0x8e;
unsigned char Rdat;
void SelectHc573(unsigned char n)
{
switch(n)
{
case 4://Y4有效,P0直接控制LED
P2=(P2 & 0x1f) | 0x80;
break;
case 5://Y5有效,P0控制蜂鸣器、继电器
P2=(P2 & 0x1f) | 0xa0;
break;
case 6://Y6有效,P0控制数码管位选
P2=(P2 & 0x1f) | 0xc0;
break;
case 7://Y7有效,P0控制数码管段选
P2=(P2 & 0x1f) | 0xe0;
break;
case 0:
P2=(P2 & 0x1f) | 0x00;
break;
}
}
//***************初始化系统******************************************
void InitSystem()
{
SelectHc573(5);//关闭蜂鸣器继电器
P0=0x00;
SelectHc573(4);//设置led初始状态为全灭
P0=0xff;
}
//***************串口初始化设置******************************************
void Init_Uart()
{
AUXR=0x00;
SCON=0x50; //0101 0000;工作在方式1,异步8位UART并且允许接收 即REN=1
TMOD=0x20; //设置定时器T1工作在方式2,8位自动重装,可用作波特率发生器
TH1=0xfd; //设置波特率为9600kbps,数值可查表找到
TL1=0xfd; //
EA=1; //打开中断总开关
ES=1; // 允许串口中断
TR1=1; //启动T0
}
//***************发送一个字节******************************************
void SendByte(unsigned char dat)
{
SBUF=dat;
while(TI==0);
TI=0;
}
//***************发送一个字符串******************************************
void SendString(unsigned char *str)
{
while(*str !='')
SendByte(*str++);
}
//***************串口中断服务子程序******************************************
void ServiceUart() interrupt 4
{
if(RI==1)
{
Rdat=SBUF;//从上位机接收到的数据
RI=0;
SelectHc573(4);
P0=Rdat;
SendByte(Rdat);
}
}
//***************主函数**************************************************
void main()
{
InitSystem();
Init_Uart();
SendString("Welcome to Serial port!rn");
while(1);
}
现象:
程序开始时先发送一个字符串,注意此时应为文本模式接收:
一个字节的接收应为hex模式,通过电脑发送数据给单片机,去控制led:
第十届国赛例题
代码:
#include"stc15f2k60s2.h"
#include"intrins.h"
#include <stdio.h>
#include <string.h>
unsigned char Recv[8]; //从串口接收的数据
unsigned char order1[8]="STrn"; //命令1:查询数据指令:”STrn"
unsigned char order2[8]="PARArn"; //命令2:查询参数指令:"PARArn"
bit flag=0; //串口接收完成标志位,若一组数据接收完成 置1
unsigned int dis=20; //距离
unsigned int temp1=24; //温度(整数部分)
unsigned int temp2=32; //温度(小数部分)
unsigned int disParm=35; //距离参数
unsigned int tempParm=30; //温度参数
void SelectHc573(unsigned char val)
{
switch(val)
{
case 4://Y4有效,P0直接控制LED
P2=(P2 & 0x1f) | 0x80;
break;
case 5://Y5有效,P0控制蜂鸣器、继电器
P2=(P2 & 0x1f) | 0xa0;
break;
case 6://Y6有效,P0控制数码管位选
P2=(P2 & 0x1f) | 0xc0;
break;
case 7://Y7有效,P0控制数码管段选
P2=(P2 & 0x1f) | 0xe0;
break;
case 0:
P2=(P2 & 0x1f) | 0x00;
break;
}
}
//***************初始化系统******************************************
void InitSystem()
{
SelectHc573(5);//关闭蜂鸣器继电器
P0=0x00;
SelectHc573(4);//设置led初始状态为全灭
P0=0xff;
}
//***************串口初始化设置******************************************
void UartInit(void) //4800bps@12MHz
{
SCON = 0x50; //8位数据,可变波特率
AUXR &= 0xBF; //定时器时钟12T模式
AUXR &= 0xFE; //串口1选择定时器1为波特率发生器
TMOD &= 0x0F; //设置定时器模式
TL1 = 0xCC; //设置定时初始值
TH1 = 0xFF; //设置定时初始值
ET1 = 0; //禁止定时器%d中断
TR1 = 1; //定时器1开始计时
EA=1; //打开中断总开关
ES=1; // 允许串口中断
}
//***************发送一个字节******************************************
void SendByte(unsigned char dat)
{
SBUF=dat;
while(TI==0);
TI=0;
}
//***************发送一个字符串******************************************
void SendString(unsigned char *str)
{
while(*str !='')
SendByte(*str++);
}
//重定向,否则无法使用printf函数
char putchar(char ch)
{
SendByte(ch);
return ch;
}
//***************串口中断服务子程序******************************************
unsigned char Rdat;
unsigned char Rcnt=0;
void ServiceUart() interrupt 4
{
if(RI==1)
{
RI=0;
Rdat=SBUF;//从上位机接收到的数据
if(Rdat != 'n')
{
Recv[Rcnt] = Rdat;
Rcnt++;
}
else
{
Recv[Rcnt] = Rdat;
Rcnt=0;
flag=1;
}
}
}
//***************主函数**************************************************
void main()
{
unsigned char ret1,ret2,i;
InitSystem();
UartInit();
SendString("hellorn");
while(1)
{
if(flag==1)
{
flag=0;
ret1=strcmp(order1,Recv); //命令1比较的返回值
ret2=strcmp(order2,Recv); //命令2比较的返回值
if(ret1==0)
printf("$%d,%d.%drn",dis,temp1,temp2);
else if(ret2==0)
printf("#%d,%drn",disParm,tempParm);
else
printf("ERRORrn");
for(i=0;i<8;i++) //清空接收数组
{
Recv[i]='';
}
}
}
}
结果: