UTS_Rivaldi
Program untuk UTSunknown
assembly_x86
8 months ago
11 kB
18
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