Cara Membaca Input Analog (ADC) pada STM32 Blue Pill dengan Rust

Sering kali kita dalam menggunakan mikrokontroler perlu mengukur level tegangan input, bukan hanya HIGH dan LOW saja. Misalnya jika kita menggunakan sensor kelembapan, untuk mengukur tingkat kelembapan kita harus menggunakan Input Analog untuk mengonversi input tegangan ke nilai digital kemudian dilakukan perhitungan untuk mendapatkan nilai tingkat persen kelembapan. Jika kita menggunakan Input biasa seperti artikel sebelumnya maka kita hanya akan mendapatkan nilai HIGH dan LOW saja tanpa mengetahui tingkat kelembapan yang sebenarnya.

1. Pengenalan Analog to Digital Converter (ADC) pada STM32F103C8

STM32F103C8 memiliki 10 pin yang dapat dikonfigurasi sebagai input Analog yaitu pin PA0-PA7 dan PB0-PB1 dengan tegangan maksimal yang dapat diterima adalah 3.3V. Ketika pin STM32F103C8 dikonfigurasi sebagai input analog maka pin tersebut akan dihubungkan ke peiferal Analog to Digital Converter (ADC) yang berfungsi mengonversi tegangan input ke nilai digital. STM32F103C8 memiliki 2 unit ADC yaitu ADC1 dan ADC2 dengan resolusi 12 bit (0-4095) dan ketelitian sekitar 0.8mV (perubahan tegangan input sebesar 0.8mV akan menyebabkan perubahan nilai digital sebesar 1). Frekuensi ADC STM32F103C8 dapat diatur hingga 14MHz.

1.1 Mode dan Fitur ADC STM32F103C8

ADC STM32F103C8 dapat bekerja dalam 4 mode yaitu:

  • Single conversion mode: ADC melakukan satu kali konversi pada channel yang dipilih kemudian berhenti. Biasanya digunakan ketika membaca sensor yang nilainya tidak berubah cepat (seperti sensor suhu, kelembapan, dan lain-lain).
  • Scan mode: ADC akan memindai dan mengonversi daftar beberapa channel yang sudah ditentukan satu per satu kemudian berhenti. Scan mode biasanya dikombinasikan dengan DMA (Direct Memory Access) agar data tidak tertumpuk di register yang sama. Mode ini digunakan untuk memantau beberapa sensor sekaligus secara sinkron.
  • Continuous conversion mode: ADC akan mengonversi secara otomatis berulang terus menerus tanpa menunggu perintah CPU lagi. Pada single channel ADC akan otomatis melakukan konversi lagi setelah konversi selesai, sedangkan pada multi channel ADC akan melakukan konversi secara otomatis ke channel paling awal dan channel seterusnya.
  • Discontinuous conversion mode: Membagi grup channel menjadi grup yang lebih kecil pada Scan Mode. ADC akan mengonversi dari channel 1 grup bagian pertama sampai channel terakhir grup bagian terakhir, dimana disetiap akhir grup bagian ADC akan mengeset register eoc (End of Conversion) ke 1.

Fitur-fitur canggih yang dimiliki ADC STM32F103C8 antara lain:

  • Simultaneous sample and hold: Menggunakan 2 unit ADC secara bersamaan (paralel). Sehingga kita bisa mengambil sampel dari dua channel yang berbeda (channel 1 ke ADC1 dan channel 2 ke ADC2) pada waktu yang benar-benar sama.
  • Interleaved sample and hold: Unit ADC1 dan ADC2 bekerja secara bergantian untuk membaca/mengonversi satu channel. Dengan mode ini kecepatan sampling ADC menjadi 2 kali lipat. Biasanya digunakan untuk membaca dengan frekuensi yang tinggi.
  • Single shunt: Membaca/mengonversi channel yang memiliki input tegangan sangat kecil (miliVolt), biasanya digunakan untuk memantau arus dengan cara membaca tegangan pada resistor.

