MODUL II

PWM, ADC, INTERRUPT, & MILLIS

1. Pendahuluan[Kembali]

  • Praktikkan telah membuat rangkaian tugas pendahuluan sebelum asistensi dilaksanakan
  • Praktikkan melaksanakan asistensi sebelum praktikum dilaksanakan
  • Praktikkan melaksanakan praktikum

2. Tujuan[Kembali]

  • Memahami cara penggunaan PWM, ADC, Interrupt, dan Millis pada Development Board yang digunakan
  • Memahami cara menggunakan komponen input dan output yang mengimplementasikan PWM, ADC, Interrupt, dan Millis pada Development Board yang digunakan

3. Alat dan bahan[Kembali]

  • Raspberry Pi Pico
  • STM32F103C8
  • LED 
  • Push Button
  • LED RGB 
  • Touch Sensor
  • Sensor Soil Moisture
  • Potensiometer
  • DHT22 
  • Motor DC (Dinamo DC, Motor Servo, dan Motor Stepper
  • LDR dan Photodioda
  • Breadboard
  • Resistor
  • Transistor
  • Driver Motor Stepper ULN2003 

4. Dasar Teori[Kembali]

4.1  PWM

PWM (Pulse Width Modulation) adalah salah satu teknik modulasi dengan mengubah lebar pulsa (duty cylce) dengan nilai amplitudo dan frekuensi yang tetap. Satu siklus pulsa merupakan kondisi high kemudian berada di zona transisi ke kondisi low. Lebar pulsa PWM berbanding lurus dengan amplitudo sinyal asli yang belum termodulasi.

Duty Cycle adalah perbandingan antara waktu ON (lebar pulsa High) dengan perioda. Duty Cycle biasanya dinyatakan dalam bentuk persen (%).

rectangular waveform

Gambar 1. Duty Cycle

Duty Cycle            = tON / ttotal

Ton                        = Waktu ON atau Waktu dimana tegangan keluaran berada pada posisi tinggi (high atau 1)

Toff                       = Waktu OFF atau Waktu dimana tegangan keluaran berada pada posisi rendah (low atau 0)

Ttotal                     = Waktu satu siklus atau penjumlahan antara Ton dengan Toff atau disebut juga dengan “periode satu gelombang”

 

PWM pada STM32 dihasilkan menggunakan timer internal yang berfungsi sebagai penghitung waktu dengan berbagai mode operasi. Mikrokontroler ini memiliki empat timer 16-bit (TIM1–TIM4), yang dapat dikonfigurasi untuk menghasilkan sinyal dengan frekuensi dan duty cycle tertentu.  Timer  bekerja  dengan  menghitung  hingga  nilai  tertentu


 

berdasarkan frekuensi clock, lalu mengubah status register untuk menghasilkan gelombang persegi.

STM32 memiliki 15 pin yang mendukung PWM, beberapa di antaranya berasal dari timer tingkat lanjut seperti TIM1, yang memiliki fitur tambahan seperti complementary output. Selain menghasilkan sinyal PWM, timer juga bisa digunakan untuk mengukur sinyal eksternal (input capture), menghasilkan sinyal berbasis waktu (output compare), dan membuat satu pulsa berdasarkan trigger (one pulse mode). PWM sering digunakan untuk mengontrol kecepatan motor, mengatur kecerahan LED, dan berbagai aplikasi berbasis waktu lainnya.

Pada Raspberry Pi Pico, terdapat blok PWM yang terdiri dari 8 unit (slice), dan masing-masing slice dapat mengendalikan dua sinyal PWM atau mengukur frekuensi serta duty cycle dari sinyal input. Dengan total 16 output PWM yang dapat dikontrol, semua 30 pin GPIO bisa digunakan untuk PWM. Setiap slice memiliki fitur utama seperti penghitung 16-bit, pembagi clock presisi, dua output independen dengan duty cycle 0–100%, serta mode pengukuran frekuensi dan duty cycle. PWM pada Raspberry Pi Pico juga mendukung pengaturan fase secara presisi serta dapat diaktifkan atau dinonaktifkan secara bersamaan melalui satu register kontrol global, sehingga memungkinkan sinkronisasi beberapa output untuk aplikasi yang lebih kompleks.

 

4.2  ADC

ADC atau Analog to Digital Converter merupakan salah satu perangkat elektronika yang digunakan sebagai penghubung dalam pemrosesan sinyal analog oleh sistem digital. Fungsi utama dari fitur ini adalah mengubah sinyal masukan yang masih dalam bentuk sinyal analog menjadi sinyal digital dengan bentuk kode-kode digital.


 

Pada mikrokontroler STM32, terdapat dua ADC (Analog-to-Digital Converter) 12-bit yang masing-masing memiliki hingga 16 kanal eksternal. ADC ini dapat beroperasi dalam mode single-shot atau scan mode. Pada scan mode, konversi dilakukan secara otomatis pada sekelompok input analog yang dipilih. Selain itu, ADC ini memiliki fitur tambahan seperti simultaneous sample and hold, interleaved sample and hold, serta single shunt. ADC juga dapat dihubungkan dengan DMA untuk meningkatkan efisiensi transfer data. Mikrokontroler ini dilengkapi dengan fitur analog watchdog yang memungkinkan pemantauan tegangan hasil konversi dengan akurasi tinggi, serta dapat menghasilkan interupsi jika tegangan berada di luar ambang batas yang telah diprogram. Selain itu, ADC dapat disinkronkan dengan timer internal (TIMx dan TIM1) untuk memulai konversi, pemicu injeksi, serta pemicu DMA, sehingga memungkinkan aplikasi untuk melakukan konversi ADC secara terkoordinasi dengan timer. Raspberry Pi Pico memiliki empat ADC (Analog-to-Digital Converter)

12-bit dengan metode SAR, tetapi hanya tiga kanal yang dapat digunakan secara eksternal, yaitu ADC0, ADC1, dan ADC2, yang terhubung ke pin GP26, GP27, dan GP28. Kanal keempat (ADC4) digunakan secara internal untuk membaca suhu dari sensor suhu bawaan. Konversi ADC dapat dilakukan dalam tiga mode: polling, interrupt, dan FIFO dengan DMA. Kecepatan konversi ADC adalah 2μs per sampel atau 500 ribu sampel per detik (500kS/s). Mikrocontroller RP2040 berjalan pada frekuensi 48MHz yang berasal dari USB PLL, dan setiap konversi ADC membutuhkan 96 siklus CPU, sehingga waktu samplingnya adalah 2μs per sampel.

 

4.3  INTERRUPT

Interrupt adalah mekanisme yang memungkinkan suatu instruksi atau perangkat I/O untuk menghentikan sementara eksekusi normal prosesor agar


 

dapat diproses lebih dulu seperti memiliki prioritas tertinggi. Misalnya, saat prosesor menjalankan tugas utama, ia juga dapat terus memantau apakah ada kejadian atau sinyal dari sensor yang memicu interrupt. Ketika terjadi interrupt eksternal, prosesor akan menghentikan sementara tugas utamanya untuk menangani interrupt terlebih dahulu, kemudian melanjutkan eksekusi normal setelah selesai menangani interrupt tersebut. Fungsi yang menangani interrupt disebut Interrupt Service Routine (ISR), yang dieksekusi secara otomatis setiap kali interrupt terjadi.

Pada STM32F103C8, semua pin GPIO dapat digunakan sebagai pin interrupt, berbeda dengan Arduino Uno yang hanya memiliki pin tertentu (misalnya pin 2 dan 3). Untuk mengaktifkan interrupt di STM32 menggunakan                                   Arduino             IDE,             digunakan fungsi attachInterrupt(digitalPinToInterrupt(pin), ISR, mode). Parameter pin menentukan pin mana yang digunakan untuk interrupt, ISR adalah fungsi yang dijalankan saat interrupt terjadi, dan mode menentukan jenis perubahan sinyal yang memicu interrupt. Mode yang tersedia adalah RISING (dari LOW ke HIGH), FALLING (dari HIGH ke LOW), dan CHANGE (baik dari LOW ke HIGH maupun HIGH ke LOW). Saat menggunakan lebih dari satu interrupt secara bersamaan, terkadang perlu memperhatikan batasan tertentu dalam pemrograman.

Pada RP2040, setiap inti prosesor dilengkapi dengan ARM Nested Vectored Interrupt Controller (NVIC) yang memiliki 32 jalur interrupt. Namun, hanya 26 jalur pertama yang digunakan, sedangkan jalur IRQ 26 hingga 31 tidak aktif. Setiap NVIC menerima interrupt yang sama, kecuali untuk GPIO, di mana setiap bank GPIO memiliki satu interrupt per inti. Ini berarti, misalnya, core 0 dapat menerima interrupt dari GPIO 0 di bank 0, sementara core 1 menerima interrupt dari GPIO 1 di bank yang sama secara independen. Jika diperlukan, inti prosesor masih bisa dipaksa masuk ke


 

interrupt handler dengan menulis bit 26 hingga 31 pada register NVIC ISPR.

 

 

4.4  MILLIS

Fungsi millis() pada mikrokontroler, seperti pada platform Arduino dan STM32 (dengan HAL), digunakan untuk menghitung waktu dalam milidetik sejak perangkat mulai berjalan. Berbeda dengan delay(), yang menghentikan eksekusi program selama waktu tertentu, millis() memungkinkan sistem untuk menjalankan beberapa tugas secara bersamaan tanpa menghentikan proses lainnya. Hal ini berguna dalam aplikasi yang memerlukan multitasking berbasis waktu, seperti pengendalian sensor, komunikasi serial, atau implementasi sistem real-time. Dalam STM32 dengan HAL, millis() dapat diimplementasikan menggunakan HAL_GetTick(), yang mengandalkan interrupt timer internal untuk mencatat waktu yang terus berjalan sejak mikrokontroler diaktifkan.

Sementara itu, pada platform seperti Raspberry Pi Pico yang sering diprogram menggunakan MicroPython, fungsi utime.ticks_ms() menyediakan fungsionalitas yang sepadan. Fungsi ini mengembalikan nilai penghitung milidetik yang bersifat monotonik (terus bertambah) sejak sistem dimulai atau modul utime dimuat. Sama seperti millis() dan HAL_GetTick(), nilai ticks_ms() juga akan mengalami wrap-around (kembali ke nol) setelah mencapai batasnya, sehingga penggunaan fungsi utime.ticks_diff() menjadi penting untuk perhitungan selisih waktu yang akurat dan aman terhadap overflow. Dengan demikian, utime.ticks_ms() memungkinkan implementasi pola penjadwalan dan delay non-blocking yang serupa untuk menciptakan aplikasi yang responsif di lingkungan MicroPython.


 

4.5  Raspberry Pi Pico

Raspberry Pi Pico adalah papan rangkaian elektronik yang di dalamnya terdapat komponen utama chip mikrokontroler RP2040, yang dirancang dan diproduksi oleh Raspberry Pi Foundatio. Tidak seperti komputer mini raspberry Pi lainnya yang menjalankan sistem operasi seperti Linux, Pico dirancang untuk tugas-tugas yang lebih sederhana dan langsung (embedded system), seperti membaca sensor, mengontrol perangkat, atau melakukan pengolahan data pada tingkat hardware.

Adapun spesifikasi dari Raspberry Pi Pico adalah sebagai berikut:

 


Gambar 1. Arduino Uno

 

Microcontroller                                           RP2040

Operating Voltage                                      3.3 V

Input Voltage (recommended)                   5 V via USB

Input Voltage (limit)                                  1.8–5.5 V

Digital I/O Pins                                          26 GPIO pins

PWM Digital I/O Pins                                16

Analog Input Pins                                        3

DC Current per I/O Pin                               16 mA

DC Current for 3.3V Pin                             300 mA


 

Flash Memory                                            2 MB on-board QSPI Flash

SRAM                                                        264 KB

Clock Speed                                               Hingga 133 MHz

 

4.6  STM32F103C8

STM32F103C8 adalah mikrokontroler berbasis ARM Cortex-M3 yang dikembangkan oleh STMicroelectronics. Mikrokontroler ini sering digunakan dalam pengembangan sistem tertanam karena kinerjanya yang baik, konsumsi daya yang rendah, dan kompatibilitas dengan berbagai protokol komunikasi. Pada praktikum ini, kita menggunakan STM32F103C8 yang dapat diprogram menggunakan berbagai metode, termasuk komunikasi serial (USART), SWD (Serial Wire Debug), atau JTAG untuk berhubungan dengan komputer maupun perangkat lain. Adapun spesifikasi dari STM32F4 yang digunakan dalam praktikum ini adalah sebagai berikut:

Gambar 3. STM32F103C8

 

Microcontroller                                           ARM Cortex-M3

Operating Voltage                                      3.3 V

Input Voltage (recommended)                   5 V

Input Voltage (limit)                                  2 – 3.6 V


 

Digital I/O Pins                                           37

PWM Digital I/O Pins                                 15

Analog Input Pins                                       10 (dengan resolusi 12-bit ADC)

DC Current per I/O Pin                               25 mA

DC Current for 3.3V Pin                             150 mA

Flash Memory                                            64 KB

SRAM                                                        20 KB

EEPROM                                                   Emulasi dalam Flash

Clock Speed                                               72 MHz


                

BAGIAN-BAGIAN PENDUKUNG

1.    Raspberry Pi Pico

1.    RAM (Random Access Memory)

Raspberry Pi Pico dilengkapi dengan 264KB SRAM on-chip. Kapasitas RAM yang lebih besar ini memungkinkan Pico menjalankan aplikasi yang lebih kompleks dan menyimpan data lebih banyak.

2.    Memori Flash Eksternal

Raspberry Pi Pico tidak memiliki ROM tradisional. Sebagai gantinya, ia menggunakan memori flash eksternal. Kapasitas memori flash ini dapat bervariasi, umumnya antara 2MB hingga 16MB, tergantung pada konfigurasi. Memori flash ini digunakan untuk menyimpan firmware dan program pengguna. Penggunaan memori flash eksternal pada Pico memberikan fleksibilitas lebih besar dalam hal kapasitas penyimpanan program.

3.    Crystal Oscillator

Raspberry Pi Pico menggunakan crystal oscillator untuk menghasilkan sinyal clock yang stabil. Sinyal clock ini penting untuk mengatur kecepatan operasi mikrokontroler dan komponen lainnya.

4.    Regulator Tegangan

Untuk memastikan pasokan tegangan yang stabil ke mikrokontroler.

5.    Pin GPIO (General Purpose Input/Output):

Untuk menghubungkan Pico ke berbagai perangkat eksternal seperti sensor, motor, dan LED.

 

2.    STM32

1.  RAM (Random Access Memory)

STM32F103C8 dilengkapi dengan 20KB SRAM on-chip. Kapasitas RAM ini


 

memungkinkan mikrokontroler menjalankan berbagai aplikasi serta menyimpan data sementara selama eksekusi program.

2.  Memori Flash Internal

STM32F103C8 memiliki memori flash internal sebesar 64KB atau 128KB, yang digunakan untuk menyimpan firmware dan program pengguna. Memori ini memungkinkan penyimpanan kode program secara permanen tanpa memerlukan media penyimpanan eksternal.

3.  Crystal Oscillator

STM32F103C8 menggunakan crystal oscillator eksternal (biasanya 8MHz) yang bekerja dengan PLL untuk meningkatkan frekuensi clock hingga 72MHz. Sinyal clock yang stabil ini penting untuk mengatur kecepatan operasi mikrokontroler dan komponen lainnya.

4.  Regulator Tegangan

STM32F103C8 memiliki sistem pengaturan tegangan internal yang memastikan pasokan daya stabil ke mikrokontroler. Tegangan operasi yang didukung berkisar antara 2.0V hingga 3.6V.

5.  Pin GPIO (General Purpose Input/Output)

STM32F103C8 memiliki hingga 37 pin GPIO yang dapat digunakan untuk menghubungkan berbagai perangkat eksternal seperti sensor, motor, LED, serta komunikasi dengan antarmuka seperti UART, SPI, dan I²C.

1.1  Prosedur Percobaan

1.         Led, Buzzer, & LDR

§  Rangkaian



 

 

§  Listing Program



 

Text Box: lux_normal = lux
print(f"Lux Normal: {lux_normal}")
print(f"LDR Value: {analog_value} | Lux: {lux}") if lux > lux_normal + 50:
led_should_blink = True # nyalakan mode kedip buzzer.duty_u16(30000) # nyalakan buzzer
for i in range(500, 1000, 100): # variasi frekuensi buzzer buzzer.freq(i)
utime.sleep(0.1)
else:
led_should_blink = False led.off() buzzer.duty_u16(0)

# Kedip LED jika perlu if led_should_blink:
current_time = utime.ticks_ms()
if utime.ticks_diff(current_time, last_blink_time) >= 1000: led_state = not led_state
led.value(led_state) last_blink_time = current_time

utime.sleep(0.1) # sedikit delay supaya tidak terlalu cepat baca LDR


 

§  Flowchart


 

2.         Led RGB, Motor Stepper, & Soil Moisture

§  Rangkaian


§  Listing Program

Text Box: #include "stm32f1xx_hal.h"

// Konfigurasi Hardware
#define STEPPER_PORT GPIOB
#define IN1_PIN GPIO_PIN_8 #define IN2_PIN GPIO_PIN_9 #define IN3_PIN GPIO_PIN_10 #define IN4_PIN GPIO_PIN_11

#define LED_RED_PIN		GPIO_PIN_12 #define LED_GREEN_PIN GPIO_PIN_13 #define LED_BLUE_PIN GPIO_PIN_14 #define LED_PORT	GPIOB

// Mode Stepper
const uint16_t STEP_SEQ_CW[4] = {0x0100, 0x0200, 0x0400, 0x0800};	// Clockwise


 

Text Box: const uint16_t STEP_SEQ_CCW[4] = {0x0800, 0x0400, 0x0200, 0x0100}; // Counter- Clockwise

ADC_HandleTypeDef hadc1;
uint8_t current_mode = 0; // 0=CW, 1=CCW, 2=Oscillate
uint8_t direction = 0;	// Untuk mode oscillate

void SystemClock_Config(void); void MX_GPIO_Init(void);
void MX_ADC1_Init(void);
void RunStepper(const uint16_t *sequence, uint8_t speed);
void Error_Handler(void);

int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_ADC1_Init();

while (1) {
// Baca potensiometer untuk pilih mode
HAL_ADC_Start(&hadc1);
if (HAL_ADC_PollForConversion(&hadc1, 10) == HAL_OK) {
uint16_t adc_val = HAL_ADC_GetValue(&hadc1);

// Tentukan mode
if (adc_val < 1365) { // Mode 1: CW current_mode = 0;
HAL_GPIO_WritePin(LED_PORT, LED_RED_PIN, GPIO_PIN_SET);
HAL_GPIO_WritePin(LED_PORT, LED_GREEN_PIN|LED_BLUE_PIN,
GPIO_PIN_RESET);
}
else if (adc_val < 2730) { // Mode 2: CCW current_mode = 1;
HAL_GPIO_WritePin(LED_PORT, LED_GREEN_PIN, GPIO_PIN_SET);


HAL_GPIO_WritePin(LED_PORT, LED_RED_PIN|LED_BLUE_PIN,

GPIO_PIN_RESET);

}

else { // Mode 3: Oscillate current_mode = 2;

HAL_GPIO_WritePin(LED_PORT, LED_BLUE_PIN, GPIO_PIN_SET);

HAL_GPIO_WritePin(LED_PORT, LED_RED_PIN|LED_GREEN_PIN,

GPIO_PIN_RESET);

}

}

 

// Eksekusi mode switch(current_mode) { case 0: // CW

RunStepper(STEP_SEQ_CW, 10);

break;

case 1: // CCW RunStepper(STEP_SEQ_CCW, 10); break;

case 2: // Oscillate

if(direction == 0) {

RunStepper(STEP_SEQ_CW, 5);

if(STEPPER_PORT->ODR == (STEPPER_PORT->ODR & 0x00FF) | STEP_SEQ_CW[3])

direction = 1;

} else {

RunStepper(STEP_SEQ_CCW, 5);

if(STEPPER_PORT->ODR == (STEPPER_PORT->ODR & 0x00FF) | STEP_SEQ_CCW[3])

direction = 0;

}

break;

}

}

}


