news 2026/9/28 20:50:34

PY32F002B串口printf重定向实操:从点灯到调试效率提升

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
PY32F002B串口printf重定向实操:从点灯到调试效率提升

不少人拿到 PY32F002B 开发板,第一件事就是点灯。点完之后呢?就不知道该干什么了。其实对于刚接触 32 位 MCU 的人来说,串口通信是最值得先打通的一环,而把 printf 重定向到串口,又是这一环里最实用的一步。我用这块几块钱级别的小板子实测了一遍,从接线、建工程到重定向、排查乱码,把整套流程踩通了。这篇文章就是我的实操记录,适合刚拿到 PY32F002B、或者从 STM32 转过来的朋友。看完你就能在串口助手上看到熟悉的 Hello PY32F002B,并且知道它背后到底是怎么工作的。

1. 核心思路拆解与硬件方案选型

1.1 为什么选 PY32F002B 做串口 printf 实战

PY32F002B 属于灵动微电子的 PY32F0 系列,内核是 ARM Cortex-M0+,定位就是低成本、低功耗、小资源。它便宜到什么程度?一块最小系统板,价格通常只有几块钱到十几块钱,比起当年动辄几十块的 STM32F103 核心板,入门门槛低了一大截。但它又是正儿八经的 32 位 MCU,不是 51 那种老架构,开发方式也和 STM32 高度接近。

选它做串口 printf 实战有几个理由。第一,外设够简单,USART 数量少(实际常用就是 USART1,具体以数据手册为准),环境够纯粹,不会被多路串口、DMA、中断一堆概念绕晕。第二,官方和社区给的 HAL 库模板非常接近 STM32 的 HAL 风格,你在这里学到的 GPIO 配置、UART 发送、printf 重定向,迁移到 STM32/GD32 几乎零成本。第三,因为便宜,随便折腾不心疼,烧坏了也不怎么肉疼。

不过它不是万能的,后面我会专门讲它的坑,尤其是 Flash 和 RAM 很紧张这一点。但也正因为紧张,你被迫学会“省着用”,这对入门来说反而是好事。很多朋友一上来就玩 STM32,资源多到不需要思考,最后代码里 printf 满天飞、浮点数随便打,到了小资源芯片上就各种翻车。先在小芯片上练一圈,后面做大项目会稳很多。

1.2 开发板与 USB 转串口模块怎么搭配

市面上的 PY32F002B 开发板大致分两类。一类是纯核心板/最小系统板,板子上只有 MCU、电源 LDO、排针,没有 USB 转串口,需要你另买一个 USB 转 TTL 模块。另一类是带板载 USB 转串口的全功能板,常见方案是 CH340N 或 CP2102,插一根 USB 数据线电脑就能识别出 COM 口,直接用。

如果是纯核心板,接线口诀是“TX 接 RX,RX 接 TX,GND 必须共地”。具体到 PY32F002B,串口默认是 USART1,一般在 PA2 和 PA3 上,PA2 是发送,PA3 是接收。USB 转 TTL 模块的 RXD 接开发板 PA2,TXD 接开发板 PA3,GND 接 GND。别接成两个 TX 对 TX,那样什么都没有。

这里有一个容易忽略的点:PY32F002B 是 3.3V 逻辑,很多 USB 转 TTL 模块虽然有 3.3V/5V 跳线,但如果你用 5V 供电、3.3V 逻辑的模式,没问题;如果模块本身只能输出 5V 的 TXD,直接接到 PA3 上,长期跑有风险。建议选支持 3.3V 逻辑的模块,或者先用板载 3.3V 供电,再共地,避免电平不匹配。我自己最常用的组合是 ST-Link 下载程序 + CH340 模块看串口,ST-Link 只负责 SWD 烧录,不碰串口。

1.3 和其他常见单片机的串口体验对比

这里做一个横向对比,方便你判断自己是不是选对了方向。STM32F103C8T6 是 Cortex-M3,主频 72MHz,Flash 64KB,USART 有 3 个,以前是入门首选,但现在价格也没有绝对优势了。STM32F030 系列是和 PY32F002B 定位接近的低成本 M0,但价格、供货一般不如国产小芯片。STC89C52 是 51 内核,UART 只有一个,下载还要冷启动,用起来确实有点“古老”。ESP32 自带 USB 转串口,写代码直接 Serial.print,但那是另一个赛道,WiFi/蓝牙那一套复杂得多。

