Skip to content

Commit ed8a7b9

Browse files
STM32F1xxx Low-level timer APIs (#625)
1 parent f0b4d1f commit ed8a7b9

File tree

4 files changed

+434
-125
lines changed

4 files changed

+434
-125
lines changed

examples/stmicro/stm32/build.zig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ pub fn build(b: *std.Build) void {
3838
.{ .target = stm32.chips.STM32F103CB, .name = "STM32F1xx_usb_cdc", .file = "src/stm32f1xx/usb_cdc.zig" },
3939
.{ .target = stm32.chips.STM32F103CB, .name = "STM32F1xx_rcc", .file = "src/stm32f1xx/rcc.zig" },
4040
.{ .target = stm32.chips.STM32F103CB, .name = "STM32F1xx_timer", .file = "src/stm32f1xx/timer.zig" },
41+
.{ .target = stm32.chips.STM32F103CB, .name = "STM32F1xx_timer_capture", .file = "src/stm32f1xx/timer_capture.zig" },
4142
};
4243

4344
for (available_examples) |example| {

examples/stmicro/stm32/src/stm32f1xx/timer.zig

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
//basic example of using timers on STM32F1xx showing how to use high-level APIs for PWM and Counter.
2+
13
const std = @import("std");
24
const microzig = @import("microzig");
35

@@ -39,10 +41,10 @@ pub fn main() !void {
3941
}
4042

4143
//first we need to configure the PWM peripheral
42-
pwm.configure_PWM(.{
44+
pwm.configure(.{
4345
.prescaler = 7, //prescaler value, 7 means pckl / 8
4446
.auto_reload = 1000, //1khz
45-
.counter_direction = .Up,
47+
.counter_mode = .{ .up = {} },
4648
});
4749

4850
//then we configure the channels
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
//NOTE: this is an advanced example and is recommended for those who already have experience with the STM32F1
2+
//Example to demonstrate the use of input capture on STM32F1xx
3+
//in this example an operation called PWM input is performed
4+
//where two channels read the same input but are triggered on different edges
5+
//thus measuring the frequency and duty cycle of a PWM signal
6+
7+
const std = @import("std");
8+
const microzig = @import("microzig");
9+
10+
//example usage
11+
const stm32 = microzig.hal;
12+
const gpio = stm32.gpio;
13+
const GPTimer = stm32.timer.GPTimer;
14+
15+
//gpios
16+
const ch1 = gpio.Pin.from_port(.A, 0);
17+
18+
const uart = stm32.uart.UART.init(.USART1);
19+
const TX = gpio.Pin.from_port(.A, 9);
20+
21+
pub const microzig_options = microzig.Options{
22+
.logFn = stm32.uart.log,
23+
.interrupts = .{ .TIM2 = .{ .c = isr_tim2 } },
24+
};
25+
26+
const comp = GPTimer.init(.TIM2);
27+
const counter = GPTimer.init(.TIM3).into_counter_mode();
28+
29+
var global_uptime: i32 = 0;
30+
var global_downtime: i32 = 0;
31+
var global_dif: u32 = 0;
32+
33+
var freq: i32 = 0;
34+
var duty_cycle: f32 = 0;
35+
36+
//function that handles timer interrupts
37+
//since the timer is reset at each rising edge,
38+
//the value of ch2 marks the moment when the signal went from high to low, indicating the duty cycle.
39+
//the value of ch1 marks the point when the signal goes back to high before the timer resets, indicating the period.
40+
fn isr_tim2() callconv(.C) void {
41+
const flags = comp.get_interrupt_flags();
42+
if (flags.channel2) {
43+
const rev_ch1 = comp.read_ccr(0);
44+
const rev_fch1: f32 = @floatFromInt(rev_ch1);
45+
const rev_ch2 = comp.read_ccr(1);
46+
const rev_fch2: f32 = @floatFromInt(rev_ch2);
47+
48+
global_uptime = rev_ch1;
49+
global_downtime = rev_ch2;
50+
51+
global_dif = @intCast(@abs(global_uptime - global_downtime));
52+
freq = @divFloor(1_000_000, global_uptime);
53+
54+
duty_cycle = (rev_fch2 / rev_fch1) * 100;
55+
}
56+
57+
comp.clear_interrupts();
58+
}
59+
60+
//timers
61+
pub fn main() !void {
62+
63+
//first we need to enable the clocks for the GPIO and TIM peripherals
64+
65+
//use HSE as system clock source, more stable than HSI
66+
try stm32.rcc.clock_init(.{ .SysClkSource = .RCC_SYSCLKSOURCE_HSE });
67+
68+
//enable GPIOA and TIM2, TIM3, AFIO clocks
69+
//AFIO is needed for alternate function remapping, not used in this example but eneble for easy remapping
70+
//if needed
71+
stm32.rcc.enable_clock(.GPIOA);
72+
stm32.rcc.enable_clock(.TIM2);
73+
stm32.rcc.enable_clock(.TIM3);
74+
stm32.rcc.enable_clock(.AFIO);
75+
stm32.rcc.enable_clock(.USART1);
76+
77+
TX.set_output_mode(.alternate_function_push_pull, .max_50MHz);
78+
79+
uart.apply(.{
80+
.baud_rate = 115200,
81+
.clock_speed = 8_000_000,
82+
});
83+
84+
stm32.uart.init_logger(&uart);
85+
86+
//counter device to genereate delays
87+
const cd = counter.counter_device(8_000_000); //8MHz clock
88+
89+
//set PA0 (TI1) to input
90+
ch1.set_input_mode(.floating);
91+
92+
//configure the timer for a frequency of 1MHz counting upwards
93+
//enable slave mode to reset the counter on the rising edge of TI1 (after filtering)
94+
//filter only can be configured by channel 1 (index 0)
95+
comp.timer_general_config(.{
96+
.prescaler = 7,
97+
.counter_mode = .{ .up = {} },
98+
.slave_config = .{
99+
.trigger_source = .TI1FP1,
100+
.mode = .ResetMode,
101+
},
102+
});
103+
comp.start();
104+
105+
//configure channel 1 and 2 for the same input (TI1) with inverted edges between them
106+
comp.configure_ccr(0, .{
107+
.ch_mode = .{ .capture = .{} },
108+
});
109+
110+
comp.configure_ccr(1, .{
111+
.ch_mode = .{ .capture = .{ .mode = .input_alternate } },
112+
.polarity = .low,
113+
.channel_interrupt_enable = true,
114+
});
115+
116+
//enable timer channels and interrupts
117+
comp.set_channel(0, true);
118+
comp.set_channel(1, true);
119+
comp.set_interrupt(true);
120+
microzig.interrupt.enable_interrupts();
121+
microzig.interrupt.enable(.TIM2);
122+
123+
while (true) {
124+
//values are received in the timer interrupt
125+
std.log.info("uptime: {d}", .{global_uptime});
126+
std.log.info("downtime: {d}", .{global_downtime});
127+
std.log.info("freq: {d}HZ", .{freq});
128+
std.log.info("duty: {d:.2}%", .{duty_cycle});
129+
130+
cd.sleep_ms(1350);
131+
}
132+
}

0 commit comments

Comments
 (0)