void RunStepper(const uint16_t *sequence, uint8_t speed) {

static uint8_t step = 0;

STEPPER_PORT->ODR = (STEPPER_PORT->ODR & 0x00FF) | sequence[step];

step = (step + 1) % 4;

HAL_Delay(speed);

}

 

void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

 

RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI; RCC_OscInitStruct.HSIState = RCC_HSI_ON; RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT; if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { Error_Handler();

}

 

RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK

|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;

RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK) {

Error_Handler();

}

}

 

void MX_GPIO_Init(void) {

GPIO_InitTypeDef GPIO_InitStruct = {0};

 

  HAL_RCC_GPIOB_CLK_ENABLE();

 

// Konfigurasi LED


 

Text Box: GPIO_InitStruct.Pin = LED_RED_PIN | LED_GREEN_PIN | LED_BLUE_PIN; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_PULLDOWN; // Tambahkan pull-down GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; // High speed untuk stabil HAL_GPIO_Init(LED_PORT, &GPIO_InitStruct);

// Konfigurasi Stepper
GPIO_InitStruct.Pin = IN1_PIN | IN2_PIN | IN3_PIN | IN4_PIN;
HAL_GPIO_Init(STEPPER_PORT, &GPIO_InitStruct);
}

void MX_ADC1_Init(void) {
ADC_ChannelConfTypeDef sConfig = {0};

hadc1.Instance = ADC1;
hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE; hadc1.Init.ContinuousConvMode = DISABLE; hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT; hadc1.Init.NbrOfConversion = 1;
if (HAL_ADC_Init(&hadc1) != HAL_OK) {
Error_Handler();
}
sConfig.Channel = ADC_CHANNEL_0; sConfig.Rank = ADC_REGULAR_RANK_1;
sConfig.SamplingTime = ADC_SAMPLETIME_71CYCLES_5;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK) {
Error_Handler();
}
}
void Error_Handler(void) {
while(1) {}
}


 