1.2 Dukungan Periferal ADC pada STM32F103C8

Kita juga dapat menggabungkan penggunaan ADC STM32F103C8 dengan periferal lain seperti:

  • ADC dengan DMA (Direct Memory Access): ADC secara langsung mengirimkan hasil konversi ke RAM tanpa perlu bantuan CPU, sehingga penggunaan CPU bisa hemat. CPU hanya perlu membaca hasil konversi di RAM setelah proses konversi selesai.
  • ADC dengan Analog Watchdog: Nilai hasil konversi ADC akan dipantau oleh Analog Watchdog, apabila berada diluar batas atas atau batas bawah yang ditentukan maka Analog Watchdog akan melakukan interupsi.
  • ADC dengan Timer: ADC akan melakukan konversi setiap waktu tertentu sesuai dengan Timer yang ditentukan.

2. Persiapan Hardware

Hardware yang digunakan dalam tutorial ini hampir sama dengan hardware yang digunakan pada artikel sebelumnya. Jika artikel sebelumnya menggunakan push button, kali ini kita akan menggunakan potensiometer untuk mengatur besarnya tegangan input ke pin GPIO STM32F103C8.

2.1 Potensiometer

Potensiometer merupakan resistor variabel yang memiliki tiga kaki. Berbeda dengan resistor biasa yang memiliki nilai hambatan tetap, potensiometer memungkinkan kita untuk mengubah nilai hambatannya secara manual dengan cara memutar tuas/knobnya. Pada tutorial ini kami akan menggunakan potensiometer dengan nilai resistansi 5KOhm.

Potensiometer untuk input analog STM32 Blue Pill
Potensiometer untuk input analog STM32 Blue Pill

Pro Tip: Nilai 5kΩ sangat ideal untuk eksperimen ini karena dapat menjadi pembagi tegangan yang stabil tanpa menarik arus yang terlalu besar dari jalur 3.3V pada STM32F103C8. Kaki tengah akan menghasilkan tegangan variabel antara 0V dan 3.3V, yang kemudian akan dikonversi oleh ADC menjadi nilai digital dari 0 hingga 4095.

3. Menggunakan Input Analog pada Blue Pill (STM32F103C8) dengan Rust

Pada tutorial ini akan kita fokus pada cara menggunakan ADC dalam mode single conversion, untuk penggunaan continous converion, scan mode dan fitur ADC STM32F103C8 yang lain akan dibahas lebih lanjut pada artikel selanjutnya. Ada beberapa metode yang dapat digunakan dalam menggunakan ADC Blue Pill diantaranya dengan metode Poll, Direct Memory Access (DMA), dan interrupt. Berikut merupakan jenis metode single conversion mode pada ADC Pada STM32F103C8 seperti:

  • Single conversion Poll: ADC hanya akan melakukan konversi ketika diberi perintah oleh CPU, lalu CPU menunggu hasil konversi untuk dibaca. Setelah itu ADC tidak akan melakukan konversi lagi sampai diberi perintah lagi oleh CPU. Metode ini cocok digunakan ketika kita tidak ingin membaca sensor terlalu sering atau membaca sensor pada waktu tertentu saja.
  • Single conversion DMA: ADC akan melakukan konversi ketika diberi perintah oleh CPU, kemudian hasil konversi akan dikirim ke RAM (buffer DMA). Selama proses konversi, CPU bisa melakukan pekerjaan lain, begitu buffer DMA terisi maka CPU membaca hasil konversi dari buffer DMA. Setelah itu ADC tidak akan melakukan konversi lagi sampai diberi perintah lagi oleh CPU. Jika disetup dengan baik metode ini akan sangat mengurangi beban CPU. Metode ini digunakan untuk membaca sensor dengan cepat dan terus menerus.
  • Single conversion Interrupt: ADC akan melakukan konversi ketika diberi perintah oleh CPU, setelah konversi selesai maka ADC akan memberitahu CPU dan CPU akan mengehentikan sementara program utamanya untuk mengeksekusi program ISR (Interupt Service Routine). Setelah itu ADC tidak akan melakukan konversi lagi sampai diberi perintah lagi oleh CPU. Jika kita melakukan konversi terlalu banyak tiap detiknya dengan metode ini maka CPU akan terus melakukan interupsi yang malah membebani CPU. Metode ini biasanya digunakan membaca sensor darurat.

