寒假第十讲(模拟赛4T二串口)

模拟赛题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控制数据)
  • 需要判断何时"一帧数据"接收完成

解决方案
采用 中断接收 + 超时判断 机制

  1. 中断接收:数据到达时立即响应,存入缓冲区

  2. 超时判断:通过定时器判断连续数据之间的间隔


    问题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 → 时间读取 → 数码管显示
     ↓
按键检测 → 事件触发 → 串口发送