§  Flowchart


3.         Motor DC (Dinamo DC),Push Button, Buzzer, & LDR/Photodioda

§  Rangkaian



 

§  Listing Program

Text Box: #include "main.h"

ADC_HandleTypeDef hadc1; TIM_HandleTypeDef htim1; TIM_HandleTypeDef htim2;

void SystemClock_Config(void); static void MX_GPIO_Init(void); static void MX_ADC1_Init(void); static void MX_TIM1_Init(void); static void MX_TIM2_Init(void);

int main(void)
{
HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_ADC1_Init(); MX_TIM1_Init(); MX_TIM2_Init();

HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1); // Motor PWM HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_3); // Buzzer PWM HAL_ADC_Start(&hadc1);

uint8_t buzzer_enabled = 1; uint32_t last_buzzer_change = 0; uint8_t buzzer_freq_index = 0;

const uint32_t buzzer_periods[] = {143999, 71999, 47999}; // Frekuensi berbeda

// Threshold (dari rendah → sedang → tinggi) const uint16_t THRESH_LOW = 1500; const uint16_t THRESH_MID = 3000;


 

Text Box: while (1)
{
HAL_ADC_Start(&hadc1); HAL_ADC_PollForConversion(&hadc1, 10); uint32_t adc_val = HAL_ADC_GetValue(&hadc1);

// --- Motor Control ---
if (adc_val < THRESH_LOW)
{
  HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, 200); // Lambat
}
else if (adc_val < THRESH_MID)
{
  HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, 600); // Sedang
}
else
{
  HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, 1000); // Cepat
}

