UTS_Rivaldi
Program untuk UTSunknown
assembly_x86
5 days ago
11 kB
9
Indexable
; ===================================================================== ; Project: ATmega328 3-Digit 7-Segment Display with Interrupt Buttons ; Target : ATmega328 ; Clock : 1 MHz (External or Internal RC calibrated) ; IDE : Microchip Studio ; Author : M. Fajar Rivaldi NIM 04221001(Balikpapan) ; Date : 2025-04-16 (Waktu Balikpapan) ; ===================================================================== .INCLUDE "m328def.inc" ; Definisi register untuk ATmega328 ; --- Definisi Register --- .DEF temp = r16 ; Register serbaguna .DEF result = r17 ; Menyimpan hasil perhitungan (+10 atau -10) .DEF disp_val = r18 ; Menyimpan nilai absolut untuk konversi digit .DEF digit1_pat = r19 ; Pola segmen untuk digit 1 (tanda/-) .DEF digit2_pat = r20 ; Pola segmen untuk digit 2 (puluhan) .DEF digit3_pat = r21 ; Pola segmen untuk digit 3 (satuan) .DEF d_tens = r22 ; Digit puluhan (0-9) .DEF d_units = r23 ; Digit satuan (0-9) .DEF temp2 = r24 ; Register serbaguna tambahan .DEF dly_cnt_1 = r25 ; Counter delay 1 (untuk Delay_10ms) .DEF dly_cnt_2 = r26 ; Counter delay 2 (untuk Delay_10ms) ; --- Definisi Konstanta --- .EQU SEG_A = 6 .EQU SEG_B = 5 .EQU SEG_C = 4 .EQU SEG_D = 3 .EQU SEG_E = 2 .EQU SEG_F = 1 .EQU SEG_G = 0 .EQU SS1_PIN = PB3 ; Digit 1 (Tanda) Control Pin (Active LOW) .EQU SS2_PIN = PB2 ; Digit 2 (Puluhan) Control Pin (Active LOW) .EQU SS3_PIN = PB1 ; Digit 3 (Satuan) Control Pin (Active LOW) .EQU BTN1_PIN = PD2 ; Button 1 (INT0) -> +10 .EQU BTN2_PIN = PD3 ; Button 2 (INT1) -> -10 .EQU SEG_MINUS = (1<<SEG_G) ; Pola untuk tanda '-' (hanya segmen G) .EQU SEG_BLANK = 0x00 ; Pola untuk blank .EQU ALL_DIGITS_OFF = (1<<SS1_PIN)|(1<<SS2_PIN)|(1<<SS3_PIN) ; Masker untuk mematikan semua digit ; --- Alokasi Memori Data (SRAM) --- .DSEG current_digit: .BYTE 1 ; Variabel di SRAM untuk melacak digit aktif (1, 2, atau 3) ; --- Segmen Kode --- .CSEG ; --- Vektor Interrupt --- .ORG 0x0000 RJMP RESET ; Reset Handler (Alamat 0x0000) .ORG INT0addr ; External Interrupt 0 Vector Address (Alamat 0x0002) RJMP INT0_ISR ; INT0 Handler (Button 1) .ORG INT1addr ; External Interrupt 1 Vector Address (Alamat 0x0004) RJMP INT1_ISR ; INT1 Handler (Button 2) .ORG OVF0addr ; Timer0 Overflow Vector Address (Alamat 0x001A atau sesuai def file) RJMP TIMER0_OVF_ISR ; Timer0 Overflow Handler (Display Multiplexing) ; --- Akhir Tabel Vektor --- ; --- Program Utama Dimulai Setelah Vektor Tertinggi --- RESET: ; Inisialisasi Stack Pointer LDI temp, HIGH(RAMEND) OUT SPH, temp LDI temp, LOW(RAMEND) OUT SPL, temp ; --- Konfigurasi Port --- ; PORTC (Segments): Output, Semua Mati (Blank) LDI temp, 0xFF OUT DDRC, temp LDI temp, SEG_BLANK OUT PORTC, temp ; PORTB (Digits): Output, Semua Mati (HIGH untuk Active Low) LDI temp, (1<<SS1_PIN)|(1<<SS2_PIN)|(1<<SS3_PIN) OUT DDRB, temp IN temp, PORTB ORI temp, ALL_DIGITS_OFF OUT PORTB, temp ; PORTD (Buttons): Input CLR temp OUT DDRD, temp ; Optional: Aktifkan pull-up internal jika tidak ada eksternal ; LDI temp, (1<<BTN1_PIN)|(1<<BTN2_PIN) ; OUT PORTD, temp ; --- Inisialisasi Variabel --- CLR result ; Set pola awal ke blank (meskipun akan diatur oleh Disable_Display) LDI digit1_pat, SEG_BLANK LDI digit2_pat, SEG_BLANK LDI digit3_pat, SEG_BLANK ; --- Konfigurasi Interrupt --- ; External Interrupts: INT0/INT1 aktif pada SETIAP PERUBAHAN LOGIKA LDI temp, (0<<ISC11)|(1<<ISC10) | (0<<ISC01)|(1<<ISC00) ; ISC10=1, ISC00=1 -> Any logical change STS EICRA, temp ; Aktifkan INT0 & INT1 LDI temp, (1<<INT1)|(1<<INT0) OUT EIMSK, temp ; --- Konfigurasi Timer0 --- ; Prescaler clk/8, TAPI Timer Overflow Interrupt DIMATIKAN awalnya LDI temp, (1<<CS01) OUT TCCR0B, temp CLR temp ; Pastikan TOIE0 = 0 STS TIMSK0, temp ; Gunakan STS untuk TIMSK0 ; Aktifkan Global Interrupts SEI MAIN_LOOP: NOP ; Tidak melakukan apa-apa, semua dikendalikan interrupt RJMP MAIN_LOOP ; --- Interrupt Service Routines (ISR) --- ; ISR untuk Tombol 1 (INT0) - Aktif pada setiap perubahan PD2 INT0_ISR: PUSH temp IN temp, SREG ; Simpan SREG PUSH temp ; Cek kondisi pin PD2 saat ini SBIS PIND, BTN1_PIN ; Loncat jika pin PD2 = 1 (HIGH - Dilepas) RJMP INT0_Pressed ; Jika pin PD2 = 0 (LOW - Ditekan) INT0_Released: ; Blok ini dieksekusi jika PD2 HIGH (Tombol dilepas) RCALL Disable_Display ; Matikan tampilan & Timer Interrupt RJMP INT0_Exit ; Langsung keluar INT0_Pressed: ; Blok ini dieksekusi jika PD2 LOW (Tombol ditekan) LDI result, 10 ; Set nilai untuk ditampilkan RCALL Update_Display_Data ; Hitung pola segmen RCALL Enable_Display ; Aktifkan tampilan & Timer Interrupt ; Lanjut ke Exit INT0_Exit: POP temp ; Restore SREG OUT SREG, temp POP temp RETI ; Return from Interrupt ; ISR untuk Tombol 2 (INT1) - Aktif pada setiap perubahan PD3 INT1_ISR: PUSH temp IN temp, SREG ; Simpan SREG PUSH temp ; Cek kondisi pin PD3 saat ini SBIS PIND, BTN2_PIN ; Loncat jika pin PD3 = 1 (HIGH - Dilepas) RJMP INT1_Pressed ; Jika pin PD3 = 0 (LOW - Ditekan) INT1_Released: ; Blok ini dieksekusi jika PD3 HIGH (Tombol dilepas) RCALL Disable_Display ; Matikan tampilan & Timer Interrupt RJMP INT1_Exit ; Langsung keluar INT1_Pressed: ; Blok ini dieksekusi jika PD3 LOW (Tombol ditekan) LDI result, -10 ; Set nilai untuk ditampilkan RCALL Update_Display_Data ; Hitung pola segmen RCALL Enable_Display ; Aktifkan tampilan & Timer Interrupt ; Lanjut ke Exit INT1_Exit: POP temp ; Restore SREG OUT SREG, temp POP temp RETI ; Return from Interrupt ; ISR untuk Timer0 Overflow - Multiplexing Display (HANYA AKTIF JIKA DIPANGGIL ENABLE_DISPLAY) TIMER0_OVF_ISR: PUSH temp IN temp, SREG PUSH temp PUSH temp2 PUSH digit1_pat ; Simpan register pola PUSH digit2_pat PUSH digit3_pat ; 1. Matikan SEMUA digit (Set pin HIGH untuk Active Low) IN temp, PORTB ORI temp, ALL_DIGITS_OFF OUT PORTB, temp LDS temp2, current_digit ; Muat digit mana yang akan ditampilkan (1, 2, atau 3) ; 2. Pilih pola dan aktifkan digit yang sesuai CPI temp2, 1 BREQ DispDigit1_ISR CPI temp2, 2 BREQ DispDigit2_ISR ; Jika bukan 1 atau 2, lanjut ke Digit 3 DispDigit3_ISR: ; Tampilkan Digit 3 (Satuan) MOV temp, digit3_pat OUT PORTC, temp IN temp, PORTB ANDI temp, ~(1<<SS3_PIN) ; Aktifkan Digit 3 (Set pin LOW) OUT PORTB, temp LDI temp2, 1 RJMP UpdateCurrentDigit_ISR ; Selalu JUMP ke update DispDigit1_ISR: ; Tampilkan Digit 1 (Tanda) MOV temp, digit1_pat OUT PORTC, temp IN temp, PORTB ANDI temp, ~(1<<SS1_PIN) ; Aktifkan Digit 1 (Set pin LOW) OUT PORTB, temp LDI temp2, 2 RJMP UpdateCurrentDigit_ISR ; Selalu JUMP ke update DispDigit2_ISR: ; Tampilkan Digit 2 (Puluhan) MOV temp, digit2_pat OUT PORTC, temp IN temp, PORTB ANDI temp, ~(1<<SS2_PIN) ; Aktifkan Digit 2 (Set pin LOW) OUT PORTB, temp LDI temp2, 3 RJMP UpdateCurrentDigit_ISR ; *** FIX: TAMBAHKAN JUMP ini agar timing sama *** UpdateCurrentDigit_ISR: STS current_digit, temp2 ; Simpan nomor digit berikutnya ke SRAM TIMER0_OVF_Exit: POP digit3_pat POP digit2_pat POP digit1_pat POP temp2 POP temp OUT SREG, temp POP temp RETI ; Mengaktifkan Timer Overflow Interrupt untuk memulai multiplexing Enable_Display: PUSH temp LDI temp, (1<<TOIE0) ; Siapkan bit untuk mengaktifkan Timer0 OVF Int STS TIMSK0, temp ; Gunakan STS untuk TIMSK0 POP temp RET ; Mematikan Timer Overflow Interrupt dan mematikan display Disable_Display: PUSH temp ; 1. Matikan Timer0 OVF Interrupt CLR temp ; Siapkan 0 STS TIMSK0, temp ; Gunakan STS untuk TIMSK0 ; 2. Matikan Segmen LDI temp, SEG_BLANK OUT PORTC, temp ; 3. Matikan Digit Select IN temp, PORTB ORI temp, ALL_DIGITS_OFF OUT PORTB, temp POP temp RET ; Update Pola Segment berdasarkan nilai di 'result' (Sama seperti sebelumnya) Update_Display_Data: PUSH ZL PUSH ZH PUSH temp PUSH temp2 PUSH disp_val PUSH d_tens PUSH d_units MOV disp_val, result TST disp_val BRMI Result_Negative_Sub Result_Positive_Or_Zero_Sub: LDI digit1_pat, SEG_BLANK RJMP Convert_Value_Sub Result_Negative_Sub: LDI digit1_pat, SEG_MINUS NEG disp_val Convert_Value_Sub: CLR d_tens CLR d_units CPI disp_val, 10 BRLO Val_Less_Than_10_Sub LDI d_tens, 1 LDI d_units, 0 RJMP Get_Patterns_Sub Val_Less_Than_10_Sub: MOV d_units, disp_val Get_Patterns_Sub: MOV temp, d_tens RCALL Get_Segment_Code MOV digit2_pat, temp MOV temp, d_units RCALL Get_Segment_Code MOV digit3_pat, temp Update_Exit_Sub: POP d_units POP d_tens POP disp_val POP temp2 POP temp POP ZH POP ZL RET ; Ambil kode segmen dari tabel (Sama seperti sebelumnya) Get_Segment_Code: PUSH temp2 PUSH r0 LDI ZL, LOW(Segment_Table*2) LDI ZH, HIGH(Segment_Table*2) MOV temp2, temp CLR temp ADD ZL, temp2 ADC ZH, temp LPM MOV temp, r0 POP r0 POP temp2 RET ; Delay - Tidak lagi dipanggil oleh ISR utama, tapi mungkin berguna nanti Delay_10ms: PUSH dly_cnt_1 ; Simpan register yang dipakai delay (r25) PUSH dly_cnt_2 ; Simpan register yang dipakai delay (r26) LDI dly_cnt_1, 40 Delay_Outer_Loop: LDI dly_cnt_2, 85 Delay_Inner_Loop: DEC dly_cnt_2 BRNE Delay_Inner_Loop DEC dly_cnt_1 BRNE Delay_Outer_Loop POP dly_cnt_2 ; Kembalikan register POP dly_cnt_1 ; Kembalikan register RET ; ===================================================================== ; Tabel Pola Segment (Common Cathode) ; ===================================================================== .ALIGN 2 Segment_Table: .DB 0x7E, 0x30, 0x6D, 0x79, 0x33, 0x5B, 0x5F, 0x70, 0x7F, 0x7B ; --- Akhir Tabel --- ; ===================================================================== ; Akhir Program ; =====================================================================
Editor is loading...
Leave a Comment