11const microzig = @import ("microzig" );
22
3+ const RCC = microzig .chip .peripherals .RCC ;
4+ const EXTEND = microzig .chip .peripherals .EXTEND ;
5+
6+ const gpioBlock = enum {
7+ A ,
8+ B ,
9+ C ,
10+ D ,
11+ };
12+
13+ pub fn enable_gpio_clock (block : gpioBlock ) void {
14+ // TODO: How do we know if we need to set the AFIOEN?
15+ switch (block ) {
16+ .A = > RCC .APB2PCENR .modify (.{
17+ .IOPAEN = 1 ,
18+ .AFIOEN = 1 ,
19+ }),
20+ .B = > RCC .APB2PCENR .modify (.{
21+ .IOPBEN = 1 ,
22+ .AFIOEN = 1 ,
23+ }),
24+ .C = > RCC .APB2PCENR .modify (.{
25+ .IOPCEN = 1 ,
26+ .AFIOEN = 1 ,
27+ }),
28+ .D = > RCC .APB2PCENR .modify (.{
29+ .IOPDEN = 1 ,
30+ .AFIOEN = 1 ,
31+ }),
32+ }
33+ }
34+
35+ const peripheral = enum {
36+ // APB2 peripherals
37+ USART1 ,
38+ SPI1 ,
39+ ADC1 ,
40+ ADC2 ,
41+ TIM1 ,
42+
43+ // APB1 peripherals
44+ USART2 ,
45+ USART3 ,
46+ I2C1 ,
47+ I2C2 ,
48+ SPI2 ,
49+ TIM2 ,
50+ TIM3 ,
51+ TIM4 ,
52+ };
53+
54+ pub fn enable_peripheral_clock (p : peripheral ) void {
55+ switch (p ) {
56+ // APB2 peripherals (high-speed bus)
57+ .USART1 = > RCC .APB2PCENR .modify (.{ .USART1EN = 1 }),
58+ .SPI1 = > RCC .APB2PCENR .modify (.{ .SPI1EN = 1 }),
59+ .ADC1 = > RCC .APB2PCENR .modify (.{ .ADC1EN = 1 }),
60+ .ADC2 = > RCC .APB2PCENR .modify (.{ .ADC2EN = 1 }),
61+ .TIM1 = > RCC .APB2PCENR .modify (.{ .TIM1EN = 1 }),
62+
63+ // APB1 peripherals (low-speed bus)
64+ .USART2 = > RCC .APB1PCENR .modify (.{ .USART2EN = 1 }),
65+ .USART3 = > RCC .APB1PCENR .modify (.{ .USART3EN = 1 }),
66+ .I2C1 = > RCC .APB1PCENR .modify (.{ .I2C1EN = 1 }),
67+ .I2C2 = > RCC .APB1PCENR .modify (.{ .I2C2EN = 1 }),
68+ .SPI2 = > RCC .APB1PCENR .modify (.{ .SPI2EN = 1 }),
69+ .TIM2 = > RCC .APB1PCENR .modify (.{ .TIM2EN = 1 }),
70+ .TIM3 = > RCC .APB1PCENR .modify (.{ .TIM3EN = 1 }),
71+ .TIM4 = > RCC .APB1PCENR .modify (.{ .TIM4EN = 1 }),
72+ }
73+ }
74+
75+ /// Enable AFIO (Alternate Function I/O) clock
76+ /// Required for pin remapping and external interrupt configuration
77+ pub fn enable_afio_clock () void {
78+ RCC .APB2PCENR .modify (.{ .AFIOEN = 1 });
79+ }
80+
381/// Initialize system clock to 48 MHz using HSI
482pub fn init_48mhz_hsi () void {
5- const RCC = microzig .chip .peripherals .RCC ;
6- const EXTEND = microzig .chip .peripherals .EXTEND ;
783 // Enable PLL HSI prescaler (HSIPRE bit)
884 EXTEND .EXTEND_CTR .modify (.{ .HSIPRE = 1 });
985
@@ -29,3 +105,107 @@ pub fn init_48mhz_hsi() void {
29105 // Wait for PLL to be used as system clock (SWS should be 2)
30106 while (RCC .CFGR0 .read ().SWS != 2 ) {}
31107}
108+
109+ const ClockSpeeds = struct {
110+ sysclk : u32 ,
111+ hclk : u32 ,
112+ pclk1 : u32 ,
113+ pclk2 : u32 ,
114+ adcclk : u32 ,
115+ };
116+
117+ // Clock constants
118+ const HSI_VALUE : u32 = 8_000_000 ; // 8 MHz internal oscillator
119+ const HSE_VALUE : u32 = 8_000_000 ; // 8 MHz external oscillator (board-dependent)
120+
121+ // Prescaler lookup table for AHB/APB buses
122+ // Index into this table with the HPRE/PPRE bits, result is the shift amount
123+ const prescaler_table = [16 ]u8 { 0 , 0 , 0 , 0 , 1 , 2 , 3 , 4 , 1 , 2 , 3 , 4 , 6 , 7 , 8 , 9 };
124+
125+ // ADC prescaler lookup table (divisor values)
126+ const adc_prescaler_table = [4 ]u8 { 2 , 4 , 6 , 8 };
127+
128+ /// Get the current clock frequencies by reading RCC registers
129+ /// Based on WCH's RCC_GetClocksFreq() implementation
130+ pub fn get_freqs () ClockSpeeds {
131+ var sysclk : u32 = 0 ;
132+
133+ // Determine system clock source from SWS (System Clock Switch Status)
134+ const sws = RCC .CFGR0 .read ().SWS ;
135+
136+ switch (sws ) {
137+ 0 = > {
138+ // HSI used as system clock
139+ sysclk = HSI_VALUE ;
140+ },
141+ 1 = > {
142+ // HSE used as system clock
143+ sysclk = HSE_VALUE ;
144+ },
145+ 2 = > {
146+ // PLL used as system clock
147+ const cfgr0 = RCC .CFGR0 .read ();
148+ const pllmul_bits = cfgr0 .PLLMUL ;
149+ const pllsrc = cfgr0 .PLLSRC ;
150+
151+ // PLL multiplication factor: PLLMUL bits + 2
152+ // Special case: if result is 17, it's actually 18
153+ var pllmul : u32 = @as (u32 , pllmul_bits ) + 2 ;
154+ if (pllmul == 17 ) pllmul = 18 ;
155+
156+ if (pllsrc == 0 ) {
157+ // PLL source is HSI
158+ const hsipre = EXTEND .EXTEND_CTR .read ().HSIPRE ;
159+ if (hsipre == 1 ) {
160+ // HSI not divided
161+ sysclk = HSI_VALUE * pllmul ;
162+ } else {
163+ // HSI divided by 2
164+ sysclk = (HSI_VALUE >> 1 ) * pllmul ;
165+ }
166+ } else {
167+ // PLL source is HSE
168+ const pllxtpre = cfgr0 .PLLXTPRE ;
169+ if (pllxtpre == 1 ) {
170+ // HSE divided by 2 before PLL
171+ sysclk = (HSE_VALUE >> 1 ) * pllmul ;
172+ } else {
173+ // HSE not divided
174+ sysclk = HSE_VALUE * pllmul ;
175+ }
176+ }
177+ },
178+ else = > {
179+ // Default to HSI
180+ sysclk = HSI_VALUE ;
181+ },
182+ }
183+
184+ // Calculate AHB clock (HCLK) from SYSCLK using HPRE prescaler
185+ const hpre = RCC .CFGR0 .read ().HPRE ;
186+ const hpre_shift = prescaler_table [hpre ];
187+ const hclk = sysclk >> @as (u5 , @intCast (hpre_shift ));
188+
189+ // Calculate APB1 clock (PCLK1) from HCLK using PPRE1 prescaler
190+ const ppre1 = RCC .CFGR0 .read ().PPRE1 ;
191+ const ppre1_shift = prescaler_table [ppre1 ];
192+ const pclk1 = hclk >> @as (u5 , @intCast (ppre1_shift ));
193+
194+ // Calculate APB2 clock (PCLK2) from HCLK using PPRE2 prescaler
195+ const ppre2 = RCC .CFGR0 .read ().PPRE2 ;
196+ const ppre2_shift = prescaler_table [ppre2 ];
197+ const pclk2 = hclk >> @as (u5 , @intCast (ppre2_shift ));
198+
199+ // Calculate ADC clock from PCLK2 using ADCPRE
200+ const adcpre = RCC .CFGR0 .read ().ADCPRE ;
201+ const adc_divisor : u32 = adc_prescaler_table [adcpre ];
202+ const adcclk = pclk2 / adc_divisor ;
203+
204+ return .{
205+ .sysclk = sysclk ,
206+ .hclk = hclk ,
207+ .pclk1 = pclk1 ,
208+ .pclk2 = pclk2 ,
209+ .adcclk = adcclk ,
210+ };
211+ }
0 commit comments