// --- Buzzer Logic ---
if (adc_val < THRESH_LOW && buzzer_enabled)
{
// Ubah frekuensi buzzer setiap 500ms
if (HAL_GetTick() - last_buzzer_change >= 500)
{
last_buzzer_change = HAL_GetTick(); buzzer_freq_index = (buzzer_freq_index + 1) % 3;

uint32_t period = buzzer_periods[buzzer_freq_index];
  HAL_TIM_SET_AUTORELOAD(&htim2, period);
  HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_3, period / 2); // 50% duty
}
}


 

Text Box: else
{
  HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_3, 0); // Matikan buzzer
}

// --- Button Logic (PB0 ditekan = nonaktifkan buzzer) ---
if (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_0) == GPIO_PIN_SET)
{
buzzer_enabled = 0;
 HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_3, 0); // Paksa matikan buzzer
}

HAL_Delay(10);
}
}


void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};

RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI; RCC_OscInitStruct.HSIState = RCC_HSI_ON; RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}

RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK


 

Text Box: |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK)
{
Error_Handler();
}
PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_ADC; PeriphClkInit.AdcClockSelection = RCC_ADCPCLK2_DIV2;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
{
Error_Handler();
}
}

static void MX_ADC1_Init(void)
{
ADC_ChannelConfTypeDef sConfig = {0}; hadc1.Instance = ADC1;
hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE; hadc1.Init.ContinuousConvMode = DISABLE; hadc1.Init.DiscontinuousConvMode = DISABLE; hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START; hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT; hadc1.Init.NbrOfConversion = 1;
if (HAL_ADC_Init(&hadc1) != HAL_OK)
{
Error_Handler();
}


 

Text Box: sConfig.Channel = ADC_CHANNEL_0; sConfig.Rank = ADC_REGULAR_RANK_1;
sConfig.SamplingTime = ADC_SAMPLETIME_1CYCLE_5;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
}

}

