完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
端口复用
STM32有很多的内置外设,这些外设的外部引脚都是与 GPIO 复用的。也就是说,一个 GPIO如果可以复用为 内置外设的功能引脚,那么当这个 GPIO 作为内置外设使用的时候,就叫做复用。这部分知识在《 STM32 中文参考手册 V10 》的 P109 P116~P121 有详细的讲解哪些 GPIO 管脚是可以复用为哪些内置外设的。这里我们就不一一讲解。 大家都知道,MCU 都有串口, STM32 有好几个串口。比如说STM32F103ZET6 有 5 个串口,我们可以查手册知道,串口 1 的引脚对应的 IO 为 PA9,PA10。 PA9 PA10 默认功能是 GPIO 所以当PA9,PA10 引脚作为串口 1 的 TX,RX 引脚使用的时候,那就是端口复用。 复用端口初始化有几个步骤: 1) GPIO 端口时钟使能 。要使用到端口复用,当然要使能端口的时钟了。 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); 1 2) 复用的外设时钟使能。比如你要将端口 PA9,PA10 复用为串口,所以要使能串口时钟。 RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); 1 3) 端口模式配置。 在 IO 复用位内置外设功能引脚的时候,必须设置 GPIO 端口的模式,至于 在 复用功能下 GPIO 的模式是怎么对应的,这个可以查看手册《 STM32 中文参考手册 V10 》 P110 的表格“ 8.1.11 外设的 GPIO 配置”。这里我们拿 Usart1 举例: 从表格中可以看出,我们要配置全双工的串口1 ,那么 TX 管脚需要配置为推挽复用输出,RX 管脚配置为浮空输入或者带上拉输入。 //USART1_TX PA.9 复用推挽输出 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9 GPIO_InitStructur e.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; 复用推挽输出 GPIO_Init(GPIOA, &GPIO_InitStructure); //USART1_RX PA.10 浮空输入 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; 10;//PA10 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; 浮空输入 GPIO_Init(GPIOA, &GPIO_InitStructure); 所以,我们在使用复用功能的是时候,最少要使能2 个时钟: 1) GPIO 时钟使能 2) 复用的外设时钟使能 同时要初始化GPIO 以 及复用外设功能。 0.2 NVIC 中断优先级管理 CM3内核支持 256 个中断,其中包含了 16 个内核中断和 240 个外部中断,并且具有 256级的可编程中断设置。但 STM32 并没有使用 CM3 内核的全部东西,而是只用了它的一部分。STM32 有 84 个中断,包括 16 个内核中断和 6 8 个可屏蔽中断,具有 16 级可编程的中断优先级。而我们常用的就是这 6 8 个可屏蔽中断, 但是 STM32 的 68 个可屏蔽中断,在STM32F103 系列上面,又只有 60 个(在 107 系列才有 68 个)。因为我们开发板选择的芯片是 STM32F103 系列的,所以我们就只针对 STM32F 103 系列 这 60 个可屏蔽中断进行介绍。 0.2.1 相关寄存器 typedef struct __IO uint32_t ISER[8]; /* 中断使能寄存器 */ uint32_t RESERVED0[24];// 保留,下同 __IO uint32_t ICER[8]; /*< 中断清除寄存器 */ uint32_t RSERVED1[24]; __IO uint32_t ISPR[8]; /*中断使能悬起寄存器 */ uint32_t RESERVED2[24]; __IO uint32_t ICPR[8]; /*中断清除悬起寄存器 */ uint3 2_t RESERVED3[24]; __IO uint32_t IABR[8]; /*中断有效位寄存器 */ uint32_t RESERVED4[56]; __IO uint8_t IP[240]; /*中断优先级寄存器(8Bit wide) */ uint32_t RESERVED5[644]; __O uint32_t STIR; /* 软件触发中断寄存器 */ } NVIC_Type
2.2 操作中断相关寄存器的库函数 NVIC 中断管理函数主要在 misc.c 文件里面。 一 中断优先级分组函数—NVIC_PriorityGroupConfig 首先要讲解的是中断优先级分组函数NVIC_PriorityGroupConfig ,其函数申明如下: void NVIC_Priori tyGroupConfig(uint32_t NVIC_PriorityGroup); 这个函数的作用是对中断的优先级进行分组,这个函数在系统中只能被调用一次,一旦分组确定就最好不要更改。这个函数唯一目的就是通过设置SCB -->AIRCR 寄存器来设置中断优先级分组,这在前面寄存器讲解的过程中已经讲到。其参数选择比较简单,可自行查看。 比如我们设置整个系统的中断优先级分组值为 2 ,那么方法是: NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); 这样就确定了一共为“ 2 位抢占优先级, 2 位响应优先级”。 二 中断初始化函数——void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct) 设置好了系统中断分组,那么对于每个中断我们又怎么确定他的抢占优先级和响应优先级呢?下面我们讲解一个重要的函数为中断初始化函数 NVIC_Init ,其函数申明为: void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct); 其中NVIC_InitTypeDef 是一个结构体,我们可以看看结构体的成员变量: typedef struct uint8_t NVIC_IRQChannel; uint8_t NVI C_IRQChannelPreemptionPriority; uint8_t NVIC_IRQChannelSubPriority; FunctionalState NVIC_IRQChannelCmd; } NVIC_InitTypeDef; NVIC_InitTypeDef结构体中间有三个成员变量,这三个成员变量的作用是: NVIC_IRQChannel:定义初始化的是哪个中断,这个我们可以在 stm32f10x.h 中找到每个中断对应的名字(在IRQn_Type 这个结构体里面包含了F103 系列全部的异常(中断)声明)。在STM32中文参考手册第九章也详细列出了各个中断名字。例如 USART1_IRQn 。 typedef enum IRQn { //Cortex-M3 处理器异常编号 NonMaskableInt_IRQn = -14, MemoryManagement_IRQn = -12, BusFault_IRQn = -11, UsageFault_IRQn = -10, SVCall_IRQn = -5, DebugMonitor_IRQn = -4, PendSV_IRQn = -2, SysTick_IRQn = -1, //STM32 外部中断编号 WWDG_IRQn = 0, PVD_IRQn = 1, TAMP_STAMP_IRQn = 2, // 限于篇幅,中间部分代码省略,具体的可查看库文件stm32f10x.h DMA2_Channel2_IRQn = 57, DMA2_Channel3_IRQn = 58, DMA2_Channel4_5_IRQn = 59 } IRQn_Type; NVIC_IRQChannelPreemptionPriority :定义这个中断的抢占优先级别。 NVIC_IRQChannelSubPriority:定义这个中断的子优先级别。 NVIC_IRQChannelCmd:该中断是否使能。操作的是NVIC_Type中的ISER 和ICER 这两个寄存器。 比如我们要使能串口1 的中断,同时设置抢占优先级为 1 ,子优先级位 2 ,初始化的方法是: NVIC_InitTypeDef NVIC_InitStructure NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; //串口 1 中断 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority= 1 ;;// 抢占优先级为 1 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2 ;;// 子优先级位 2 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ 通道使能 NVIC_Init(&NVIC_InitStructure); 根据上面指定的参数初始化 NVIC 寄存器 0.2.3 小结 这里我们讲解了中断的分组的概念以及设定优先级值的方法,至于每种优先级还有一些关于清除中断,查看中断状态,这在后面我们讲解每个中断的时候会详细讲解到。 最后我们总结一下中断优先级设置的步骤:
在配置每个中断的时候一般有3个编程要点: 1 使能外设某个中断,这个具体由每个外设的相关中断使能位控制。比如串口有发送完成中断,接收完成中断,这两个中断都由串口控制寄存器的相关中断使能位控制。 2 初始化NVIC_InitTypeDef结构体,配置中断优先级分组,设置抢占优先级和子优先级, 使能中断请求。NVIC_InitTypeDef结构体在固件库头文件misc.h中定义。 3 编写中断服务函数 在启动文件startup_stm32f10x_hd.s中我们预先为每个中断都写了一个中断服务函数,只是这些中断函数都是为空,为的只是初始化中断向量表。 实际的中断服务函数都需要我们重新编写,为了方便管理我们把中断服务函数统一写在stm32f10x_it.c这个库文件中。关于中断服务函数的函数名必须跟启动文件里面预先设置的一样,如果写错,系统就在中断向量表中找不到中断服务函数的入口, 直接跳转到启动文件里面预先写好的空函数,并且在里面无限循环,实现不了中断。 1 串口有关(待补充) 1、STM32F103VET6系统控制器有三个USART和两个UART,其中USART1和时钟来源于APB2总线时钟,其最大频率为72MHz, 其他四个的时钟来源于APB1总线时钟,其最大频率为36MHz。UART只是异步传输功能,所以没有SCLK、nCTS和nRTS功能引脚。 2 USART相关寄存器 2.1 数据寄存器(USART_DR) USART 数据寄存器(USART_DR)只有低9 位有效,并且第9 位数据是否有效要取决于USART 控制寄存器1(USART_CR1)的M位设置,当M位为0 时表示8 位数据字长,当M位为1 表示9 位数据字长,我们一般使用8 位数据字长。 USART_DR 包含了已发送的数据或者接收到的数据。USART_DR 实际是包含了两个 寄存器,一个专门用于发送的可写TDR,一个专门用于接收的可读RDR。当进行发送操作时,往USART_DR 写入数据会自动存储在TDR 内;当进行读取操作时,向USART_DR读取数据会自动提取RDR 数据。 TDR 和RDR 都是介于系统总线和移位寄存器之间。串行通信是一个位一个位传输的,发送时把TDR 内容转移到发送移位寄存器,然后把移位寄存器数据每一位发送出去,接收时把接收到的每一位顺序保存在接收移位寄存器内然后才转移到RDR。 2.2 状态寄存器(USART_SR) 串口的状态可以通过状态寄存器 USART_SR 读取。 USART_SR 的各位描述 如下图所示: 这里我们关注一下两个位,第5 、 6 位 RXNE 和 TC 。 RXNE(读数据寄存器非空),当该位被置 1 的时候,就是提示已经有数据被接收到了,并且可以读出来了。这时候我们要做的就是尽快去读取 USART_DR ,通过读 USART_DR 可以将该位清零,也可以向该位写0 ,直接清除。 TC(发送完成),当该位被置位(置位就是设置为1,复位是设置为0)的时候,表示 USART_DR 内的数据已经被发送完成了。如果设置了这个位的中断,则会产生中断。该位也有两种清零方式: 1 )读 USART_SR ,写USART_DR 。 2 )直接向该位写 0 。 状态寄存器的其他位我们这里就不做过多讲解,大家需要可以查看中文参考手册。 2.3 控制寄存器1,2,3 USART 有专门控制发送的发送器、控制接收的接收器,还有唤醒单元、中断控制等等。使用USART 之前需要向USART_CR1 寄存器的UE 位置1 使能USART,UE 位用来开启供给给串口的时钟。发送或者接收数据字长可选8 位或9 位,由USART_CR1 的M位控制。 下面简要介绍几个重要的位:
[tr]名称描述[/tr]
不展开。 3 相关的库函数 3.1 串口初始化函数 USART_Init() 函数原型: void USART_Init(USART_TypeDef* USARTx, USART_InitTypeDef* USART_InitStruct)
typedef struct { uint32_t USART_BaudRate; // 波特率 uint16_t USART_WordLength; // 字长 uint16_t USART_StopBits; // 停止位 uint16_t USART_Parity; // 校验位 uint16_t USART_Mode; // USART模式 uint16_t USART_HardwareFlowControl; // 硬件流控制 } USART_InitTypeDef;
3.2 数据发送与接收 STM32库函数 操作 USART_DR 寄存器 发送数据的函数是: void USART_SendData(USART_TypeDef* USARTx, uint16_t Data); 通过该函数向串口寄存器USART_DR 写入一个 数据。 STM32 库函数 操作 USART_DR 寄存器 读取串口接收到的 数据的函数是: uint16_t USART_ReceiveData(USART_TypeDef* USARTx); 通过该函数可以读取串口接受到的数据。 3.3 串口状态。 串口的状态可以通过状态寄存器 USART_SR 读取。 在我们固件库函数里面,读取串口状态的函数是: FlagStatus USART_GetFlagSt atus(USART_TypeDef* USARTx, uint16_t USART_FLAG); 这个函数的第二个入口参数非常关键,它是标示我们要查看串口的哪种状态(也就是说要读取SR寄存器的哪个位), 比如上面讲解的RXNE( 读数据寄存器非空)以及 TC( 发送完成) 。例如 我们要判断读寄存器是否非空 ( RXNE)操作库函数的方法是: USART_GetFlagStatus (USART1,USART_FLAG_RXNE); 我们要判断发送是否完成 ( TC),操作库函数的方法是: USART_GetFlagStatus (USART1, USART_ FLAG_TC); 这些标识号是有宏定义的: #define USART_IT_PE ((uint16_t) #define USART_IT_TXE ((uint16_t) #define USART_IT_TC ((uint16_t) #define USART_IT_RXNE ((uint16_t) #define USART_IT_IDLE ((uint16_t) #define USART_IT_LBD ((uint16_t) #define USART_IT_CTS ((uint16_t) #define USART_IT_ERR ((uint16_t) #define USART_IT_ORE ((uint16_t) #define USART_IT_NE ((uint16_t) #define USART_IT_FE ((uint16_t) 3.4 使能串口相应中断 原型为: void USART_ITConfig(USART_TypeDef* USARTx, uint16_t USART_IT,FunctionalState Ne wState); USART 有多个中断请求事件,具体见下图: 上面的事件标志都是状态寄存器(USART_SR)里面的状态,使能控制位是控制寄存器里面的控制位。 USART_ITConfig()函数的第二个入口参数是标示使能串口的类型也就是使能哪种中断, 因为串口的中断类型有很多种。这个函数第二个参数控制的就是控制寄存器里面的各个中断使能控制位。 比如 在接收到数据的时候( RXNE 读数据寄存器非空),我们要产生中断,那么我们开启中断 的方法是: USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);// 开启中断 ,接收到数据中断 我们在发送数据结束的时候TC 发送完成) 要产生中断,那么方法是: USART_ITConfig( USART1,USART_IT_TC ENABLE); 【注】:注意区分NVIC中断控制和这里的串口相应中断。NVIC主要是设置中断分组,凡是使用中断的,必须先调用这个函数。而这里的串口相应中断是在NVIC的基础上设置详细的中断细节。 3.5 获取相应中断状态。 当我们使能了某个中断的时候,当该中断发生了,就会设置某个寄存器中的某个标志位。经常我们在中断处理函数中,要判断该中断是哪种中断,使用的函数是: ITStatus USART_GetITStatus(USART_TypeDef* USARTx, uint16_t USART_IT); 比如我们使能了串口发送完成中断,那么当中断发生了,我们便可以在中断处理函数中调用这 个函数来判断到底是否是串口发送完成中断,方法是: USART_GetITStatus(USART1, USART_IT_TC); 返回值是SET ,说明是串口发送完成中断发生。返回值是RESET,表明没有中断发生。 4 代码编写 将实现如下功能: STM32 F1 通过串口和上位机的对话, STM32 F1 在收 到上位机发过来的字符串后,原原本本的返回给上位机。 4.1 初始化串口 初始化串口思路: ① 串口时钟使能, GPIO 时钟使能 ② 串口复位 ③ GPIO 端口模式设置 ④ 串口参数初始化 ⑤ 初始化 NVIC 并且开启串口接收中断 ⑥ 使能串口 void uart_init(u32 bound) { GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE); //使能 USART1 GPIOA 时钟 //可以加串口复位 //USART_DeInit(USART1); //复位串口 1 //USART1_TX GPIOA.9 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //**复用推挽输出** GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.9 //USART1_RX GPIOA.10初始化 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入 GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10 //Usart1 NVIC 配置 NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级3 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级3 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能 NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化NVIC寄存器 //USART 初始化配置 USART_InitStructure.USART_BaudRate = bound;//串口波特率 USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位 USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位 USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位 USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件控制流 USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式 USART_Init(USART1, &USART_InitStructure); //初始化串口1 USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启串口接收中断 USART_Cmd(USART1, ENABLE); //使能串口1 } 4.2 收发数据 【注】这里需要注意USART_SendData和USART_ReceiveData的工作方式:
1 接收到电脑发来的数据,再讲数据发送回电脑,有两种编写思路,一种是在中断服务程序中,接收到什么就发送什么。另一种是,在中断服务函数中将接收到的数据放在一个寄存器中,再在main函数中试下发送功能。 2 printf函数的实现? 4.4 printf函数的重定向 在前面学习了STM32的串口编程,通过USART1向计算机的串口调试助手打印数据,或者接收计算机串口调试助手的数据,接下来我们可以实现STM32工程上的printf()函数了,方便用于程序开发中调试信息的打印。 1 法1:使用MicroLIB库 1.1 KEIL-MDK中的Use MicroLIB选项 在MDK开发环境中: MicroLib是缺省c库的备选库,它可装入少量内存中,与嵌入式应用程序配合使用,且这些应用程序不在操作系统中运行。 MicroLib进行了高度优化以使代码变得很小,功能比缺省c库少,不具备某些ISO c特性,部分库函数的运行速度也比较慢,如内存拷贝函数memcpy()。 MicroLib与缺省c库之间的主要差异在网上有许多文章都有写到,这里摘抄记录: (1)MicroLib 不符合 ISO C 库标准。 不支持某些 ISO 特性,并且其他特性具有的功能也较少。 (2)MicroLib 不符合 IEEE 754 二进制浮点算法标准。 (3)MicroLib 进行了高度优化以使代码变得很小。 (4)无法对区域设置进行配置。 缺省 C 区域设置是唯一可用的区域设置。 (5)不能将 main() 声明为使用参数,并且不能返回内容。 (6)不支持 stdio,但未缓冲的 stdin、stdout 和 stderr 除外。 (7)MicroLib对 C99 函数提供有限的支持。 (8)MicroLib不支持操作系统函数。 (9)MicroLib不支持与位置无关的代码。 (10)MicroLib不提供互斥锁来防止非线程安全的代码。 (11)MicroLib不支持宽字符或多字节字符串。 (12)与stdlib不同,MicroLib不支持可选择的单或双区内存模型。MicroLib只提供双区内存模型,即单独的堆栈和堆区。 MicroLib提供了一个有限的stdio子系统,它仅支持未缓冲的stdin、stdout和stderr,那么也就是说勾选了Use MicroLib选项后,在代码工程中就可以使用printf()函数咯? 然而事实并非如此,这样直接使用printf()函数,其打印的字符串最终不知道打印到何处。我们要做的是将调试信息打印到USART1中,所以需要对printf()函数所依赖的打印输出函数fputc()重定向(MicroLib中的printf()函数打印操作依赖fputc())。 1.2 重定向fputc函数 在MicroLib的stdio.h中,fputc()函数的原型为: int fputc(int ch, FILE* stream) 1 此函数原本是将字符ch打印到文件指针stream所指向的文件流去的,现在我们不需要打印到文件流,而是打印到串口1。基于前面的代码: #include int fputc(int ch, FILE* stream) { //USART_SendData(USART1, (unsigned char) ch); //while (!(USART1->SR & USART_FLAG_TXE)); USART_SendChar(USART1, (uint8_t)ch); return ch; } 注意,需要包含头文件stdio.h,否则FILE类型未定义。 勾选了Use MicroLib选项,重定向fputc()函数后,我们就可以在工程代码中使用printf()函数了: int main(void) { USART_Configuration(); //USART_SendString(USART1, "HelloWorldn"); //USART_SendChar(USART1, 'h'); printf("rnstm32f103rct6rn"); printf("rnCortex-M3rn"); while (1); return 0; } printf()函数的使用方法跟之前一样 2. 法2:不使用MicroLIB库 2.1 半主机模式 半主机模式是ARM的一种机制,实现将来ARM应用程序代码的输入/输出请求传送至运行着调试器的主机。例如设置使用半主机模式下的ARM应用程序,可以使用printf()和scanf()来使用主机的显示器和键盘,而不需要在ARM系统上搭配显示器和键盘。 半主机通过一组定义好的软件指令(如SVC)来实现的,这些指令在程序控制下产生异常,ARM应用程序调用半主机对应的异常处理函数,然后调试代理处理该异常。 第二段话感觉理解起来有点模糊,但是第一段还是懂它在讲什么的。一般的ARM应用程序中并不需要半主机操作,在这里为确保ARM应用程序中没有链接MicroLib的半主机相关函数,我们要取消ARM的半主机工作模式。 2.2 实现代码 //取消ARM的半主机工作模式 #pragma import(__use_no_semihosting) struct __FILE { int handle; }; FILE __stdout; _sys_exit(int x) { x = x; } int fputc(int ch, FILE *f){ while((USART1->SR&0X40)==0); USART1->DR = (u8) ch; return ch; } 上面的代码摘自正点原子的范例程序,具体每一行的意义目前也不大清楚。这样操作后,在不使用MicroLib的前提下,仍能使用printf()函数将调试信息打印到USART1上了。 |
|
|
|
只有小组成员才能发言,加入小组>>
调试STM32H750的FMC总线读写PSRAM遇到的问题求解?
1885 浏览 1 评论
X-NUCLEO-IHM08M1板文档中输出电流为15Arms,15Arms是怎么得出来的呢?
1663 浏览 1 评论
1149 浏览 2 评论
STM32F030F4 HSI时钟温度测试过不去是怎么回事?
763 浏览 2 评论
ST25R3916能否对ISO15693的标签芯片进行分区域写密码?
1720 浏览 2 评论
1964浏览 9评论
STM32仿真器是选择ST-LINK还是选择J-LINK?各有什么优势啊?
790浏览 4评论
STM32F0_TIM2输出pwm2后OLED变暗或者系统重启是怎么回事?
616浏览 3评论
631浏览 3评论
stm32cubemx生成mdk-arm v4项目文件无法打开是什么原因导致的?
593浏览 3评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2025-1-14 04:31 , Processed in 0.826590 second(s), Total 75, Slave 59 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (威廉希尔官方网站 图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号