Kita akan mencoba untuk membaca nilai tegangan input dari potensiometer yang kemudian nilai digitalnya akan ditampilkan pada terminal PC/laptop. Kali ini kami akan menggunakan pin GPIO PA0 sebagai input analog.

3.1 Rangkaian Blue Pill (STM32F103C8) dengan Potensiometer

Kita akan memanfaatkan potensiometer sebagai perangkat pembagi tegangan yang akan bertindak seperti sensor. Ayo ambil komponen-komponen yang diperlukan dan mulai membuat rangkaian sesuai dengan gambar skematik berikut:

Rangkaian potensiometer dan STM32 Blue Pill untuk Input Analog
Rangkaian potensiometer dan STM32 Blue Pill untuk Input Analog

Sesuai dengan gambar skematik tersebut besarnya tegangan yang diterima oleh pin PA0 akan berubah ketika potensiometer diputar, sehingga nilai digital akan berubah juga. Ketika knob potensiometer diputar ke arah GND maka resistansi antara pin PA0 dengan GND semakin kecil dan tahanan antara pin PA0 dengan 3.3V semakin besar sehingga tegangan yang masuk ke pin PA0 juga semakin kecil. Begitu Pula sebaliknya, ketika knob diputar ke arah 3.3V maka tahanan antara pin PA0 dengan GND semakin besar dan resistansi antara pin PA0 dengan 3.3V semakin kecil sehingga tegangan yang masuk ke pin PA0 juga semakin besar.

3.2 Memprogram Blue Pill (STM32F103C8) Sebagai Input Analog dengan Single Conversion Poll

Silakan buat project Rust baru lalu atur sesuai dengan artikel Setup STM32F103C8 dengan Rust. Buka file Cargo.toml lalu tambahkan kode berikut untuk menambahkan binary baru dengan nama ‘single-conversion-poll’:

1[[bin]]
2name = "single-conversion-poll"
3path = "src/main.rs"
4test = false
5bench = false