static void MX_TIM1_Init(void)
{

TIM_MasterConfigTypeDef sMasterConfig = {0}; TIM_OC_InitTypeDef sConfigOC = {0}; TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig = {0};

htim1.Instance = TIM1; htim1.Init.Prescaler = 0;
htim1.Init.CounterMode = TIM_COUNTERMODE_UP; htim1.Init.Period = 65535;
htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; htim1.Init.RepetitionCounter = 0;
htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_PWM_Init(&htim1) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET; sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE; if (HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig) !=
HAL_OK)
{
Error_Handler();
}


 

Text Box: sConfigOC.OCMode = TIM_OCMODE_PWM1; sConfigOC.Pulse = 0;
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH; sConfigOC.OCNPolarity = TIM_OCNPOLARITY_HIGH; sConfigOC.OCFastMode = TIM_OCFAST_DISABLE; sConfigOC.OCIdleState = TIM_OCIDLESTATE_RESET; sConfigOC.OCNIdleState = TIM_OCNIDLESTATE_RESET;
if (HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_1) !=
HAL_OK)
{
Error_Handler();
}
sBreakDeadTimeConfig.OffStateRunMode = TIM_OSSR_DISABLE; sBreakDeadTimeConfig.OffStateIDLEMode = TIM_OSSI_DISABLE; sBreakDeadTimeConfig.LockLevel = TIM_LOCKLEVEL_OFF; sBreakDeadTimeConfig.DeadTime = 0; sBreakDeadTimeConfig.BreakState = TIM_BREAK_DISABLE; sBreakDeadTimeConfig.BreakPolarity = TIM_BREAKPOLARITY_HIGH;
sBreakDeadTimeConfig.AutomaticOutput = TIM_AUTOMATICOUTPUT_DISABLE;
if (HAL_TIMEx_ConfigBreakDeadTime(&htim1, &sBreakDeadTimeConfig) !=
HAL_OK)
{
Error_Handler();
}

HAL_TIM_MspPostInit(&htim1);

}

static void MX_TIM2_Init(void)
{

TIM_MasterConfigTypeDef sMasterConfig = {0};
TIM_OC_InitTypeDef sConfigOC = {0};


 

Text Box: htim2.Instance = TIM2; htim2.Init.Prescaler = 0;
htim2.Init.CounterMode = TIM_COUNTERMODE_UP; htim2.Init.Period = 65535;
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; if (HAL_TIM_PWM_Init(&htim2) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET; sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE; if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) !=
HAL_OK)
{
Error_Handler();
}
sConfigOC.OCMode = TIM_OCMODE_PWM1; sConfigOC.Pulse = 0;
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH; sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
if (HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_3) !=
HAL_OK)
{
Error_Handler();
}

HAL_TIM_MspPostInit(&htim2);

}

static void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};


 

Text Box:   HAL_RCC_GPIOD_CLK_ENABLE();
  HAL_RCC_GPIOA_CLK_ENABLE();
  HAL_RCC_GPIOB_CLK_ENABLE();

/*Configure GPIO pin : PB0 */ GPIO_InitStruct.Pin = GPIO_PIN_0; GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_PULLUP; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

}

void Error_Handler(void)
{

 disable_irq(); while (1)
{
}

}

#ifdef USE_FULL_ASSERT
void assert_failed(uint8_t *file, uint32_t line)
{

}
#endif /* USE_FULL_ASSERT */


 

§  Flowchart



 

4.         Motor Servo, Buzzer, & Potensiomter

§  Rangkaian


§  Listing Program

Text Box: from machine import Pin, PWM, ADC from time import sleep
import utime

# Inisialisasi
pot = ADC(26) # GP26 = ADC0
servo = PWM(Pin(16)) buzzer = PWM(Pin(14))

# Konfigurasi PWM
servo.freq(50) # 50 Hz untuk servo buzzer.freq(1000) # Awal frekuensi buzzer

def map_value(value, in_min, in_max, out_min, out_max):
return int((value - in_min) * (out_max - out_min) / (in_max - in_min) + out_min)


 

Text Box: while True:
val = pot.read_u16() # Nilai ADC 16-bit (0 - 65535)

# === Servo Motor ===
# Membaca nilai potensiometer (0 - 65535) pot_value = pot.read_u16()

# Konversi ke sudut servo (0° - 180°)
angle = map_value(pot_value, 0, 65535, 0, 180)

# Konversi sudut ke duty cycle (1500 - 7500) → sesuai servo PWM duty = map_value(angle, 0, 180, 1500, 7500)
servo.duty_u16(duty)

