Arduino_UDP通信问题

1.问题描述

在Arduino-ESP32编写的UDP通信中,利用udp.write(data,len)发送图片会出现图片显示不全

2.问题出现原因

Arduino-ESP32封装的UDP函数udp.write(data,len)中的data无论是多大的数据,函数只会发送前1436个数据,这样导致每次只传输图片数据流的前1436个数据,后面所有的数据被遗弃。

3.问题解决办法

当需要传输数据量比较大的数据时,可以将数据分片发送,设置每片的数据大小为1430或者其他大小,然后一片一片发送,接收端需要对应对一片数据进行判断。判断的方法一般有一下几种:

​ ① 接收端根据发送端进行的切片数量开启同样数量的端口来接收发送段的数据

​ 比如需要传输1920×1080的视频,通过一个端口进行数据发送会导致画面出现卡顿现象,可以通过将一个图片等宽截成20份通过20个链路发送过来,这样传过来的视频会比较流畅。还可以通过并行加速技术提高速度。

​ ② 发送端一片一片的发送数据,接收方只需要一个端口进行数据接收

​ 当需要发送的视频流对画质的要求不高时,我们不需要开启多个端口同时进行接收,一个端口即可完成图片的传输,此时可以将图片切片发送,接收端将收到的多片数据拼接还原出图片,这种方法需要加上开始接收的标记和停止接收的标记。

IIC的组成

​ IIC系统中只需要用到的两根线为串行数据线(Serial DAta , SDA),串行时钟线(Serial CLock , SCL)。SDA和SCL管脚都是漏极开路输出结构,因此在实际使用时,SDA和SCK信号线都必须要加上拉电阻Rp(Pull-Up Resistor)。连接时需要共地以保持数据的一致性。

​ 1.串行数据线SDA

​ SDA具备两种功能

​ 主机模式下,当SCL为高电平时,SDA由高电平向低电平跳变,产生开始信号重新开始信号; SDA由低电平向高电平跳变,则产生结束信号。当SCL为低电平时,SDA拉高表示信号”1”,SDA拉低则表示信号”0”。

​ 从机模式下,当主机发送了8位数据后,第9个时钟周期出现,主发送器必须在这一时钟位上释放数据线,由接收设备拉低SDA电平来产生应答信号

​ 2.串行时钟线SCL

​ SCL处于高电平时,由SDA线拉低/拉高产生(重新)开始信号/停止信号;SCL处于低电平时,由SDA线拉低/拉高产生信号”1”/信号”0”。由于开始信号的需要SCL处于高电平时才能产生,所以SCL线和SDA线平时都处于高电平。

IIC的信号类型

​ IIC总线在传送数据过程中共有4种类型信号,分别是开始信号、停止信号、重新开始信号和应答信号。

​ 1.开始信号

​ 当SCL为高电平时,SDA由高电平向低电平跳变,产生开始信号。当总线空闲的时候,主机通过发送开始信号(START)建立通信。

​ (Tips:总线空闲时,SDA和SCL线都处于高电平)

​ 2.停止信号

​ 当SCL为高电平时,SDA由低电平向高电平跳变,产生停止信号。主机通过发送停止信号,结束时钟信号和数据通信。SDA和SCL都将被复位为高电平状态。

​ 3.重新开始信号

​ 在IIC总线上,主机可以在调用一个没有产生STOP信号后,产生一个开始信号。主机通过使用一个重复开始信号来和另一个从机通信或者同一个从机的不同模式通信。由主机发送一个开始信号启动一次通信后,在首次发送停止信号之前,主机通过发送重新开始信号,可以转换与当前从机的通信模式,或是切换到与另一个从机通信。

​ 4.应答信号

​ 接收数据的IC在接收到8位数据后,向发送数据的主机IC发出的特定的低电平脉冲。每一个数据字节后面都要跟一位应答信号,表示已收到数据。

IIC的通信过程

主机向从机写一个字节数据或者读一个字节数据的过程基本是类似的,唯一不同的就是产生开始信号后数据方向位的不同,0表示主机发送数据,1表示主机读取数据。读/写一个数据时,主机首先产生START信号,然后紧跟着发送一个从机地址(7位),查询相应的从机,紧接着的第8位是数据方向位(R/W)–(1/0),这时候主机要等待从机的应答信号,当主机收到应答信号时,发送给从机一个位置参数,告诉从机主机的数据在从机接收数组中存放的位置,继续等待从机的应答信号,然后继续等待从机的响应信号,当主机收到响应信号时,发送一个字节的数据,继续等待从机的响应信号

