External Interrupt pada STM32 Blue Pill dengan Rust
Pada artikel kali ini kita akan membahas mengenai external interrupt pada mikrokontroler STM32. Dengan external interrupt kita dapat mendeteksi perubahan tegangan pada GPIO dan kemudian memanggil CPU untuk menjalankan perintah/handler tertentu (Interrupt Service Routine/ISR).
External Interrupt pada STM32 Blue Pill
External interrupt merupakan fitur pada mikrokontroler STM32 yang berfungsi untuk mendeteksi perubahan tegangan pada GPIO, kemudian menginterupsi CPU untuk menjalakan Interrupt Service Routine (ISR) yang telah ditentukan. Berikut merupakan jenis-jenis external interrupt pada STM32 serta pin GPIO yang digunakan:
| EXTI | Pin GPIO | Handler di Rust / C | Sifat Handler |
|---|---|---|---|
| EXTI0 | PA0, PB0, PC0* | EXTI0() | Mandiri |
| EXTI1 | PA1, PB1, PC1* | EXTI1() | Mandiri |
| EXTI2 | PA2, PB2, PC2* | EXTI2() | Mandiri |
| EXTI3 | PA3, PB3, PC3* | EXTI3() | Mandiri |
| EXTI4 | PA4, PB4, PC4* | EXTI4() | Mandiri |
| EXTI5 | PA5, PB5, PC5* | EXTI9_5() | Berbagi |
| EXTI6 | PA6, PB6, PC6* | EXTI9_5() | Berbagi |
| EXTI7 | PA7, PB7, PC7* | EXTI9_5() | Berbagi |
| EXTI8 | PA8, PB8, PC8* | EXTI9_5() | Berbagi |
| EXTI9 | PA9, PB9, PC9* | EXTI9_5() | Berbagi |
| EXTI10 | PA10, PB10, PC10* | EXTI15_10() | Berbagi |
| EXTI11 | PA11, PB11, PC11* | EXTI15_10() | Berbagi |
| EXTI12 | PA12, PB12, PC12* | EXTI15_10() | Berbagi |
| EXTI13 | PA13, PB13, PC13 | EXTI15_10() | Berbagi |
| EXTI14 | PA14, PB14, PC14 | EXTI15_10() | Berbagi |
| EXTI15 | PA15, PB15, PC15 | EXTI15_10() | Berbagi |
Catatan:
- *: Pin tidak ada pada mikrokontroler STM32F103C8
Seperti yang kita lihat pada tabel, external interrupt 5 sampai external interrupt 9 dan external interrupt 10 sampai external interrupt 15 memiliki hander yang sama yaitu EXTI9_5() dan EXTI15_10() . Sehingga external interrupt tersebut memiliki sifat berbagi (shared). Sedangkan external interrupt 0 sampai 4 memiliki sifat mandiri karena masing-masing memiliki handler tersendiri yaitu EXTI0() , EXTI1() , EXTI2() , EXTI3() , dan EXTI4() .
External interrupt dapat mendeteksi perubahan tegangan pada pin GPIO sebagai berikut:
- Rising edge: Mendeteksi perubahan tegangan dari 0V ke 3.3V. CPU akan diinterupsi jika terjadi perubahan tegangan dari LOW ke HIGH pada GPIO.
- Falling edge: Mendeteksi perubahan tegangan dari 3.3V ke 0V. CPU akan diinterupsi jika terjadi perubahan tegangan dari HIGH ke LOW pada GPIO.
- Rising dan Falling edge: Mendeteksi perubahan tegangan dari 0V ke 3.3V dan dari 3.3V ke 0V. CPU akan diinterupsi jika terjadi perubahan tegangan dari LOW ke HIGH atau dari HIGH ke LOW pada GPIO.
Catatan: Setiap 1 external interrupt hanya boleh menggunakan 1 pin GPIO. Sehingga jika kita menggunakan external interrupt 1 (
EXTI1) maka hanya dapat menggunakan salah satu dari pinPA1, PB1, dan PC1.
Persiapan Hardware
Berikut merupakan komponen-komponen hardware yang digunakan dalam tutorial ini:
Mikrokontroler STM32F103C8 (Blue Pill)
Mikrokontroler STM32F103C8 merupakan salah satu varian dari mikrokontroler STM32. Pada kesempatan kali ini kami akan menggunakan board development Blue Pill. Mikrokontroler ini akan kita program untuk memanfaatkan fitur external interrupt

Board sistem minimum STM32F103C8T6 (Blue Pill) ST-Link USB Downloader Debuger
ST-LINK USB Downloader Debuger berfungsi sebagai penghubung antara PC/laptop dengan STM32F103C8, sehingga kita bisa memprogram STM32F103C8 dari PC/laptop.

ST-Link USB Downloader Debuger untuk memrogram board Blue Pill Breadboard
Breadboard (Project Board) digunakan untuk membuat prototipe rangkaian elektronik tanpa perlu menggunakan solder.