PY32F002B 的优势就在这里:M0+ 核,开发逻辑和主流 ARM 生态一致,但价格便宜、封装小、功耗低。缺点是 Flash/RAM 小、外设少、主频低,不适合复杂应用。对于想用最低成本把“单片机程序到底是怎么跑起来的”搞明白的人,它几乎是同价位里最合适的。尤其是串口通信这一步,熟练掌握之后,后面换任何一颗 MCU,核心思路都差不多。

芯片/开发板内核主频典型Flash典型RAMUSART数量上手难度
PY32F002BCortex-M0+24MHz max几KB~十几KB级2KB级1路常用低
STM32F103C8T6Cortex-M372MHz64KB20KB3路中
STM32F030C8Cortex-M048MHz64KB8KB2路以上中低
STC89C525112MHz左右8KB512B1路低但冷启动下载麻烦
ESP32 DevKitXtensa双核240MHz4MB以上320KB多路中高

2. 环境搭建与工程模板:不踩坑的第一步

2.1 开发环境选择与安装

Windows 下最省事的就是 Keil MDK 5 加灵动官方的 PACK 包。在 Pack 安装器里搜 PY32F0,或者直接装官方提供的 Device Family Pack,装完就能在芯片列表里看到对应型号。然后装 USB 转串口驱动,CH340 装 WCH 官方驱动,CP2102 装 Silicon Labs 驱动。驱动没装好,插线后会识别成未知设备,很多新手在这里卡半天。

Linux 下喜欢命令行的人可以用 arm-none-eabi-gcc 交叉编译链加社区模板,配合 py32-openocd 这类开源工具下载。如果有人非要把开发板挂到 Ubuntu 虚拟机里交叉编译,操作也不复杂,但你得先确保虚拟机识别到了 USB 设备,并且给 openocd 配好权限。新手阶段建议先用 Keil,等把流程跑通了再折腾工具链。开发工具不宜贪多,能跑通一个就先用一个,后面有精力再扩展。

2.2 创建工程:别从零开始,直接改官方例程

第一次接触 PY32F002B,不建议从零建工程。官方固件库里已经有完整的 USART 例程,直接把它复制出来改,是最省力、最不容易出错的路径。

步骤大致是这样。先到灵动微官网或官方代码托管仓库找 PY32F0xx 固件库压缩包,解压后看 Examples 或 Projects 目录。找一个和串口相关的例程,比如 USART_TxRx 或 PrintfDemo。复制整个工程文件夹,不要在原工程上改,防止搞乱原始代码。打开 Keil 工程,确认处理器型号选了 PY32F002B 对应那颗,然后再看 Options for Target 里的 Debug 设置,选 ST-Link 或者你手上的调试器。编译,下载,打开串口助手,正常就能看到输出。

这中间最值得强调的一点是:PY32 系列用的 Flash 下载算法和 STM32 不一样,很多朋友用 STM32 的工程格式去套,编译过了但下载失败,报错类似 Cannot access target 或者 Flash Algorithm 不对。解决方法是装好灵动的 PACK 后,在 Utilities 或 Flash Download 页面里选择对应芯片的 Flash 算法。如果实在找不到,去看看官方模板工程里 Utilities 页是怎么选的,照着抄。

2.3 串口引脚与最小系统接线图

下面画一个最简接线图,这对纯核心板用户很有用:

USB转TTL模块 PY32F002B核心板 RXD <--------- PA2 (USART1_TX) TXD ---------> PA3 (USART1_RX) GND --------- GND

板载串口芯片的板子就不用管这套了,USB 一插就行。注意 PA2/PA3 这两个引脚在 PY32F002B 上默认复用为 USART1,但代码里必须在 GPIO 初始化时配置成 AF_PP 并指定复用功能号 AF1_USART1,否则引脚不会连到串口外设上。这个坑和 STM32 一模一样,忘了配复用,所有配置看起来都对,但就是不输出。

3. printf 重定向:原理、代码与参数计算