20201116会议要点总结

会议精神:

1.学习有输入,也要有输出。

2.研究生要具备科学研究能力。

3.学习不能浮在面上,需要问题不了嗯躲着走。

4.学习要深入到细节,没有细节就没有现代科学

5.传感器老化问题,神经网络回归、所有老化问题都会被数学问题解决,滤波问题的重要性。

第10周-学习备忘录

日期 星期 时间记录 学习内容 日合计
11.9 1 9:00:11:30
14:00-17:30
19:00-20:00
组会
ESP32网页版人脸识别
EEESP7
11.10 2 9:00:11:30
14:00-17:30
19:00-20:00
ESP32网页版人脸识别 7
11.11 3 9:00:11:30
14:00-17:30
19:00-20:00
ESP32-SPI传输
SPI原理学习
7
11.12 4 9:00:11:30
14:00-17:30
19:00-20:00
AHL-GEC-IDE界面设计
ESP32-SPI传输
SPI原理学习
7
11.13 5 9:00:11:30
14:00-17:30
19:00-20:00
AHL-GEC-IDE界面设计
ESP32-SPI传输
SPI原理学习
7
11.14 6 9:00:11:30
14:00-17:30
AHL-GEC-IDE界面设计
ESP32-SPI传输
SPI原理学习
6
11.15 7
周合计时间
周平均时间

第9周-学习备忘录

日期 星期 时间记录 学习内容 日合计
11.2 1 9:00:11:30
14:00-17:30
19:00-20:00
会议
AI-EORS图像数据采集软件wifi版制作
7
11.3 2 9:00:11:30
14:00-17:30
19:00-20:00
AI-EORS图像数据采集软件wifi版制作 7
11.4 3 9:00:11:30
14:00-17:30
19:00-20:00
AI-EORS图像数据采集软件wifi版制作 7
11.5 4 9:00:11:30
14:00-17:30
19:00-20:00
AI-EORS图像数据采集软件wifi版制作 7
11.6 5 9:00:11:30
14:00-17:30
19:00-20:00
ESP32网页版视频传输学习 7
11.7 6 9:00:11:30
14:00-17:30
ESP32网页版视频传输学习 6
11.8 7
周合计时间
周平均时间

第8周-学习备忘录

日期 星期 时间记录 学习内容 日合计
10.26 1 9:00-21:30 比赛 12.5
10.27 2 9:00-21:30 比赛 12.5
10.28 3 9:00-21:30 比赛 12.5
10.29 4 9:00-21:30 比赛 12.5
10.30 5 9:00-21:30 比赛收尾 12.5
10.31 6 9:00-21:30 比赛收尾 12.5
11.1 7
周合计时间
周平均时间

SPI的组成

​ SPI系统中四根线为串行时钟引脚SCK从机选择引脚SS(Slave Select)主出从入引脚MOSI主入从出引脚MISO,如果从机不需要向主机发送消息的情况下,可以不需要主入从出引脚MISO。

​ 1.串行时钟引脚SCK

​ SCK用于控制主机和从机之间的数据传输。时钟信号由主机提供,从主机的SCK引脚输出给从机的SCK引脚,由主机控制传输的速度。一次传输中,从主机SCK引脚中输出自动产生的8个时钟周期信号,SCK信号的一个沿跳变进行一位数据移位传输。

​ 2.从机选择引脚SS

​ 在主机方式下工作,引脚SS为高电平;在从机方式下工作,引脚SS设置为低,则表示主机选中了该从机,引脚SS设置为高,表示主机未选中该从机。

​ 在一个主MCU带多个从属MCU的系统中,主MCU的引脚SS直接接高电平,从机MCU的引脚SS接主机的IO口,由主机输出低/高电平表明主机是/否选中该从机。

​ 3.主出从入引脚MOSI