Breadboard (Project Board) Push Button
Push Button akan kita gunakan sebagai input ke pin GPIO STM32F103C8. Kedua kaki pada Push Button akan saling terhubung ketika Push Button ditekan, sehingga memungkinkan arus listrik mengalir dia antara kedua kakinya.

Push Button Kabel Jumper (male to male dan female to female)
Kabel jumper femal to female digunakan untuk menghubungkan Blue Pill ke ST-LINK USB Downloader Debuger. Sedangkan kabel jumper male to male digunakan untuk memebuat rangkaian di breadboard.
Menggunakan External Interrupt pada STM32F103C8 (Blue Pill) dengan Rust
Pada tutorial ini kita akan mencoba menggunakan external interrupt 0 pada Blue Pill untuk mendeteksi falling edge pada pin GPIO PA0.
Skematik Rangkaian untuk External Interrupt
Pertama siapkan komponen-komponen yang dibutuhkan, kemudian buat rangkaian pada breadboard seperti gambar skematik berikut:

Pada gamabar tersebut ketika push button ditekan maka pin PA0 akan terhubung ke GND (0V). Pin PA0 akan kita konfigurasi sebagai input pull-up sehingga secara default akan bernilai HIGH. Ketika push button ditekan maka akan terjadi perubahan tegangan dari 3.3V ke 0V pada pin PA0 (terjadi falling edge).
Membuat Program External Interrupt pada STM32F103C8 (Blue Pill) Menggunakan Rust
Pertama kita akan membuat project Rust untuk embedded system sesuai dengan yang dijelaskan pada artikel ini. Buka file Cargo.toml kemudian tambahkan kode berikut untuk mendefiniskan binary executable baru dengan nama 'gpio-external-interrupt' :
Toml
1[[bin]]
2name = "gpio-external-interrupt"
3path = "src/main.rs"
4test = false
5bench = false 1[package]
2name = "gpio-external-interrupt"
3version = "0.1.0"
4edition = "2024"
5
6[[bin]]
7name = "gpio-external-interrupt"
8path = "src/main.rs"
9test = false
10bench = false
11
12[dependencies]
13cortex-m = { version = "0.7.7", features = ["critical-section-single-core"] }
14cortex-m-rt = "0.7.5"
15stm32f1xx-hal = { version = "0.11.0", features = ["stm32f103", "medium"] }
16panic-probe = { version = "1.0", features = ["print-defmt"] }
17defmt = "1.0.1"
18defmt-rtt = "1.1.0"
19
20[profile.dev]
21opt-level = 's'
22codegen-units = 1
23
24[profile.release]
25opt-level = 'z'
26lto = trueKemudian buka file src/main.rs, lalu isi dengan kode berikut:
Rust
1#![no_std]
2#![no_main]
3
4use defmt_rtt as _;
5use panic_probe as _;
6
7use cortex_m_rt::entry;
8use stm32f1xx_hal::{
9 flash::FlashExt,
10 gpio::ExtiPin,
11 pac::{self, interrupt},
12 prelude::*,
13 rcc::{Config, RccExt},
14 time::Hertz,
15 timer::Timer,
16};Mengonfigurasi Rust toolchain agar tidak menggunakan standard library dan program tidak dijalankan dalam sistem operasi (bare-metal).
Mendefinisikan semua library yang akan digunakan:
- Library/crate
cortex_m_rtuntuk menentukan fungsi entry program akan mulai berjalan dan menangani proses startup program. defmt_rttberfungsi untuk mengirimkan data ke PC/laptop untuk logging menggunakan protokol real time transfer.- Library
stm32f1xx_halberfungsi agar kita dapat mengakses periferal mikrokontroler STM32F103C8 secara aman. panic_probedigunakan untuk menangani jika terjadi runtime error, dan akan otomatis mengirimkan log eror yang terjadi ke host PC/laptop.
Di dalam fungsi main isi dengan kode berikut:
Rust
1defmt::println!("STM32F103C8 External interrupt");Mengirim pesan ke terminal PC/laptop untuk menandai sebagai program external interrupt.
Rust
1let dp = pac::Peripherals::take().unwrap();
2
3let cp = cortex_m::Peripherals::take().unwrap();Mengakses periferal STM32F103C8 dan periperal CPU Cortex-M3.
Rust
1let mut flash = dp.FLASH.constrain();
2
3let rcc = dp.RCC.constrain();
4
5let clock_config = Config::default()
6 .use_hse(Hertz::MHz(8))
7 .sysclk(Hertz::MHz(72))
8 .hclk(Hertz::MHz(72));
9
10let mut clocks = rcc.freeze(clock_config, &mut flash.acr);Mengonfigurasi clock dengan menggunakan clock external ( use_hse ) 8 MHz, kemudian mengatur clock system ( sysclk ) ke 72 MHz dan clock advance high performace bus ( hclk ) ke 72 MHz.
Rust
1let mut gpioa = dp.GPIOA.split(&mut clocks);
2
3let mut pa0 = gpioa.pa0.into_pull_up_input(&mut gpioa.crl);Mengakses periferal GPIO port A dan mengonfigurasi pin PA0 sebagai input pull-up.
Rust
1let mut afio = dp.AFIO.constrain(&mut clocks);
2let mut exti = dp.EXTI;Mengakses periferal AFIO (Alternate Function Input/Output) dan EXTI (External Interrupt).
Rust
1pa0.make_interrupt_source(&mut afio);Menggunakan pin PA0 sebagai sumber external interrupt.
Rust
1// trigger on falling edge
2pa0.trigger_on_edge(&mut exti, stm32f1xx_hal::gpio::Edge::Falling);mengonfigurasi pin PA0 untuk mendeteksi falling edge. Disini kita mengatur edge yang ingin dideteksi (rising edge, falling edge, atau rising and falling edge).
Rust
1// enable interrupt
2pa0.enable_interrupt(&mut exti);Mengaktifkan external interrupt pada pin PA0.
Rust
1unsafe {
2 pac::NVIC::unmask(pac::interrupt::EXTI0);
3}Mengaktifkan interrupt EXTI0 pada NVIC (Nested Vector Interrupt Controller).
Rust
1let mut delay = Timer::syst_external(cp.SYST, &mut clocks.clocks).delay();Membuat delay dengan menggunakan System Timer.
Rust
1loop {
2 defmt::println!("Main program");
3 delay.delay_ms(1000u32);
4}Membuat program utama untuk mengirimkan pesan ke terminal PC/laptop setiap 1 detik secara terus menerus.
Rust
1#[interrupt]
2fn EXTI0() {
3 let exti = unsafe { &*pac::EXTI::ptr() };
4
5 // clear pending register bit 0 (EXTI0)
6 exti.pr().write(|w| w.pr0().bit(true));
7
8 defmt::println!("interrupt: push button pressed");
9}Membuat fungsi interrupt (ISR) yang dijalankan ketika terjadi external interrupt pada EXTI0. Fungsi itu akan membersihkan pending register bit 0 (EXTI0) dan mengirimkan pesan ke terminal PC/laptop ketika terjadi falling edge pada pin PA0 akibat push button ditekan.
1#![no_std]
2#![no_main]
3
4use defmt_rtt as _;
5use panic_probe as _;
6
7use cortex_m_rt::entry;
8use stm32f1xx_hal::{
9 flash::FlashExt,
10 gpio::ExtiPin,
11 pac::{self, interrupt},
12 prelude::*,
13 rcc::{Config, RccExt},
14 time::Hertz,
15 timer::Timer,
16};
17
18#[entry]
19fn main() -> ! {
20 defmt::println!("STM32F103C8 External interrupt");
21
22 let dp = pac::Peripherals::take().unwrap();
23
24 let cp = cortex_m::Peripherals::take().unwrap();
25
26 let mut flash = dp.FLASH.constrain();
27
28 let rcc = dp.RCC.constrain();
29
30 let clock_config = Config::default()
31 .use_hse(Hertz::MHz(8))
32 .sysclk(Hertz::MHz(72))
33 .hclk(Hertz::MHz(72));
34
35 let mut clocks = rcc.freeze(clock_config, &mut flash.acr);
36
37 let mut gpioa = dp.GPIOA.split(&mut clocks);
38
39 let mut pa0 = gpioa.pa0.into_pull_up_input(&mut gpioa.crl);
40
41 let mut afio = dp.AFIO.constrain(&mut clocks);
42 let mut exti = dp.EXTI;
43
44 pa0.make_interrupt_source(&mut afio);
45
46 // trigger on falling edge
47 pa0.trigger_on_edge(&mut exti, stm32f1xx_hal::gpio::Edge::Falling);
48
49 // enable interrupt
50 pa0.enable_interrupt(&mut exti);
51
52 unsafe {
53 pac::NVIC::unmask(pac::interrupt::EXTI0);
54 }
55
56 let mut delay = Timer::syst_external(cp.SYST, &mut clocks.clocks).delay();
57
58 loop {
59 defmt::println!("Main program");
60 delay.delay_ms(1000u32);
61 }
62}
63
64#[interrupt]
65fn EXTI0() {
66 let exti = unsafe { &*pac::EXTI::ptr() };
67
68 // clear pending register bit 0 (EXTI0)
69 exti.pr().write(|w| w.pr0().bit(true));
70
71 defmt::println!("interrupt: push button pressed");
72}Jalankan program dengan perintah ‘cargo run --bin gpio-external-interrupt’ pada terminal di dalam folder project.
Source Code
Source code yang digunakan pada artikel ini dapat diakses di repositori Github
Jika anda mengalami kendala dalam mengikuti tutorial ini jangan ragu untuk menghubungi kami melalui halaman kontak