目录
2.每隔一秒发送字符串"C is YYDS"(demo2.c)
一、串口相关概念
1.什么是串口
串行接口(Serial Interface)简称串口,也称串行通信接口或串行通讯接口(通常指COM接口),是采用串行通信方式的扩展接口。(来源于百度)
✒️全双工(双向通信)
✒️数据一位一位传输
✒️设备间的通讯方式
2.串行接口分类(按电器标准及协议)
串行接口按电气标准及协议来分包括RS-232-C、RS-422、RS485等。RS-232-C、RS-422与RS-485标准只对接口的电气特性做出规定,不涉及接插件、电缆或协议。USB是近年来开发的新接口标准,主要用于高速数据传输领域。(来源于百度)
3.单片机常见的串口
📓标准UART接口
UART(Universal Asynchronous Receiver/Transmitter),异步串行接口(常说的串口)
📓I2C总线接口、SPI接口、USB接口等。
二、串口相关的寄存器
STC89C51RC/RD+系列单片机的串行口设有两个控制寄存器:串行控制寄存器SCON和波特率选择特殊功能寄存器PCON。(芯片手册)
1.串行控制寄存器SCON
(1)SM0/SM1
SCON的bit7和bit6,组合共同确认串口工作模式的控制位。
(同前面定时器的使用类似,使用串口前需要配置工作模式,根据芯片手册,串口工作模式的功能和波特率有关,而波特率的计算又和定时器1的溢出率和SMOD值相关。详见以下芯片手册所述)
✒️波特率相关计算
以9600的波特率为例,选择方式1的模式。如何确定TH1的初值?(SMOD取0波特率不加倍)
(2)REN
SCON的bit4,允许或禁止串口接收控制位(置1允许,置0禁止)
(3)TI和RI
TI | SCON的bit1 | 发送数据中断位 | 置1请求中断(硬件),响应中断后软件置0 |
RI | SCON的bit0 | 接收数据中断位 | 置1请求中断(硬件),响应中断后软件置0 |
(TI和RI均必须软件清0,否则将出现一次请求多次响应的情况,且中断处理函数中需判断是TI还是RI) |
(4)其他位:SM2、TB8、RB8
2.波特率选择特殊功能寄存器PCON
(1) SMOD
PCON的bit7,波特率选择位(置1串行通信方式1、2、3波特率加倍;置0各工作方式波特率不加倍 <这里芯片手册所述有误>)
(2)其他位(暂略)
3.串口数据缓冲寄存器SBUF
(见代码理解即可)
三、串口使用总结
1.配置串口工作模式
1)SM0/EF:SCON的bit6(置1——以工作模式1为例,也推荐使用模式1)
2)SM1:SCON的bit7(置0——以工作模式1为例,也推荐使用模式1)
2.配置定时器1(实质上,timer1这里作波特率发生器)
1)8位自动重载模式(TMOD的bit4置0,bit5置1)
2)允许位TR1
3)TH1和TL1(根据波特率计算得到)
3.配置串口接收数据位REN
1)SCON的bit4(置1允许接收数据)
4.配置串口中断
1)发生数据中断位TI(硬件置1,需软件置0)
2)接收数据中断位RI(硬件置1,需软件置0)
(注意在串口中断函数中判断是发送还是接收的中断)
四、串口编程
1.每隔一秒发送一个字符'a'(demo1.c)
#include "reg52.h"
#include <intrins.h>
void uartInit(){
SCON |= 0x01<<6; //串口工作模式(8位波特率可变--方式1)
SCON &= ~(0x01<<7);
//PCON |= 0x01<<7; //波特率选择位PCON的bit7(置1加倍,置0不加倍)
TMOD |= 0x01<<5; //定时器1(8位自动重载模式)
TMOD &= ~(0x01<<4);
TR1 = 1; //开启定时器(波特率发生器)
TH1 = 0xFD; //计数器初值(通过波特率计算得到)
TL1 = 0xFD;
}
void Delay1000ms() //软件延时1s{
unsigned char i, j, k;
_nop_();
i = 8;
j = 1;
k = 243;
do{
do{
while (--k);
} while (--j);
} while (--i);
}
void main(void){
uartInit();
while(1){
SBUF = 'a';
Delay1000ms();
}
}
📓字符'a'是如何从单片机串口传输到PC上的?
串口工作模式1,一帧数据包括起始位(0)、数据位、停止位(1);字符a的ASSII码是97,转二进制0101 0001,那么这8位就是数据位。加上起始位和停止位,字符a的一帧数据就是
0 01010001 1。一次从低位到高位发送和接收。
2.每隔一秒发送字符串"C is YYDS"(demo2.c)
🔖先看以下这段实现发送字符串的代码
🔖使用以上代码发送字符串时,会发现sendString( )调用sendChar( )时候,由于中间执行的时间差,会导致字符串“Hello World”不能正常发送到PC。虽然在sendChar( )中加入10ms的延时会较好地显示,但也有明显的不流畅。此处,利用发送数据中断位TI来处理更好。 | |
🔖所以,我们有必要先了解串口中断再完成demo2的代码 |
★再识中断—串口中断
这里已经是第三次学习到中断系统,同前面(定时器中断、外部中断)一样,从中断触发行为、中断寄存器、中断优先级来了解使用串口中断。
(1)串口中断触发行为
(芯片手册图) | 触发行为:数据发送或接收完成 |
(2)串口中断寄存器
🔖ES(IE的bit4):串口中断允许位(1允许;0禁止)
(3)串口中断优先级
(4)使用注意事项
✒️在SCON寄存器中,有一个发送数据中断位(TI(SCON的bit1)),请求中断时它由硬件置1,响应中断后需要我们手动置0。还有一个接收数据中断位RI,注意将REN(SCON的bit4)置1才可接收数据。
📓使用中断修改后的代码
#include "reg52.h"
#include <intrins.h>
void uartInit(void){
SCON &= ~(0x01<<7);
SCON |= 0x01<<6;
TMOD |= 0x01<<5;
TMOD &= ~(0x01<<4);
TR1 = 1;
TH1 = 0xFD;
TL1 = 0xFD;
EA = 1;
ES = 1;
}
void Delay1000ms(){
unsigned char i, j, k;
i = 8;
j = 1;
k = 243;
do{
do{
while (--k);
} while (--j);
} while (--i);
}
void sendChar(char datas){
SBUF = datas;
while(!TI);
TI = 0;
}
void sendString(char *datas){
while(*datas != '\0'){
sendChar(*datas);
datas++;
}
}
void main(void){
uartInit();
while(1){
sendString("Hello World\t\n");
Delay1000ms();
}
}
3. 全双工收发数据(demo3.c)
每隔一秒发送字符串"C is YYDS";当接收到指令o,点亮led;指令c,熄灭led。
#include "reg52.h"
#include <intrins.h>
sbit led1 = P3^7;
sfr AUXR = 0x8E;
char cmd;
void uartInit(void){
AUXR = 0x01;
SCON &= ~(0x01<<7);
SCON |= 0x01<<6;
TMOD &= ~(0x01<<4);
TMOD |= 0x01<<5;
TR1 = 1;
TH1 = 0xFD;
TL1 = 0xFD;
EA = 1;
ES = 1;
REN = 1;
}
void Delay1500ms() //@11.0592MHz
{
unsigned char i, j, k;
_nop_();
i = 11;
j = 130;
k = 111;
do{
do{
while (--k);
} while (--j);
} while (--i);
}
void sendString(char *datas){
while(*datas != '\0'){
SBUF = *datas;
while(!TI);
TI = 0;
datas++;
}
}
void main(void){
uartInit();
led1 = 1;
while(1){
Delay1500ms();
sendString("C is YYDS\t\n");
}
}
void interruptHandle() interrupt 4{
if(RI == 1){
RI = 0;
cmd = SBUF;
if(cmd == 'o'){
led1 = 0;
}
if(cmd == 'c'){
led1 = 1;
}
}
}
说明:由于笔者水平有限,文中不可避免有所错漏,敬请各读者斧正