​ 主机发出启动信号开始,主机将要传送的数据装入8位移位寄存器,同时产生8个时钟信号依次从SCK引脚送出,在SCK信号的控制下,主机中的8位移位寄存器中的数据依次从MOSI引脚送出到从机的MOSI引脚再送到从机的8位移位寄存器。

​ 4.主入从出引脚MISO

​ 主机发出启动信号开始,从机收到启动信号后,从机将要传送的数据装入8位寄存器,根据主机发过来的时钟信号,在SCK信号的控制下,从机中的8位移位寄存器中的数据依次从MISO引脚送出到主机的MISO引脚再送到主机的8位寄存器。

SPI的时序

​ 由于SPI的数据传输是在时钟信号SCK(同步信号)的控制下完成的,就会产生不取数的时候空闲电平的选择以及什么时候去取数的问题。空闲电平为低,那么SPI的时钟极性CPOL就为0,空闲电平为高,那么SPI的时钟极性CPOL就为1。当数据采样是在第一个边沿,时钟相位则为0,数据采样在第二个边沿时,时钟相位为1。根据这两个性质的选择,就会有4种可能情况。

​ 1.CPOL = 0,CPHA = 0

SPI时序图

​ 平时处于低电平,在数据上线半个周期后,处于稳定状态时,接收方此时开始采集数据,再半个周期后,时钟信号又达低电平,此时可以更换数据,在半个小时后的上升沿时又采集一次数据,以此类推。这种模式决定接收方会在时钟的上升沿时取数。

​ 2.CPOL = 1,CPHA = 0

​ 与第一种的区别是,该情况下平时处于高电平,同样接收方会在数据上线半个周期后开始取数。这种模式决定接收方会在时钟的下降沿时取数。

​ 3.CPOL = 0,CPHA = 1

​ 与第一种的区别时,该情况下接收方会在第二个跳变沿开始取数,那么此时数据不需要提前上线,由于平时是低电平,第一次跳变沿为上升沿,第二次跳变沿为下降沿。所以这种模式决定接收方会在时钟的下降沿时取数。

​ 4.CPOL = 1, CPHA = 1

​ 与第一种模式相反,平时处于高电平,接收方会在第二个跳变沿开始取数,数据不提前上线,第二个跳变沿为上升沿。这种模式就会决定接收方会在时钟的上升沿时取数。

SPI构件设计

​ SPI构件主要有初始化SPI_init,发送一字节数据SPI_send1,发送多个字节数据SPI_sendN,接收一字节数据SPI_receive1,接收多个字节数据SPI_receiveN,打开SPI接收中断SPI_enable_re_int,关闭SPI接收中断SPI_disable_re_int。

​ 在从机不回发的情况下,主机主要使用到的函数有SPI_init,SPI_sendN(N*SPI_send1)。

​ 从机主要使用到的函数有SPI_init,SPI_receiveN(N*SPI_receive1),SPI_enable_re_int,SPI_disable_re_int。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//For Example
int main()
{
//【*】将芯片中SPI0作为主机进行数据发送,SPI1作为从机进行数据接收,会有如下代码。

//【1】初始化SPI0为主机模式,波特率6000,时钟极性0,时钟相位0
SPI_init(SPI_0,1,6000,0,0); //第一个1代表是主机模式,第二个6000代表波特率为6000,第三个0代表时钟极性为0,第四个0代表时钟相位为0
//【2】初始化SPI1为从机模式,波特率6000,时钟极性0,时钟相位0
SPI_init(SPI_1,0,6000,0,0); //第一个0代表是从机模式,第二个6000代表波特率为6000,第三个0代表时钟极性为0,第四个0代表时钟相位为0
//【3】打开SPI1的接收中断
SPI_enable_re_int(SPI_1);
//【4】主循环通过SPI发送一个数据
for(;;)
{
SPI_send1(SPI_0,TransferTemp);
TransferTemp++;
}
}

void SPI_Handler()
{
//【*】当有数据发送过来时,会产生中断,此时接收一个数据即可

uint_8 redata;
redata = SPI_receive1(SPI_1);
}

各函数主要功能设计如下:

