什么?!只有三天了吗!分分钟拿下串口!

串口使用总结

一、串口底层模板(uart.c)

void Uart1_Init(void)    //9600bps@12.000MHz
{
    SCON = 0x50;
    AUXR |= 0x01;        // 串口1选择定时器2为波特率发生器
    AUXR &= 0xFB;        // 定时器时钟12T模式
    T2L = 0xE6;          // 9600bps@12MHz
    T2H = 0xFF;
    AUXR |= 0x10;        // 定时器2开始计时
    ES = 1;              // 使能串口1中断
    EA = 1;
}

extern char putchar(char ch)
{
    SBUF = ch;
    while(TI == 0);
    TI = 0;
    return ch;
}

波特率对照表(@12.000MHz,12T模式)

波特率 T2H T2L
9600 0xFF 0xE6
4800 0xFF 0xCC

二、串口标准用法(main.c 模板)

1. 顶部变量声明

idata unsigned char Uart_Rx_Index;
pdata unsigned char Uart_Rx_Buf[10];
idata unsigned char Uart_Rx_Flag;
idata unsigned char Uart_Rx_Tick;

2. Timer1 中断里加超时计数(每1ms)

void Timer1_Isr(void) interrupt 3
{
    // ... 其他代码 ...
    if (Uart_Rx_Flag)
        Uart_Rx_Tick++;   // 接收超时计时
}

3. 串口接收中断(照抄)

void Uart1_Isr(void) interrupt 4
{
    if (RI)
    {
        Uart_Rx_Flag = 1;
        Uart_Rx_Tick = 0;
        Uart_Rx_Buf[Uart_Rx_Index++] = SBUF;
        RI = 0;
        if (Uart_Rx_Index > 10)
        {
            Uart_Rx_Index = 0;
            memset(Uart_Rx_Buf, 0, 10);
        }
    }
}

4. 调度器注册

{Uart_Proc, 10, 0},   // 每10ms处理一次串口

5. 发送 — 直接用 printf

printf("数值: %bu\r\n", val);     // %bu = unsigned char
printf("数值: %u\r\n", val);      // %u  = unsigned int
printf("字符串: %s\r\n", str);    // %s  = 字符串
// 注意:C51默认不支持 %f,浮点用整数拆分代替

三、题目实战:1.5串口功能

题目要求

  • 波特率:4800bps,无校验,1位停止位
  • 收到 ST\r\n → 返回 $距离,温度\r\n(距离整数,温度两位小数)
  • 收到 PARA\r\n → 返回 #距离参数,温度参数\r\n
  • 收到错误指令 → 返回 ERROR\r\n
  • 响应时间 ≤500ms

修改点1:uart.c 改波特率

T2L = 0xCC;   // 原0xE6,改为4800bps
T2H = 0xFF;

修改点2:main.c 顶部变量

// 温度存×100倍,才能输出两位小数
idata unsigned int Temperature_100x;

// 参数变量(根据题目赋初值)
idata unsigned char Dist_Para = 35;
idata unsigned char Temp_Para = 30;

修改点3:Get_Temperature() 改×100

void Get_Temperature()
{
    Temperature_100x = rd_temperature() * 100;
}

修改点4:Uart_Proc() 实现协议

void Uart_Proc()
{
    if (Uart_Rx_Index == 0) return;
    if (Uart_Rx_Tick >= 10)
    {
        Uart_Rx_Flag = 0;
        Uart_Rx_Tick = 0;

        if (strcmp(Uart_Rx_Buf, "ST\r\n") == 0)
        {
            // 输出示例:$20,74.37\r\n
            printf("$%bu,%u.%bu%bu\r\n",
                   Distance,
                   Temperature_100x / 100,
                   (Temperature_100x % 100) / 10,
                   Temperature_100x % 10);
        }
        else if (strcmp(Uart_Rx_Buf, "PARA\r\n") == 0)
        {
            // 输出示例:#35,30\r\n
            printf("#%bu,%bu\r\n", Dist_Para, Temp_Para);
        }
        else
        {
            printf("ERROR\r\n");
        }

        memset(Uart_Rx_Buf, 0, Uart_Rx_Index);
        Uart_Rx_Index = 0;
    }
}

四、注意事项

  1. 超时判帧:收到数据后10ms内不再有新数据,才认为一帧完整。
  2. 缓冲区溢出保护:超过10字节自动清空缓冲区。
  3. C51 printf 不支持 %f:浮点数拆成整数部分+小数部分分别输出。
  4. Seg_Buf 联动:若显示屏也要显示温度,需同步把 Temperature_10x 改为 Temperature_100x
  5. Keil 无需额外设置:代码中只用 %bu/%u/%s,默认库即可支持,不需要勾选任何选项。