Looping Example
Design Objective
When the user presses the button, read first 3 switches (least significant), if the number is less than or equal to 5 then calculate factorial. If greater than 5 turn on decimal point. Display the least significant 4 bits of the answer.
My Design Steps
Step 1: Initialized Ports
; Disable interrupts and configure stack pointer for 328P
cli
; Initialize Switches with Pull-up resistors and Test LEDs
in r16,DDRC // input Port C Data Direction Register (0x07) for switches 5 to 0
cbr r16,0b00111111 // define bits 5 to 0 as input (clear bit register)
out DDRC,r16 // output
in r16,PORTC // input Port C Register (0x08) for switches 5 to 0
sbr r16,0b00111111 // add pull-up resistors (PUR)
out PORTC,r16 // output
in r16,DDRD // input Port D Data Direction Register (0x0A) for switches 7 to 6
cbr r16,0b11000000 // define bits 7 to 6 as input (clear)
out DDRD,r16 // output
in r16,PORTD // input Port D Register (0x0B) for switches 7 to 6
sbr r16,0b11000000 // add pull-up resistors (PUR)
out PORTD,r16 // output
; Initialize SPI Port and Test LEDs
in r16,DDRB // Input from Port B Data Direction Register (DDRB) at i/o address 0x04
sbr r16,0b00101111 // Set PB5, PB3, PB2 (SCK, MOSI, SS) and PB1, PB0 (TEST LEDs) as outputs
out DDRB,r16 // Output to Port B Data Direction Register (DDRB) at i/o address 0x04
in r16,PORTB // input Port B Register (0x05) bit 2 (SS) at i/o address 0x05
cbr r16,0b00000111 // bit 1 (TEST LED1), bit 0 (TEST LED0)
out PORTB,r16 // output
ldi r16,0b01010001 // Set SPCR Enable (SPE) bit 6, Master (MSTR) bit 4,
// clock rate fck/16 (SPR1 = 0,SPR0 = 1)
out SPCR,r16 // Output to SPI Control Register (SPCR) at i/o address 0x2c
Step 2: Turned on LED 0 to indicate initialization complete
sbi PORTB, 0 // Turn on LED 0
Step 3: Wrote code to pulse the clock
start:
cbi PORTD, 5
sbi PORTD, 5
Step 4: Read in pin waiting for the button to be pressed (Loop Example 1)
// check button
sbic PIND, 2
rjmp start
Step 5: Need to filter out Bounce (Loop Example 2)
delay_50:
ldi r16, 0 // 256
wait:
dec r16 // 1 clock cycle
brne wait // + 2 cycle if true, 1 cycles if false
// 3 cycles x 256 - 1 = 599 x 1/16 MHz = 48 usec
Maximum delay that could be generated was only 48 usec
Step 6: Added a NOP instruction, max delay was now 64 usec
Set delay for nice even number of 50 usec
delay_50:
ldi r16, 200 // 200 = 0xC8
wait:
nop // 1 clock cycle
dec r16 // 1 clock cycle
brne wait // + 2 cycle if true, 1 cycles if true
// 4 cycles x 200 - 1 = 799 x 1/16 MHz = 50 usec
Step 7: Made an outside loop of 10 (Loop Example 3)
delay_500:
ldi r17, 10
delay_50:
ldi r16, 200 // 200 = 0xC8
wait:
nop // 1 clock cycle
dec r16 // 1 clock cycle
brne wait // + 2 cycle if true, 1 cycles if true
// 4 cycles x 200 - 1 = 799 x 1/16 MHz = 50 usec
dec r17
brne delay_50 // 10 x 50 usec = 500 us (approx)
Step 8: Converted loop to a subroutine so I could change condition to button release.
; --------------------------
Delay500:
push r16
push r17
ldi r17, 10 // was 10
delay_50:
ldi r16, 200 // 200 = 0xC8
wait:
nop // 1 clock cycle
dec r16 // 1 clock cycle
brne wait // + 2 cycle if true, 1 cycles if true
// 4 cycles x 200 - 1 = 799 x 1/16 MHz = 50 usec
dec r17
brne delay_50 // 10 x 50 usec = 500 us (approx)
dec r18
brne delay_500 // 10 x 50 usec = 500 us (approx)
pop r17
pop r16
ret
Step 9: Check for button pressed and then released
start:
cbi PORTD, 5
sbi PORTD, 5
// check button down
sbic PIND, 2
rjmp start
rcall Delay500 // remove bounce
check_button:
cbi PORTD, 5
sbi PORTD, 5
// check button up
sbis PIND, 2
rjmp check_button
rcall Delay500 // remove bounce
Step 10: Read Switch and check if less than or equal to 5
in r16, PINC
cbr r16, 0b11110000 // clear undefined bits
cpi r16, 6 // no unsigned less than or equal to 5
brlo factorial
// error condition
ldi r16, 0x80 // decimal point
mov r8, r16
rcall writeDisplay
rjmp start
Step 11: Calculate Factorial (Loop Example 4)
factorial:
ldi r17, 1
mov r0, r17
calculate:
mul r0, r16 // r1:r0 = r0 x r16
dec r16
brne calculate
Step 12: Convert least significant nibble to 7-segment display (Flash Program Indirect Addressing Mode)
display_answer:
ldi r16, 0b00001111 // limit to least significant nibble
and r0, r16
ldi ZL,low(table<<1) // load address of look-up
ldi ZH,high(table<<1)
clr r1
add ZL, r0
adc ZH, r1
lpm spi7SEG, Z
rcall
writeDisplay
rjmp start
// gfedcba gfedcba gfedcba gfedcba gfedcba gfedcba
table: .DB 0b00111111, 0b00000110, 0b01011011, 0b01001111, 0b01100110, 0b01101101
// 0 1 2 3 4 5
.DB 0b01111101, 0b00000111, 0b01111111, 0b01100111, 0b01110111, 0b01111100
// 6 7 8 9 A B
.DB 0b00111001, 0b01011110, 0b01111001, 0b01110001
// C D E F