通信的三种基本类型

常用的通信从传输方向上可以分为单工通信半双工通信全双工通信三类。

单工通信就是指只允许一方向另外一方传送信息,而另一方不能回传信息。比如电视遥控器、收音机广播等,都是单工通信技术。

半双工通信是指数据可以在双方之间相互传播,但是同一时刻只能其中一方发给另外一方,比如我们的对讲机就是典型的半双工。

全双工通信就发送数据的同时也能够接收数据,两者同步进行,就如同我们的电话一样,我们说话的同时也可以听到对方的声音。

UART模块介绍

51单片机的UART串口的结构由串行口控制寄存器SCON、发送和接收电路三部分组成。

串口控制寄存器SCON如下表所示。

对于串口的四种模式,模式1是最常用的,也就是1位起始位,8位数据位和1位停止位。下面就详细介绍模式1。

在硬件串口模块中,有一个专门的波特率发生器用来控制发送和接收数据的速度。对于STC89C52单片机来讲,这个波特率发生器只能由定时器T1和定时器T2产生,而不能由定时器T0产生。

如果使用定时器2,需要配置额外的寄存器,默认是使用定时器1的,我们这里只讨论定时器T1,方式1下的波特率发生器必须使用定时器T1的模式2,也就是自动重装载模式。

定时器的重载值计算公式为TH1 = TL1 = 256 - 晶振值 / 12 / 2 / 16 / 波特率,和波特率有关的还有一个寄存器,是一个电源管理寄存器 PCON,他的最高位可以把波特率提高一倍,也就是如果写 PCON |= 0x80 以后,计算公式就成了:TH1 = TL1 = 256 - 晶振值 / 12 / 16 / 波特率

这里解释一下公式,256是8位定时器的溢出值,也就是TL1的溢出值,晶振值即板子上的晶振频率(普中51开发板即11059200),12是说1个机器周期等于12个时钟周期,这个16是一个分频值,记住就行。

串口通信的发送和接收电路在物理上有 2 个名字相同的 SBUF 寄存器,它们的地址也都是 0x99,但是一个用来做发送缓冲,一个用来做接收缓冲。意思就是说,有 2 个房间,两个房间的门牌号是一样的,其中一个只出人不进人,另外一个只进人不出人,这样的话,我们就可以实现 UART 的全双工通信,相互之间不会产生干扰。但是在逻辑上呢,我们每次只操作 SBUF,单片机会自动根据对它执行的是“读”还是“写”操作来选择是接收 SBUF 还是发送 SBUF,后边通过程序,我们就会彻底了解这个问题。

UART 串口程序

一般情况下,我们编写串口程序的基本步骤如下所示:

  1. 配置串口为模式1.
  2. 配置定时器T1为模式2,即自动重装载模式。
  3. 根据波特率计算TH1和TL1的初值,如果有需要可以使用PCON进行波特率加倍。
  4. 打开定时器控制寄存器TR1,让定时器跑起来。

当然,这个时候不能再使能T1中断了。

下面我们的程序实现的是由电脑发送一个字符给我们的普中51开发板,再由开发板+1后发送给电脑。

#include <REG52.H>

void configUART(unsigned int baud);

void main()
{
    configUART(9600); // 配置波特率为9600

    while (1)
    {
        while (!RI) // 等待接受完毕
            ;
        RI = 0;          // 软件清零接收中断标志位
        SBUF = SBUF + 1; // 接收到的数据+1后发送回去
        while (!TI)      // 等待发送完毕
            ;
        TI = 0; // 软件清零发送中断标志位
    }
}

void configUART(unsigned int baud)
{
    SCON = 0x50;                               // 配置串口为模式1,并且使能串行接收
    TMOD &= 0x0F;                              // 清零T1的控制位
    TMOD |= 0x20;                              // 配置T1为模式2
    TH1 = 256 - 11059200 / 12 / 2 / 16 / baud; // 计算T1重载值
    TL1 = TH1;                                 // 初值等同于重载值
    TR1 = 1;                                   // 启动T1
}

当然,我们在实际工程中要使用串口中断来判断中断的类型,而不是在主程序中。

#include <REG52.H>

void configUART(unsigned int baud);

void main()
{
    configUART(9600);

    while (1)
        ;
}

void configUART(unsigned int baud)
{
    EA = 1;
    SCON = 0x50;
    TMOD &= 0x0F;
    TMOD |= 0x20;
    TH1 = 256 - 11059200 / 12 / 2 / 16 / baud;
    TL1 = TH1;
    ES = 1; // 使能串口中断
    TR1 = 1;
}

void interruptTimer1() interrupt 4
{
    if (RI)
    {
        RI = 0;
        SBUF = SBUF + 1;
    }
    if (TI)
    {
        TI = 0;
    }
}

下面我们就做一个简单的例程,实现单片机串口调试助手发送的数据,在我们开发板上的数码管上显示出来。

#include <REG52.H>

#define LED P0

unsigned char code LedChar[] = {
    0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8,
    0x80, 0x90, 0x88, 0x83, 0xC6, 0xA1, 0x86, 0x8E};

unsigned char ledBuf[8] = {
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};

unsigned char T0RH = 0;
unsigned char T0RL = 0;
unsigned char rxdByte = 0;

void configTimer0(unsigned int ms);
void configUART(unsigned int baud);

void main()
{
    EA = 1;
    configTimer0(1);
    configUART(9600);

    while (1)
    { // 将接收字节在数码管上以十六进制形式显示出来
        ledBuf[0] = LedChar[rxdByte & 0x0F];
        ledBuf[1] = LedChar[rxdByte >> 4];
    }
}
/*配置并启动T0即T0中断,ms为T0定时时间*/
void configTimer0(unsigned int ms)
{
    unsigned long temp = 0;

    temp = 11059200 / 12;
    temp = (temp * ms) / 1000;
    temp = 65536 - temp;
    temp = temp + 13;                  // 补偿中断响应延时造成的误差
    T0RH = (unsigned char)(temp >> 8); // 定时器重载值拆分为高低字节
    T0RL = (unsigned char)temp;
    TMOD &= 0xF0;
    TMOD |= 0x01;
    TH0 = T0RH;
    TL0 = T0RL;
    ET0 = 1;
    TR0 = 1;
}
/*串口配置函数,baud为通信波特率*/
void configUART(unsigned int baud)
{
    SCON = 0x50;
    TMOD &= 0x0F;
    TMOD |= 0x20;
    TH1 = 256 - 11059200 / 12 / 2 / 16 / baud;
    TL1 = TH1;
    ES = 1; // 使能串口中断
    TR1 = 1;
}

void ledScan()
{
    static unsigned char i = 0;

    LED = 0x00;
    P2 = (P2 & 0xE3) | (i << 2);
    LED = ~ledBuf[i];
    if (i <= 6)
    {
        i++;
    }
    else
    {
        i = 0;
    }
}

void interruptTimer0() interrupt 1
{
    TH0 = T0RH;
    TL0 = T0RL;
    ledScan();
}

void interruptTimer1() interrupt 4
{
    if (RI)
    {
        RI = 0;
        rxdByte = SBUF;
        SBUF = rxdByte;
    }
    if (TI)
    {
        TI = 0;
    }
}
最后修改:2024 年 02 月 02 日
如果觉得我的文章对你有用,请随意赞赏