# Print untuk debugging
print(f"Pot Value: {pot_value}, Angle: {angle}, Duty: {duty}")

# === Buzzer ===
# Ubah val ke frekuensi (200 Hz - 2000 Hz) freq = int(200 + (val / 65535) * (2000 - 200)) buzzer.freq(freq)
buzzer.duty_u16(30000) # Volume/suaranya

sleep(0.05)


 

§  Flowchart


 

5.         Motor Servo, Buzzer, Potensiomter, & DHT22

§  Rangkaian


§  Listing Program

Text Box: from machine import Pin, ADC, PWM import time
import dht

# Inisialisasi potensiometer pada GPIO 26 (ADC0) pot = ADC(26)

# Inisialisasi motor servo pada GPIO 15 (PWM) servo = PWM(Pin(15))
servo.freq(50) # Frekuensi PWM untuk servo

# Inisialisasi buzzer sebagai PWM pada GPIO 14 buzzer = PWM(Pin(14))
buzzer.duty_u16(0) # Tidak ada suara saat awal

# Inisialisasi sensor DHT22 pada GPIO 16 dht_sensor = dht.DHT22(Pin(16))


 

Text Box: # Fungsi mapping nilai
def map_value(value, in_min, in_max, out_min, out_max):
return int((value - in_min) * (out_max - out_min) / (in_max - in_min) + out_min)

# Fungsi untuk membunyikan buzzer dengan nada tertentu def play_buzzer_note(frequency, duration_ms):
buzzer.freq(frequency) buzzer.duty_u16(32768) # Volume setengah time.sleep_ms(duration_ms) buzzer.duty_u16(0)
time.sleep_ms(50) # Jeda antar nada

# Fungsi melodi untuk suhu tinggi def play_high_temp_melody():
play_buzzer_note(880, 200) # A5 play_buzzer_note(988, 200) # B5 play_buzzer_note(1047, 200) # C6

# Fungsi melodi untuk kelembapan tinggi def play_high_humidity_melody():
play_buzzer_note(659, 200) # E5 play_buzzer_note(698, 200) # F5 play_buzzer_note(784, 200)  # G5

# Fungsi melodi kombinasi suhu dan kelembapan tinggi def play_combined_alert_melody():
play_buzzer_note(1047, 200) # C6 play_buzzer_note(1175, 200) # D6 play_buzzer_note(1319, 200) # E6

while True:
# Baca potensiometer pot_value = pot.read_u16()
servo_angle = map_value(pot_value, 0, 65535, 0, 180)


 

Text Box: servo_duty = map_value(servo_angle, 0, 180, 1638, 8192) servo.duty_u16(servo_duty)

# Baca suhu dan kelembapan dari DHT22 try:
dht_sensor.measure()
temperature = dht_sensor.temperature() humidity = dht_sensor.humidity()
print(f"Suhu: {temperature}°C | Kelembapan: {humidity:.1f}%") # Cek kondisi dan mainkan melodi sesuai kondisi
if temperature > 35 and humidity > 50:
print("🔥💧 Buzzer ON: Suhu & Kelembapan tinggi!") play_combined_alert_melody()
elif temperature > 35:
print("🔥 Buzzer ON: Suhu tinggi!") play_high_temp_melody()
elif humidity > 50:
print("💧 Buzzer ON: Kelembapan tinggi!") play_high_humidity_melody()
else:
buzzer.duty_u16(0) # Matikan suara jika normal

except OSError:
print("⚠️ Gagal membaca sensor DHT22.")

time.sleep(0.5)


 

§  Flowchart


 

6.         Motor Servo, LED RGB, & Potensiometer

§  Rangkaian


§  Listing Program

Text Box: from machine import Pin, PWM, ADC import utime

# Definisi pin
servo = PWM(Pin(16)) # Servo pada GP16
pot = ADC(Pin(28))	# Potensiometer pada GP28 led_red = Pin(1, Pin.OUT) # LED Merah pada GP1 led_green = Pin(2, Pin.OUT) # LED Hijau pada GP2 led_blue = Pin(3, Pin.OUT) # LED Biru pada GP3

# Konfigurasi servo (frekuensi 50Hz) servo.freq(50)

# Fungsi map seperti di Arduino
def map_value(value, in_min, in_max, out_min, out_max):
return int((value - in_min) * (out_max - out_min) / (in_max - in_min) + out_min)

# Variabel untuk kontrol kedipan LED last_blink = utime.ticks_ms()
led_state = True # Mulai dalam keadaan menyala


 

Text Box: while True:
# Waktu sekarang
now = utime.ticks_ms()

# Membaca nilai potensiometer (0 - 65535) pot_value = pot.read_u16()

# Konversi ke sudut servo (0° - 180°)
angle = map_value(pot_value, 0, 65535, 0, 180)

# Konversi sudut ke duty cycle (1500 - 7500) → sesuai servo PWM duty = map_value(angle, 0, 180, 1500, 7500)
servo.duty_u16(duty)

# Print untuk debugging
print(f"Pot Value: {pot_value}, Angle: {angle}, Duty: {duty}")

# Jika sudah lewat 1000 ms (1 detik), ubah status LED RGB if utime.ticks_diff(now, last_blink) >= 1000:
led_state = not led_state
last_blink = now # reset waktu blink

# Nyalakan salah satu warna LED berdasarkan sudut if 0 <= angle <= 60:
led_red.value(led_state) led_green.value(0) led_blue.value(0)
elif 60 < angle <= 120: led_red.value(0) led_green.value(led_state) led_blue.value(0)
else:
led_red.value(0) led_green.value(0)


 

Text Box: led_blue.value(led_state)

utime.sleep_ms(50) # Delay pendek untuk kestabilan pembacaan

§  Flowchart


 

7.         Led RGB, Buzzer, Soil Moisture, & Push Button

§  Rangkaian


§  Listing Program

Text Box: #include "stm32f1xx_hal.h"

ADC_HandleTypeDef hadc1; TIM_HandleTypeDef htim2; uint8_t sound_pattern = 0;