SPI_init

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
void SPI_init(uint8_t No, uint8_t MSTR, uint16_t BaudRate, uint8_t CPOL, uint8_t CPHA)
{
uint8_t BaudRate_High;
uint8_t BaudRate_Low;
uint8_t BaudRate_Mode;
//【1】根据传入的No的值选择对应的模块/引脚组

if(No < 0 || No > 1)
No = 0; //如果通道号传错,则强制选择0号模块,提高检错能力

if(No == 0)
{
//【1】打开SPI0模块时钟
BSET(SIM_SCGC4_SPI0_SHIFT,SIM_SCGC4);

//【2】根据引脚组的不同将对应引脚组复用为SPI0功能
#if(SPI_0_GROUP == 1)
PORTD_PCR0 = (0|PORT_PCR_MUX(0x02));
PORTD_PCR1 = (0|PORT_PCR_MUX(0x02));
PORTD_PCR2 = (0|PORT_PCR_MUX(0x02));
PORTD_PCR3 = (0|PORT_PCR_MUX(0x02));
#endif

#if(SPI_0_GROUP == 2)
PORTA_PCR14 = (0|PORT_PCR_MUX(0x02));
PORTA_PCR15 = (0|PORT_PCR_MUX(0x02));
PORTA_PCR16 = (0|PORT_PCR_MUX(0x02));
PORTA_PCR17 = (0|PORT_PCR_MUX(0x02));
#endif

#if(SPI_0_GROUP == 3)
PORTC_PCR4 = (0|PORT_PCR_MUX(0x02));
PORTC_PCR5 = (0|PORT_PCR_MUX(0x02));
PORTC_PCR6 = (0|PORT_PCR_MUX(0x02));
PORTC_PCR7 = (0|PORT_PCR_MUX(0x02));
#endif

//【3】将SPI控制寄存器1清零
SPI0_C1 = 0x00;

//【4】对SPI控制寄存器1的相应位进行赋值进行功能选择
//【4.1】对使能位赋值使能SPI模块
BSET(SPI_C1_SPE_SHIFT, SPI0_C1);
//【4.2】对主从机选择位赋值进行主从机的选择
(MSTR == 1)?BSET(SPI_C1_MSTR_SHIFT, SPI0_C1):BSET(SPI_C1_SPIE_SHIFT, SPI0_C1);//主机置1,从机则打开中断准备接收数据
//【4.3】对时钟极性位赋值进行时钟极性的选择
(CPOL == 0)?BCLR(SPI_C1_CPOL_SHIFT, SPI0_C1):BSET(SPI_C1_CPOL_SHIFT, SPI0_C1);//极性为0则赋值为0,极性为1则赋值为1
//【4.4】对时钟相位位赋值进行时钟相位的选择
(CPHA == 0)?BCLR(SPI_C1_CPHA_SHIFT, SPI0_C1):BSET(SPI_C1_CPHA_SHIFT, SPI0_C1);//相位为0则赋值为0,相位为1则赋值为1
//【4.5】对选择输出使能位进行赋值配置模块输出方式
BSET(SPI_C1_SSOE_SHIFT, SPI0_C1);

//【5】将SPI控制寄存器2清零
SPI0_C2 = 0x00;

//【6】对SPI控制寄存器2的相应位进行赋值进行功能选择
if(MSTR == 1) BSET(SPI_C2_MODFEN_SHIFT, SPI0_C2);

//【7】清空波特率寄存器
SPI_BR = 0x00U;

//【8】重新设置波特率
BaudRate_High = 0;
BaudRate_Low = 0;
BaudRate_Mode = 12000/BaudRate;
while(BaudRate_Mode%2 == 0)
{
BaudRate_Mode = BaudRate_Mode/2;
BaudRate_Low++;
}
BaudRate_High = --BaudRate_Mode;
SPI0_BR = BaudRate_High<<4;
SPI0_BR |= BaudRate_Low;
}
else
{
//【*】与上面类似, 从SPI_0的设置变为对SPI_1的设置
}
}

SPI_send1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
uint_8 SPI_send1(uint8_t No, uint8_t data)
{
uint32_t i;
//【1】利用封装好的SPI_baseadd获得基地址
SPI_MemMapPtr baseadd = SPI_baseadd(No);
//【2】判断状态寄存器中的发送区状态,有数据状态为1,无数据状态为0。此时等待状态为0
while(!(SPI_S_REG(baseadd)&SPI_S_SPTEF_MASK));
//【3】将数据放入数据寄存器
SPI_D_REG(baseadd) = data;

for(i = 0; i < 0xFFF0; i++)
{
if((SPI_S_REG(baseadd)&SPI_S_SPTEF_MASK)) //放入数据后状态发送区状态为1,即代表成功。
{
return(1);
}
}
return(0);
}

