libxr  1.0
Want to be the best embedded framework
Loading...
Searching...
No Matches
ch32_usb_rcc.hpp
1#pragma once
2
3#include <cstdint>
4
5#include "libxr_def.hpp"
6#include DEF2STR(LIBXR_CH32_CONFIG_FILE)
7
8namespace LibXR::CH32UsbRcc
9{
10
11inline uint32_t GetSysclkHz()
12{
13 RCC_ClocksTypeDef clk{};
14 RCC_GetClocksFreq(&clk);
15 return clk.SYSCLK_Frequency;
16}
17
18inline void ConfigureUsb48MFromSysclk()
19{
20 // 传统 CH32 器件直接从 SYSCLK / PLL 分频得到共享 USB 48 MHz 时钟。
21 // Legacy CH32 parts derive the shared USB 48 MHz clock directly from SYSCLK / PLL
22 // dividers.
23 const uint32_t sysclk_hz = GetSysclkHz();
24
25#if defined(RCC_USBCLKSource_PLLCLK_Div1) && defined(RCC_USBCLKSource_PLLCLK_Div2) && \
26 defined(RCC_USBCLKSource_PLLCLK_Div3)
27 if (sysclk_hz == 144000000u)
28 {
29 RCC_USBCLKConfig(RCC_USBCLKSource_PLLCLK_Div3);
30 return;
31 }
32 if (sysclk_hz == 96000000u)
33 {
34 RCC_USBCLKConfig(RCC_USBCLKSource_PLLCLK_Div2);
35 return;
36 }
37 if (sysclk_hz == 48000000u)
38 {
39 RCC_USBCLKConfig(RCC_USBCLKSource_PLLCLK_Div1);
40 return;
41 }
42#if defined(RCC_USB5PRE_JUDGE) && defined(RCC_USBCLKSource_PLLCLK_Div5)
43 if (sysclk_hz == 240000000u)
44 {
45 ASSERT(RCC_USB5PRE_JUDGE() == SET);
46 RCC_USBCLKConfig(RCC_USBCLKSource_PLLCLK_Div5);
47 return;
48 }
49#endif
50#elif defined(RCC_USBCLK48MCLKSource_PLLCLK) && \
51 defined(RCC_USBFSCLKSource_PLLCLK_Div1) && \
52 defined(RCC_USBFSCLKSource_PLLCLK_Div2) && defined(RCC_USBFSCLKSource_PLLCLK_Div3)
53 RCC_USBCLK48MConfig(RCC_USBCLK48MCLKSource_PLLCLK);
54
55 if (sysclk_hz == 144000000u)
56 {
57 RCC_USBFSCLKConfig(RCC_USBFSCLKSource_PLLCLK_Div3);
58 return;
59 }
60 if (sysclk_hz == 96000000u)
61 {
62 RCC_USBFSCLKConfig(RCC_USBFSCLKSource_PLLCLK_Div2);
63 return;
64 }
65 if (sysclk_hz == 48000000u)
66 {
67 RCC_USBFSCLKConfig(RCC_USBFSCLKSource_PLLCLK_Div1);
68 return;
69 }
70#endif
71
72 ASSERT(false);
73}
74
75#if defined(RCC_HSBHSPLLCLKSource_HSE) && defined(RCC_USBPLL_Div1) && \
76 defined(RCC_USBPLL_Div2) && defined(RCC_USBPLL_Div3) && defined(RCC_USBPLL_Div4) && \
77 defined(RCC_USBPLL_Div5) && defined(RCC_USBPLL_Div6) && defined(RCC_USBPLL_Div7) && \
78 defined(RCC_USBPLL_Div8) && defined(RCC_USBHSPLLCKREFCLK_3M) && \
79 defined(RCC_USBHSPLLCKREFCLK_4M) && defined(RCC_USBHSPLLCKREFCLK_5M) && \
80 defined(RCC_USBHSPLLCKREFCLK_8M)
81struct UsbHsPllConfig
82{
83 uint32_t divider_cfg = 0u;
84 uint32_t ref_cfg = 0u;
85};
86
87struct UsbHsPllTableEntry
88{
89 uint32_t hse_hz = 0u;
90 uint32_t divider_cfg = 0u;
91 uint32_t ref_cfg = 0u;
92};
93
94inline bool TryGetUsbHsPllConfigForHse(uint32_t hse_hz, UsbHsPllConfig& cfg)
95{
96 // 这里保留显式查表,不做动态推导;
97 // USBHS 合法参考时钟是离散集合,精确表更容易审计。
98 // Keep this as an explicit lookup table instead of inferring combinations dynamically;
99 // legal USBHS reference clocks are discrete and easier to audit in table form.
100 constexpr UsbHsPllTableEntry table[] = {
101 {3000000u, RCC_USBPLL_Div1, RCC_USBHSPLLCKREFCLK_3M},
102 {4000000u, RCC_USBPLL_Div1, RCC_USBHSPLLCKREFCLK_4M},
103 {5000000u, RCC_USBPLL_Div1, RCC_USBHSPLLCKREFCLK_5M},
104 {6000000u, RCC_USBPLL_Div2, RCC_USBHSPLLCKREFCLK_3M},
105 {8000000u, RCC_USBPLL_Div2, RCC_USBHSPLLCKREFCLK_4M},
106 {9000000u, RCC_USBPLL_Div3, RCC_USBHSPLLCKREFCLK_3M},
107 {10000000u, RCC_USBPLL_Div2, RCC_USBHSPLLCKREFCLK_5M},
108 {12000000u, RCC_USBPLL_Div3, RCC_USBHSPLLCKREFCLK_4M},
109 {15000000u, RCC_USBPLL_Div3, RCC_USBHSPLLCKREFCLK_5M},
110 {16000000u, RCC_USBPLL_Div4, RCC_USBHSPLLCKREFCLK_4M},
111 {18000000u, RCC_USBPLL_Div6, RCC_USBHSPLLCKREFCLK_3M},
112 {20000000u, RCC_USBPLL_Div5, RCC_USBHSPLLCKREFCLK_4M},
113 {21000000u, RCC_USBPLL_Div7, RCC_USBHSPLLCKREFCLK_3M},
114 {24000000u, RCC_USBPLL_Div6, RCC_USBHSPLLCKREFCLK_4M},
115 {25000000u, RCC_USBPLL_Div5, RCC_USBHSPLLCKREFCLK_5M},
116 {28000000u, RCC_USBPLL_Div7, RCC_USBHSPLLCKREFCLK_4M},
117 {30000000u, RCC_USBPLL_Div6, RCC_USBHSPLLCKREFCLK_5M},
118 {32000000u, RCC_USBPLL_Div8, RCC_USBHSPLLCKREFCLK_4M},
119 {35000000u, RCC_USBPLL_Div7, RCC_USBHSPLLCKREFCLK_5M},
120 {40000000u, RCC_USBPLL_Div8, RCC_USBHSPLLCKREFCLK_5M},
121 {48000000u, RCC_USBPLL_Div6, RCC_USBHSPLLCKREFCLK_8M},
122 {56000000u, RCC_USBPLL_Div7, RCC_USBHSPLLCKREFCLK_8M},
123 {64000000u, RCC_USBPLL_Div8, RCC_USBHSPLLCKREFCLK_8M},
124 };
125
126 for (const auto& entry : table)
127 {
128 if (entry.hse_hz != hse_hz)
129 {
130 continue;
131 }
132 cfg.divider_cfg = entry.divider_cfg;
133 cfg.ref_cfg = entry.ref_cfg;
134 return true;
135 }
136
137 return false;
138}
139
140inline void ConfigureUsbHsPhyFromHse()
141{
142 // USBHS PHY 路径通过 HSE -> (分频, 参考时钟) 查表,
143 // 保证合法组合保持显式且可审计。
144 // The USBHS PHY path uses an HSE -> (divider, ref clock) lookup so the
145 // legal combinations stay explicit and auditable.
146 UsbHsPllConfig cfg = {};
147 const uint32_t hse_hz = static_cast<uint32_t>(HSE_VALUE);
148 ASSERT(TryGetUsbHsPllConfigForHse(hse_hz, cfg));
149
150 RCC_USBHSPLLCLKConfig(RCC_HSBHSPLLCLKSource_HSE);
151 RCC_USBHSConfig(cfg.divider_cfg);
152 RCC_USBHSPLLCKREFCLKConfig(cfg.ref_cfg);
153 RCC_USBHSPHYPLLALIVEcmd(ENABLE);
154}
155#endif
156
157inline void ConfigureUsb48M()
158{
159#if defined(RCC_USBCLK48MCLKSource_USBPHY) && defined(RCC_HSBHSPLLCLKSource_HSE)
160 // 部分 CH32V30x 可以从 USBHS PHY PLL 提供共享 48 MHz USB 时钟;
161 // 这种情况下先选 PHY 路径,再让各控制器自己打开总线时钟。
162 // Some CH32V30x parts can source the shared 48 MHz USB clock from the USBHS PHY PLL;
163 // in that case, select the PHY path first and let each controller enable its own bus clock.
164 ConfigureUsbHsPhyFromHse();
165 RCC_USBCLK48MConfig(RCC_USBCLK48MCLKSource_USBPHY);
166#else
167 // 经典路径:直接从 SYSCLK / PLL 分频得到共享 48 MHz 时钟。
168 // Classic path: derive the shared 48 MHz clock directly from SYSCLK / PLL dividers.
169 ConfigureUsb48MFromSysclk();
170#endif
171}
172
173} // namespace LibXR::CH32UsbRcc