Skip to content

Commit 689f4cc

Browse files
Stm32f1 adc (#582)
1 parent fd3f6e5 commit 689f4cc

File tree

6 files changed

+1491
-0
lines changed

6 files changed

+1491
-0
lines changed

examples/stmicro/stm32/build.zig

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ pub fn build(b: *std.Build) void {
2424
// .{ .target = stm32.boards.stm32f429idiscovery, .name = "stm32f429idiscovery", .file = "src/blinky.zig" },
2525
.{ .target = stm32.chips.STM32F103C8, .name = "STM32F1xx_blink", .file = "src/blinky.zig" },
2626
.{ .target = stm32.chips.STM32F100RB, .name = "STM32F1xx_semihost", .file = "src/semihosting.zig" }, //QEMU target: stm32vldiscovery
27+
.{ .target = stm32.chips.STM32F103C8, .name = "STM32F1xx_adc", .file = "src/stm32f1xx/adc.zig" },
28+
.{ .target = stm32.chips.STM32F103C8, .name = "STM32F1xx_adv_adc", .file = "src/stm32f1xx/advanced_adc.zig" },
29+
.{ .target = stm32.chips.STM32F103C8, .name = "STM32F1xx_dual_adc", .file = "src/stm32f1xx/adc_dualmode.zig" },
2730
.{ .target = stm32.chips.STM32F103C8, .name = "STM32F1xx_gpio", .file = "src/stm32f1xx/gpio.zig" },
2831
.{ .target = stm32.chips.STM32F103C8, .name = "STM32F1xx_uart_echo", .file = "src/stm32f1xx/uart_echo.zig" },
2932
.{ .target = stm32.chips.STM32F103C8, .name = "STM32F1xx_uart_log", .file = "src/stm32f1xx/uart_log.zig" },
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
const std = @import("std");
2+
const microzig = @import("microzig");
3+
4+
const RCC = microzig.chip.peripherals.RCC;
5+
const stm32 = microzig.hal;
6+
const timer = microzig.hal.timer.GPTimer.init(.TIM2);
7+
8+
const uart = stm32.uart.UART.init(.USART1);
9+
const gpio = stm32.gpio;
10+
const TX = gpio.Pin.from_port(.A, 9);
11+
12+
const ADC = microzig.hal.adc.ADC;
13+
const ADC_pin1 = gpio.Pin.from_port(.A, 1);
14+
const ADC_pin2 = gpio.Pin.from_port(.A, 2);
15+
16+
const v25 = 1.43;
17+
const avg_slope = 0.0043; //4.3mV/°C
18+
19+
fn adc_to_temp(val: usize) f32 {
20+
const temp_mv: f32 = (@as(f32, @floatFromInt(val)) / 4096) * 3.3; //convert to voltage
21+
return ((v25 - temp_mv) / avg_slope) + 25; //convert to celsius
22+
}
23+
24+
pub const microzig_options = microzig.Options{
25+
.logFn = stm32.uart.log,
26+
};
27+
28+
pub fn main() !void {
29+
RCC.APB1ENR.modify(.{
30+
.TIM2EN = 1,
31+
});
32+
RCC.APB2ENR.modify(.{
33+
.AFIOEN = 1,
34+
.USART1EN = 1,
35+
.GPIOAEN = 1,
36+
.ADC1EN = 1,
37+
});
38+
const counter = timer.into_counter(8_000_000);
39+
const adc = ADC.init(.ADC1);
40+
var adc_out_buf: [10]u16 = undefined;
41+
42+
TX.set_output_mode(.alternate_function_push_pull, .max_50MHz);
43+
ADC_pin1.set_input_mode(.analog);
44+
ADC_pin2.set_input_mode(.analog);
45+
46+
uart.apply(.{
47+
.baud_rate = 115200,
48+
.clock_speed = 8_000_000,
49+
});
50+
51+
stm32.uart.init_logger(&uart);
52+
53+
adc.enable(&counter);
54+
adc.set_channel_sample_rate(16, .@"239.5");
55+
adc.set_channel_sample_rate(17, .@"239.5");
56+
adc.set_channel_sample_rate(1, .@"13.5");
57+
adc.set_channel_sample_rate(2, .@"13.5");
58+
adc.load_sequence(&.{ 16, 17, 1, 2 }); //CH16: CPU temp, CH17: Vref, CH1: ADC_pin1, CH2: ADC_pin2
59+
std.log.info("start ADC scan", .{});
60+
while (true) {
61+
const adc_buf: []const u16 = try adc.read_multiple_channels(&adc_out_buf);
62+
counter.sleep_ms(100);
63+
std.log.info("\x1B[2J\x1B[H", .{}); // Clear screen and move cursor to 1,1
64+
std.log.info("CPU temp: {d:.1}C", .{adc_to_temp(adc_buf[0])});
65+
std.log.info("Vref: {d:0>4}", .{adc_buf[1]});
66+
std.log.info("CH1: {d:0>4}", .{adc_buf[2]});
67+
std.log.info("CH2 {d}", .{adc_buf[3]});
68+
}
69+
}
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
//this example is an extension of advanced_adc.zig
2+
//here it shows how to configure and use the Dual ADC mode using the advanced ADC API
3+
4+
const std = @import("std");
5+
const microzig = @import("microzig");
6+
7+
const RCC = microzig.chip.peripherals.RCC;
8+
const DMA = microzig.chip.peripherals.DMA1;
9+
const DMA_t = microzig.chip.types.peripherals.bdma_v1;
10+
const stm32 = microzig.hal;
11+
const timer = microzig.hal.timer.GPTimer.init(.TIM2);
12+
13+
const uart = stm32.uart.UART.init(.USART1);
14+
const gpio = stm32.gpio;
15+
const TX = gpio.Pin.from_port(.A, 9);
16+
17+
const AdvancedADC = microzig.hal.adc.AdvancedADC;
18+
const ADC_pin1 = gpio.Pin.from_port(.A, 1);
19+
const ADC_pin2 = gpio.Pin.from_port(.A, 2);
20+
21+
pub const microzig_options = microzig.Options{
22+
.logFn = stm32.uart.log,
23+
};
24+
25+
const AdcData = packed struct(u32) {
26+
adc1: u16,
27+
adc2: u16,
28+
};
29+
30+
const v25 = 1.43;
31+
const avg_slope = 0.0043; //4.3mV/°C
32+
33+
fn adc_to_temp(val: usize) f32 {
34+
const temp_mv: f32 = (@as(f32, @floatFromInt(val)) / 4096) * 3.3; //convert to voltage
35+
return ((v25 - temp_mv) / avg_slope) + 25; //convert to celsius
36+
}
37+
38+
fn DMA_init(arr_addr: u32, adc_addr: u32) void {
39+
const CH1: *volatile DMA_t.CH = @ptrCast(&DMA.CH);
40+
CH1.CR.raw = 0; //disable channel
41+
CH1.NDTR.raw = 0;
42+
CH1.CR.modify(.{
43+
.DIR = DMA_t.DIR.FromPeripheral,
44+
.CIRC = 1, //disable circular mode
45+
.PL = DMA_t.PL.High, //high priority
46+
.MSIZE = DMA_t.SIZE.Bits32,
47+
.PSIZE = DMA_t.SIZE.Bits32,
48+
.MINC = 1, //memory increment mode
49+
.PINC = 0, //peripheral not incremented
50+
});
51+
52+
CH1.NDTR.modify(.{ .NDT = 2 }); //number of data to transfer, 2 samples
53+
CH1.PAR = adc_addr; //peripheral address
54+
CH1.MAR = arr_addr; //memory address
55+
CH1.CR.modify(.{ .EN = 1 }); //enable channel
56+
}
57+
58+
pub fn main() !void {
59+
RCC.AHBENR.modify(.{
60+
.DMA1EN = 1,
61+
});
62+
RCC.APB1ENR.modify(.{
63+
.TIM2EN = 1,
64+
});
65+
RCC.APB2ENR.modify(.{
66+
.AFIOEN = 1,
67+
.USART1EN = 1,
68+
.GPIOAEN = 1,
69+
.ADC1EN = 1,
70+
.ADC2EN = 1,
71+
});
72+
const counter = timer.into_counter(8_000_000);
73+
const adc1 = AdvancedADC.init(.ADC1);
74+
const adc2 = AdvancedADC.init(.ADC2);
75+
76+
const adc_data_addr: u32 = @intFromPtr(&adc1.regs.DR);
77+
var adc_buf: [2]AdcData = undefined;
78+
const adc_buf_addr: u32 = @intFromPtr(&adc_buf[0]);
79+
80+
TX.set_output_mode(.alternate_function_push_pull, .max_50MHz);
81+
ADC_pin1.set_input_mode(.analog);
82+
ADC_pin2.set_input_mode(.analog);
83+
84+
uart.apply(.{
85+
.baud_rate = 115200,
86+
.clock_speed = 8_000_000,
87+
});
88+
89+
stm32.uart.init_logger(&uart);
90+
91+
adc1.enable(true, &counter);
92+
adc1.enable_reftemp(&counter);
93+
adc2.enable(false, &counter);
94+
95+
try adc1.configure_dual_mode(.{ .Regular = .{
96+
.dma = true,
97+
.master_seq = &.{ 16, 17 },
98+
.slave_seq = &.{ 1, 2 },
99+
.rate_seq = &.{ .@"239.5", .@"239.5" },
100+
.trigger = .SWSTART,
101+
.mode = .{ .Continuous = {} },
102+
} });
103+
104+
std.log.info("start Dual ADC scan", .{});
105+
DMA_init(adc_buf_addr, adc_data_addr);
106+
adc1.software_trigger(); //start conversion
107+
while (true) {
108+
counter.sleep_ms(250);
109+
const temp = adc_buf[0].adc1;
110+
const vref = adc_buf[1].adc1;
111+
const ch1 = adc_buf[0].adc2;
112+
const ch2 = adc_buf[1].adc2;
113+
std.log.info("\x1B[2J\x1B[H", .{}); // Clear screen and move cursor to 1,1
114+
std.log.info("CPU temp: {d:.1}C", .{adc_to_temp(temp)});
115+
std.log.info("Vref: {d}", .{vref});
116+
std.log.info("CH1: {d}", .{ch1});
117+
std.log.info("CH2 {d}", .{ch2});
118+
}
119+
}
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
//example for the advanced ADC API, this API is recommended for those already familiar with the ADC of STM32s
2+
//this example configures a regular group with software trigger, an injected group configured as auto injected,
3+
//and an analog comparator (ADC watchdog)
4+
5+
//before creating a program using ADC from older ST families, such as in this case STM32F1
6+
//be aware of possible hardware bugs and limitations
7+
//for example:
8+
// temperature sensor/VREF cannot be read in interleaved mode as it requires a sample time greater than 17
9+
// readings after 1ms from the previous reading may contain more noise than expected
10+
//Voltage glitch on ADC input 0
11+
12+
const std = @import("std");
13+
const microzig = @import("microzig");
14+
const interrupt = microzig.interrupt;
15+
16+
const RCC = microzig.chip.peripherals.RCC;
17+
const DMA = microzig.chip.peripherals.DMA1;
18+
const DMA_t = microzig.chip.types.peripherals.bdma_v1;
19+
const stm32 = microzig.hal;
20+
const timer = microzig.hal.timer.GPTimer.init(.TIM2);
21+
22+
const uart = stm32.uart.UART.init(.USART1);
23+
const gpio = stm32.gpio;
24+
const TX = gpio.Pin.from_port(.A, 9);
25+
26+
pub const microzig_options = microzig.Options{
27+
.logFn = stm32.uart.log,
28+
.interrupts = .{ .ADC1_2 = .{ .c = watchdog_handler } },
29+
};
30+
31+
const AdvancedADC = microzig.hal.adc.AdvancedADC;
32+
const adc = AdvancedADC.init(.ADC1);
33+
const ADC_pin1 = gpio.Pin.from_port(.A, 1);
34+
const ADC_pin2 = gpio.Pin.from_port(.A, 2);
35+
const ADC_pin3 = gpio.Pin.from_port(.A, 3);
36+
37+
const v25 = 1.43;
38+
const avg_slope = 0.0043; //4.3mV/°C
39+
40+
var ovf_flag: bool = false;
41+
pub fn watchdog_handler() callconv(.C) void {
42+
ovf_flag = true;
43+
adc.clear_flags(adc.read_flags()); //clear all active flags
44+
}
45+
46+
fn adc_to_temp(val: usize) f32 {
47+
const temp_mv: f32 = (@as(f32, @floatFromInt(val)) / 4096) * 3.3; //convert to voltage
48+
return ((v25 - temp_mv) / avg_slope) + 25; //convert to celsius
49+
}
50+
51+
fn DMA_init(arr_addr: u32, adc_addr: u32) void {
52+
const CH1: *volatile DMA_t.CH = @ptrCast(&DMA.CH);
53+
CH1.CR.raw = 0; //disable channel
54+
CH1.NDTR.raw = 0;
55+
CH1.CR.modify(.{
56+
.DIR = DMA_t.DIR.FromPeripheral,
57+
.CIRC = 1, //disable circular mode
58+
.PL = DMA_t.PL.High, //high priority
59+
.MSIZE = DMA_t.SIZE.Bits16,
60+
.PSIZE = DMA_t.SIZE.Bits16,
61+
.MINC = 1, //memory increment mode
62+
.PINC = 0, //peripheral not incremented
63+
});
64+
65+
CH1.NDTR.modify(.{ .NDT = 4 }); //number of data to transfer, 4 samples
66+
CH1.PAR = adc_addr; //peripheral address
67+
CH1.MAR = arr_addr; //memory address
68+
CH1.CR.modify(.{ .EN = 1 }); //enable channel
69+
}
70+
71+
pub fn main() !void {
72+
RCC.AHBENR.modify(.{
73+
.DMA1EN = 1,
74+
});
75+
RCC.APB1ENR.modify(.{
76+
.TIM2EN = 1,
77+
});
78+
RCC.APB2ENR.modify(.{
79+
.AFIOEN = 1,
80+
.USART1EN = 1,
81+
.GPIOAEN = 1,
82+
.ADC1EN = 1,
83+
});
84+
85+
const counter = timer.into_counter(8_000_000);
86+
87+
const adc_data_addr: u32 = @intFromPtr(&adc.regs.DR);
88+
var adc_buf: [10]u16 = .{0} ** 10;
89+
const adc_buf_addr: u32 = @intFromPtr(&adc_buf);
90+
const ref_ovf_flag: *volatile bool = &ovf_flag;
91+
92+
//configure UART log
93+
94+
TX.set_output_mode(.alternate_function_push_pull, .max_50MHz);
95+
uart.apply(.{
96+
.baud_rate = 115200,
97+
.clock_speed = 8_000_000,
98+
});
99+
stm32.uart.init_logger(&uart);
100+
101+
//configure adc
102+
interrupt.enable(.ADC1_2); //enalbe ADC1 interrupt
103+
ADC_pin1.set_input_mode(.analog);
104+
ADC_pin2.set_input_mode(.analog);
105+
ADC_pin3.set_input_mode(.analog);
106+
107+
//enable ADC and VREF/tempsensor
108+
adc.enable(true, &counter);
109+
adc.enable_reftemp(&counter);
110+
111+
//regular group configuration
112+
try adc.configure_regular(.{
113+
.dma = true,
114+
.trigger = .SWSTART,
115+
.mode = .{
116+
.Single = .{
117+
.seq = &.{ 16, 17, 2, 3 },
118+
.channels_conf = &.{
119+
.{ .channel = 17, .sample_rate = .@"239.5" }, //Vrefint
120+
.{ .channel = 16, .sample_rate = .@"239.5" }, //temperature sensor
121+
.{ .channel = 1, .sample_rate = .@"13.5" }, //ADC1 channel 1
122+
.{ .channel = 3, .sample_rate = .@"13.5" }, //ADC1 channel 2
123+
},
124+
},
125+
},
126+
});
127+
128+
//injected group configuration, AUTO INJECTED mode starts right after the regular group and therefore does not require an external trigger
129+
try adc.configure_injected(.{
130+
.mode = .{
131+
.auto_injected = .{
132+
.seq = &.{1},
133+
},
134+
},
135+
});
136+
137+
//despite the name, this is just an analog comparator
138+
adc.configure_watchdog(
139+
.{
140+
.guard_mode = .{ .single_injected = 1 },
141+
.interrupt = true,
142+
.high_treshold = 4095,
143+
.low_treshold = 800,
144+
},
145+
);
146+
147+
std.log.info("start Advanced ADC scan", .{});
148+
149+
DMA_init(adc_buf_addr, adc_data_addr);
150+
151+
while (true) {
152+
adc.software_trigger(); //start conversion
153+
counter.sleep_ms(100);
154+
std.log.info("\x1B[2J\x1B[H", .{}); // Clear screen and move cursor to 1,1
155+
std.log.info("CPU temp: {d:.1}C", .{adc_to_temp(adc_buf[0])});
156+
std.log.info("Vref: {d:0>4}", .{adc_buf[1]});
157+
std.log.info("CH1: {d:0>4}", .{adc_buf[2]});
158+
std.log.info("CH3 {d}", .{adc_buf[3]});
159+
if (ref_ovf_flag.*) {
160+
std.log.info("Injected: OVERFLOW", .{});
161+
ref_ovf_flag.* = false;
162+
continue;
163+
}
164+
std.log.info("Injected: {d}", .{adc.read_injected_data(0)});
165+
}
166+
}

0 commit comments

Comments
 (0)