Skip to content

UART串口通信

串口通信

串口通信简介

串口通信(Serial Communication)是一种用于计算机和其他设备之间进行数据传输的通信方式。串口通信使用串行数据流,通过一条数据线进行数据传输,可以实现数据的同步传输。

串口通信最简单使用方法

参考数据手册 的第 13 章(第457页),串口通信。

一.使用定时器1的8位自动重载模式

此模式下9600波特率是可以正常工作的,115200工作不太正常

1. 初始化串口(使用定时器1的8位自动重载模式)

c

#define FOSC 35000000L      // 定义主时钟频率为35MHz
#define BAUD 9600         // 目标波特率

// 定时器1重载值计算(1T模式,8位自动重装模式)
#define TIMER1_RELOAD (256 - (FOSC / 32 / BAUD))


void Uart_Init(void) {
    // 设置串口引脚模式(P3.0为RX准双向,P3.1为TX强推挽)
    P3M0 = 0x02;  // P3.1(TX)设置为强推挽输出
    P3M1 = 0x00;  // 其他引脚默认准双向
    // 配置串口控制寄存器(SCON)
    SCON = 0x50;   // 串口模式1(8位数据,可变波特率),允许接收
    AUXR |= 0x40;  // 定时器1为1T模式(AUXR.6=1)
    AUXR &= 0xFE;  // 串口1选择定时器1为波特率发生器(AUXR.0=0)

    // 配置定时器1(定时器1为1T模式)
    TMOD &= 0x0F;   // 清除定时器1模式位
    TMOD |= 0x20;   // 定时器1模式2(8位自动重装)
    TH1 = TL1 = TIMER1_RELOAD;  // 设置重载值
    TR1 = 1;        // 启动定时器1

    // 配置中断
    ES = 1;         // 允许串口中断(可选)
    EA = 1;         // 开启总中断(可选)


}

需要说明:

  • 串口初始化时,需要设置串口引脚模式,包括RX和TX的模式。
  • 配置串口控制寄存器(SCON),设置串口模式和使能接收。
  • 串口发送数据,需要一个波特率,这个波特率是由定时器来控制的. 通过 AUXR 来选择定时器1为波特率发生器。
  • 配置定时器的工作模式: TMOD 寄存器的相关位来设置定时器的工作模式。
  • 配置定时器的重载值: TH1TL1 寄存器来设置定时器的重载值。
  • 启动定时器: TR1 寄存器的相关位来启动定时器。
  • 配置中断: ES 寄存器的相关位来使能串口中断(可选),EA 寄存器的相关位来开启总中断(可选)。

以上的每一步都是必需的,否则串口通信将无法正常工作。

2. 发送数据

c


void UartSendByte(unsigned char dat) {
    SBUF = dat;            // 发送当前数据
    while(!(TI));          // 等待数据发送完成
    TI = 0;                // 清除发送完成标志
}

需要说明:

  • 发送数据时,需要将要发送的数据写入到 SBUF 寄存器中。只要 SBUF 寄存器的值不为空,就会自动发送。
  • 等待前一个数据发送完成: 可以通过检查 TI 寄存器的状态来等待前一个数据发送完成。
  • 清除发送完成标志: 发送完成后,需要手动清除 TI 寄存器的状态。

二.使用定时器1的16位自动重载模式

这种模式下,波特率9600或是115200,都是可以正常工作的。

1. 初始化串口(使用定时器1的16位自动重载模式)

c
#define FOSC 35000000L      // 定义主时钟频率为35MHz(烧录时,必须选择35MHz的晶振)
#define BAUD 115200         // 目标波特率
#define BRT (65536 - (FOSC / 4 / BAUD))// 波特率重装值(p473)


void Uart_Init(void) {
    // 设置串口引脚模式(P3.0为RX准双向,P3.1为TX强推挽)
    P3M0 = 0x02;  // P3.1(TX)设置为强推挽输出
    P3M1 = 0x00;  // 其他引脚默认准双向

    // 关于串口的配置
    SCON = 0x50;           // 8位数据,可变波特率(p469)
    AUXR &= 0xBE;
    AUXR |= 0x40;          // 串口1选择定时器1为波特率发生器(p470)


    //关于定时器2的配置(p376)
    TMOD &= 0x0F;   // 清除定时器1模式位
    TMOD |= 0x00;   // 定时器1模式0(16位自动重装)
    TL1 = BRT;             // 设置定时初始值
    TH1 = BRT >> 8;        // 设置定时初始值
    TR1 = 1;        // 启动定时器1


    ES = 1;         // 允许串口中断(可选)
    EA = 1;         // 开启总中断(可选)

}

需要说明:

  • 串口初始化时,需要设置串口引脚模式,包括RX和TX的模式。
  • 配置串口控制寄存器(SCON),设置串口模式和使能接收。
  • 串口发送数据,需要一个波特率,这个波特率是由定时器来控制的. 通过 AUXR 来选择定时器1为波特率发生器。
  • 配置定时器的工作模式: TMOD 寄存器的相关位来设置定时器的工作模式。
  • 配置定时器的重载值: TH1TL1 寄存器来设置定时器的重载值。
  • 启动定时器: TR1 寄存器的相关位来启动定时器。
  • 配置中断: ES 寄存器的相关位来使能串口中断(可选),EA 寄存器的相关位来开启总中断(可选)。 以上的每一步都是必需的,否则串口通信将无法正常工作。

2. 发送数据

c