3.1 printf 输出背后的完整链路

先花两分钟理解一下 printf 的原理,后面遇到问题才好排查。printf 是 C 标准库函数,它把格式化的数据写到标准输出 stdout。在 PC 上,stdout 默认是屏幕,所以 printf 结果显示在终端;在单片机裸机环境里,并没有屏幕这个东西,stdout 是“悬空”的。重定向要做的事情,就是把 stdout 的底层写操作接到 UART 上。

在 Keil 的 ARMCC/MicroLIB 环境下,编译器会调用 fputc(c, f) 作为字符输出函数;在 GCC 的 newlib 环境下,底层是 _write(file, ptr, len)。所以你只需要实现这两个函数之一,把字符通过 HAL_UART_Transmit 发到串口,printf 的路线就通了。很多人问“为什么我 include 了 stdio.h 还是不行”,就是因为只 include 了头文件,没做这个底层重定向。

这里顺带回答一个经常被纠结的问题:printf 和 HAL_UART_Transmit 到底选哪个?直接 HAL_UART_Transmit 当然能发数据,但没有格式化能力。你临时调试要看一个变量的十六进制值,还得自己转字符串或者一个字符一个字符地发,太麻烦。printf 的 %d、%x、%02X 甚至 %.2f,改一行代码就能搞定,调试效率完全不同。代价是引入标准库的格式化代码,占用 Flash 和 RAM,这个后面会详细说。

3.2 Keil MDK 下的重定向完整代码

先给一个能直接跑的代码模板。我在工程里一般把串口初始化和重定向分开,一个放 usart.c,一个放 retarget.c,main.c 只负责调接口。

usart.c:

#include "usart.h" UART_HandleTypeDef huart1; void MX_USART1_UART_Init(void) { GPIO_InitTypeDef GPIO_InitStruct = {0}; __HAL_RCC_GPIOA_CLK_ENABLE(); __HAL_RCC_USART1_CLK_ENABLE(); /* PA2: USART1_TX, PA3: USART1_RX */ GPIO_InitStruct.Pin = GPIO_PIN_2 | GPIO_PIN_3; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_PULLUP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; GPIO_InitStruct.Alternate = GPIO_AF1_USART1; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); huart1.Instance = USART1; huart1.Init.BaudRate = 115200; huart1.Init.WordLength = USART_WORDLENGTH_8B; huart1.Init.StopBits = USART_STOPBITS_1; huart1.Init.Parity = USART_PARITY_NONE; huart1.Init.Mode = USART_MODE_TX_RX; huart1.Init.HardwareFlowControl = USART_HARDWAREFLOWCONTROL_NONE; huart1.Init.OverSampling = USART_OVERSAMPLING_16; HAL_UART_Init(&huart1); }

retarget.c:

#include <stdio.h> #include "usart.h" #if defined(__CC_ARM) || (defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050)) #pragma import(__use_no_semihosting) struct __FILE { int handle; }; FILE __stdout; void _sys_exit(int x) { x = x; } int fputc(int ch, FILE *f) { HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xFFFF); return ch; } #endif

如果你在 Keil 里勾选了 Use MicroLIB,其实只需要 fputc 这一个函数;上面那一段 __use_no_semihosting 和 _sys_exit 是为了不勾 MicroLIB 时,避免因为半主机模式导致程序死在启动阶段。两种方式我实测都能跑,建议新手直接勾 MicroLIB,省代码空间也省事。MicroLIB 的 printf 对浮点支持有限,但整数和字符串输出完全够用。

然后 main.c 里这样写:

#include "main.h" UART_HandleTypeDef huart1; int main(void) { HAL_Init(); MX_USART1_UART_Init(); printf("PY32F002B UART printf test\r\n"); printf("SystemCoreClock = %d Hz\r\n", (int)SystemCoreClock); while (1) { printf("Hello PY32F002B\r\n"); HAL_Delay(1000); } }

注意 printf 字符串后面加 \r\n,不要只加 \n。很多串口工具对裸 \r 或裸 \n 处理不一致,加上 \r\n 显示最整齐。这是个小细节,能省不少看着“输出错位”的烦恼。

3.3 GCC 环境下的重定向实现

