模拟赛题4T(串口)
总体架构设计
系统组成模块
- 串口通信模块:负责与上位机(串口助手)数据交互
- 中断处理模块:包括串口中断和定时器中断
- 数据处理模块:解析接收数据,控制LED显示
- 按键处理模块:响应按键操作,读取DS1302时间
- 数码管显示模块:显示当前时间信息
超时解析底层与中断
首先接收一个字节数据 + 设置保护机制防止缓冲区溢出(中断+定时器)
//中断接收函数
pdata unsigned char Uart_Buf[10] = {0}; // 接收缓冲区
idata unsigned char Uart_Rx_Index = 0; // 接收索引(当前存储位置)
idata unsigned char Uart_Recv_Tick = 0; // 接收计时器
idata unsigned char Uart_Rx_Flag = 0; // 接收标志位
//串口中断接收函数,设置缓存区保存SBUF中的字节
void Uart1_Isr(void) interrupt 4
{
if(RI) // 如果收到数据
{
Uart_Rx_Flag = 1; // 设置接收标志
Uart_Recv_Tick = 0; // 计时器清零
Uart_Buf[Uart_Rx_Index++] = SBUF; // 保存数据
RI = 0; // ⚠ 必须清除接收标志!
// 防止数组越界(安全保护)
if(Uart_Rx_Index >= 10)
{
Uart_Rx_Index = 0;
memset(Uart_Buf, 0, 10);
}
}
}
//定时器中断
//RI等于1时,Flag置1开始计时
void Timer1_Isr(void) interrupt 3
{
if(Uart_Rx_Flag) // 如果正在接收数据
{
Uart_Recv_Tick++; // 计时增加
}
}
要求:通过串口助手向设备发送1001 0011,控制八个LED指示灯亮灭,0是亮,1是灭
问题1:如何可靠接收不定长的串口数据?
场景分析:
- 串口接收数据是异步的,数据可能随时到达
- 数据长度不固定(本项目中为8位LED控制数据)
- 需要判断何时"一帧数据"接收完成
解决方案:
采用 中断接收 + 超时判断 机制
-
中断接收:数据到达时立即响应,存入缓冲区
-
超时判断:通过定时器判断连续数据之间的间隔
问题3:如何将ASCII字符转换为LED控制信号?
技术难点:
- 串口发送的是字符’0’和’1’(ASCII码48和49)
- LED需要的是二进制0和1
- 需要转换并实现"0亮1灭"的逻辑
void Uart_Proc()
{
unsigned char i = 0;
if(Uart_Rx_Index==0)
return;//无数据,直接返回
if(Uart_Recv_Tick>=10)
{//超时解析
Uart_Recv_Tick=0;
Uart_Rx_Flag=0;
//检查是否收到八位数据
if(Uart_Rx_Index==8)
{//将接收到的8位数据存入ucLed数组
for(i=0;i<8;i++)
{
Seg_Buf[i]=Uart_Buf[i]-'0';//将字符转换为数字
unLed[i]=(Seg_Buf[i])? 0 : 1 ;
}
}
//清空数组
memset(Uart_Buf,0,Uart_Rx_Index);
Uart_Rx_Index=0;
}
}
要求:读取DS1302通过串口返回时间;
S4按下返回时,S8按下返回分,S12按下返回秒;
按键
void Key_Proc()
{
Key_Val=Key_Read();
Key_Down = Key_Val&(Key_Old ^ Key_Val);
Key_Up = ~Key_Val&(Key_Old ^ Key_Val);
Key_Old=Key_Val;
if(Key_Down==4)
printf("Hour:%bu",ucRtc[0]);
else if(Key_Down==8)
printf("Minute:%bu",ucRtc[1]);
else if(Key_Down==12)
printf("Second:%bu",ucRtc[2]);
}
数码管
void Seg_Proc()
{
Seg_Buf[0] = ucRtc[0] / 10; // 小时的十位
Seg_Buf[1] = ucRtc[0] % 10; // 小时的个位
Seg_Buf[2] = 11; // 分隔符(-)
Seg_Buf[3] = ucRtc[1] / 10; // 分钟的十位
Seg_Buf[4] = ucRtc[1] % 10; // 分钟的个位
Seg_Buf[5] = 11; // 分隔符(-)
Seg_Buf[6] = ucRtc[2] / 10; // 秒的十位
Seg_Buf[7] = ucRtc[2] % 10; // 秒的个位
}
底层(串口重定向)
#include "stdio.h"
extern char putchar(char ch)
{
SBUF = ch;
// 将字符放入发送缓冲区
while(TI == 0); // 等待发送完成
TI = 0; // 清除发送完成标志
return ch; // 返回发送的字符
}
//时间读取函数
void Get_Time(void)
{
Read_Rtc(ucRtc);
}
数据流总结
串口数据 → 中断接收 → 缓冲区 → 超时判断 → 解析处理 → LED控制
↓
DS1302 → 时间读取 → 数码管显示
↓
按键检测 → 事件触发 → 串口发送