Selanjutnya buka file src/main.rs lalu isi dengan kode berikut (seperti biasa penjelasan fungsi kode berupa komentar di setiap barisnya):

 1#![no_std]
 2#![no_main]
 3
 4use defmt_rtt as _;
 5use panic_probe as _;
 6
 7use cortex_m::delay::Delay;
 8use cortex_m_rt::entry;
 9use stm32f1xx_hal::{
10    adc,
11    flash::FlashExt,
12    gpio::GpioExt,
13    pac,
14    prelude::*,
15    rcc::{Config, RccExt},
16    time::Hertz,
17};
18
19#[entry]
20fn main() -> ! {
21    defmt::println!("STM32F103C8 Acess GPIO as Input Analog Single Conversion Poll");
22
23    // Mengakses periferal STM32F103C8
24    let dp = pac::Peripherals::take().unwrap();
25
26    // Mengakses core peripherals (cortex-M3) STM32F103C8
27    let cp = pac::CorePeripherals::take().unwrap();
28
29    // Mengakses periferal FLASH yang akan digunakan untuk menyesuaikan
30    // 'wait states' saat konfigurasi Clock.
31    let mut flash = dp.FLASH.constrain();
32
33    // Mengakses periferal Reset & Control Clock agar dapat dikonfigurasi
34    let rcc = dp.RCC.constrain();
35
36    // membuat konfigurasi Reset & Control clock (RCC)
37    let clock_config = Config::default()
38        // menggunakan clock external 8MHZ
39        .use_hse(Hertz::MHz(8))
40        // mengatur frekuensi CPU ke 72MHZ
41        .sysclk(Hertz::MHz(72))
42        // mengatur frekuensi BUS (jalur akses SRAM dan FLASH) ke 72MHZ
43        .hclk(Hertz::MHz(72))
44        // mengatur frekuensi ADC ke 9MHZ
45        .adcclk(Hertz::MHz(9));
46
47    // mengaplikasikan konfigurasi clock ke rcc dan
48    // menyesuaikan 'wait states' memori flash
49    let mut clocks = rcc.freeze(clock_config, &mut flash.acr);
50
51    // membuat instans delay dengan menggunakan Timer System Coretex-M3.
52    let mut delay = Delay::new(cp.SYST, clocks.clocks.hclk().to_Hz());
53
54    // Mengakses periferal ADC1
55    let mut adc1 = adc::Adc::new(dp.ADC1, &mut clocks);
56
57    // Mengakses periferal GPIO Port A
58    let mut gpioa = dp.GPIOA.split(&mut clocks);
59    // Mengonfigurasi PA0 sebagai input analog
60    let mut potentio = gpioa.pa0.into_analog(&mut gpioa.crl);
61
62    // disini program akan dijalankan secara berulang terus menerus
63    loop {
64        // CPU memerintahkan ADC1 untuk membaca dan
65        // mengonversi tegangan input ke nilai digital
66        let data: u16 = adc1.read(&mut potentio).unwrap();
67        // menghitung tegangan input dari nilai digital yang dibaca
68        let voltage_input = 3.3f32 * data as f32 / 4095.0f32;
69        // mengirim pesan ke PC/laptop
70        defmt::println!(
71            "Digital value: {} || voltage input: {}V",
72            data,
73            voltage_input
74        );
75        // tunggu 200 ms
76        delay.delay_ms(200);
77    }
78}

Pasang ST-Link ke PC/laptop, lalu jalankan kode program Rust dengan perintah ‘cargo run --bin single-conversion-poll’ pada terminal VSCode. Coba putar knob potensiometer hasil pembacaan input analog akan muncul di terminal. Berikut merupakan hasil dari percobaan membaca input analog single conversion poll:

3.3 Memprogram Blue Pill (STM32F103C8) Sebagai Input Analog dengan Single Conversion DMA

Pada STM32F103C8 hanya ADC1 yang bisa digunakan dengan DMA, sedangkan ADC2 tidak bisa. Untuk menggunakan DMA kita harus menginisialisasi DMA terlebih dahulu sebelum menggunakannya, dan penjelasan detail mengenai DMA (Direct Memory Access) akan kita bahas pada artikel berikutnya.

Catatan: Untuk ADC1 channel DMA yang bisa digunakan adalah channel 1, jika Anda menghubungkan dengan channel lain maka program tidak akan berjalan.

Ayo kita mulai, pada file Cargo.toml tambahkan kode berikut untuk menambahkan binary baru:

1[[bin]]
2name = "single-conversion-dma"
3path = "src/single_conversion_dma.rs"
4test = false
5bench = false

Selanjutnya, buat file src/single_conversion_dma.rs, lalu isi dengan kode sebagai berikut:

 1#![no_std]
 2#![no_main]
 3
 4use cortex_m::delay;
 5use defmt_rtt as _;
 6use panic_probe as _;
 7
 8use cortex_m_rt::entry;
 9use stm32f1xx_hal::{adc, pac, prelude::*, rcc::Config, time::Hertz};