SPI_sendN

1
2
3
4
5
6
7
8
9
10
11
12
13
14
void SPI_sendN(uint8_t No, uint8_t n, uint8_t data[])
{
uint32_t k;
//【1】利用封装好的SPI_baseadd获得基地址
SPI_MemMapPtr baseadd = SPI_baseadd(No);

for(k = 0; k < n; k++)
{
//【*】重复步骤:Step1.判断发送区状态 Step2.放入数据 Step3.清除发送区状态
while(!(SPI_S_REG(baseadd)&SPI_S_SPTEF_MASK)); //Step1
SPI_D_REG(baseadd) = data[k]; //Step2
SPI_S_REG(baseadd) != SPI_S_SPTEF_MASK; //Step3
}
}

SPI_receive1

1
2
3
4
5
6
7
8
9
uint8_t SPI_receive1(uint8_t No)
{
//【1】利用封装好的SPI_baseadd获得基地址
SPI_MemMapPtr baseadd = SPI_baseadd(No);
//【2】判断接收区寄存器状态,有数据状态为1,无数据状态为0。
while(!(SPI_S_REG(baseadd) & SPI_S_SPRF_MASK));
//【3】数据寄存器取数后,接收区状态从1被修改为0
return SPI_D_REG(baseadd);
}

SPI_receiveN

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
uint8_t SPI_receive1(uint8_t No, uint8_t n, uint8_t data[])
{
//【1】利用封装好的SPI_baseadd获得基地址
SPI_MemMapPtr baseadd = SPI_baseadd(No);
uint32_t m = 0;
while(m < n)
{
//【*】重复步骤:Step1.判断接收区区状态 Step2.取出数据(取出数据后接收区状态会变成0)
if(SPI_S_REG(baseadd) & SPI_S_SPRF_MASK)
{
data[m] = SPI_D_REG(baseadd);
m++;
}
}
return (1);
}

SPI寄存器

​ SPI需要用到6个8位寄存器,其中含有两个控制寄存器,一个波特率寄存器,一个状态寄存器,一个数据寄存器,一个匹配寄存器。

​ 1.两个控制寄存器SPIx_C1,SPIx_C2中,SPIx_C1主要负责SPI使能控制、中断使能和配置选项(主从模式选择、时钟极性位选择、时钟相位位选择、移位器方向选择)。SPIx_C2。。。。。。

​ 2.波特率寄存器SPIx_BR。该寄存器用于位一个SPI主机设定标器和位速率分频因子。该寄存器的8位分别为D7(保留位),D6-D4(SPPR-SPI波特率预分频系数),通过这三位位段可以得到SPI波特率预分频系数。SPPR[2:0]所表示的十进制数为x(0-7),则预分频系数SPPR=x+1(1-8)。输入为总线速率时钟(BUSCLK),输出驱动SPI波特率系数的输入。以及D3-D0(SPR-SPI波特率系数),通过这四位位段可以得到波特率系数。SPR[3:0]所表示的十进制数为y(0-7),则波特率系数SPR=2^(y+1)(2-256)。其输入来自SPI波特率预分频器,输出是主模式的SPI波特率时钟。

​ SPI主模式波特率 = f_BUSCLK /(SPPR×SPR),其中f_BUSCLK为总线时钟,SPPR和SPR可分别由SPI波特率预分频系数和SPI波特率系数得出。

​ 3.状态寄存器SPIx_S。该寄存器主要用于SPI发送和接收缓存区满空的判断和设置,其中4个只读状态位(D7-D4)。D7位SPRF(SPI Receive Flag)为SPI接收缓冲器满标志。在一次SPI传输完成时,SPRF被置位,表明接收到的数据可以从SPI数据寄存器(SPI_D)读取。当SPRF被置位时,通过读SPRF,然后读取SPI数据寄存器,可将其清除。D6位SPMF(SPI Match Flag)为SPI匹配标志。表明接收数据缓冲区的值是否与匹配寄存器中的值相同,硬件值与软件值进行比对。D5位SPTEF(SPI Transmit Empty Flag)位SPI发送缓冲器空标志。表明发送缓冲区是否为空。通过读取SPIx_S,可将其清除。并且在向SPIx_D写数据之前,需要清SPTEF位,否则写入无效。在DMA方式下,产生DMA请求后,SPTEF自动清除0。D4位MODF为主模式故障标志。。。。。。

