赞
踩
51单片机串口通信原理及代码,实现单片机与PC的交互
数据传输方式分类:
数据同步方式分类
数据传输方向分类
通信速率
位 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | |
---|---|---|---|---|---|---|---|---|---|
字节地址:98H | SM0 | SM1 | SM2 | REN | TB8 | RB8 | TI | RI | SCON |
REN: 允许串行接收位。
R
E
N
=
1
REN=1
REN=1启用串口接收数据,反之禁止。
RB8: 在方式2或3中(见下表
),为接收
数据的第9位,作为奇偶校验位或地址帧/数据帧的标志位。在方式1时,若
S
M
2
=
0
SM2=0
SM2=0,则RB8是接收到的停止位。
RI: 接收中断标志位。
在方式0中,接收到第8位数据结束时,在其他方式中,接收到停止位时,硬件置1,发出中断申请,须在中断程序中软件清0,取消此中断。
SM2: 多机通信控制位,主要用于方式2和3(见下表)。
当
S
M
2
=
1
SM2=1
SM2=1时,启用多机通信;接收
的第九位数据赋值给RB8,当
R
B
8
=
0
RB8=0
RB8=0,不激活RI(即
R
I
=
0
RI=0
RI=0),收到的数据丢弃;当
R
B
8
=
1
RB8=1
RB8=1,激活RI(即
R
I
RI
RI硬件置1),产生中断从SBUF中读取数据。当
S
M
2
=
0
SM2=0
SM2=0,不管RB8为0或1,均可读取其中数据。
TB8: 方式0和1中未用到。在方式2和3中,是发送
数据的第9位,可以软件定义其作用,作为校验位或标志位。
TI: 发送中断标志位。
在方式0中,发送第8位数据结束时,在其他方式中,发送停止位时,硬件置1,发出中断申请,须在中断程序中软件清0,取消此中断。
SM0和SM1为工作方式选择位
SM0 | SM1 | 方式 | 说明 | 波特率 |
---|---|---|---|---|
0 | 0 | 0 | 移位寄存器 | f o s c / 12 f_{osc}/12 fosc/12 |
0 | 1 | 1 | 10位异步收发器(8位数据) | 可变 |
1 | 0 | 2 | 11位一步收发器(9位数据) | f o s c / 64 f_{osc}/64 fosc/64或 f o s c / 32 f_{osc}/32 fosc/32 |
1 | 1 | 3 | 11位异步收发器(9位数据) | 可变 |
f o s c f_{osc} fosc为外部晶振频率
位 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | |
---|---|---|---|---|---|---|---|---|---|
字节地址:97H | SMOD | PCON |
SMOD: 波特率倍增位。
在串口方式1、2、3(见工作方式选择表)时,波特率和SMOD有关。
S
M
O
D
=
1
SMOD=1
SMOD=1,波特率提高一倍,复位时置0。
如何理解波特率,及其倍增位,以全双工异步通信为例,如下图所示。
图中结构可以看出为全双工通信,如果为异步通信(即接收端与发送端双方的时钟由各自控制
),为了保证设备正常工作,需要尽可能让双方时钟保持一致;如果为同步通信,则发送端除了控制自己的时钟,同时还要控制接收端的时钟,使双方时钟保持一致。
举例说明,当发送端与接收端同步时(即双方波特率比值为1:1),发送端发送一个a,接收端接收一个a。
当发送端与接收端时钟不同步时(例如双方波特率比值为2:1)。发送端发送一个a,接收端接收到两个a。
这就是双方时钟不一致(即波特率不同)产生的现象。
无论同步通信还是异步通信都要使双方时钟保持一致,区别只在于同步时钟是由谁控制。至于为什么要进行波特率倍增,大概可能估计是由于硬件限制,考虑到最大波特率也无法进行正常通信的情况。
接收到的数据和需要发送的数据存储在两段相互独立的内存中,但两段内存对外都命名为SBUF,因此对发送数据和接收数据的处理只需对SBUF处理即可。
无论是工作在哪种工作模式,在输入数据到单片机时,均须使SCON寄存器中的REN置1。
串口为同步移位寄存器的输入输出方式。主要用于扩展并行输入或输出口。数据由RXD(P3.0)引脚输入或输出,同步位移脉冲由TXD
(P3.1)引脚输出。例如外部设备须并行输出数据到单片机,但单片机没有多余的引脚,则可以使用该引脚通信功能,与74HC595扩展的区别在于需要使用TXD引脚同步双方时钟。
发送和接收均为8位数据,从低至高位依次发送。
先将数据写入SBUF,从低至高依次输出数据,在第八位结束时,硬件置TI=1(详见SCON中TI的描述)
,产生中断结束发送数据,后续还需软件置0。
输入前先将输入中断RI软件置0,输入完毕后RI硬件置1
(详见SCON中RI描述),产生中断,结束输入。
一帧数据由1位起始位,8位数据位,1位停止位组成。
接收到停止位时,TI硬件置1
(详见SCON中TI描述),产生中断,结束输出。
接收到起始位开始输入,至接收到停止位时,前八数据为存入SBUF,停止位进入RB8,RI硬件置1,产生中断,结束输入。
上述为SM2=1,多机通信中的场景。在SM2=0,单机通信中,接收到停止位即产生中断,结束输入。
数据格式
当输出时,数据第九位为TB8,为输出的标志位或校验位。
当输入时,数据第九位位RB8,为输入的标志位或校验位。
如上图所示,当移位寄存器检测到上种情况时,TI硬件置1,产生中断终止输出。
波特率计算使用提供的波特率计算软件即可。电路图不太能看懂就省略了。
功能:
这是简单的设备之间的通信,后续单片机如果有合适的显示设备,或许会完善使用按键矩阵和独立按键实现拼音9键,实现单片机和PC的英文交互。
#ifndef _DELAY_H__
#define _DELAY_H__
/*10us=1,时延为10us*/
void delay_10us(unsigned int _10us){
while(_10us--){}
}
/*1ms=1,时延为1ms*/
void delay_1ms(unsigned int _1ms){
_1ms*=110;
while(_1ms--){
}
}
#endif
#ifndef _INTERRUPT_H_ #define _INTERRUPT_H_ #include "reg52.h" /*中断初始化*/ /*外部中断0*/ void Int0_Init(const unsigned char *mode){//外部中断0的触发方式,0低电平触发,1下降沿触发 EA=1;//总中断允许位 EX0=1;//外部中断0允许位 IT0=*mode; } /*外部中断1*/ void Int1_Init(const unsigned char *mode){//外部中断1的触发方式,0低电平触发,1下降沿触发 EA=1;//总中断允许位 EX1=1;//外部中断0允许位 IT1=*mode; } void Timer0_Init(const unsigned char *mode ,const unsigned char *HighVal , const unsigned char *LowVal ){ EA=1;//打开中断总允许位 ET0=1;//打开T0中断允许位 TR0=1;//定时/计数器中断0开始工作 TMOD |= *mode;//设置T0工作模式 TH0 = *HighVal; //高八位寄存器初值 TL0 = *LowVal; //第八位寄存器初值 } void Timer1_Init(const unsigned char *mode ,const unsigned char *HighVal , const unsigned char *LowVal ){ EA=1;//打开中断总允许位 ET1=1;//打开T0中断允许位 TR1=1;//定时/计数器中断0开始工作 TMOD |= *mode;//设置T0工作模式 TH1 = *HighVal; //高八位寄存器初值 TL1 = *LowVal; //第八位寄存器初值 } #endif
#ifndef __UART_INIT_H__ #define __UART_INIT_H__ #include "interrupt_utils.h" unsigned char data_buffer[10];//存储接收的数据 unsigned char Send_buffer[10];//要发送的数据 unsigned char Num=0; unsigned char Send_Num=0; /*设置串口寄存器参数 1.设置串口寄存器 2.设置波特率参数 */ void Set_Uart( unsigned char *Uart_Reg_mode , unsigned char *Baud_mode){ SCON = *Uart_Reg_mode; PCON |= *Baud_mode; } /*串口初始化函数*/ void Uart_Init(unsigned char T1_Mode , unsigned char Uart_Reg_mode , unsigned char Baud_mode , unsigned char Init_Val ){ Set_Uart(&Uart_Reg_mode, &Baud_mode); Timer1_Init(&T1_Mode ,&Init_Val , &Init_Val ); ES=1; //打开串口中断允许位 ET1=0;//关闭定时器中断1的允许位 } /*串口发送一个字符*/ void Uart_Send_byte(unsigned char dat){ SBUF = dat; while(!TI);//发出中断,TI硬件置1,跳出循环 TI=0;//重置,取消本次中断 } /*串口发送一串字符*/ void Uart_Send_String(){ unsigned char i; for(i=0 ; i!=Send_Num;++i){ Uart_Send_byte(Send_buffer[i]); } Send_Num=0; } /*串口接受数据(不超过10位)*/ void UART_Routine( ) interrupt 4{ RI=0; //将接收中断标志清0 if(Num>9){ //超出缓存大小,覆盖从头开始存储 Num=0; } data_buffer[Num++] = SBUF; /*由于单片机暂无合适的显示设备,将接收到的数据返回给电脑*/ Uart_Send_byte(data_buffer[Num-1]); } #endif
#ifndef __SCAN_BUTTON_H__ #define __SCAN_BUTTON_H__ /*扫描矩阵按键*/ #include "reg52.h" #include "delay.h" typedef unsigned int uint; typedef unsigned char uchar; sbit line0 = P1^7; sbit line1 = P1^6; sbit line2 = P1^5; sbit line3 = P1^4; sbit col0 = P1^3; sbit col1 = P1^2; sbit col2 = P1^1; sbit col3 = P1^0; char Table16[4][4] = {{'a','b','c','d'}, {'e','f','g','h'}, {'i','j','k','l'}, {'m','n','o','p'} } ; /*确定行*/ uint get_line(void){ uint retVal=4; col0=0; col1=0; col2=0; col3=0; if(!line0) retVal=0; if(!line1) retVal=1; if(!line2) retVal=2; if(!line3) retVal=3; col0=1; col1=1; col2=1; col3=1; return retVal; } /*确定列*/ uint get_col(void){ uint retVal=4; line0=0; line1=0; line2=0; line3=0; if(!col0) retVal=0; if(!col1) retVal=1; if(!col2) retVal=2; if(!col3) retVal=3; line0=1; line1=1; line2=1; line3=1; return retVal; } /*监测按键,并返回按键值*/ char Scan_Button(){ uint row; uint col; delay_10us(6000); row = get_line(); col = get_col(); delay_10us(6000); if(row!=4&&col!=4) return Table16[row][col]; return 0; } #endif
#include "Uart_Init.h" #include "Scan_Button.h" /* 1.由于暂时无合适的显示设备,所以将单片机接收到的信息返回给电脑显示。 2.可以通过矩阵键盘输入信息,按独立按键k3进行发送 */ void main(){ Int0_Init(0);//外部中断0初始化 Uart_Init(0x20,0x50,0x80,0xFA);//串口中断初始化 while(1){ char cha; cha=Scan_Button(); if(cha) Send_buffer[Send_Num++] = cha; } } void Int0_Rountine() interrupt 0{ //按键矩阵输入完毕,按独立按键K3中断发送 Uart_Send_String(); }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。