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:

EXTIPin GPIOHandler di Rust / CSifat Handler
EXTI0PA0, PB0, PC0*EXTI0()Mandiri
EXTI1PA1, PB1, PC1*EXTI1()Mandiri
EXTI2PA2, PB2, PC2*EXTI2()Mandiri
EXTI3PA3, PB3, PC3*EXTI3()Mandiri
EXTI4PA4, PB4, PC4*EXTI4()Mandiri
EXTI5PA5, PB5, PC5*EXTI9_5()Berbagi
EXTI6PA6, PB6, PC6*EXTI9_5()Berbagi
EXTI7PA7, PB7, PC7*EXTI9_5()Berbagi
EXTI8PA8, PB8, PC8*EXTI9_5()Berbagi
EXTI9PA9, PB9, PC9*EXTI9_5()Berbagi
EXTI10PA10, PB10, PC10*EXTI15_10()Berbagi
EXTI11PA11, PB11, PC11*EXTI15_10()Berbagi
EXTI12PA12, PB12, PC12*EXTI15_10()Berbagi
EXTI13PA13, PB13, PC13EXTI15_10()Berbagi
EXTI14PA14, PB14, PC14EXTI15_10()Berbagi
EXTI15PA15, PB15, PC15EXTI15_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:

  1. Rising edge: Mendeteksi perubahan tegangan dari 0V ke 3.3V. CPU akan diinterupsi jika terjadi perubahan tegangan dari LOW ke HIGH pada GPIO.
  2. Falling edge: Mendeteksi perubahan tegangan dari 3.3V ke 0V. CPU akan diinterupsi jika terjadi perubahan tegangan dari HIGH ke LOW pada GPIO.
  3. 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 pin PA1, PB1, dan PC1 .

Persiapan Hardware

Berikut merupakan komponen-komponen hardware yang digunakan dalam tutorial ini:

  1. 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

    Blue Pill (STM32F103C8)
    Board sistem minimum STM32F103C8T6 (Blue Pill)
  2. 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
    ST-Link USB Downloader Debuger untuk memrogram board Blue Pill
  3. Breadboard

    Breadboard (Project Board) digunakan untuk membuat prototipe rangkaian elektronik tanpa perlu menggunakan solder.

    Breadboard (Project Board)
    Breadboard (Project Board)
  4. 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
    Push Button
  5. 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:

Skematik Push Button dan Blue Pill untuk External Interrupt
Skematik Push Button dan Blue Pill untuk External Interrupt

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
Tampilkan kode full: Cargo.toml
 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 = true

Kemudian 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_rt untuk menentukan fungsi entry program akan mulai berjalan dan menangani proses startup program.
  • defmt_rtt berfungsi untuk mengirimkan data ke PC/laptop untuk logging menggunakan protokol real time transfer.
  • Library stm32f1xx_hal berfungsi agar kita dapat mengakses periferal mikrokontroler STM32F103C8 secara aman.
  • panic_probe digunakan 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.

Tampilkan kode full: src/main.rs
 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