#define LED_RED_PIN		GPIO_PIN_12 #define LED_GREEN_PIN GPIO_PIN_13 #define LED_BLUE_PIN GPIO_PIN_14 #define LED_PORT	GPIOB
#define BUTTON_PIN	GPIO_PIN_0 #define BUTTON_PORT		GPIOB


 

Text Box: #define BUZZER_PIN	GPIO_PIN_2

#define ADC_THRESH_HIGH 3000
#define ADC_THRESH_MID  1500
const uint32_t pwm_periods[] = {1000, 50000, 719999}; void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_ADC1_Init(void); static void MX_TIM2_Init(void);
void update_leds_and_buzzer(uint32_t adc_val, uint8_t btn_state); void change_sound_pattern(void);
void Error_Handler(void);

int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_ADC1_Init(); MX_TIM2_Init();

HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_3);
  HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_3, 0);
HAL_ADC_Start(&hadc1);

while (1) {
static uint32_t last_adc_tick = 0; static uint32_t last_sound_change = 0;
uint8_t button_state = HAL_GPIO_ReadPin(BUTTON_PORT, BUTTON_PIN);

if (HAL_GetTick() - last_adc_tick > 200) { last_adc_tick = HAL_GetTick(); HAL_ADC_Start(&hadc1);
if (HAL_ADC_PollForConversion(&hadc1, 10) == HAL_OK) {


update_leds_and_buzzer(HAL_ADC_GetValue(&hadc1), button_state);

}

}

 

if (button_state == GPIO_PIN_RESET && (HAL_ADC_GetValue(&hadc1) < ADC_THRESH_MID)) {

if (HAL_GetTick() - last_sound_change > 1000) { last_sound_change = HAL_GetTick(); change_sound_pattern();

}

}

 

HAL_Delay(10);

}

}

 

void update_leds_and_buzzer(uint32_t adc_val, uint8_t btn_state) { HAL_GPIO_WritePin(LED_PORT, LED_RED_PIN | LED_GREEN_PIN |

LED_BLUE_PIN, GPIO_PIN_RESET);

 

if (adc_val >= ADC_THRESH_HIGH) { HAL_GPIO_WritePin(LED_PORT, LED_GREEN_PIN, GPIO_PIN_SET);

  HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_3, 0);

}

else if (adc_val >= ADC_THRESH_MID) { HAL_GPIO_WritePin(LED_PORT, LED_BLUE_PIN, GPIO_PIN_SET);

  HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_3, 0);

}

else {

HAL_GPIO_WritePin(LED_PORT, LED_RED_PIN, GPIO_PIN_SET);

if (btn_state == GPIO_PIN_RESET) {

  HAL_TIM_SET_AUTORELOAD(&htim2, pwm_periods[sound_pattern]);

  HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_3,

pwm_periods[sound_pattern] / 2);

} else {

  HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_3, 0);


}

}

}

 

void change_sound_pattern(void) { sound_pattern = (sound_pattern + 1) % 3;

if (HAL_ADC_GetValue(&hadc1) < ADC_THRESH_MID && HAL_GPIO_ReadPin(BUTTON_PORT, BUTTON_PIN) == GPIO_PIN_SET) {

  HAL_TIM_SET_AUTORELOAD(&htim2, pwm_periods[sound_pattern]);

  HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_3,

pwm_periods[sound_pattern] / 2);

}

}

 

void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};

 

RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; RCC_OscInitStruct.HSEState = RCC_HSE_ON; RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1; RCC_OscInitStruct.HSIState = RCC_HSI_ON; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;

if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) Error_Handler();

 

RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK |

RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;

RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;


if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK) Error_Handler();

 

PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_ADC; PeriphClkInit.AdcClockSelection = RCC_ADCPCLK2_DIV6;

if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK) Error_Handler();

}

 

static void MX_ADC1_Init(void) { ADC_ChannelConfTypeDef sConfig = {0};

 

hadc1.Instance = ADC1;

hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE; hadc1.Init.ContinuousConvMode = DISABLE; hadc1.Init.DiscontinuousConvMode = DISABLE; hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START; hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT; hadc1.Init.NbrOfConversion = 1;

if (HAL_ADC_Init(&hadc1) != HAL_OK) Error_Handler();

 

sConfig.Channel = ADC_CHANNEL_0; sConfig.Rank = ADC_REGULAR_RANK_1;

sConfig.SamplingTime = ADC_SAMPLETIME_1CYCLE_5;

if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK) Error_Handler();

}

 

static void MX_TIM2_Init(void) { TIM_MasterConfigTypeDef sMasterConfig = {0}; TIM_OC_InitTypeDef sConfigOC = {0};

 

htim2.Instance = TIM2; htim2.Init.Prescaler = 0;

htim2.Init.CounterMode = TIM_COUNTERMODE_UP; htim2.Init.Period = 65535;

htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;


htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; if (HAL_TIM_PWM_Init(&htim2) != HAL_OK) Error_Handler();

 

sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET; sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;

if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK) Error_Handler();

 

sConfigOC.OCMode = TIM_OCMODE_PWM1; sConfigOC.Pulse = 0;

sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH; sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;

if (HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_3) != HAL_OK) Error_Handler();

 

HAL_TIM_MspPostInit(&htim2);

}

 

static void MX_GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStruct = {0};

 

  HAL_RCC_GPIOA_CLK_ENABLE();

  HAL_RCC_GPIOB_CLK_ENABLE();

 

GPIO_InitStruct.Pin = LED_RED_PIN | LED_GREEN_PIN | LED_BLUE_PIN; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;

GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(LED_PORT, &GPIO_InitStruct);

 

GPIO_InitStruct.Pin = BUTTON_PIN; GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_PULLUP; HAL_GPIO_Init(BUTTON_PORT, &GPIO_InitStruct);

}


 

Text Box: void Error_Handler(void) {
 disable_irq(); while (1) {}
}

§  Flowchart



 

 

 

8.         Motor DC (Dinamo DC), Motor Stepper, Touch Sensor, Potensiometer

§  Rangkaian


§  Listing Program

Text Box: #include "stm32f1xx_hal.h"