在 Linux 或 macOS 下用 arm-none-eabi-gcc,标准库是 newlib,重定向函数换成 _write 即可:

int _write(int file, char *ptr, int len) { HAL_UART_Transmit(&huart1, (uint8_t *)ptr, len, 0xFFFF); return len; }

newlib 会自己在内部调用这个符号,把 printf 的输出一截一截传过来。也就是说,fputc 一次发一个字符,_write 一次可能发一整段字符串,效率反而更高一些。这段代码在 GCC 环境下要注意,不要和 retarget.c 里的 Keil 专用代码混在一起编译,建议用宏区分平台。

用 newlib 的时候,一个额外问题是浮点支持。如果你链接命令里用了 --specs=nano.specs 来裁剪体积,printf 默认不带浮点解析,遇到 %.2f 会输出空或者不支持。解决办法是链接时加 -u _printf_float。代价是代码体积变大,其实和 Keil MicroLIB 的情况类似。对 PY32F002B 这种小 Flash 芯片,强烈建议能不用浮点就不用浮点,把整数拆开打印,能用 %d 解决就别上 %.2f。

3.4 波特率计算与内部时钟误差分析

HAL 库的 HAL_UART_Init 会自动帮你计算 USART 波特率寄存器 BRR 的值,可以不用手算,但建议知道它怎么来的。标准的异步串口公式是:

波特率 = fck / (16 * USARTDIV)

对于 PY32F002B 内部 HSI 为 24MHz、目标波特率 115200 的情况:

USARTDIV = 24000000 / (16 * 115200) = 13.0208

整数部分是 13,小数部分在寄存器里用 4bit 左右表示,实际算出来的波特率和目标值误差很小,大约千分之一点六。这个误差对 115200 来说完全没问题,因为在 16 倍过采样下,双方时钟误差容忍度通常在 2% 到 3% 左右。

问题容易出在 HSI 这个内部 RC 振荡器本身。它的精度一般有限,而且不同批次、不同温度下会漂。如果你的板子没有外部晶振,全靠内部 HSI,串口助手那边的 USB 转 TTL 模块用的是精度很高的晶振,两边可能因为时钟偏差,表现为高波特率偶尔乱码、低波特率正常。遇到这种问题,我给你的第一个排查动作永远是:把波特率从 115200 改成 9600 试试。如果 9600 正常而 115200 乱码,那基本就是时钟误差问题,不是代码逻辑问题。极端情况下可以微调波特率数值,比如电脑端设 120000 来匹配芯片实际输出,但这是应急手段,治标不治本。

4. 常见问题与排查技巧实录

4.1 printf 中文乱码的 3 个根因

很多人一搜串口,问得最多的就是“printf 中文乱码”。我实测总结下来,中文乱码分三种情况。

一是源文件编码和串口工具编码不一致。Keil 默认编辑器保存的文件可能带 BOM,也可能按 GB2312/GBK 存,而串口助手上选择的是 UTF-8,两边对不上,中文自然乱成一团。解决方法是把编码统一。最简单:先用纯英文串测通,再把串口工具切到 UTF-8,或者把源码用编辑器另存为不带 BOM 的 UTF-8,看你用的串口工具支持哪种就统一用哪种。

二是波特率误差带来的整体错乱。这种现象不只是中文乱码,英文也乱,有时候看起来像“乱码但偶尔能认出几个字符”。先降波特率排除。

三是 TX/RX 接反或者接触不良。这时往往完全没输出,不会“乱码”。但如果接触不良导致电平不稳定,会出现零星乱字符。重新插拔,然后做回环测试:把 PA2 和 PA3 直接短接,代码发什么就收什么,能回环就说明串口外设没问题,问题在外部接线。

现象优先怀疑项处置方法
完全没输出接线反了、没下载进程序TX/RX 对调,确认烧录成功
英文乱码波特率、时钟误差降到 9600,检查时钟配置
只有中文乱码编码不匹配统一源码与串口工具编码
偶尔丢字卡顿中断优先级/缓冲溢出不要在中断里 printf,用队列缓冲

4.2 PY32F002B 的硬性资源限制与避坑

