<think>我们之前定义的数据结构是Curtime[][10],即12行,每行10个字符。而发送的数据字符串"2021-02-05 13:12:52Wed"长度为22(包括结束符前),但我们的Curtime数组需要12行,每行10个字符,总共120个字符。显然,发送的数据长度与接收方期望的数据结构不匹配。
因此,我们需要明确数据格式:
按照Curtime数组的初始化:
static unsigned char Curtime[][10]={"2025","-","07","-","02"," ","13",":","12",":","25","Wed"};
这个数组实际上有12个元素,每个元素是一个字符串(最多10个字符,但实际每个字符串都很短)。
但是,在串口传输中,我们之前设计的接收方式是将接收到的数据按顺序填充到Curtime的每个字符位置。而发送的数据"2021-02-05 13:12:52Wed"是一个连续的字符串,没有按照Curtime的每个元素分开。
所以,我们需要确定发送的数据格式是否与接收方期望的格式一致。
接收方期望的数据格式:
12个字符串,每个字符串最多10个字符,按顺序发送每个字符(即先发送第一个字符串的所有字符,再发送第二个字符串的所有字符,...),最后以'\0'结束。
而发送的字符串"2021-02-05 13:12:52Wed"是22个字符(不包括结束符),如果直接发送这个字符串,那么接收方会将其拆分成:
第0行:前10个字符 -> "2021-02-05"
第1行:接下来的10个字符 -> " 13:12:52W"
第2行:接下来的2个字符(不足10个) -> "ed" 和后面的8个字符(但是后面没有了,所以会接着用后面的数据?)
这显然与Curtime的原始结构不一致。
因此,我们需要按照Curtime数组的结构来发送数据。即发送12个字符串,每个字符串固定10个字符(不足的补空格或空字符),这样总共120个字符,然后加一个结束符'\0'。
但是,原始Curtime数组中的每个字符串长度并不都是10,例如"2025"只有4个字符,"-"只有1个字符。所以,在发送之前,需要将每个字符串扩展到10个字符(右补空格或0),然后按顺序发送每个字符。
另一种方法是:发送方按照一个连续的内存块发送,即把整个Curtime数组视为一个120字节的连续内存块(因为数组在内存中是连续的),然后接收方直接按顺序接收120个字节,然后以每10个字节为一组,复制到Curtime的每一行。
所以,发送的数据应该是120个字节(12行*10列),然后加上结束符'\0'。
因此,发送"2021-02-05 13:12:52Wed"这样的字符串(22字节)是不正确的,因为长度不够,结构也不匹配。
正确做法:
发送方应该发送一个120字节的数据块,其中包含12个字符串,每个字符串10个字符。例如:
第0行: "2025 " (4个字符后补6个空格)
第1行: "- " (1个字符后补9个空格)
等等。
但是,这样会浪费大量带宽。因此,我们可能需要重新设计协议。
考虑到实际需求,我们可能不需要发送整个120字节。我们可以设计一个更紧凑的协议,例如只发送有效数据,然后接收方按照预定的结构解析。
然而,根据问题要求,我们保持原有变量名不变,且不改变原有数据结构。因此,我们只能按照原有数据结构进行发送和接收。
所以,如果发送方发送的是22字节的字符串(以'\0'结尾),那么接收方会将其当作120字节数据的一部分,导致解析错误。
因此,需要修改发送方,使其发送120字节的数据块(每个字符按顺序对应Curtime数组的每个元素中的每个字符),最后以'\0'结束。
但是,120字节+1个结束符,共121字节。而我们的缓冲区是120字节,所以结束符会导致溢出。因此,我们需要将缓冲区扩大1字节,或者结束符不存入缓冲区,而是作为结束信号。
在之前的代码中,我们是在接收到结束符时触发解析,结束符本身不存入缓冲区。所以,我们只需要发送120个字节的数据,然后发送一个结束符'\0'。
所以,发送方应该发送120个字节(即12行*10列,每行10个字符,共120个字符),然后发送一个'\0'。
因此,对于你提出的问题:发送"2021-02-05 13:12:52Wed"(22字节)是不正确的。
正确发送方式示例:
假设Curtime数组的当前值:
Curtime[0] = "2025" -> 发送时应该发送10个字符:'2','0','2','5', 然后6个空格(或0,但建议空格)?
但是注意,Curtime数组的初始化是字符串,每个字符串以空字符结束吗?实际上,在C语言中,二维数组的每一行是一个字符串,但我们在存储时是按字符存储的,所以每个字符串的长度是10(包括字符串结束符?)但这里不是,因为初始化时每个字符串字面量长度都小于10,所以编译器会自动用0填充剩余部分(即字符串结束符后的位置为0)。但是,在串口传输中,我们传输的是每个字符,包括字符串结束符后的内容。
实际上,在内存中,这个二维数组是连续的120个字节,每个字符串占10个字节,即使字符串实际长度不足10,后面也是用0填充(因为静态存储区的未初始化部分为0,但初始化时如果字符串长度不足10,则剩余部分自动置0)。
所以,如果我们直接发送整个120字节的内存块(即整个Curtime数组的连续内存),接收方再按相同结构解析,那么就能正确恢复。
因此,发送方应该:
for (int i=0; i<12; i++)
for (int j=0; j<10; j++)
发送 Curtime[i][j]
然后发送一个结束符'\0'。
这样,接收方接收到结束符时,将缓冲区中前120个字节(即12*10)按照同样的结构复制到Curtime数组中。
但是,在接收端代码中,我们并没有将整个120字节连续存储,而是按照行和列复制到Curtime数组。所以,发送方必须发送120字节的数据。
结论:发送"2021-02-05 13:12:52Wed"这样的字符串是不正确的,因为长度不足,且结构不匹配。
修改建议:
1. 发送方发送120字节的数据(即Curtime数组的120个字符),然后发送一个'\0'。
2. 如果觉得120字节太长,可以修改协议,但需要同时修改接收和发送的代码(因为问题要求不改变原变量名,但没说不允许改变协议,但数据结构是固定的,所以协议必须匹配)。
因此,如果要发送22字节的字符串,那么接收方必须能够解析这个字符串并填充到Curtime数组中。这需要修改接收端的中断处理程序。
由于问题要求不改变原变量名,但可以修改代码逻辑,我们可以重新设计串口接收中断,使其能够解析紧凑格式的字符串。
但是,这需要改变数据解析方式。因此,我们需要权衡。
如果必须支持发送紧凑格式(如"2021-02-05 13:12:52Wed"),那么我们需要修改中断服务程序,使其能够按照紧凑格式解析。
下面给出一个修改方案,支持接收紧凑格式的字符串(以'\0'结束),并解析到Curtime数组中:
我们假设发送的字符串格式为: "YYYY-MM-DD HH:MM:SSWeek" 共22个字符(不包括结束符),其中:
YYYY: 4字符
MM: 2字符
DD: 2字符
HH: 2字符
MM: 2字符
SS: 2字符
Week: 3字符
总共4+1+2+1+2+1+2+1+2+1+3 = 20个字符?实际上:
"2021-02-05 13:12:52Wed" 这个字符串的组成:
2021 -> 4
- -> 1
02 -> 2
- -> 1
05 -> 2
空格 -> 1
13 -> 2
: -> 1
12 -> 2
: -> 1
52 -> 2
Wed -> 3
总共:4+1+2+1+2+1+2+1+2+1+2+3 = 22个字符
而我们的Curtime数组有12个元素,每个元素是一个字符串,但实际每个字符串只有几个字符。我们可以将接收到的字符串拆分成12个部分,分别对应每个元素。
但是,在中断服务程序中,我们需要解析这个22字节的字符串,然后分别填充到Curtime的12个字符串中。注意,Curtime的每个字符串最多10个字符,所以我们可以将每个部分复制到对应的字符串,后面补0。
具体分配:
Curtime[0] = "2025" -> 对应字符串的前4个字符,然后我们需要补0(作为字符串结束符)?但是注意,我们的数组是二维数组,每个字符串固定10个字符,所以我们要复制4个字符,后面6个字符为0(或者空格,但建议0,因为C字符串以0结尾)。
但是,在中断服务程序中做复杂的字符串解析会占用较多时间,可能影响其他中断。
因此,我们可以设计一个解析函数,在接收到结束符后调用该函数进行解析。但为了不占用中断太多时间,我们可以在中断中只接收数据,在主循环中解析。但这样需要状态标志。
由于问题要求优化串口接收部分,且不改变原变量名,我们可以在中断中接收,并在接收到结束符后设置一个标志,然后在主循环中解析。
但是,原代码中并没有主循环解析的机制,所以我们还是尝试在中断中解析。
考虑到字符串长度固定为22(不包括结束符),我们可以这样做:
修改后的串口中断函数:
注意:由于数据长度固定,我们可以直接按位置解析。
但是,我们首先需要将接收到的22个字符(加上结束符共23个)存入缓冲区,然后在接收到结束符后解析。
由于数据长度固定,我们可以这样:
void Serial_interrput() interrupt 4
{
static unsigned char frame_buffer[23]; // 22个数据+1个结束符(但结束符不存入)
static unsigned int frame_index = 0;
if (RI)
{
RI = 0;
unsigned char ch = SBUF;
if (ch == '\0') // 结束符
{
// 检查frame_index是否为22,如果不是,说明数据错误,丢弃
if (frame_index == 22)
{
// 解析22字节的数据到Curtime
// 按位置解析
// Curtime[0] 取前4个字符 "2021"
// 注意:我们需要将每个字符串复制到Curtime的相应行,并且每行要补足10个字符(后面补0)
// 这里我们手动解析
// 例如:
// 位置0-3 -> Curtime[0]
// 位置4 -> Curtime[1] (字符'-')
// 位置5-6 -> Curtime[2] ("02")
// 等等
// 具体解析规则:
unsigned char *p = frame_buffer;
// 复制到Curtime[0](年份)
memcpy(Curtime[0], p, 4); Curtime[0][4] = '\0'; // 但注意,我们的数组是10个字符,所以后面6个位置需要置0?实际上,我们只需要复制有效部分,因为Curtime数组是静态的,后面已经是0(因为静态变量初始化为0)?但是,每次接收后覆盖,我们需要确保后面是0吗?
// 由于我们只修改了部分字符,所以需要将每行的剩余部分清零?或者我们直接覆盖整个行?但是,我们只复制了部分数据,所以需要将每行的剩余部分清零(或者之前就是0,因为静态变量只初始化一次,但后面可能被修改?)
// 更安全的方法:每行复制后,将后面的字符清零(从复制的长度到9的位置)
// 但我们选择:将整个Curtime数组的120个字节全部清零一次?或者,在解析时,每行复制完有效数据后,将剩余部分置0。
// 这里我们选择:复制有效数据,然后该行的剩余部分置0。
// 但是,在中断中做这么复杂的操作可能会影响其他中断,所以建议只设置标志,在主循环中解析。
// 由于问题要求优化串口接收部分,且要求代码在中断中,我们这里简化处理:只复制数据,不处理剩余部分(因为静态数组在初始化后,未修改的部分仍然是0,但之后被修改过就不一定了)
// 因此,我们直接按位置复制到每行的开头,每行只复制该部分对应的长度,其余部分保持原样(但这样可能有问题,因为之前的数据可能还在)。
// 所以,我们需要在复制前将每行清零?或者每次复制前将整个Curtime数组清零?这不可行,因为其他部分可能正在使用。
// 因此,我们只修改接收到的数据对应的行,并且只修改该行的有效长度部分,其余部分保持不变。但这样会导致旧数据残留。
// 所以,最好的做法是:在复制每个字符串时,先复制有效数据,然后在该字符串的有效数据后面加上结束符,这样在作为字符串使用时就不会读到后面的旧数据。但是,我们的数组是字符数组,不是字符串数组(每个行是固定10个字符,不一定以0结尾)。在原始代码中,Segdisp函数如何显示?它需要知道每个字符串的长度吗?还是按照10个字符显示?
// 查看初始化:Curtime的每个字符串都是字符串字面量,所以它们以0结尾,但长度不足10。而Segdisp函数可能是按字符显示,而不是按字符串(遇到0停止)?这取决于Segdisp的实现。
// 由于不知道Segdisp的具体实现,我们无法确定。所以,为了安全,我们按照每行10个字符全部覆盖(包括后面的0)。
// 因此,我们需要将每行的10个字符都设置,包括有效数据和填充0。
// 所以,我们改为:将整个120字节的Curtime数组视为一个整体,我们只修改其中22个字节的位置(对应12行,每行只修改前几个字节),这样不行,因为修改的位置不连续。
// 由于时间关系,我们采用另一种方法:重新设计接收,放弃之前的120字节结构,而是直接接收22字节,然后按照我们定义的格式填充到Curtime数组的相应位置,并且每行只复制有效数据,并在有效数据后添加结束符(这样每行就变成了标准的C字符串,长度小于10)。
// 但是,这样就需要修改Curtime数组的使用,因为原来每行是10个字符,现在每行是变长的字符串。
// 由于问题要求不改变原变量名,所以Curtime数组的声明和定义不能改,它是一个12行10列的二维数组。
// 因此,我们只能将接收到的数据复制到每行的开头,并且每行只复制该部分应占的字节数,然后该行后面的字节保持不变。这样,如果之前后面是0,那么就是0,否则就是旧数据。
// 所以,我们这样做:
// 解析22字节的数据到Curtime数组的12行,每行复制对应的字节数,然后该行剩下的字节我们不处理(保留原状)。但是,为了确保每行作为字符串使用时不会超出应有长度,我们在每行的有效数据后面加上结束符(覆盖该行有效数据后的第一个字节为0)。但是,这样会破坏该行后面的数据(如果该行有效数据长度小于10,那么我们在有效数据后加0,然后后面的字节不变,这样当使用strlen访问该行时,就会在0处停止,但后面的字节还是旧的,不过不会影响字符串使用)。
// 具体解析:
// 定义每行对应的数据长度和起始位置
// 行0: 年份,4字节,起始位置0
// 行1: "-", 1字节,起始位置4
// 行2: 月份,2字节,起始位置5
// 行3: "-", 1字节,起始位置7
// 行4: 日,2字节,起始位置8
// 行5: 空格,1字节,起始位置10
// 行6: 小时,2字节,起始位置11
// 行7: ":", 1字节,起始位置13
// 行8: 分钟,2字节,起始位置14
// 行9: ":", 1字节,起始位置16
// 行10: 秒,2字节,起始位置17
// 行11: 星期,3字节,起始位置19
// 然后,我们按行复制:
int pos = 0;
// 行0: 4字节
for (int i=0; i<4; i++) Curtime[0][i] = frame_buffer[pos++];
Curtime[0][4] = '\0'; // 结束符
// 行1: 1字节
Curtime[1][0] = frame_buffer[pos++];
Curtime[1][1] = '\0';
// 行2: 2字节
for (int i=0; i<2; i++) Curtime[2][i] = frame_buffer[pos++];
Curtime[2][2] = '\0';
// 行3: 1字节
Curtime[3][0] = frame_buffer[pos++];
Curtime[3][1] = '\0';
// 行4: 2字节
for (int i=0; i<2; i++) Curtime[4][i] = frame_buffer[pos++];
Curtime[4][2] = '\0';
// 行5: 1字节
Curtime[5][0] = frame_buffer[pos++];
Curtime[5][1] = '\0';
// 行6: 2字节
for (int i=0; i<2; i++) Curtime[6][i] = frame_buffer[pos++];
Curtime[6][2] = '\0';
// 行7: 1字节
Curtime[7][0] = frame_buffer[pos++];
Curtime[7][1] = '\0';
// 行8: 2字节
for (int i=0; i<2; i++) Curtime[8][i] = frame_buffer[pos++];
Curtime[8][2] = '\0';
// 行9: 1字节
Curtime[9][0] = frame_buffer[pos++];
Curtime[9][1] = '\0';
// 行10: 2字节
for (int i=0; i<2; i++) Curtime[10][i] = frame_buffer[pos++];
Curtime[10][2] = '\0';
// 行11: 3字节
for (int i=0; i<3; i++) Curtime[11][i] = frame_buffer[pos++];
Curtime[11][3] = '\0';
}
frame_index = 0; // 重置索引
}
else
{
if (frame_index < 22)
{
frame_buffer[frame_index++] = ch;
}
else
{
// 数据超长,丢弃
frame_index = 0;
}
}
}
if (TI) TI = 0;
}
但是,这个方案在中断中做了大量的解析工作,可能会影响系统的实时性。
因此,我们建议设置一个标志,在中断中只接收数据,然后在主循环中解析。
由于问题要求优化串口接收部分,且不改变原变量名,我们这里给出在中断中解析的方案,但要注意中断执行时间。
另外,我们也可以优化解析代码,用循环和偏移量表来减少代码量。
下面给出优化后的解析代码(在中断中):
// 定义每行的复制长度
static const unsigned char lengths[12] = {4,1,2,1,2,1,2,1,2,1,2,3};
static const unsigned char starts[12] = {0,4,5,7,8,10,11,13,14,16,17,19};
if (frame_index == 22)
{
for (int i=0; i<12; i++)
{
// 复制数据
for (int j=0; j<lengths[i]; j++)
{
Curtime[i][j] = frame_buffer[starts[i]+j];
}
// 添加结束符
Curtime[i][lengths[i]] = '\0';
}
}
注意:这里我们添加了两个数组lengths和starts,分别表示每行数据的长度和在22字节中的起始位置。
但是,这样会占用额外的RAM,对于51单片机可能紧张。我们可以用查表的方式,将 starts 和 lengths 放在code区(程序存储器)。
修改为:
code unsigned char lengths[12] = {4,1,2,1,2,1,2,1,2,1,2,3};
code unsigned char starts[12] = {0,4,5,7,8,10,11,13,14,16,17,19};
然后在中断中使用。
综上,我们给出完整的修改方案:
void Serial_interrput() interrupt 4
{
static unsigned char frame_buffer[22]; // 存放22字节的数据
static unsigned char frame_index = 0;
code unsigned char lengths[12] = {4,1,2,1,2,1,2,1,2,1,2,3};
code unsigned char starts[12] = {0,4,5,7,8,10,11,13,14,16,17,19};
if (RI)
{
RI = 0;
unsigned char ch = SBUF;
if (ch == '\0')
{
if (frame_index == 22)
{
for (unsigned char i=0; i<12; i++)
{
for (unsigned char j=0; j<lengths[i]; j++)
{
Curtime[i][j] = frame_buffer[starts[i]+j];
}
Curtime[i][lengths[i]] = '\0'; // 添加结束符
}
}
frame_index = 0;
}
else
{
if (frame_index < 22)
{
frame_buffer[frame_index++] = ch;
}
else
{
// 数据超长,复位
frame_index = 0;
}
}
}
if (TI) TI = 0;
}
注意:这里我们假设发送方发送的是22字节的数据,然后紧跟着一个'\0'。如果发送方发送的数据包含22个字节,不包括结束符,那么结束符是额外的一个字节。
这个方案接收22字节的数据帧,然后解析到Curtime数组的12个字符串中,每个字符串在有效数据后添加了结束符,这样它们就是标准的C字符串了。
但是,我们也要注意,原来的Curtime数组是12行10列,现在我们每行只修改了前lengths[i]+1个字符(有效数据+结束符),该行后面的字符保持不变。如果之前后面有数据(比如上一次接收留下的),那么可能不会被覆盖。不过,每次解析都会重新设置,所以问题不大。
另外,结束符占用了该行的一个字符位置,所以该行的有效字符数为lengths[i](不包括结束符),这与原数组设计一致。
因此,发送"2021-02-05 13:12:52Wed"这样的数据(22字节)然后跟着一个'\0',接收方就可以正确解析。
所以,答案是:如果发送方发送的数据是22字节的紧凑格式(不包括结束符),那么接收方使用上述代码就可以正确解析。
但是,原代码中的串口发送函数Serial_send(Curtime)发送的是120字节,所以发送方也要相应修改,改为发送22字节的紧凑格式和结束符。
总结:
1. 接收方使用上述中断服务程序。
2. 发送方需要改为发送紧凑格式(22字节)然后发送一个结束符'\0'。
注意:紧凑格式的字符串必须 exactly 22字节,不足22字节会导致解析错误,超过22字节会导致只取前22字节。
因此,我们完成了代码优化,支持以'\0'结束的22字节紧凑格式的日期时间字符串。
最后,给出完整的串口中断函数代码(优化后):</think>以下是针对"以'\0'为结束信号"且接收"2021-02-05 13:12:52Wed"格式数据的优化方案:
### 优化后的串口接收代码
```c
void Serial_interrput() interrupt 4
{
static unsigned char frame_buffer[24]; // 缓冲区(22数据+1结束符)
static unsigned char frame_index = 0; // 缓冲区索引
if (RI) // 处理接收中断
{
RI = 0; // 清除标志
unsigned char ch = SBUF; // 读取字符
// 结束信号检测
if (ch == '\0')
{
// 解析22字节格式:YYYY-MM-DD HH:MM:SSWeek
if (frame_index == 22)
{
// 年份 (4字节)
Curtime[0][0] = frame_buffer[0];
Curtime[0][1] = frame_buffer[1];
Curtime[0][2] = frame_buffer[2];
Curtime[0][3] = frame_buffer[3];
Curtime[0][4] = '\0'; // 结束符
// 分隔符 (1字节)
Curtime[1][0] = frame_buffer[4];
Curtime[1][1] = '\0';
// 月份 (2字节)
Curtime[2][0] = frame_buffer[5];
Curtime[2][1] = frame_buffer[6];
Curtime[2][2] = '\0';
// 分隔符 (1字节)
Curtime[3][0] = frame_buffer[7];
Curtime[3][1] = '\0';
// 日期 (2字节)
Curtime[4][0] = frame_buffer[8];
Curtime[4][1] = frame_buffer[9];
Curtime[4][2] = '\0';
// 空格 (1字节)
Curtime[5][0] = frame_buffer[10];
Curtime[5][1] = '\0';
// 小时 (2字节)
Curtime[6][0] = frame_buffer[11];
Curtime[6][1] = frame_buffer[12];
Curtime[6][2] = '\0';
// 冒号 (1字节)
Curtime[7][0] = frame_buffer[13];
Curtime[7][1] = '\0';
// 分钟 (2字节)
Curtime[8][0] = frame_buffer[14];
Curtime[8][1] = frame_buffer[15];
Curtime[8][2] = '\0';
// 冒号 (1字节)
Curtime[9][0] = frame_buffer[16];
Curtime[9][1] = '\0';
// 秒钟 (2字节)
Curtime[10][0] = frame_buffer[17];
Curtime[10][1] = frame_buffer[18];
Curtime[10][2] = '\0';
// 星期 (3字节)
Curtime[11][0] = frame_buffer[19];
Curtime[11][1] = frame_buffer[20];
Curtime[11][2] = frame_buffer[21];
Curtime[11][3] = '\0';
}
frame_index = 0; // 重置索引
return;
}
// 缓冲区溢出保护
if (frame_index >= sizeof(frame_buffer) - 1)
{
frame_index = 0; // 重置缓冲区
return;
}
// 存储有效数据
frame_buffer[frame_index++] = ch;
}
if (TI) TI = 0; // 清除发送中断标志
}
```
### 关键优化说明
1. **数据结构适配**:
- 专用22字节缓冲区(实际数据长度)
- 显式解析每个字段:年份(4B)、月份(2B)、日期(2B)等
- 为每个字段添加结束符`'\0'`确保字符串完整性
2. **格式验证**:
- 严格检查帧长度(`if (frame_index == 22)`)
- 自动重置无效帧(长度不足或溢出)
- 位置绑定解析(避免格式错位)
3. **内存优化**:
- 缓冲区从120字节精简到24字节
- 移除冗余的行列索引变量
- 静态索引分配(无运行时计算开销)
4. **错误处理**:
- 溢出保护(`sizeof(frame_buffer)-1`)
- 自动重置机制
- 隔离无效数据影响
### 数据格式说明
```
// 正确格式(22字节 + \0)
"2021-02-05 13:12:52Wed\0"
// 数据结构映射:
[0-3] : 年份 -> Curtime[0]
[4] : '-' -> Curtime[1]
[5-6] : 月份 -> Curtime[2]
[7] : '-' -> Curtime[3]
[8-9] : 日期 -> Curtime[4]
[10] : 空格 -> Curtime[5]
[11-12]: 小时 -> Curtime[6]
[13] : ':' -> Curtime[7]
[14-15]: 分钟 -> Curtime[8]
[16] : ':' -> Curtime[9]
[17-18]: 秒钟 -> Curtime[10]
[19-21]: 星期 -> Curtime[11]
```
### 发送端实现示例
```c
void send_time_data()
{
// 准备22字节数据
char timestamp[23] = "2025-07-02 13:12:52Wed";
// 发送数据+结束符
for(int i=0; i<22; i++) {
SBUF = timestamp[i]; // 发送数据字节
while(!TI); // 等待发送完成
TI = 0; // 清除标志
}
SBUF = '\0'; // 发送结束符
while(!TI);
TI = 0;
}
```
### 注意事项
1. **严格长度要求**:
- 必须精确发送22字节有效数据
- 不足22字节会导致解析失败
- 超过22字节会被截断
2. **时间敏感处理**:
- 在`Clock_interrupt()`中禁用串口接收
- 使用双缓冲避免显示刷新冲突
```c
volatile bit uart_busy = 0;
void Clock_interrput() interrupt 1
{
if(!uart_busy) {
// 更新显示
}
}
void Serial_interrput() interrupt 4
{
uart_busy = 1;
// ...接收处理...
uart_busy = 0;
}
```
3. **数据验证增强**(可选):
```c
// 添加简单校验和
unsigned char checksum = 0;
for(int i=0; i<21; i++) checksum ^= frame_buffer[i];
if(checksum != frame_buffer[21]) {
// 校验失败处理
}
```