// Konfigurasi Hardware
#define STEPPER_PORT GPIOB
#define IN1_PIN GPIO_PIN_8 #define IN2_PIN GPIO_PIN_9 #define IN3_PIN GPIO_PIN_10 #define IN4_PIN GPIO_PIN_11

#define TOUCH_SENSOR_PORT GPIOB
#define TOUCH_SENSOR_PIN GPIO_PIN_0 #define MOTOR_DC_PORT GPIOB
#define MOTOR_DC_PIN GPIO_PIN_7


 

Text Box: // Mode Stepper
const uint8_t STEP_SEQ_CW[4] = { (1<<0), // IN1
(1<<1), // IN2
(1<<2), // IN3
(1<<3)  // IN4
};

const uint8_t STEP_SEQ_CCW[4] = { (1<<3), // IN4
(1<<2), // IN3
(1<<1), // IN2
(1<<0)  // IN1
};

ADC_HandleTypeDef hadc1;
uint8_t current_mode = 0; // 0=CW, 1=CCW volatile uint8_t touch_state = 0;

void SystemClock_Config(void); void MX_GPIO_Init(void);
void MX_ADC1_Init(void);
void RunStepper(const uint8_t *sequence, uint8_t speed); void Error_Handler(void);

int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_ADC1_Init();

while (1) {
// Saat tidak disentuh, jalankan stepper seperti biasa
if (HAL_GPIO_ReadPin(TOUCH_SENSOR_PORT, TOUCH_SENSOR_PIN) == GPIO_PIN_RESET) {


HAL_ADC_Start(&hadc1);

if (HAL_ADC_PollForConversion(&hadc1, 10) == HAL_OK) { uint16_t adc_val = HAL_ADC_GetValue(&hadc1); current_mode = (adc_val < 2048) ? 0 : 1; // 0 = CW, 1 = CCW

}

 

if (current_mode == 0) { RunStepper(STEP_SEQ_CW, 5);

} else {

RunStepper(STEP_SEQ_CCW, 5);

}

}

 

HAL_Delay(1);

}

}

 

 

void RunStepper(const uint8_t *sequence, uint8_t speed) { static uint8_t step = 0;

 

HAL_GPIO_WritePin(STEPPER_PORT, IN1_PIN, (sequence[step] & (1<<0)) ? GPIO_PIN_SET : GPIO_PIN_RESET);

HAL_GPIO_WritePin(STEPPER_PORT, IN2_PIN, (sequence[step] & (1<<1)) ? GPIO_PIN_SET : GPIO_PIN_RESET);

HAL_GPIO_WritePin(STEPPER_PORT, IN3_PIN, (sequence[step] & (1<<2)) ? GPIO_PIN_SET : GPIO_PIN_RESET);

HAL_GPIO_WritePin(STEPPER_PORT, IN4_PIN, (sequence[step] & (1<<3)) ? GPIO_PIN_SET : GPIO_PIN_RESET);

 

step = (step + 1) % 4; HAL_Delay(speed);

}

 

void MX_GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStruct = {0};


 

  HAL_RCC_GPIOB_CLK_ENABLE();

  HAL_AFIO_REMAP_SWJ_NOJTAG(); // Optional: disable JTAG to free PB3-PB4 if needed

 

// Konfigurasi Touch Sensor sebagai input dengan EXTI (interrupt) GPIO_InitStruct.Pin = TOUCH_SENSOR_PIN; GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING_FALLING; GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(TOUCH_SENSOR_PORT, &GPIO_InitStruct);

 

// Aktifkan NVIC untuk EXTI0 HAL_NVIC_SetPriority(EXTI0_IRQn, 0, 0); HAL_NVIC_EnableIRQ(EXTI0_IRQn);

 

// Konfigurasi Motor DC (PB7) GPIO_InitStruct.Pin = MOTOR_DC_PIN;

GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(MOTOR_DC_PORT, &GPIO_InitStruct);

 

// Konfigurasi Stepper Motor (PB8-PB11)

GPIO_InitStruct.Pin = IN1_PIN | IN2_PIN | IN3_PIN | IN4_PIN; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL;

GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(STEPPER_PORT, &GPIO_InitStruct);

}

 

void MX_ADC1_Init(void) { ADC_ChannelConfTypeDef sConfig = {0};

 

hadc1.Instance = ADC1;

hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;


hadc1.Init.ContinuousConvMode = DISABLE; hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT; hadc1.Init.NbrOfConversion = 1;

if (HAL_ADC_Init(&hadc1) != HAL_OK) { Error_Handler();

}

 

sConfig.Channel = ADC_CHANNEL_0; sConfig.Rank = ADC_REGULAR_RANK_1;

sConfig.SamplingTime = ADC_SAMPLETIME_71CYCLES_5;

if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK) { Error_Handler();

}

}

 

void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

 

RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI; RCC_OscInitStruct.HSIState = RCC_HSI_ON; RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT; if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) {

Error_Handler();

}

 

RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK

|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;

RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK)

{


 

Text Box: Error_Handler();
}
}

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { if (GPIO_Pin == TOUCH_SENSOR_PIN) {
GPIO_PinState pinState = HAL_GPIO_ReadPin(TOUCH_SENSOR_PORT, TOUCH_SENSOR_PIN);

if (pinState == GPIO_PIN_SET) {
// Touch sensor ditekan - nyalakan motor DC, matikan stepper HAL_GPIO_WritePin(MOTOR_DC_PORT, MOTOR_DC_PIN, GPIO_PIN_SET); HAL_GPIO_WritePin(STEPPER_PORT, IN1_PIN|IN2_PIN|IN3_PIN|IN4_PIN,
GPIO_PIN_RESET);
} else {
// Touch sensor dilepas - matikan motor DC HAL_GPIO_WritePin(MOTOR_DC_PORT, MOTOR_DC_PIN,
GPIO_PIN_RESET);
}
}
}


// IRQ Handler untuk EXTI0 void EXTI0_IRQHandler(void) {
HAL_GPIO_EXTI_IRQHandler(TOUCH_SENSOR_PIN);
}

void Error_Handler(void) { while(1) {}
}


 

§  Flowchart