Percobaan 7
Led RGB, Buzzer, Soil Moisture, & Push Button
2. Setelah semua komponen (Buzzer, LED RGB, Push Button, dan Soil Moisture) dihubungkan, lalu hubungkan USB STM32 ke laptop.
4. Setelah program selesai, simulasikan rangkaian.
2. Hardware dan Diagram Blok[Kembali]
Hardware :
- STM32
- Push button
- LED RGB
3. Rangkaian Simulasi[Kembali]
Rangkailah seperti rangkaian percobaan 7 pada modul
- Prinsip Kerja
Setelah semua periferal diinisialisasi, program masuk ke
dalam loop utama (while(1)), di mana sistem terus memantau dua hal utama: nilai
tegangan dari ADC (yang merepresentasikan sensor) dan status tombol. ADC akan
dibaca secara periodik setiap 200 milidetik. Jika ADC berhasil memberikan
nilai, maka nilai tersebut digunakan untuk menentukan status output LED dan
buzzer dengan memanggil fungsi update_leds_and_buzzer().
Fungsi update_leds_and_buzzer() menentukan output
berdasarkan besar nilai ADC:
- Jika
nilai ADC tinggi (≥ 3000), maka LED hijau menyala dan buzzer mati.
- Jika
nilai ADC sedang (≥ 1500 tetapi < 3000), LED biru menyala dan buzzer
tetap mati.
- Jika
nilai ADC rendah (< 1500), LED merah menyala. Jika tombol ditekan (btn_state
== GPIO_PIN_RESET), buzzer akan menghasilkan suara PWM sesuai pola sound_pattern.
Jika tombol tidak ditekan, buzzer akan mati.
Setiap 1 detik, jika tombol ditekan dan ADC < 1500, pola
suara buzzer akan berubah ke pola berikutnya (dari array pwm_periods[]) melalui
fungsi change_sound_pattern().
PWM untuk buzzer dikendalikan oleh TIM2 Channel 3. Nilai
frekuensi ditentukan oleh variabel pwm_periods[], dan duty cycle-nya diatur
pada nilai 50% dari periodenya.
Secara umum, alat ini bekerja sebagai indikator level sensor
menggunakan LED dan suara. Level tinggi ditunjukkan dengan LED hijau, level
sedang dengan biru, dan level rendah dengan merah ditambah buzzer jika tombol
ditekan. Selain itu, buzzer dapat berubah frekuensi suaranya setiap kali tombol
ditekan dalam kondisi sensor rendah.
4. Flowchart dan Listing Program[Kembali]
- Flowchart
- Listing Program
#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
#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);
}
void Error_Handler(void) {
__disable_irq();
while (1) {}
}
ANALISA MODUL 2: PWM, ADC, INTERRUPT, & MILLIS
1. Analisa bagaimana perbedaan implementasi PWM antara STM32 dan Raspberry Pi Pico serta dampaknya terhadap kontrol motor dan Buzzer ?
STM32 menggunakan timer hardware yang dikonfigurasi melalui register prescaler dan auto-reload untuk mengatur frekuensi PWM, sedangkan duty cycle diatur melalui capture/compare register (CCR). Pendekatan ini memberikan resolusi tinggi (hingga 16-bit) dan presisi yang baik, sehingga cocok untuk aplikasi seperti kontrol motor yang membutuhkan respons halus atau buzzer yang memerlukan frekuensi stabil.
Raspberry Pi Pico menggunakan modul PWM terpisah yang tidak bergantung pada timer khusus. Frekuensi PWM diatur melalui pembagi integer, yang kurang presisi dibanding STM32. Resolusi defaultnya 8-bit (dapat ditingkatkan), sehingga lebih sederhana tetapi kurang akurat untuk aplikasi yang membutuhkan ketepatan tinggi. Akibatnya, kontrol motor mungkin kurang halus, dan frekuensi buzzer bisa memiliki variasi lebih besar.
Pada STM32, ADC bekerja dengan multi-channel dan dapat menggunakan DMA untuk pembacaan efisien tanpa intervensi CPU. Sampling rate diatur melalui konfigurasi clock dan sample time register, serta dilengkapi kalibrasi internal untuk meningkatkan akurasi. Pembacaan nilai ADC biasanya menggunakan fungsi HAL seperti HAL_ADC_Start() dan HAL_ADC_GetValue().
Raspberry Pi Pico memiliki ADC 12-bit dengan 4 channel (termasuk satu channel untuk sensor suhu internal). Pembacaannya lebih sederhana karena tidak memerlukan konfigurasi kompleks seperti STM32. Nilai ADC dapat dibaca langsung menggunakan fungsi read_u16() dari library ADC Pico. Namun, Pico tidak memiliki fitur kalibrasi otomatis, sehingga mungkin memerlukan kalibrasi manual jika dibutuhkan akurasi tinggi.
3. Analisa bagaimana penggunaan interrupt eksternal dalam mendeteksi input dari sensor pada STM32 dan Raspberry Pi Pico ?
Di STM32, interrupt eksternal dikonfigurasi melalui EXTI (External Interrupt) dan dihubungkan ke pin GPIO tertentu. Setiap perubahan tegangan (rising/falling edge) dapat memicu interrupt handler yang ditentukan, misalnya menggunakan HAL dengan callback seperti HAL_GPIO_EXTI_Callback(). Pendekatan ini efisien untuk respon cepat terhadap sensor seperti encoder atau limit switch.
Pada Raspberry Pi Pico, interrupt eksternal diatur melalui fungsi gpio_set_irq_enabled_with_callback(), yang memungkinkan penanganan interrupt berbasis event pada pin tertentu. Pico menggunakan mekanisme IRQ handler yang fleksibel tetapi memerlukan pengaturan manual untuk filter debouncing atau prioritas interrupt.
4. Analisa bagaimana cara kerja fungsi HAL_GetTick() pada STM32 dan utime.ticks_ms() pada Raspberry Pi Pico dalam menghitung waktu sejak sistem dinyalakan ?
Pada Raspberry Pi Pico, utime.ticks_ms() juga mengembalikan waktu dalam milidetik, tetapi menggunakan timer hardware internal Pico yang tidak tergantung pada interrupt. Pico menggunakan timer 64-bit yang terus bertambah, sehingga lebih stabil untuk penghitungan waktu jangka panjang tanpa overflow.
5. Analisa bagaimana perbedaan konfigurasi dan kontrol pin PWM serta pemanfaatan timer internal pada STM32 dan Raspberry Pi Pico dalam menghasilkan sinyal gelombang persegi ?
STM32 menghasilkan PWM menggunakan timer hardware (seperti TIM1, TIM2, dll.) yang dapat dikonfigurasi untuk frekuensi dan resolusi berbeda melalui register prescaler dan auto-reload. Setiap channel PWM dihubungkan ke capture/compare register (CCR) untuk mengatur duty cycle. Pendekatan ini memungkinkan multiple PWM dengan sinkronisasi ketat dan fase terkontrol.
Raspberry Pi Pico menggunakan modul PWM independen yang tidak terikat pada timer khusus. Frekuensi PWM diatur melalui pembagi integer dan penghitung wrap, sedangkan duty cycle dikontrol via level threshold. Meskipun lebih sederhana, Pico kurang fleksibel dalam sinkronisasi multiple PWM dibanding STM32. Namun, Pico mendukung lebih banyak pin PWM secara bersamaan tanpa konflik resource timer.
Tidak ada komentar:
Posting Komentar