版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
第6章UART通信引言本章学习目标了解UART通信的概念。理解UART的通信协议。UART模块。2掌握开发板UART编程与调试方法。6.1UART通信简介6.1.1概述4
通用异步收发器UART(UniversalAsynchronousReceiver/Transmitter),是一种广泛应用于嵌入式领域的串行、异步、全双工的通信协议,其工作原理是逐位传输数据的每个二进制位,将所需传输的数据一位接一位地传输,在UART通讯协议中信号线上的状态位高电平代表‘1’,低电平代表‘0’。其特点是通信线路简单,只要一对传输线就可以实现双向通信,大大降低了成本。通用异步收发器UART是一个异步串行的通信接口,UART主要用于和外部芯片的UART进行对接,实现两芯片间的通信。芯片提供3个UART单元,支持2线模式。UART通信如图6.1所示。图6.1UART通信示意图6.1.1概述5
串行通信是指利用一根传输线逐位依次传输数据,也可以用两根信号线组成全双工通信,如:RS232。特点是通信线路简单,使用简单的电缆即可实现通信,成本降低,适合远距离通信的应用,但传输速度慢。
异步通信以一个字符为传输单位。通信中两个字符之间的时间间隔不固定,但同一字符中两个相邻位之间的时间间隔是固定的。一般来说,两个UART设备之间的通信不需要时钟线。此时,需要在两个UART设备上指定相同的传输速率,以及空闲位、起始位、奇偶校验位和结束位,即遵循相同的协议。
数据传输速率以波特率表示,即每秒传输的位数。例如,如果数据传输速率为120个字符/秒,每个字符为10位(1个起始位、7个数据位、1个校验位、1个停止位),则其传输的波特率为10×120=1200个字符/秒=1200波特率。6.1.2功能描述6UART具有以下功能特点:
支持64×8bit的发送FIFO和64×12bit的接收FIFO(FirstInFirstOut)。支持数据位和停止位的位宽可编程:
(1)数据位可通过编程设定为5/6/7/8bit。
(2)停止位可通过编程设定为1/2bit。
支持奇、偶校验方式或无校验。
支持传输速率可编程、支持整数小数分频。支持接收FIFO中断、发送FIFO中断、接收超时中断、错误中断。支持初始中断状态查询和屏蔽后中断状态查询。支持通过编程禁止UART模块或UART发送/接收功能以降低功耗。UART0不支持硬件流控;UART1、UART2支持硬件流控。6.1.3接口信号7UART接口信号描述如表6.1所示。
信号名宽度(bit)方向功能描述RXD1I输入数据。TXD1O输出数据。CTS1I清除发送信号,用于硬件流控,低有效。RTS1O请求发送信号,用于硬件流控,低有效。表6.1UART接口信号描述6.2UART通信协议6.2UART通信协议9当两个设备需要通过UART协议进行通讯时,它们需要同时约定好以下内容:
(1)每一位信号的时间长度T(波特率=1/T)。
(2)帧结构中每一项的具体位数。
(3)是否有校验位,以及校验位的机制(奇/偶等)。数据通信格式如图6.2所示。图6.2数据通讯格式图6.2UART通信协议10波特率是UART协议,或者说所有异步串行协议,非常重要的一个概念,即单位时间内(1秒)可表示的bit位个数,或者也可以表述为bit位宽的倒数。例如一个波特率为115200的UART波形表示1秒可容纳115200个bit位,也就是说每一位bit数据占大约8.68uS的时长。波特率的定义如图6.3所示。图6.3波特率定义起始位:每次通信开始时,发送方发送一个逻辑“0”信号(VOL),表示传输字符的开始。由于总线空闲时为高电平,因此在开始通信时先发送一个与空闲状态明显不同的信号,即VOL。数据位:起始位之后是我们要传输的数据。数据位可以是5、6、7、8、9位等组成一个字符(通常是8位)。如ASCII码(7位)、扩展BCD码(8位)。首先发送最低位,最后发送最高位。用低电平
表示“0”,高电平表示“1”,完成数据位的传输。6.2UART通信协议11图6.3波特率定义奇偶校验位:将该位添加到数据位后,“1”位的个数应为偶数(偶校验)或奇数(奇校验),以验证数据传输的正确性。校验位实际上就是调整数,串口校验分为几种方式:奇校验:如果数据位中“1”的个数为偶数,则奇偶校验位为“1”,如果“1”的个数为奇数,则奇偶校验位为“0”。偶校验:如果数据中“1”的个数为偶数,则奇偶校验位为“0”,如果数据中“1”的个数为奇数,则奇偶校验位为“1”。标记奇偶校验:校验位始终为1(不常用)。奇偶校验:奇偶校验位始终为0(不常用)。停止位:它是字符数据的结束标记。它可以是1位、1.5位或2位VOH。由于数据在传输线上是定时的,并且每个设备都有自己的时钟,所以在通信时两个设备之间很可能会出现小的不同步。因此停止位不仅表示传输的结束,而且还为计算机提供了纠正时钟的机会。停止位越多,数据传输越稳定,但数据传输速度越慢。6.3UART模块6.3.1概述6.3.2功能描述13
通用异步收发器UART是一个异步串行的通信接口,UART主要用于和外部芯片的UART进行对接,实现两芯片间的通信。芯片提供3个UART单元,支持2线模式。概述功能描述—UART具有以下功能特点支持64×8bit的发送FIFO和64×12bit的接收FIFO(FirstInFirstOut)。支持数据位和停止位的位宽可编程:
①数据位可通过编程设定为5/6/7/8bit。
②停止位可通过编程设定为1/2bit。支持奇、偶校验方式或无校验。支持传输速率可编程、支持整数小数分频。支持接收FIFO中断、发送FIFO中断、接收超时中断、错误中断。支持初始中断状态查询和屏蔽后中断状态查询。支持通过编程禁止UART模块或UART发送/接收功能以降低功耗。UART0不支持硬件流控;UART1、UART2支持硬件流控。6.3.3工作方式141.接口信号UART接口信号描述如表6.2所示信号名宽度(bit)方向功能描述RXD1I输入数据。TXD1O输出数据。CTS1I清除发送信号,用于硬件流控,低电平有效。RTS1O清求发送信号,用于硬件流控,低电平有效。表6.2UART接口信号描述6.3.3工作方式152.初始化配置
UART初始化配置流程如下:
读UART_FR[busy],等待UART状态为空闲,对UART模块进行软复位。写UART_CR[uarten]为0,禁止UART。写UART_LCR_H为0,清空UART的配置。根据波特率配置UART_IBRD和UART_FBRD,设置波特率分频值。根据需求配置UART_LCR_H,设置奇偶校验位、数据长度、FIFO使能、停止位个数。写UART_IFLS,设置发送FIFO水线、接收FIFO水线、流控水线。如果需要进行DMA搬移,写UART_DMACR[txdmae]或[rxdmae]为1,使能UART的DMA。如果需要使能UART的硬件流控功能,写UART_CR[cts_en]、[rts_en]为1,启动硬件流控功能。写UART_CR[rxe]为1,配置UART接收使能;或写UART_CR[txe]为1,配置UART。发送使能;写UART_CR[uarten]为1,配置UART使能。在初始化寄存器之后便可以开始数据的传输。6.3.4寄存器概览16UART寄存器概览如表6.3所示。(UART0基址是0x4000_8000、UART1基址是0x4000_9000、UART2基址是0x4000_A000)。
偏移地址名称描述0x000UART_DRUART数据寄存器0x004UART_RSR接收状态/错误清除寄存器0x018UART_FRUART标志寄存器0x024UART_IBRD整数波特率寄存器0x028UART_FBRD小数波特率寄存器0x02CUART_LCR_H传输模式控制寄存器0x030UART_CRUART控制寄存器0x034UART_IFLS中断FIFO阈值选择寄存器0x038UART_IMSC中断屏蔽寄存器0x03CUART_RIS原始中断状态寄存器0x040UART_MIS屏蔽后中断状态寄存器0x044UART_ICR中断清除寄存器0x048UART_DMACRDMA控制寄存器表6.3UART寄存器概览6.3.5寄存器描述171.UART_DRUART_DR为UART数据寄存器。存放接收数据和发送数据,同时可以从该寄存器中读出接收状态。偏移地址为:0x000,总复位值为0x0000。其描述如表6.4所示。BitsAccess名称描述复位[15:12]-reserved保留。0×0[1]ROoe溢出错误状态位。0:无溢出错误;1:有溢出错误(即接收FIFO满且接收了一个数据)。0×0[10]RObeBreak错误状态位。0:无Break错误;1:有Break错误。Bresk的条件:接收数据的输入保持低电平的时间比一个全字传输(包括:start、data、parity、stopbit)还要长。0×0[9]ROpe校验错误状态位。0:无校验错误;1:有校验错误。0x0[8]ROfe帧错误状态位。0:无帧错误;1:有帧错误。0x0[7:0]RWdata接收数据和发送数据。0x00表6.4UART_DR描述6.3.5寄存器描述182.UART_RSRUART_RSR为接收状态/错误清除寄存器。读时作为接收状态寄存器;写时作为错误清除寄存器。偏移地址为:0x004,总复位值为0x00。其描述如表6.5所示。表6.5UART_RSR描述BitsAccess名称描述复位[7:4]-reserved保留。0×0[3]RWoe溢出错误状态和清除位。0:无溢出错误;1:有溢出错误当FIFO满时,FIFO中的内容保持有效(因为不会有下一个数据写到FIFO中,只是移位寄存器会溢出)。CPU必须立刻读数据以腾空FIFO。0×0[2]RWbeBreak错误状态和清除位。0:无Break错误;1:有Break错误。Bresk的条件:接收数据的输入保持低电平的时间比一个全字传输(包括:start、data、parity、stopbit)还要长。0×0[1]RWpe校验错误状态和清除位。0:无校验错误;1:接收数据和校验错误。0x0[0]RWfe帧错误状态和清除位。0:无帧错误;1:接收到的数据的停止位错误(有效的停止位为高电平)。0x06.3.5寄存器描述193.UART_FRUART_FR为UART标志寄存器。偏移地址为:0x018,总复位值为0x0197。其描述如表6.6所示。表6.6UART_FR描述BitsAccess名称描述复位[15:8]-reserved保留。0×0[7]ROtxfe发送FIFO空状态位。当UART_LCR_H[fen]为0时:0:发送HoldingRegister非空;1:发送HoldingRegister空。当UART_LCR_H[fen]为1时:0:发送FIFO为非空;1:发送FIFO为空。0×0[6]ROrxff接收FIFO满状态位。当UART_LCR_H[fen]为0时:0:接收HoldingRegister非满;1:接收HoldingRegister满。当UART_LCR_H[fen]为1时:0:接收FIFO为非满;1:接收FIFO为满。0×0[5]ROtxff发送FIFO满状态位。当UART_LCR_H[fen]为0时:0:发送HoldingRegister非满;1:发送HoldingRegister满。当UART_LCR_H[fen]为1时:0:发送FIFO为非满;1:发送FIFO为满。0x0[4]ROrxfe接收FIFO空状态位。当UART_LCR_H[fen]为0时:0:接收HoldingRegister非空;1:接收HoldingRegister空。当UART_LCR_H[fen]为1时:0:接收FIFO为非空;1:接收FIFO为空。0x0[3]RObusyUART忙闲状态位。0:空闲或完成发送数据;1:正忙于发送数据。该bit一旦置位,该状态一直保持到整个字节(包括所有的停止位)完全从移位寄存器中发送出去。发送FIFO非空,该bit置1。
[2:0]-reserved保留。6.3.5寄存器描述204.UART_IBRDUART_IBRD为整数波特率寄存器。偏移地址为:0x024,总复位值为0x0000。其描述如表6.7所示。表6.7UART_IBRD描述BitsAccess名称描述复位[15:0]RWbauddivint整数波特率分频值。注意:复位时全部清零。0×00006.3.5寄存器描述215.UART_FBRDUART_FBRD为小数波特率寄存器。UART_IBRD和UART_FBRD的值必须等到当前数据发送和接收完成才更新。最小的分频值为1,最大的分频值为65535(2^16-1)。UART_IBRD=0是无效的,此时UART_FBRD将被忽略;如果UART_IBRD=65535(0xFFFF),则UART_FBRD必须为0,否则会导致发送和接收的失败。
波特率分频值计算方法:计算理论分频系数n,UART的波特率=内部总线频率/(16×分频系数);分频值的整数部分UART_IBRD的配置值为integer(n);分频值得小数部分UART_FBRD的值为integer((n-integer(n))×64+0.5),integer()为向下取整函数。其描述如表6.8所示,偏移地址为:0x028,总复位值为0x00。BitsAccess名称描述复位[7:6]-reserved保留。0x0[5:0]RWbanddivfrac小数波特率分频值。注意:复位时全部清零。0×00表6.8
UART_FBRD描述6.3.5寄存器描述226.UART_LCR_HUART_LCR_H为传输模式控制寄存器。偏移地址为:0x02C,总复位值为0x0000。其描述如表6.9所示。BitsAccess名称描述复位[15:8]-reserved保留。0×00[7]RWsps校验位设置位。0:StickParity禁止;1:当[pen]、[eps]被置1时,奇偶校验位发送和校验按0校验;当[pen]被置1且[eps]为0时,奇偶校验位发送和校验按1校验。0x0[6:5]RWwlen指示发送和接收一帧数据的数据位数。00:5bit;01:6bit;10:7bit;11:8bit。0x0[4]RWfen发送和接收
FIFO使能控制。0:禁止;1:使能。0x0[3]RWstp2发送帧尾
2bit停止位判断。0:无2bit停止位;1:有2bit停止位。注意:接收逻辑在接收时不检查
2bit的停止位。0x0[2]RWeps发送和接收过程中的奇偶校验选择。0:生成或检查奇校验;1:生成或检查偶校验。注意:当[pen]为0时,该位不起作用。0x0[1]RWpen校验选择位。0:不作校验;1:发送方向产生校验,接收方向进行校验检查。0x0[0]RWbrk发送
Break选择位。0:无效;1:在完成当前数据的发送后,UTXD连续输出低电平。注意:须正确执行
Break命令,软件将该bit置1的时间必须超过2个完整帧;在正常使用中,该bit必须清零。0x0表6.9
UART_LCR_H描述6.3.5寄存器描述237.UART_CRUART_CR为UART控制寄存器。偏移地址为:0x030,总复位值为0x0000。其描述如表6.10所示。表6.10UART_CR描述BitsAccess名称描述复位[15]RWclsenCTS硬件流控使能。0:禁止;1:使能(仅当nUARTCTS信号有效时才发送数据)。0×00[14]RWrtsenRTS硬件流控使能。0:禁止;1:使能(仅当接收FIFO有空间时才请求接收数据)。0x0[13:12]-reserved保留。0x0[11]RWrts请求发送设置位。该
bit为UARTModem状态输出信号nUARTRTS的取反。0:输出信号不变;1:输出信号为0。0x0[10]RWdtr数据发送准备设置位。该
bit为UARTModem状态输出信号nUARTDTR的取反。0:输出信号不变;1:输出信号为0。0x0[9]RWrxeUART接收使能。0:禁止;1:使能。注意:在接收的过程中如果
UART被禁止,则当前数据的接收会在正常停止之前结束。0x0[8]RWtxeUART发送使能。0:禁止;1:使能。注意:在发送的过程中如果
UART被禁止,则当前数据的发送会在正常停止之前结束。0x0[7]RWlbe环回使能。0:禁止;1:UARTTXD输出环回到UARTRXD。0x0[6:1]-reserved保留。
[0]RWuartenUART使能。0:禁止;1:使能。注意:如果在发送和接收过程中将
UART禁止,则会在正常停止之前结束当前数据的传送。6.3.5寄存器描述248.UART_IFLSUART_IFLS为中断FIFO阈值选择寄存器。用于设置FIFO的中断(UART_TXINTR或UART_RXINTR)触发线。偏移地址为:0x034,总复位值为0x0092。其描述如表6.11所示。表6.11UART_IFLS描述BitsAccess名称描述复位[15:9]-reserved保留。0×00[8:6]RWrtsflsel硬件流控
rts_n的触发条件选择(触发点如下)。000:接收FIFO≥1/8full;001:接收FIFO≥1/4full;010:接收FIFO≥1/2full;011:接收FIFO≥3/4full;100:接收FIFO≥7/8full;101~111:保留。0x2[5:3]RWrxiflsel接收中断
FIFO的阈值选择(接收中断的触发点如下)。000:接收FIFO≥1/8full;001:接收FIFO≥1/4full;010:接收FIFO≥1/2full;011:接收FIFO≥3/4full;100:接收FIFO≥7/8full;101~111:保留。0x2[2:0]RWtxiflsel发送中断
FIFO的阈值选择(发送中断的触发点如下)。000:发送FIFO≤1/8full;001:发送FIFO≤1/4full;011:发送FIFO≤3/4full;010:发送FIFO≤1/2full;100:发送FIFO≤7/8full;101~111:保留。0x26.3.5寄存器描述259.UART_IMSCUART_IMSC为中断屏蔽寄存器。偏移地址为:0x038,总复位值为0x0000。其描述如表6.12所示。表6.12UART_IFLS描述BitsAccess名称描述复位[15:11]-reserved保留。0×00[10]RWoeim溢出错误中断的屏蔽状态。0:屏蔽;1:不屏蔽。0x0[9]RWbeimBreak错误中断的屏蔽状态。0:屏蔽;1:不屏蔽。0x0[8]RWpeim校验中断的屏蔽状态。0:屏蔽;1:不屏蔽。0x0[7]RWfeim帧错误中断的屏蔽状态。0:屏蔽;1:不屏蔽。0x0[6]RWrtim接收超时中断的屏蔽状态。0:屏蔽;1:不屏蔽。0x0[5]RWtxim发送中断的屏蔽状态。0:屏蔽;1:不屏蔽。0x0[4]RWrxim接收中断的屏蔽状态。0:屏蔽;1:不屏蔽。0x0[3:0]-reserved保留。0x06.3.5寄存器描述2610.UART_RISUART_RIS为原始中断状态寄存器。偏移地址为:0x03C,总复位值为0x000F。其描述如表6.13所示。表6.13UART_RIS描述BitsAccess名称描述复位[15:11]-reserved保留。0×00[10]ROoeris原始的溢出错误中断状态。0:未产生中断;1:已产生中断。0x0[9]ROberis原始的
Break错误中断状态。0:未产生中断;1:已产生中断。0x0[8]ROperis原始的校验中断状态。0:未产生中断;1:已产生中断。0x0[7]ROferis原始的错误中断状态。0:未产生中断;1:已产生中断。0x0[6]ROrtris原始的接收超时中断状态。0:未产生中断;1:已产生中断。0x0[5]ROtxris原始的发送中断状态。0:未产生中断;1:已产生中断。0x0[4]ROrxris原始的接收中断状态。0:未产生中断;1:已产生中断。0x0[3:0]-reserved保留。0xF6.3.5寄存器描述2711.UART_MISUART_MIS为屏蔽后中断状态寄存器。偏移地址为:0x040,总复位值为0x0000。其描述如表6.14所示。表6.14UART_MIS描述BitsAccess名称描述复位[15:11]-reserved保留。0×00[10]ROoemis屏蔽后的溢出错误中断状态。0:未产生中断;1:已产生中断。0x0[9]RObemis屏蔽后的
Break错误中断状态。0:未产生中断;1:已产生中断。0x0[8]ROpemis屏蔽后的校验中断状态。0:未产生中断;1:已产生中断。0x0[7]ROfemis屏蔽后的错误中断状态。0:未产生中断;1:已产生中断。0x0[6]ROrtmis屏蔽后的接收超时中断状态。0:未产生中断;1:已产生中断。0x0[5]ROtxmis屏蔽后的发送中断状态。0:未产生中断;1:已产生中断。0x0[4]ROrxmis屏蔽后的接收中断状态。0:未产生中断;1:已产生中断。0x0[3:0]-reserved保留。0xF6.3.5寄存器描述2812.UART_ICRUART_ICR为中断清除寄存器。偏移地址为:0x044,总复位值为0x0000。其描述如表6.15所示。表6.15UART_ICR描述BitsAccess名称描述复位[15:11]-reserved保留。0×00[10]WOoeic清除溢出错误中断。0:无效;1:清除中断。0x0[9]WObeic清除
Break错误中断。0:无效;1:清除中断。0x0[8]WOpeic清除校验中断。0:无效;1:清除中断。0x0[7]WOfeic清除错误中断。0:无效;1:清除中断。0x0[6]WOrtic清除接收超时中断。0:无效;1:清除中断。0x0[5]WOtxic清除发送中断。0:无效;1:清除中断。0x0[4]WOrxic清除接收中断。0:无效;1:清除中断。0x0[3:0]-reserved保留。0x06.3.5寄存器描述2913.UART_DMACRUART_DMACR为DMA控制寄存器。用于配置发送FIFO和接收FIFO的DMA使能。偏移地址为:0x048,总复位值为0x0000。其描述如表6.16所示。表6.16UART_DMACR描述BitsAccess名称描述复位[15:3]-reserved保留。0×0000[2]RWdmaonerrUART错误中断(UARTEINTR)有效时,接收通道DMA使能控制。0:请求输出(UARTRXDMASREQ或UARRTXDMABREQ)有效;1:请求输出(UARTRXDMASREQ或UARRTXDMABREQ)无效。0x0[1]RWtxdmae发送
FIFO的DMA使能控制。0:禁止;1:使能。0x0[0]RWrxdmae接收
FIFO的DMA使能控制。0:禁止;1:使能。0x06.4UART编程与应用6.4UART编程与应用31UART串口为端口3,位于开发板右侧,如图6.4所示为UART串口位置,图6.5为UART电路原理图。图6.4UART串口位置图6.5UART电路原理图6.4UART编程与应用32在使用UART进行通信时,首先要调用UartOpen获取UART设备句柄,该函数会返回指定端口号的UART设备句柄:structDevHandle*UartOpen(uint32_tport);在通信之前,需要设置UART的波特率,设置波特率的函数如下所示:int32_tUartSetBaud(structDevHandle*handle,uint32_tbaudRate);设置UART的波特率后,可以通过获取波特率接口来查看UART当前的波特率,获取波特率的函数如下所示:int32_tUartGetBaud(structDevHandle*handle,uint32_t*baudRate);在通信之前,需要设置UART的设备属性,设置设备属性的函数如下图所示:int32_tUartSetAttribute(structDevHandle*handle,structUartAttribute*attribute);设置UART的设备属性后,可以通过获取设备属性接口来查看UART当前的设备属性,获取设备属性的函数如下图所示:int32_tUartGetAttribute(structDevHandle*handle,structUartAttribute*attribute);6.4UART编程与应用33在通信之前,需要设置UART的传输模式,设置传输模式的函数如下图所示:int32_tUartSetTransMode(structDevHandle*handle,enumUartTransModemode);向UART设备写入指定长度的数据,对应的接口函数如下所示:int32_tUartWrite(structDevHandle*handle,uint8_t*data,uint32_tsize);从UART设备中读取指定长度的数据,对应的接口函数如下所示:int32_tUartRead(structDevHandle*handle,uint8_t*data,uint32_tsize);UART通信完成之后,需要销毁UART设备句柄,函数如下所示:voidUartClose(structDevHandle*handle);该函数会释放申请的资源。如图6.6为UART使用流程图。图6.6UART使用流程图6.4UART编程与应用34如下所示为UART通信的使用示例,使用了OpenHarmony操作系统的API。#include<stdio.h>#include<unistd.h>#include<string.h>#include"iot_gpio_ex.h"#include"ohos_init.h"#include"cmsis_os2.h"#include"iot_gpio.h"#include"iot_uart.h"#include"hi_uart.h"#include"iot_watchdog.h"#include"iot_errno.h"#defineUART_BUFF_SIZE100#defineU_SLEEP_TIME1000006.4UART编程与应用35voidUart1GpioInit(void){IoTGpioInit(IOT_IO_NAME_GPIO_0);//设置GPIO0的管脚复用关系为UART1_TXSetthepinreuserelationshipofGPIO0toUART1_TXIoSetFunc(IOT_IO_NAME_GPIO_0,IOT_IO_FUNC_GPIO_0_UART1_TXD);IoTGpioInit(IOT_IO_NAME_GPIO_1);//设置GPIO1的管脚复用关系为UART1_RXSetthepinreuserelationshipofGPIO1toUART1_RXIoSetFunc(IOT_IO_NAME_GPIO_1,IOT_IO_FUNC_GPIO_1_UART1_RXD);}voidUart1Config(void){uint32_tret;/*初始化UART配置,波特率9600,数据bit为8,停止位1,奇偶校验为NONE*//*InitializeUARTconfiguration,baudrateis9600,databitis8,stopbitis1,parityisNONE*/
6.4UART编程与应用36IotUartAttributeuart_attr={.baudRate=9600,.dataBits=8,.stopBits=1,.parity=0,};ret=IoTUartInit(HI_UART_IDX_1,&uart_attr);if(ret!=IOT_SUCCESS){printf("InitUart1FaliedErrorNo:%d\n",ret);return;}}staticvoidUartTask(void){constchar*data="HelloOpenHarmony!!!\n";uint32_tcount=0;uint32_tlen=0;unsignedcharuartReadBuff[UART_BUFF_SIZE]={0};
6.4UART编程与应用37//对UART1的一些初始化SomeinitializationofUART1Uart1GpioInit();//对UART1参数的一些配置SomeconfigurationsofUART1parametersUart1Config();while(1){//通过UART1发送数据SenddatathroughUART1IoTUartWrite(HI_UART_IDX_1,(unsignedchar*)data,strlen(data));//通过UART1接收数据ReceivedatathroughUART1len=IoTUartRead(HI_UART_IDX_1,uartReadBuff,UART_BUFF_SIZE);if(len>0){//把接收到的数据打印出来Printthereceiveddataprintf("UartReadDatais:[%d]%s\r\n",count,uartReadBuff);}usleep(U_SLEEP_TIME);count++;}}6.4UART编程与应用38voidUartExampleEntry(void){osThreadAttr_tattr;IoTWatchDogDisable();="UartTask";attr.attr_bits=0U;attr.cb_mem=NULL;attr.cb_size=0U;attr.stack_mem=NULL;attr.stack_size=5*1024;//任务栈大小*1024stacksize5*1024attr.priority=osPriorityNormal;if(osThreadNew((osThreadFunc_t)UartTask,NULL,&attr)==NULL){printf("[UartTask]FailedtocreateUartTask!\n");}}APP_FEATURE_INIT(UartExampleEntry);6.4UART编程与应用39下面是代码的主要功能和一些关键点的解释:头文件包含:代码首先包含了一些必要的头文件,包括标准输入输出库stdio.h,用于进程间通信的unistd.h,以及OpenHarmony操作系统的GPIO、UART、看门狗等硬件接口和系统初始化的头文件。宏定义:定义了两个宏UART_BUFF_SIZE和U_SLEEP_TIME,分别用于设置UART接收缓冲区的大小和线程休眠时间。GPIO初始化函数:Uart1GpioInit函数初始化了用于UART1通信的GPIO管脚,并设置了它们的复用功能。UART配置函数:Uart1Config函数配置了UART1的参数,包括波特率、数据位、停止位和奇偶校验位。UART任务函数:UartTask是一个无限循环的任务,它通过UART1发送和接收数据。发送的数据是字符串"HelloOpenHarmony!!!\n",接收到的数据会被打印出来。线程创建:UartExampleEntry函数用于创建一个线程来运行UartTask函数。线程的属性被设置,包括名称、栈大小和优先级。应用程序特性初始化:使用APP_FEATURE_INIT宏来初始化应用程序的特性。代码中使用了IoTUartWrite和IotUartRead函数来发送和接收数据,这两个函数是OpenHarmony操作系统提供的标准API。此外,usleep函数用来使线程休眠,以避免CPU过度占用。6.4UART编程与应用40#include<stdio.h>包含标准输入输出库的头文件,它提供了输入输出函数,如printf。#include<unistd.h>包含Unix标准函数库的头文件,提供了一些基本的系统调用,如usleep。#include<string.h>包含字符串操作函数的头文件,如strlen。#include"iot_gpio_ex.h";包含GPIO扩展功能的头文件,用于操作通用输入输出(GPIO)。#include"ohos_init.h"包含OpenHarmony操作系统初始化功能的头文件。#include"cmsis_os2.h"包含CMSIS-RTOSAPI的头文件,用于操作系统服务。6.4UART编程与应用41包含GPIO功能的头文件。#include"iot_uart.h"包含UART(通用异步接收/发送器)功能的头文件。#include"hi_uart.h"包含特定于HiSilicon平台的UART功能的头文件。#include"iot_watchdog.h"包含看门狗定时器功能的头文件。#include"iot_errno.h"包含错误号定义的头文件。#defineUART_BUFF_SIZE100定义一个宏UART_BUFF_SIZE,设置UART接收缓冲区的大小为100字节。#include"iot_gpio.h"6.4UART编程与应用42包含GPIO功能的头文件。#include"iot_uart.h"包含UART(通用异步接收/发送器)功能的头文件。#include"hi_uart.h"包含特定于HiSilicon平台的UART功能的头文件。#include"iot_watchdog.h"包含看门狗定时器功能的头文件。#include"iot_errno.h"包含错误号定义的头文件。#defineUART_BUFF_SIZE100定义一个宏UART_BUFF_SIZE,设置UART接收缓冲区的大小为100字节。#include"iot_gpio.h"6.4UART编程与应用43定义一个宏U_SLEEP_TIME,设置线程休眠时间为100000微秒(即100毫秒)。voidUart1GpioInit(void)定义一个函数Uart1GpioInit,用于初始化UART1的GPIO管脚。IoTGpioInit(IOT_IO_NAME_GPIO_0);初始化GPIO0管脚。。IoSetFunc(IOT_IO_NAME_GPIO_0,IOT_IO_FUNC_GPIO_0_UART1_TXD);设置GPIO0管脚的功能为UART1的发送(TX)。IoTGpioInit(IOT_IO_NAME_GPIO_1);初始化GPIO1管脚。IoSetFunc(IOT_IO_NAME_GPIO_1,IOT_IO_FUNC_GPIO_1_UART1_RXD);设置GPIO1管脚的功能为UART1的接收(RX)。#defineU_SLEEP_TIME1000006.4UART编程与应用44定义一个函数Uart1Config,用于配置UART1的参数。IotUartAttributeuart_attr={...};定义一个IotUartAttribute结构体,用于存储UART的配置属性。IoTUartInit(HI_UART_IDX_1,&uart_attr);使用IoTUartInit函数初始化UART1,传入UART索引和配置属性。staticvoidUartTask(void)定义一个静态函数UartTask,这是一个线程函数,用于处理UART通信。constchar*data="HelloOpenHarmony!!!\n";定义一个字符串data,用于通过UART发送。uint32_tcount=0;定义一个计数器count,用于记录发送数据的次数。voidUart1Config(void)6.4UART编程与应用45定义一个缓冲区uartReadBuff,用于存储从UART接收的数据。IoTUartWrite(HI_UART_IDX_1,(unsignedchar*)data,strlen(data));通过UART1发送数据。IoTUartRead(HI_UART_IDX_1,uartReadBuff,UART_BUFF_SIZE);从UART1接收数据。printf("UartReadDatais:[%d]%s\r\n",count,uartReadBuff);打印接收到的数据。usleep(U_SLEEP_TIME);使线程休眠一段时间。voidUartExampleEntry(void)定义一个函数UartExampleEntry,用于初始化和启动UART通信任务。unsignedcharuartReadBuff[UART_BUFF_SIZE]={0};6.4UART编程与应用46定义一个线程属性结构体attr。IoTWatchDogDisable();禁用看门狗定时器。if(osThreadNew((osThreadFunc_t)UartTask,NULL,&attr)==NULL){...}创建并启动一个新线程,运行UartTask函数。PP_FEATURE_INIT(UartExampleEntry);使用宏APP_FEATURE_INIT来初始化应用程序的特性,调用UartExampleEntry函数。#include"iot_errno.h"#include"iot_gpio_ex.h"#include"hi_gpio.h"#include"hi_io.h"#include"hi_task.h"#include"hi_types_base.h"osThreadAttr_tattr;6.4UART编程与应用47unsignedintIoSetPull(unsignedintid,IotIoPullval){if(id>=HI_GPIO_IDX_MAX){returnIOT_FAILURE;}returnhi_io_set_pull((hi_io_name)id,(hi_io_pull)val);}unsignedintIoSetFunc(unsignedintid,unsignedcharval){if(id>=HI_GPIO_IDX_MAX){returnIOT_FAILURE;}returnhi_io_set_func((hi_io_name)id,val);}6.4UART编程与应用48unsignedintTaskMsleep(unsignedintms){if(ms<=0){returnIOT_FAILURE;}returnhi_sleep((hi_u32)ms);}这段代码定义了三个函数,它们用于设置GPIO的上拉/下拉电阻、配置GPIO的功能以及使任务休眠。下面是对每个函数的详细解释:(1)IoSetPull函数这个函数用于设置指定GPIO的上拉或下拉电阻。它接收两个参数:id是GPIO的索引,val是指定的上拉或下拉配置值。unsignedintIoSetPull(unsignedintid,IotIoPullval)6.4UART编程与应用49if(id>=HI_GPIO_IDX_MAX){returnIOT_FAILURE;}首先检查提供的GPIO索引id是否超出了允许的最大值HI_GPIO_IDX_MAX。如果超出,函数返回错误代码IOT_FAILURE。(1)IoSetPull函数returnhi_io_set_pull((hi_io_name)id,(hi_io_pull)val);如果索引有效,函数调用hi_io_set_pull来设置GPIO的上拉或下拉电阻。hi_io_name和hi_io_pull是类型转换,确保传入的参数类型正确。6.4UART编程与应用50(2)IoSetFunc函数unsignedintIoSetFunc(unsignedintid,unsignedcharval)这个函数用于设置指定GPIO的功能。它接收两个参数:id是GPIO的索引,val是指定的功能配置值。if(id>=HI_GPIO_IDX_MAX){returnIOT_FAILURE;}与IoSetPull类似,这个函数首先检查GPIO索引是否有效。returnhi_io_set_func((hi_io_name)id,val);如果索引有效,函数调用hi_io_set_func来设置GPIO的功能。6.4UART编程与应用51(2)TaskMsleep函数unsignedintTaskMsleep(unsignedintms)这个函数用于使当前任务休眠指定的毫秒数。它接收一个参数:ms是休眠的毫秒数。if(ms<=0){returnIOT_FAILURE;}如果传入的休眠时间小于或等于0,函数返回错误代码IOT_FAILURE。returnhi_sleep((hi_u32)ms);如果休眠时间有效,函数调用hi_sleep来使任务休眠指定的毫秒数。hi_u32是一个类型转换,确保传入的参数类型正确。这些函数都是与硬件交互的基础函数,通常在嵌入式系统编程中使用,特别是在需要直接控制硬件设备时。IOT_FAILURE是一个错误代码,通常定义在iot_errno.h头文件中,表示函数执行失败。6.4UART编程与应用52另外,需要头文件用于嵌入式系统中对GPIO(通用输入输出)进行配置和管理。下面是对头文件中定义的主要内容的详细解释:#ifndefIOT_GPIO_EX_H#defineIOT_GPIO_EX_H(1)防止头文件重复包含的宏这部分是标准的C语言预处理指令,用于防止头文件被重复包含,从而避免定义重复的问题。(2)枚举类型定义IotIoPull:定义了GPIO的上拉或下拉设置,包括无上拉下拉(IOT_IO_PULL_NONE)、上拉(IOT_IO_PULL_UP)和下拉(IOT_IO_PULL_DOWN)。(3)GPIO管脚ID枚举IotIoName:列出了所有可用的GPIO管脚ID,例如IOT_IO_NAME_GPIO_0到IOT_IO_NAME_GPIO_14,以及其他特殊功能的管脚如IOT_IO_NAME_SFC_CSN等。6.4UART编程与应用53#endif(4)特定GPIO管脚功能枚举例如IotIoFuncGpio0枚举定义了GPIO_0管脚可以配置的不同功能,如普通GPIO、UART1的TXD、SPI1的时钟线等。(5)驱动能力枚举IotIoDriverStrength:定义了GPIO驱动能力的级别,从0(驱动能力最高)到7(驱动能力最低)。(6)函数声明IoSetPull(unsignedintid,IotIoPullval):声明了一个函数,用于设置指定GPIO管脚的上拉或下拉电阻。IoSetFunc(unsignedintid,unsignedcharval):声明了一个函数,用于设置指定GPIO管脚的功能。TaskMsleep(unsignedintms):声明了一个函数,用于使当前任务休眠指定的毫秒数。(7)结束宏这行代码标志着头文件的结束,与最开始的#ifndef和#define配合使用,防止头文件内容被多次处理。Thankyou
forwatching!第7章I²C通信55引言本章学习目标了解I²C总线对于实际应用中起到的作用熟悉I²C总线的功能特性及基本构成理解总线的通信过程以及在通信过程中的协议术语56掌握利用I²C总线通讯来进行的编程应用,将所学到的知识与实验进行有机结合第7章I²C通信7.1I²C总线简介7.2I²C总线协议7.3I²C编程与应用577.1I²C总线简介58I²C(Inter-IntegratedCircuit)总线是Philips公司在八十年代初推出的一种串行、半双工的总线,主要用于近距离、低速芯片之间的通信;I²C总线有两根双向的信号线,一根数据总线SDA用于收发数据,一根时钟总线SCL用于通信双方时钟的同步;I²C总线硬件结构简单,简化了PCB布线,降低了系统成本,提高了系统可靠性,因此在各个领域得到了广泛应用。I²C总线可以通过外部连线进行在线检测,便于系统故障诊断和调试,故障可以立即被寻址,软件也有利于标准化和模块化,缩短开发时间。总线上可挂接的设备数量受总线的最大电容限制。串行的8位双向数据传输速率在标准模式下可达100Kbit/s,快速模式下可达400Kbit/s,高速模式下可达3.4Mbit/s。同时总线具有极低的电流消耗,抗噪声干扰能力强,增加总线驱动器可以使总线电容扩大10倍,传输距离达到15m;也可兼容不同电压等级的器件,且工作温度范围宽。第7章I²C通信7.3I²C编程与应用597.2I²C总线协议7.1I²C总线简介7.2.1I²C总线通信过程60I²C总线工作是按照一定的协议来运行的,接下来就看一下I²C协议。I²C是支持多从机的,也就是一个I²C控制器下可以挂多个I²C从设备,这些不同的I²C从设备有不同的器件地址,这样I²C主控制器就可以通过I²C设备的器件地址访问指定的I²C设备了,I²C总线连接多个I²C设备的连接图如图所示(其中,上拉电阻为4.7K):7.2.1I²C总线通信过程61I²C总线通信过程如下:(1)主机发送起始信号启用总线;(2)主机发送一个字节数据指明从机地址和后续字节的传送方向;(3)被寻址的从机发送应答信号回应主机;(4)发送器发送一个字节数据;(5)接收器发送应答信号回应发送器;(6)循环步骤4、5;(7)通信完成后主机发送停止信号释放总线;第4步和第5步使用的是发送器和接收器,并不是主机和从机,因为这是由第一个字节的最后一位来决定的是主给从发,还是从给主发。也就是说,第一个字节和最后的停止信号一定是主机发给从机。但中途并不固定。且发送数据过程中不允许改变发送方向。7.2.2I²C协议术语62起始信号和结束信号起始信号:当SCL为高电平时,SDA从高到低的跳变产生的信号为起始信号。即SDA出现下降沿表示为起始位;结束信号:当SCL为高电平时,SDA从低到高的跳变产生的信号为结束信号。即SDA出现上升沿表示为起始位;起始信号和停止信号都是由主机发出,起始信号产生后总线处于占用状态,停止信号产生后总线被释放,处于空闲状态。空闲时,SCL与SDA都是高电平。如图所示:7.2.1任务分发器类型63字节传输格式与应答信号I²C总线通信时每个字节为8位长度,数据传送时,先传送最高位,后传送低位,发送器发送完一个字节数据后接收器必须发送1位应答位来回应发送器,即一帧共有9位。且I²C每次发送数据必须是8位。数据的先后顺序为:MSB固定,先发高位,再发低位。协议规定数据传输过程必须包含应答(ACK)。接收器通过应答告知发送的字节已被成功接收,之后发送器可以进行下一个字节的传输。主机产生数据传输过程中的所有时钟,包括用于应答的第9个时钟。发送器在应答时钟周期内释放对SDA总线的控制,这样接收器可以通过将SDA线拉低告知发送器:数据已被成功接收。应答信号分为两种:(1)当第9位(应答位)为低电平时,为ACK(Acknowledge)
信号。(2)当第9位(应答位)为高电平时,为NACK(NotAcknowledge)信号。7.2.1任务分发器类型64主机发送数据,从机接收时,ACK信号由从机发出。当在SCL第9位时钟高电平信号期间,如果SDA仍然保持高电平,则主机可以直接产生STOP条件终止以后的传输或者继续开始一个新的传输。从机发送数据,主机读取数据时,ACK信号由主机给出。主机响应ACK表示还需要再接收数据,而当主机接收完想要的数据后,通过发送NACK告诉从机读取数据结束、释放总线。随后主机发送STOP命令,将总线释放,结束读操作。I²C信号传输图如图所示:7.2.2I²C协议术语65寻址方式I²C总线上传送的数据是广义的,既包括地址,又包括真正的数据。主机在发送起始信号后必须先发送一个字节的数据,该数据的高7位为从机地址,最低位表示后续字节的传送方向,‘0’表示主机发送数据给->从机,‘1’表示从机发送数据给->主机。总线上所有的从机接收到该字节数据后都将这7位地址与自己的地址进行比较,如果相同,则认为自己被主机寻址,然后再根据第8位将自己定为发送器或接收器。7.2.2I²C协议术语66同步数据信号I²C总线在进行数据传送时,时钟线SCL为低电平期间,发送器向数据线上发送一位数据,在此期间数据线上的信号允许发生变化,时钟线SCL为高电平期间,接收器从数据线上读取一位数据,在此期间数据线上的信号不允许发生变化,必须保持稳定。I²C同步数据信号图如图所示:7.2.2I²C协议术语67I²C时序主机通过I²C总线与从机之间进行通信不外乎两个操作:写和读,I²C总线单字节写时序如图所示:7.2.2I²C协议术语68I²C单字节读时序比写时序要复杂一点,读时序分为4大步,第一步是发送设备地址,第二步是发送要读取的寄存器地址,第三步重新发送设备地址,最后一步就是I²C从器件输出要读取的寄存器值。7.2.2I²C协议术语69时钟同步与仲裁SCL线的同步(时钟同步):SCL同步是由于总线具有线“与”的逻辑功能,即只要有一个节点发送低电平时,总线上就表现为低电平。当所有的节点都发送高电平时,总线才能表现为高电平。正是由于线“与”逻辑功能的原理,当多个节点同时发送时钟信号时,在总线上表现的是统一的时钟信号。SDA线的仲裁:SDA线的仲裁也是建立在总线具有线“与”逻辑功能的原理上的。节点在发送1位数据后,比较总线上所呈现的数据与自己发送的是否一致。是,继续发送;否则,退出竞争。SDA线的仲裁可以保证I²C总线系统在多个主节点同时企图控制总线时通信正常进行并且数据不丢失。总线系统通过仲裁只允许一个主节点可以继续占据总线。7.2.2I²C协议术语70仲裁过程:DATA1和DATA2分别是主节点向总线所发送的数据信号,SDA为总线上所呈现的数据信号,SCL是总线上所呈现的时钟信号。当主节点1、2同时发送起始信号时,两个主节点都发送了高电平信号。这时总线上呈现的信号为高电平,两个主节点都检测到总线上的信号与自己发送的信号相同,继续发送数据。第2个时钟周期,2个主节点都发送低电平信号,在总线上呈现的信号为低电平,仍继续发送数据。在第3个时钟周期,主节点1发送高电平信号,而主节点2发送低电平信号。根据总线的线“与”的逻辑功能,总线上的信号为低电平,这时主节点1检测到总线上的数据和自己所发送的数据不一样,就断开数据的输出级,转为从机接收状态。这样主节点2就赢得了总线,而且数据没有丢失,即总线的数据与主节点2所发送的数据一样,而主节点1在转为从节点后继续接收数据,同样也没有丢掉SDA线上的数据。因此在仲裁过程中数据没有丢失。SDA仲裁和SCL时钟同步处理过程没有先后关系,而是同时进行的。仲裁过程如下图所示:
7.2.2I²C协议术语71第7章I²C通信727.1I²C总线简介7.3I²C编程与应用7.2I²C总线7.3.1OLED显示731.实验目的●学会I²C总线与ssd1306芯片进行通信。●了解ssd1306芯片相关知识。●熟悉HarmonyOS系统的编译流程和设计步骤,能够进行设计、编程、调试。●检测搭建的软件及硬件环境是否可正常使用。2.实验要求
在OLED显示屏上显示一个模拟时钟,用来显示日期时间。3.实验原理
在本实验中,使用I²C总线与ssd1306进行通信,在0.96寸显示屏上显示数据。该实验的硬件连接图
如下图所示:7.3.1OLED显示74Hi3861核心板原理图7.3.1OLED显示750.96寸OLED显示屏的电路原理图7.3.1OLED显示76SSD1306芯片简介SSD1306是一款携带控制器的用于OLED点阵图形显示系统的单片CMOSOLED/PLED驱动器。它由128个SEG(列输出)和64个COM(行输出)组成。该芯片专为共阴极OLED面板设计。SSD1306内置对比度控制器、显示RAM(GDDRAM)和振荡器,以此减少了外部元件的数量和功耗。该芯片有256级亮度控制。数据或命令由通用微控制器通过硬件选择的6800/8000系统通用并行接口、I²C接口或串行外围接口发送,以适应不同的应用需求。SSD1306芯片因其低功耗、高对比度以及灵活的接口选项,在OLED显示领域得到了广泛应用,例如,该芯片适用于许多小型便携式应用,如手机副显示屏、MP3播放器和计算器等。此外,它还拥有支持水平和垂直方向的屏幕滚动功能以及支持行重映射和列重映射的重映射功能,从而大大增加了显示的灵活性。7.3.1OLED显示77图像显示RAM(GDDRAM):GDDRAM是位映射静态RAM,大小为128x64位。GDDRAM分为8页(PAGE0~PAGE7),每页内1个SEG对应1Byte数据,一页由128Byte组成。一帧显示数据为1024Byte(1KB)。即屏幕每8行像素点(8*PIXEL)记为一页(PAGE),64行即为8页,则屏幕变为128列(ROW)8页(PAGE),若要显示整个屏幕,则需要128*8个1字节数。GDDRAM的内部结构如下图所示。7.3.1OLED显示78由该芯片进行驱动的0.96寸的OLED显示屏引脚功能表如下所示:芯片与I²C总线通信协议I²C总线包含从机地址位SA0(D/C#),数据信号线SDA和时钟信号线SCL组成。SDA和SCL线都必须接上拉电阻,RES#引脚用来初始化芯片且低电平有效。7.3.1OLED
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- (2026版)医院培训计划
- 2026注册安全工程师考试建筑施工(中级)安全生产专业实务试卷及解答参考
- 击奏乐器制作工创新方法强化考核试卷含答案
- 畜禽屠宰加工工岗前冲突解决考核试卷含答案
- 边坡工安全生产知识水平考核试卷含答案
- 数据安全管理员安全理论竞赛考核试卷含答案
- 刻瓷工安全实操强化考核试卷含答案
- 网球制作工岗位教育考核试卷含答案
- 特种弹簧制作工安全文明评优考核试卷含答案
- 母婴护理员岗位实践水平考核试卷含答案
- 2026年安全总监(安质部长)及安全管理人员试题及答案
- 视网膜静脉阻塞诊断与处理专家共识
- 智造未来-2026年智能建造技术与施工机器人应用全景
- 自考 07049 心理咨询原理与技术・重点精简版(速记 + 考点)
- 智慧施工组织设计课件 模块7、8- 编制施工现场平面布置图、编制施工管理计划
- 国家管网集团2027届高校毕业生招聘考试参考题库及答案解析
- T/TMAC 248-2025连续石墨化炉能源消耗限额
- 2026年摩托车全新考试题库(含详细答案解析、考场原题风格)
- 2026年中盐榆林盐化有限公司秋季招聘笔试模拟试题及答案详解
- 2026年秋新教材粤教粤科版小学科学五年级上册教学计划及进度表
- 2026消防宣传面试题及答案
评论
0/150
提交评论