10
11#[entry]
12fn main() -> ! {
13    defmt::println!("STM32F103C8 Acess GPIO as Input Analog Single Conversion DMA");
14
15    // Mengakses periferal STM32F103C8
16    let dp = pac::Peripherals::take().unwrap();
17
18    // Mengakses core peripherals (cortex-M3) STM32F103C8
19    let cp = pac::CorePeripherals::take().unwrap();
20
21    // Mengakses periferal FLASH yang akan digunakan untuk menyesuaikan
22    // 'wait states' saat konfigurasi Clock.
23    let mut flash = dp.FLASH.constrain();
24
25    // Mengakses periferal Reset & Control Clock agar dapat dikonfigurasi
26    let rcc = dp.RCC.constrain();
27
28    // membuat konfigurasi Reset & Control clock (RCC)
29    let clock_config = Config::default()
30        // menggunakan clock external 8MHZ
31        .use_hse(Hertz::MHz(8))
32        //mengatur frekuensi CPU ke 72MHZ
33        .sysclk(Hertz::MHz(72))
34        //mengatur frekuensi BUS (jalur akses SRAM dan FLASH) ke 72MHZ
35        .hclk(Hertz::MHz(72))
36        //mengatur frekuensi ADC ke 9MHZ
37        .adcclk(Hertz::MHz(9));
38
39    // mengaplikasikan konfigurasi clock ke rcc dan
40    // menyesuaikan 'wait states' memori flash
41    let mut clocks = rcc.freeze(clock_config, &mut flash.acr);
42
43    // membuat instans delay dengan menggunakan Timer System Coretex-M3.
44    let mut delay = delay::Delay::new(cp.SYST, clocks.clocks.hclk().to_Hz());
45
46    // inisialisasi periferal ADC1
47    let adc1 = adc::Adc::new(dp.ADC1, &mut clocks);
48
49    // konfigurasi GPIO PA0 sebagai Input Analog
50    let mut gpioa = dp.GPIOA.split(&mut clocks);
51    let potentio = gpioa.pa0.into_analog(&mut gpioa.crl);
52
53    // inisialisasi DMA1
54    let dma1_channels = dp.DMA1.split(&mut clocks);
55
56    // menghubungkan ADC dengan DMA Channel 1
57    let mut adc_dma = adc1.with_dma(potentio, dma1_channels.1);
58
59    // siapkan Buffer yang akan diisi oleh DMA
60    // (Misal kita ambil 10 sampel untuk dirata-rata)
61    let mut buffer = cortex_m::singleton!(: [u16; 10] = [0; 10]).unwrap();
62
63    loop {
64        // CPU memerintahkan ADC untuk mengonversi dan
65        // menggunakan DMA untuk menyimpan data di buffer
66        let transfer = adc_dma.read(buffer);
67
68        // membaca hasil konversi di buffer DMA
69        let (recovered_buffer, recovered_adc_dma) = transfer.wait();
70
71        // mengolah data (rata-rata 10 sampel) dan menghitung tegangan
72        // dari nilai digital yang dibaca
73        let sum: u32 = recovered_buffer.iter().map(|&v| v as u32).sum();
74        let avg = (sum / recovered_buffer.len() as u32) as u16;
75        let voltage = (avg as f32 / 4095.0) * 3.3;
76        // mengirim pesan ke PC/laptop
77        defmt::println!("Raw Avg: {} | Volt: {}V", avg, voltage);
78
79        // mengembalikan variable buffer dan adc untuk digunakan di loop berikutnya
80        adc_dma = recovered_adc_dma;
81        buffer = recovered_buffer;
82
83        // tunggu 200ms
84        delay.delay_ms(200);
85    }
86}

Jalankan kode program rust dengan perintah ‘cargo run --bin single-conversion-dma’ pada terminal VSCode, lalu coba putar knob potensiometer. Berikut merupakan hasil dari percobaan membaca input analog single conversion dma:

3.4 Memprogram Blue Pill (STM32F103C8) Sebagai Input Analog dengan Single Conversion Interrupt

Karena crate stm32f1xx_hal tidak menyediakan method untuk membaca ADC melalui interup, maka kita akan terjun lebih dalam ke level bawah untuk mengakses register ADC agar kita dapat menggunakan ADC dengan interup. Program ini akan kami buat sesederhana mungkin agar mudah dipahami. Untuk penjelasan mengenai interup akan kita bahas pada artikel berikutnya.