PY32F002B 的 Flash 和 RAM 非常小,这是它最需要正视的坑。以常见型号为例,代码空间在几 KB 到十几 KB 这个量级,RAM 更是捉襟见肘。printf 本身就占空间,尤其一旦用到浮点格式化,代码体积会明显膨胀。我实际测过一个工程,加了一行 printf("%.2f", value),Flash 占用立马上涨了好几 KB,差点超容量。

解决方向有三个。第一,多用整数格式化,少用浮点。第二,使用 MicroLIB 或者 GCC nano newlib 裁剪标准库。第三,去掉不用的 HAL 模块,不用的外设初始化代码删干净,编译器优化等级开到 -Os。这些都是常规操作,但能明显缓解容量焦虑。

另外,别把 printf 塞进中断服务函数里。HAL_UART_Transmit 是阻塞发送,在中断里调用会让中断挂在那里,高优先级中断被低优先级逻辑拖住,系统行为会变得非常诡异。我见过有人直接在 UART 接收中断里回显一串 printf,结果接收中断被阻塞,后续字节全丢。更好的做法是中断里只放一个标志,或者把数据塞进环形队列,printf 放到主循环里处理。

还有一个下载相关的坑:SWD 引脚尽量不要复用成普通 IO,尤其是调试器要用的那两根。一旦你把这些引脚配置成了不接调试器的状态,下一次烧录可能连不上芯片。PY32 不像老 51 有专门的 ISP 串口下载模式,救砖步骤相对麻烦一些,所以新手尽量保住下载口。

4.3 串口调试的实用工具与效率技巧

串口工具不强求,推荐几个我用着顺手的:Windows 下的 XCOM、MobaXterm 自带的串口会话,Linux 下直接用 screen 或 minicom,都行。驱动和工具安装包尽量去官方渠道下载,避免第三方打包网站捆绑乱七八糟的东西。

效率技巧方面,有几个值得记一下。第一,在开机启动 printf 里打印编译日期和版本号。有时候串口数据很多,不确定固件是不是最新版,一条 printf("[%s] build\r\n",DATE) 能省很多查版本的时间。第二,用逻辑分析仪抓 TX 引脚,可以直接量出真实波特率,判断是不是内部 RC 偏差太大。这个比瞎猜靠谱得多。第三,串口助手设置里尽量选显示时间戳,结合程序里的 HAL_GetTick(),能快速定位程序卡在哪一步。

5. 从 printf 到交互式调试:下一步可以怎么玩

5.1 串口接收中断:让开发板能“听”你说话

printf 打通之后,很自然会想往开发板发数据控制它。这个时候启用接收中断就行,关键代码就几行:

uint8_t rx_byte = 0; /* 主循环初始化后启动接收 */ HAL_UART_Receive_IT(&huart1, &rx_byte, 1); void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if (huart->Instance == USART1) { /* 收一个回显一个,相当于本地串口回环 */ HAL_UART_Transmit(&huart1, &rx_byte, 1, 0xFFFF); HAL_UART_Receive_IT(&huart1, &rx_byte, 1); } }

难点在于:如果你收到的命令长度不固定,就不能太天真地一个字节一个字节处理。最常见做法是维护一个接收缓冲区和计数变量,在回调里把字节写入缓冲,等收到 \r 或 \n 再解析整行命令。这套思路在所有 MCU 上都是通用的,不局限于 PY32F002B,学会了一次性受用很久。

5.2 用 printf 搭建轻量级日志系统

printf 裸用时间长了,会发现代码里到处都是 printf,后期想关掉某个模块的日志很痛苦。这时候可以包一层日志宏:

#define LOG_LEVEL_ERROR 0 #define LOG_LEVEL_INFO 1 #define LOG_LEVEL_DEBUG 2 #define LOG_E(fmt, ...) printf("[E] " fmt "\r\n", ##__VA_ARGS__) #define LOG_I(fmt, ...) printf("[I] " fmt "\r\n", ##__VA_ARGS__) #define LOG_D(fmt, ...) printf("[D] " fmt "\r\n", ##__VA_ARGS__)

