我找到了官方工程师(zhp)对该问题的权威回复。现在我可以给出完整、准确的回答。让我再确认一下楼主帖子中关于 ES4 位位置的正确性。
根据手册 IE2 寄存器定义:
- B7 = EUSB(0x80)
- B6 = ET4(0x40)
- B5 = ET3(0x20)
- B4 = ES4(0x10) ✅ 青穗说串口4在位4是正确的
- B3 = ES3(0x08)
- B2 = ET2(0x04)
- B1 = ESPI(0x02)
- B0 = ES2(0x01)
青穗的建议 IE2 |= 0x90 = 0x80 (EUSB) + 0x10 (ES4),这个理解正确。但问题的关键不在于此——关键在于 EUSB 位是只写位,不能通过"读-改-写"指令(如 |=)来修改其他位,否则会把 EUSB 清0。
现在我可以给出权威、完整的解答了。
关于 STC32G12K128 初始化 USB 与串口4 顺序导致 USB 失效的问题
楼主你好,这个问题我结合手册和论坛中官方工程师的确认,给你一个准确解答。
一、问题根源:EUSB 位是只写位
根据 STC32G 数据手册《中断系统》 章节中 IE2 寄存器的定义:
EUSB:USB中断允许位。(注意:此位为只写位,不可读取,读取始终为0)
EUSB 是只写位,读取恒为 0。 这正是问题的关键所在。
二、为什么"先初始化USB再初始化串口4"会导致 USB 失效
先看 IE2 寄存器的位定义(地址 AFH):
| B7 |
B6 |
B5 |
B4 |
B3 |
B2 |
B1 |
B0 |
| EUSB |
ET4 |
ET3 |
ES4 |
ES3 |
ET2 |
ESPI |
ES2 |
| 0x80 |
0x40 |
0x20 |
0x10 |
0x08 |
0x04 |
0x02 |
0x01 |
当你先初始化 USB 时,代码中会用类似 EUSB = 1(或 IE2 |= 0x80)的方式打开 USB 中断。
随后再初始化串口4,如果你的代码写的是类似:
IE2 |= 0x10; // 使能串口4中断 ES4
这条语句是 读-改-写(Read-Modify-Write) 操作。由于 EUSB 位读取时恒为 0,那么"读"回的值中 EUSB 已经是 0,再"写"回去时,EUSB 就被清 0 了,USB 中断就被意外关闭,导致 USB 无法正常工作。
反过来,如果先初始化串口4,再初始化 USB(此时用 EUSB = 1 直接赋值),由于对 EUSB 的置位发生在串口4的读-改-写之后,所以 EUSB 不会被清掉,USB 就能正常使用。这与你在论坛观察到的现象完全一致。
三、验证:青穗的回复中 ES4 位位置正确
青穗回复说"串口4 在位4,应该是 0x10",这一点是对的(手册中 ES4 确实在 B4,即 0x10)。他建议的 IE2 |= 0x90:
- 0x80 = EUSB(B7)
- 0x10 = ES4(B4)
从位值上看没问题,但这样写仍然存在读-改-写陷阱——它同样会清掉 EUSB。官方推荐的写法是直接赋值给 IE2,而不是用 |=。
四、官方推荐的解决方案
据 STC 官方工程师(zhp)在论坛 TID 2417 中的确认,这个问题是 STC32G12K128 系列的设计局限(EUSB 为只写位,读取恒为 0),官方建议规避方式有两种:
方案一:在每次读-改-写 IE2 其他位之后,再补一次 EUSB = 1;
// 初始化串口4
IE2 |= 0x10; // 使能串口4中断(此句会把 EUSB 清0)
EUSB = 1; // 补救:重新打开 USB 中断
方案二(推荐):不使用 |=,直接用等号整体赋值 IE2
// 同时打开 USB 中断和串口4中断
IE2 = 0x90; // = 0x80 (EUSB) + 0x10 (ES4)
不过要注意,方案二要求你能一次列出所有需要的使能位,避免遗漏。
补充说明
- STC8H8K64U 无此问题(据官方工程师在论坛反馈),这是 STC32G 系列特有的设计局限。
- 手册中已在 IE2 的 EUSB 位处明确标注了"只写位、读取始终为0"的注意事项,建议代码中涉及 IE2 时尽量用整体赋值,避免对所有位做"读-改-写"。
如果你目前在 STC32G12K128 上写库函数(如在 Keil C251 中),建议把 USB 的使能放在所有涉及 IE2 位操作的初始化最末尾,或者统一使用整体赋值的方式,就能规避这个问题。如果还有其他疑问,欢迎继续交流!
🔍 搜索 "https://www.stcaimcu.com/thread-25180-1-1.html"
⏱️ 查找用时 13.9s | 总回答用时 23.4s