Pertama ayo tambahkan binary baru, buka file Cargo.toml lalu tambahkan kode berikut:

1[[bin]]
2name = "single-conversion-interrupt"
3path = "src/single_conversion_interrupt.rs"
4test = false
5bench = false

Selanjutnya buat file src/single_conversion_interrupt.rs, lalu isi dengan kode sebagai berikut:

  1#![no_std]
  2#![no_main]
  3
  4use core::sync::atomic::AtomicI16;
  5use core::sync::atomic::Ordering;
  6
  7use cortex_m::delay;
  8use defmt_rtt as _;
  9use panic_probe as _;
 10
 11use cortex_m_rt::entry;
 12use stm32f1xx_hal::adc::SampleTime;
 13use stm32f1xx_hal::gpio::PinExt;
 14use stm32f1xx_hal::pac::Interrupt;
 15use stm32f1xx_hal::pac::interrupt;
 16use stm32f1xx_hal::{adc, pac, prelude::*, rcc::Config, time::Hertz};
 17
 18// Global variable untuk menyimpan nilai ADC. Menggunakan tipe atomic 
 19// untuk menghindari race condition ketika data diubah di fungsi interup
 20// dan dibaca di loop utama.
 21static ADC_VALUE: AtomicI16 = AtomicI16::new(0);
 22
 23#[entry]
 24fn main() -> ! {
 25    defmt::println!("STM32F103C8 Acess GPIO as Input Analog Single Conversion Interrupt");
 26
 27    // Mengakses periferal STM32F103C8
 28    let dp = pac::Peripherals::take().unwrap();
 29
 30    // Mengakses core peripherals (cortex-M3) STM32F103C8
 31    let cp = pac::CorePeripherals::take().unwrap();
 32
 33    // Mengakses periferal FLASH yang akan digunakan untuk menyesuaikan
 34    // 'wait states' saat konfigurasi Clock.
 35    let mut flash = dp.FLASH.constrain();
 36
 37    // Mengakses periferal Reset & Control Clock agar dapat dikonfigurasi
 38    let rcc = dp.RCC.constrain();
 39
 40    // membuat konfigurasi Reset & Control clock (RCC)
 41    let clock_config = Config::default()
 42        // menggunakan clock external 8MHZ
 43        .use_hse(Hertz::MHz(8))
 44        //mengatur frekuensi CPU ke 72MHZ
 45        .sysclk(Hertz::MHz(72))
 46        //mengatur frekuensi BUS (jalur akses SRAM dan FLASH) ke 72MHZ
 47        .hclk(Hertz::MHz(72))
 48        //mengatur frekuensi ADC ke 9MHZ
 49        .adcclk(Hertz::MHz(9));
 50
 51    // mengaplikasikan konfigurasi clock ke rcc dan
 52    // menyesuaikan 'wait states' memori flash
 53    let mut clocks = rcc.freeze(clock_config, &mut flash.acr);
 54
 55    // membuat instans delay dengan menggunakan Timer System Coretex-M3.
 56    let mut delay = delay::Delay::new(cp.SYST, clocks.clocks.hclk().to_Hz());
 57
 58    // inisialisasi periferal ADC1
 59    let mut adc1 = adc::Adc::new(dp.ADC1, &mut clocks);
 60
 61    // mengaktifkan interrupt eoc (End of Conversion) pada ADC1
 62    adc1.enable_eoc_interrupt();
 63
 64    // konfigurasi GPIO PA0 sebagai Input Analog
 65    let mut gpioa = dp.GPIOA.split(&mut clocks);
 66    let potentio = gpioa.pa0.into_analog(&mut gpioa.crl);
 67
 68    // mengatur sample time ADC untuk channel PA0 ke default
 69    adc1.set_channel_sample_time(potentio.pin_id(), SampleTime::default());
 70
 71    // Mengaktifkan interupsi ADC1_2.
 72    unsafe {
 73        pac::NVIC::unmask(Interrupt::ADC1_2);
 74    }
 75
 76    // Mengakses lebih dalam ke pointer register ADC1
 77    let adc_reg = unsafe { &*pac::ADC1::ptr() };
 78
 79    // mengatur channel ADC yang akan dikonversi.
 80    // Disini menggunakan PA0 yang berarti channel 0.
 81    adc_reg
 82        .sqr3()
 83        .modify(|_, w| unsafe { w.sq1().bits(potentio.pin_id()) });
 84
 85    loop {
 86        // mengirim pesan ke PC/laptop
 87        defmt::println!("Main program");
 88
 89        // CPU memerintahkan ADC1 untuk memulai konversi. Dengan cara
 90        // mengubah bit swstart ke 1 pada register CR2.
 91        adc_reg.cr2().modify(|_, w| w.swstart().set_bit());
 92
 93        // mengambil nilai dari global variabel ADC_VALUE dan menghitung tegangan
 94        let val = ADC_VALUE.load(Ordering::Relaxed);
 95        let voltage = (val as f32 / 4095.0) * 3.3;
 96        // mengirim pesan ke PC/laptop
 97        defmt::println!("Raw Avg: {} | Volt: {}V", val, voltage);
 98
 99        // tunggu 200ms
100        delay.delay_ms(200);
101    }
102}
103
104// Fungsi Interup: fungsi yang dijalankan CPU ketika ADC mengirim sinyal
105// interup ke CPU. Nama fungsi harus sesuai dengan nama interup yang
106// digunakan: Disini kita menggunakan interup ADC1_2, maka nama fungsinya
107// juga ADC1_2.
108#[interrupt]
109fn ADC1_2() {
110    // mengirim pesan ke PC/laptop
111    defmt::println!("Interrupt ADC");
112
113    // Mengakses lebih dalam ke pointer register ADC1
114    let adc_reg = unsafe { &*pac::ADC1::ptr() };
115
116    // mengecek apakah ADC1 melakukan interup karena eoc atau hal lain.
117    // jika bit eoc pada register SR bernilai 1 artinya ADC selesai melakukan konversi
118    if adc_reg.sr().read().eoc().bit_is_set() {
119        // membaca nilai pada register data (DR) dari ADC1
120        let val = adc_reg.dr().read().bits();
121        // menyimpan nilai ADC ke global variabel ADC_VALUE
122        ADC_VALUE.store(val as i16, Ordering::Relaxed);
123    }
124}