想关掉调试日志,把 LOG_D 改成空宏就行,不用删代码。再进一步,还可以在宏里加 HAL_GetTick() 时间戳,方便后期看执行时序。注意这种可变参数宏写法在 GCC 和 ARMCC 下都支持,但别在中断函数里调用这些日志宏,原因前面讲过了。

5.3 适合零基础复刻的迷你项目

串口打通后,有两个很适合拿来练手的小项目。一个是“按键事件日志”:按键按下去,串口打印出按下时间、按键编号和持续周期。这个项目能练会外部中断、消抖和 printf 组合使用。另一个是“传感器数据串口监测”:接一个常见的 DHT11 或者 DS18B20,把温湿度数值通过 printf 打印出来,串口助手上能看到实时数据变化。再进阶一点,收到特定字符串就切换采集周期,这已经有一点“交互式命令行”的雏形了。

我在实际折腾 PY32F002B 的时候最大的感受是:不要因为它便宜、资源小就小看它,串口 printf 这个能力一旦打通,调试效率会直接上一个台阶。芯片资源紧张反而逼着我把代码写得更克制,改掉了一上来就浮点满天飞的坏毛病。这个习惯后来在做其他项目时帮了我大忙。新手朋友完全可以照着这个路径走,先把串口玩明白,再去折腾接收、中断、通信协议,一步步来,基础就扎实了。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/28 20:50:28

测试人转型AI测试开发:大模型与Agent落地实践指南

测试圈里最近两年有个很明显的现象&#xff1a;招聘网站上"测试开发"岗位的JD里&#xff0c;开始频繁出现"熟悉大模型应用""有AI测试经验优先""了解Agent框架"这类要求。与此同时&#xff0c;不少做了三五年功能测试的朋友开始焦虑——…

作者头像 李华
网站建设 2026/9/28 20:50:23

理解门店重装升级意义:从工艺展示区看家装企业的施工规范

从门店重装升级看家装企业的工艺展示与施工规范在家居装修行业&#xff0c;门店的重装升级往往不仅仅是店面形象的美化&#xff0c;更是品牌服务体系、施工工艺展示及供应链整合能力的集中呈现。对于消费者而言&#xff0c;观察一家装企如何进行门店升级&#xff0c;是判断其当…

作者头像 李华
网站建设 2026/9/28 20:49:19

渗透测试踩坑实录:这些误区千万别踩

渗透测试踩坑实录&#xff1a;这些误区千万别踩 前言 很多网安学习者都会遇到一个诡异的瓶颈&#xff1a;靶场乱杀、实战拉胯。 DVWA、WebGoat、vulhub 靶场通关无数&#xff0c;各类漏洞原理背得滚瓜烂熟&#xff0c;工具命令倒背如流&#xff0c;但是一接手真实渗透测试项…

作者头像 李华
网站建设 2026/9/28 20:48:20

工程车辆数据集训练YOLOv8:1000张图从体检到落地的完整指南

简介&#xff1a;面向车辆检测与目标识别研究的工程车辆图像数据集&#xff0c;共收录1000张已标注图片&#xff0c;涵盖重型卡车、沥青车、搅拌车、清障车、洒水车、拖拉机、挖掘机、压路机、吊车、自卸车等常见类型&#xff0c;可支撑YOLO、Faster R-CNN、SSD等深度学习模型的…

作者头像 李华
网站建设 2026/9/28 20:47:49

CiLocks Metasploit入门:4种Listener模式与msfconsole快速上手

CiLocks Metasploit入门&#xff1a;4种Listener模式与msfconsole快速上手 【免费下载链接】CiLocks Crack Interface lockscreen, Metasploit and More Android/IOS Hacking 项目地址: https://gitcode.com/GitHub_Trending/ci/CiLocks CiLocks 是一款面向 Android/iOS…

作者头像 李华
网站建设 2026/9/28 20:45:56

手电筒内部的充电回路

手电筒充电电路01 【手电筒充电电路】 一、拆卸手电筒 这是一个要废弃掉的可充电手电筒。 准备将它抛弃。 那下面有一个问题&#xff0c;它里边究竟是如何来充电的&#xff1f; 使用什么样的充电池&#xff1f; 为什么它现在损坏了&#xff1f; 下面我们打开它来一探究竟。 不…

作者头像 李华