void UartSendByte(unsigned char dat) {
    SBUF = dat;            // 发送当前数据
    while(!(TI));          // 等待前一个数据发送完成
    TI = 0;                // 清除发送完成标志
}

void UartSendStr(char *s) {
    while(*s) {            // 检测字符串结束符
        UartSendByte(*s++);// 发送当前字符
    }
}

需要说明:

  • 发送数据时,需要将要发送的数据写入到 SBUF 寄存器中。只要 SBUF 寄存器的值不为空,就会自动发送。
  • 等待前一个数据发送完成: 可以通过检查 TI 寄存器的状态来等待前一个数据发送完成。
  • 清除发送完成标志: 发送完成后,需要手动清除 TI 寄存器的状态。

三.如何进行串口接收数据

  • 添加中断服务函数
  • 开启串口接收中断
  • 读取接收数据

1.中断服务函数

c
#define BUFFER_SIZE 32
xdata uint8_t rx_buffer[32];
volatile unsigned char rx_index;
volatile bit data_ready;


void Uart_ISR(void) interrupt 4 {
    if(RI) {
        RI = 0;        // 必须手动清除接收标志
        rx_buffer[rx_index] = SBUF; // 读取接收数据

        // 检测结束符(如回车换行)
        if(rx_buffer[rx_index] == '\n' && rx_buffer[rx_index - 1] == '\r') {
            rx_buffer[rx_index - 1] = '\0'; // 终止字符串
            data_ready = 1;      // 设置数据就绪标志
            rx_index = 0;        // 重置索引
        } else {
            if(++rx_index >= BUFFER_SIZE) rx_index = 0;  // 防溢出
        }
    }
}

需要说明:

  • 中断服务函数的名称必须为 UART_ISR,中断号为4。
  • 检查接收中断标志: 可以通过检查 RI 寄存器的状态来判断是否有数据接收到。
  • 读取接收到的数据: 可以通过读取 SBUF 寄存器来获取接收到的数据。
  • 清除接收中断标志: 读取完数据后,需要手动清除 RI 寄存器的状态。
  • 特别说明: xdata 关键字用于定义变量在外部数据区,这样可以提高变量的访问速度。因为单片机的内部数据区太小了.只是能使用扩展数据区.

2.开启串口接收中断

c
void Uart_Init(void) {
    // 设置串口引脚模式(P3.0为RX准双向,P3.1为TX强推挽)
    P3M0 = 0x02;  // P3.1(TX)设置为强推挽输出
    P3M1 = 0x00;  // 其他引脚默认准双向

    // 关于串口的配置
    SCON = 0x50;           // 8位数据,可变波特率(p469)
    AUXR &= 0xBE;
    AUXR |= 0x40;          // 串口1选择定时器1为波特率发生器(p470)

    //关于定时器2的配置(p376)
    TMOD &= 0x0F;   // 清除定时器1模式位
    TMOD |= 0x00;   // 定时器1模式0(16位自动重装)
    TL1 = BRT;             // 设置定时初始值
    TH1 = BRT >> 8;        // 设置定时初始值
    TR1 = 1;        // 启动定时器1


    ES = 1;         // 允许串口中断(可选)
    EA = 1;         // 开启总中断(可选)

}

需要说明:

  • ES=1 允许串口中断,EA=1 开启总中断。 这样,当串口接收到数据时,就会触发中断。
  • SCON 寄存器的 REN 位为1,表示允许接收。
  • 其它的部分都是与上一个例子相同的。在发送数据时也需要相应的配置.

3.数据处理

c

bit Uart_DataReady(void) {
    return data_ready;
}

char *Uart_GetData(void) {
    data_ready = 0; // 清除数据就绪标志
    return rx_buffer;
}

需要说明:

  • 检查数据是否就绪: 可以通过检查 data_ready 标志来判断是否有数据接收到。
  • 获取接收到的数据: 可以通过返回 rx_buffer 数组的指针来获取接收到的数据。
  • 清除数据就绪标志: 获取完数据后,需要手动清除 data_ready 标志。

4.数据使用

main 函数中,可以通过调用 Uart_DataReady 函数来检查是否有数据接收到,然后调用 Uart_GetData 函数来获取接收到的数据。

c
void main(void) {
    while(1) {
        if(Uart_DataReady()) { // 检测是否有数据到达
            dat = Uart_GetData(); // 获取接收到的数据
            UartSendStr(dat); // 发送接收到的数据
        }
    }
}

需要说明:

  • main 函数中,可以通过调用 Uart_DataReady 函数来检查是否有数据接收到,然后调用 Uart_GetData 函数来获取接收到的数据。

四.具体的代码

这是最简单的代码:

c
#include <STC8G.H>

// 串口初始化
void UART_Init() {
    SCON = 0x50;        // 8位数据,可变波特率
    AUXR |= 0x40;       // 定时器1时钟为Fosc,即1T
    AUXR &= 0xFE;       // 串口1选择定时器1为波特率发生器
    TMOD &= 0x0F;       // 设定定时器1为16位自动重装方式
    TL1 = 0xE0;         // 设定定时初值
    TH1 = 0xFE;         // 设定定时初值
    ET1 = 0;            // 禁止定时器1中断
    TR1 = 1;            // 启动定时器1
    ES = 1;             // 使能串口中断
    EA = 1;             // 开启全局中断
}

// 发送字符串
void UART_SendString(char *s) {
    while(*s) {
        SBUF = *s++;
        while(!TI);
        TI = 0;
    }
}