Daftar lengkap register periferal pada STM32F103C8 dapat dilihat di datahseet.

Catatan: ADC Pada STM32F103C8 (Blue Pill) memiliki interup khusus bernama ADC1_2 yang bisa digunakan untuk ADC1 dan ADC2. Interup ini yang akan memberitahu CPU ketika terjadi peristiwa pada ADC (seperti: eoc, jeoc, dan lain-lain).

Jalankan kode program rust dengan perintah ‘cargo run --bin single-conversion-interrupt’ pada terminal VSCode, lalu coba putar knob potensiometer. Berikut merupakan hasil dari percobaan membaca input analog single conversion interrupt:

Permasalahan & Kesimpulan

Berikut merupakan masalah yang kami alami ketika membuat tutorial ini:

  • Crate stm32f1xx-hal tidak menyediakan method untuk membaca ADC melalui interup: Solusinya dengan mengakses register langsung dari ADC.

Single conversion DMA merupakan cara yang paling efisien, karena tidak terlalu membebani CPU. Pada metode Poll CPU akan menunggu hingga proses konversi oleh ADC selesai, sedangkan pada metode Interrupt jika melakukan terlalu banyak konversi tiap detiknya maka CPU akan terus melakukan interupsi yang malah membebani CPU.

Source code yang digunakan pada tutorial ini dapat diakses di repositori Github.

Jika anda menemukan masalah lain, ingin bertanya, atau menyampaikan kritik dan saran, silakan hubungi kami melalui halaman kontak.