串口使用总结
一、串口底层模板(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;
}
}
四、注意事项
- 超时判帧:收到数据后10ms内不再有新数据,才认为一帧完整。
- 缓冲区溢出保护:超过10字节自动清空缓冲区。
- C51 printf 不支持 %f:浮点数拆成整数部分+小数部分分别输出。
- Seg_Buf 联动:若显示屏也要显示温度,需同步把
Temperature_10x 改为 Temperature_100x。
- Keil 无需额外设置:代码中只用
%bu/%u/%s,默认库即可支持,不需要勾选任何选项。