​ 4.数据寄存器SPIx_D。读取该寄存器将返回从接收数据缓冲器中读取的数据。写该寄存器将会把数据写入发送数据缓冲器。当SPI被设置为主模式时,写入数据到传输数据缓冲器发起一次SPI传输。除非SPI发送缓冲器空标志(SPTEF)被置位,数据不应被写入到发送数据缓冲器,表明发送缓冲器内有空间来排列一个新的发送字节。在SPRF置位后且另以传输完成之前的任意时刻,数据都可以从SPID中被读取。在一个新的传输完成前,从接收数据缓冲器读出数据失败,将会引起一个接收丢包状态,并且新传输的数据也会丢失。

​ 5.匹配寄存器SPIx_M。此寄存器存储硬件比较值,用来与SPI接收数据缓冲区中的值进行比较。当SPI接收数据缓冲区收到的值等于此硬件比较值时,SPI匹配标志(SPMF)置位。

GPIO模拟SPI

​ 利用GPIO模拟SPI主要通过对使用四个GPIO引脚分别对应SS引脚、SCLK引脚、MOSI引脚、MISO引脚,利用延时函数模拟时钟周期,进行对SPI通信的模拟

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
#define	SS		GPIO0
#define SCLK GPIO1
#define MOSI GPIO2
#define MISO GPIO3
#define OUTPUT 1
#define INPUT 0

void SPI_init()
{
Set_GPIO_Direction(SS, OUTPUT);
Set_GPIO_Direction(SCLK, OUTPUT);
Set_GPIO_Direction(MOSI, OUTPUT);
Set_GPIO_Direction(MISO, INPUT);

Set_GPIO_Value(SCLK, 0);
Set_GPIO_Value(MOSI, 0);
}

void SS_Enable(int enable)
{
if(enable) Set_GPIO_Value(SS, 0);
else Set_GPIO_Value(SS, 1);
}

void SPI_Write1(uint8_t ch)
{
for(int i = 7; i >= 0; i--)
{
Set_GPIO_Value(SCLK, 0);
Set_GPIO_Value(MOSI, ch&(1<<i));
Delay();
Set_GPIO_Value(SCLK, 1);
Delay();
}
}

uint8_t SPI_Read1()
{
uint8_t ch;
for(int i = 0; i < 8; i++)
{
Set_GPIO_Value(SCLK, 0);
Delay();
Set_GPIO_Value(SCLK, 1);
ch = (ch<<1) | Get_GPIO_Value(MISO);
Delay();
}
return ch;
}

void SPI_Write(uint8_t* buf, int len)
{
SS_Enable(1);
Delay();
for(int i = 0; i < len; i++) SPI_Write1(buf[i]);
Delay();
SS_Enable(0);
}

void SPI_Read(uint8_t* buf, int len)
{
SS_Enable(1);
Delay();
for(int i = 0; i < len; i++) buf[i] = SPI_Read1();
Delay();
SS_Enable(0);
}

第7周-学习备忘录

日期 星期 时间记录 学习内容 日合计
10.19 1 9:00-21:30 比赛 12.5
10.20 2 9:00-21:30 比赛 12.5
10.21 3 9:00-21:30 比赛 12.5
10.22 4 9:00-21:30 比赛 12.5
10.23 5 9:00-21:30 比赛 12.5
10.24 6 9:00-21:30 比赛 12.5
10.25 7
周合计时间
周平均时间

第6周-学习备忘录

日期 星期 时间记录 学习内容 日合计
10.12 1 9:00-21:30 比赛 12.5
10.13 2 9:00-21:30 比赛 12.5
10.14 3 9:00-21:30 比赛 12.5
10.15 4 9:00-21:30 比赛 12.5
10.16 5 9:00-21:30 比赛 12.5
10.17 6 9:00-21:30 比赛 12.5
10.18 7
周合计时间
周平均时间