Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /*
- * eVm1a.asm
- * This program is a simple voltmeter application that will intake
- an analog signal, convert it to a digital signal, and then display the
- voltage onto an LCD display. Data will be transmitted serially
- This will run continously
- * Created: 11/12/2014
- * Author: Christopher Earls And Kevin Kim
- Lab: 02- Bench 9
- LAB 10
- */
- //Sets the ISR and the initial program
- .org 0
- rjmp hard_reset
- ;.org $014
- ;rjmp complete_isr
- .nolist
- .include "m16def.inc"
- .list
- .include "lcd_dog_asm_driver_m16A.inc"
- hard_reset: ;This subroutine ios a hard reset, it completely re-initializes all aspects of the circuit.
- ;configure PA7 as an INPUT
- ldi r16, $00 ;Load all or porta with 0's
- out DDRA, r16 ;Porta as input
- ldi r16, $FF ;load r16 with all 1's
- out PORTD, r16 ;Enable pullup resistors
- ;configure Port B as Output, PB0
- ldi r16, $BF ;load r16 with all ones
- out DDRB, r16 ;port b all outputs
- ldi r16, $40
- out PORTB, r16 ;enable pullup resistor
- SBI PORTB, 0 ;sET PORT bIT 0 to 1 as a disable so you can use the LCD dog
- ;initialize stack pointer register
- ldi r16, LOW(RAMEND) ; load SPL with low byte of
- out SPL, r16 ; RAMEND address.
- ldi r16, HIGH(RAMEND) ; load SPH with high byte of
- out SPH, r16 ; RAMEND address.
- sei ; set the global interrupt
- //Initial display for the bottom two lines, this will take care of the bottom two liens of text as they do not change in the program.
- //It is also good to check if the LCD is prperly on and displaying correctly.
- init_display:
- rCALL init_lcd_dog //initializes LCD for display
- ;load 1st line of prompt message into dbuff2
- ldi ZH, high(line1_message<<1) ;
- ldi ZL, low(line1_message<<1) ;
- rcall load_msg ; load message into buffer(s).
- ;load 2st line of prompt message into dbuff2
- ldi ZH, high(line2_message<<1) ;
- ldi ZL, low(line2_message<<1) ;
- rcall load_msg ; load message into buffer(s).
- ;load 3nd line of prompt message into dbuff3
- ldi ZH, high(line3_message<<1) ;
- ldi ZL, low(line3_message<<1) ;
- rcall load_msg ; load message into buffer(s).
- rcall update_lcd_dog
- //this is the main code, when the ADC is complete, it will call out to the Violtage reading function and then will
- //use the arithmatic to change it to a displayable value, This also includes the reset function.
- reset:
- ;clt //clears the tflag initially
- ;call SPI_MasterInit //initializes the SPI master control
- ;call adc_init
- main:
- ;BRTS adc_read //if t flag is set, continue on
- call adc_read
- rjmp main
- ;This code will initialize the master/slave commands for the SPI interfacing.
- ;This is proveded by the Atmel data sheets
- init_adc:
- ldi r16, $50
- out SPCR, R16 ;Set up control register
- ldi r16, $00
- out SPSR, R16 ;Setup status register
- ret
- adc_read:
- rcall init_adc ;initialize SPI for the ADC
- cbi PORTB, 0 ;Turn on the ADC
- rcall delay_9us ;wait 9us for the adc to turn on
- ldi r16, $00
- ;out SPSR, r16 ;Clear out the SPSR register
- out spdr, r16 ;clean out the data register to start a conversion
- rcall wait
- in r19, SPDR ;Intake the upper byte from the conversion
- andi r19, $0f ;mask any unnecessary data
- ldi r16, $00 ;load r16 with 0 to restart next 8 bits
- ;out SPSR, r16 ;Clear interrupt on the status register
- out SPDR, r16 ;Start serial transfer
- rcall wait ;Will come into the wait for the finished transfer
- in r18, SPDR ;Intake the lower 8 bit s for the conversion.
- mov r17, r19
- mov r16, r18
- sbi PORTB, 0 ;Turn off the ADC before the LCD is used
- ;rcall init_spi_lcd ;Update the SPI so that it will display rather than use the ADC
- rcall bin2BCD16
- rcall unpack2r4r0 ;unpack the values to bcd and ascii
- STS dsp_buff_1, r5 ;Places the numbers onto the display
- STS dsp_buff_1+2, r6
- sts dsp_buff_1+3, r7
- sts dsp_buff_1+4, r8
- STS dsp_buff_1+5, r9
- rcall update_lcd_dog ;updates the LCD
- jmp reset ;restarts the program
- ;This subroutine will check if the SPI transfer is completed, and will jump back once finished
- wait:
- in r16, SPSR ;check if the interrupt is triggered
- andi r16, $80 ;mask all but bit 7
- cpi r16, $80 ;Check if the data transfer is finished
- brne wait ;if not, it will loop
- ret ;if it is, it will return
- delay_9us:
- nop ; fine tune delay
- nop
- push r24
- ldi r24, 0x01 ; load delay count.
- d10_loop: dec r24 ; count down to
- brne d10_loop ; zero.
- pop r24
- ret
- /* ;***************************************************************************
- ;*
- ;* "div32u" - 32/32 Bit Unsigned Division
- ;*
- ;* Ken Short
- ;*
- ;* This subroutine divides the two 32-bit numbers
- ;* "dd32u3:dd32u2:dd32u1:dd32u0" (dividend) and "dv32u3:dv32u2:dv32u3:dv32u2"
- ;* (divisor).
- ;* The result is placed in "dres32u3:dres32u2:dres32u3:dres32u2" and the
- ;* remainder in "drem32u3:drem32u2:drem32u3:drem32u2".
- ;*
- ;* Number of words :
- ;* Number of cycles :655/751 (Min/Max) ATmega16
- ;* #Low registers used :2 (drem16uL,drem16uH)
- ;* #High registers used :5 (dres16uL/dd16uL,dres16uH/dd16uH,dv16uL,dv16uH,
- ;* dcnt16u)
- ;* A $0000 divisor returns $FFFF
- ;*
- ;***************************************************************************
- ;***** Subroutine Register Variables
- .def drem32u0=r12 ;remainder
- .def drem32u1=r13
- .def drem32u2=r14
- .def drem32u3=r15
- .def dres32u0=r18 ;result (quotient)
- .def dres32u1=r19
- .def dres32u2=r20
- .def dres32u3=r21
- .def dd32u0 =r18 ;dividend
- .def dd32u1 =r19
- .def dd32u2 =r20
- .def dd32u3 =r21
- .def dv32u0 =r22 ;divisor
- .def dv32u1 =r23
- .def dv32u2 =r24
- .def dv32u3 =r25
- .def dcnt32u =r17
- ;***** Code
- div32u:
- clr drem32u0 ;clear remainder Low byte
- clr drem32u1
- clr drem32u2
- sub drem32u3,drem32u3;clear remainder High byte and carry
- ldi dcnt32u,33 ;init loop counter
- d32u_1:
- rol dd32u0 ;shift left dividend
- rol dd32u1
- rol dd32u2
- rol dd32u3
- dec dcnt32u ;decrement counter
- brne d32u_2 ;if done
- ret ; return
- d32u_2:
- rol drem32u0 ;shift dividend into remainder
- rol drem32u1
- rol drem32u2
- rol drem32u3
- sub drem32u0,dv32u0 ;remainder = remainder - divisor
- sbc drem32u1,dv32u1
- sbc drem32u2,dv32u2
- sbc drem32u3,dv32u3 ;
- brcc d32u_3 ; branch if reult is pos or zero
- add drem32u0,dv32u0 ; if result negative restore remainder
- adc drem32u1,dv32u1
- adc drem32u2,dv32u2
- adc drem32u3,dv32u3
- clc ; clear carry to be shifted into result
- rjmp d32u_1 ;else
- d32u_3: sec ; set carry to be shifted into result
- rjmp d32u_1
- */
- /* ;***************************************************************************
- ;*
- ;* "mpy16u" - 16x16 Bit Unsigned Multiplication
- ;*
- ;* This subroutine multiplies the two 16-bit register variables
- ;* mp16uH:mp16uL and mc16uH:mc16uL.
- ;* The result is placed in m16u3:m16u2:m16u1:m16u0.
- ;*
- ;* Number of words :14 + return
- ;* Number of cycles :153 + return
- ;* Low registers used :None
- ;* High registers used :7 (mp16uL,mp16uH,mc16uL/m16u0,mc16uH/m16u1,m16u2,
- ;* m16u3,mcnt16u)
- ;*
- ;***************************************************************************
- ;***** Subroutine Register Variables
- .def mc16uL =r16 ;multiplicand low byte
- .def mc16uH =r17 ;multiplicand high byte
- .def mp16uL =r18 ;multiplier low byte
- .def mp16uH =r19 ;multiplier high byte
- .def m16u0 =r18 ;result byte 0 (LSB)
- .def m16u1 =r19 ;result byte 1
- .def m16u2 =r20 ;result byte 2
- .def m16u3 =r21 ;result byte 3 (MSB)
- .def mcnt16u =r22 ;loop counter
- ;***** Code
- mpy16u: clr m16u3 ;clear 2 highest bytes of result
- clr m16u2
- ldi mcnt16u,16 ;init loop counter
- lsr mp16uH
- ror mp16uL
- m16u_1: brcc noad8 ;if bit 0 of multiplier set
- add m16u2,mc16uL ;add multiplicand Low to byte 2 of res
- adc m16u3,mc16uH ;add multiplicand high to byte 3 of res
- noad8: ror m16u3 ;shift right result byte 3
- ror m16u2 ;rotate right result byte 2
- ror m16u1 ;rotate result byte 1 and multiplier High
- ror m16u0 ;rotate result byte 0 and multiplier Low
- dec mcnt16u ;decrement loop counter
- brne m16u_1 ;if not done, loop more
- ret
- */
- ;***************************************************************************
- ;*
- ;* "bin2BCD16" - 16-bit Binary to BCD conversion
- ;*
- ;* This subroutine converts a 16-bit number (fbinH:fbinL) to a 5-digit
- ;* packed BCD number represented by 3 bytes (tBCD2:tBCD1:tBCD0).
- ;* MSD of the 5-digit number is placed in the lowermost nibble of tBCD2.
- ;*
- ;* Number of words :25
- ;* Number of cycles :751/768 (Min/Max)
- ;* Low registers used :3 (tBCD0,tBCD1,tBCD2)
- ;* High registers used :4(fbinL,fbinH,cnt16a,tmp16a)
- ;* Pointers used :Z
- ;*Courtesy of ATMEL
- ;***************************************************************************
- ;***** Subroutine Register Variables
- .equ AtBCD0 =13 ;address of tBCD0
- .equ AtBCD2 =15 ;address of tBCD1
- .def tBCD0 =r13 ;BCD value digits 1 and 0
- .def tBCD1 =r14 ;BCD value digits 3 and 2
- .def tBCD2 =r15 ;BCD value digit 4
- .def fbinL =r16 ;binary value Low byte
- .def fbinH =r17 ;binary value High byte
- .def cnt16a =r18 ;loop counter
- .def tmp16a =r19 ;temporary value
- ;***** Code
- bin2BCD16:
- ldi cnt16a,16 ;Init loop counter
- clr tBCD2 ;clear result (3 bytes)
- clr tBCD1
- clr tBCD0
- clr ZH ;clear ZH (not needed for AT90Sxx0x)
- bBCDx_1:lsl fbinL ;shift input value
- rol fbinH ;through all bytes
- rol tBCD0 ;
- rol tBCD1
- rol tBCD2
- dec cnt16a ;decrement loop counter
- brne bBCDx_2 ;if counter not zero
- ret ; return
- bBCDx_2:ldi r30,AtBCD2+1 ;Z points to result MSB + 1
- bBCDx_3:
- ld tmp16a,-Z ;get (Z) with pre-decrement
- ;----------------------------------------------------------------
- ;For AT90Sxx0x, substitute the above line with:
- ;
- ; dec ZL
- ; ld tmp16a,Z
- ;
- ;----------------------------------------------------------------
- subi tmp16a,-$03 ;add 0x03
- sbrc tmp16a,3 ;if bit 3 not clear
- st Z,tmp16a ; store back
- ld tmp16a,Z ;get (Z)
- subi tmp16a,-$30 ;add 0x30
- sbrc tmp16a,7 ;if bit 7 not clear
- st Z,tmp16a ; store back
- cpi ZL,AtBCD0 ;done all three?
- brne bBCDx_3 ;loop again if not
- rjmp bBCDx_1
- ;**************
- ;Name: unpack2r4r0
- ;Desc: A subroutine to takes as input five (5) packed BCD digits, located
- ; right justified, in the register combination r15:r14:r13. The result
- ; is fived unpacked BCD digits, placed in registers r0 through r4, in
- ; ascending order
- ; Inputs: r15:r14:r13 (BCD4?BCD3BCD2?BCD1BCD0). Uppe nibble of r15 == 0000
- ; Outputs: r4: 0000 BCD4
- ; r3: 0000 BCD3
- ; r2: 0000 BCD2
- ; r1: 0000 BCD1
- ; r0: 0000 BCD0
- ; Alters: r0?r4, SREG, ;* r10, r16
- ; calls: None
- ;*********************************************************************
- ;
- unpack2r4r0:
- ldi r16, 0x0f ;load mask into r10 for future use
- mov r10, r16
- mov r4, r15 ;moves BCD4 and mask
- and r4, r10 ;upper nibble
- mov r2, r14 ;moves BCD2
- and r2, r10 ;mask BCD3
- mov r3, r14 ;move BCD3
- swap r3 ;swap to lower position
- and r3, r10 ;and mask upper nibble
- mov r0, r13 ;move BCD0 and
- and r0, r10 ;mask BCD1
- mov r1, r13 ;moves BCD1
- swap r1 ;swap to lower position
- and r1, r10 ;mask upper nibble
- ;**************
- ;Name: to_ascii_r9r5
- ;Desc: A subroutine to takes as input five (5) unpacked BCD digits, located
- ; r0 to r4, and outputs the ascii equivalents to r5 to r9. The result
- ; is fived unpacked BCD digits (r0..r4), directly followed by their
- ; respective ascii equivalents in the next five registers (r5..r9).
- ;
- ; Inputs: r0 to r4 (unpacked BCD digits, with upper nibble == 0000)
- ;
- ; Outputs: r5 to r9 (ascii equivantlents to r0 to r4, respectively)
- ;
- ; Alters: r16, r9?r5, SREG
- ; calls: None
- ;*********************************************************************
- to_ascii_r9r5:
- ldi r16, 0x30 ;r0 to ascii to r5
- or r16, r0
- mov r5, r16
- ldi r16, 0x30 ;r1 to ascii to r6
- or r16, r1
- mov r6, r16
- ldi r16, 0x30 ;r2 to ascii to r7
- or r16, r2
- mov r7, r16
- ldi r16, 0x30 ;r3 to ascii to r8
- or r16, r3
- mov r8, r16
- ldi r16, 0x30 ;r4 to ascii to r9
- or r16, r4
- mov r9, r16
- ret
- ;*******************
- ; COURTESY OF SCOTT
- ;NAME: load_msg
- ;FUNCTION: Loads a predefined string msg into a specified diplay
- ; buffer.
- ;ASSUMES: Z = offset of message to be loaded. Msg format is
- ; defined below.
- ;RETURNS: nothing.
- ;MODIFIES: r16, Y, Z
- ;CALLS: nothing
- ;CALLED BY:
- ;********************************************************************
- ; Message structure:
- ; label: .db <buff num>, <text string/message>, <end of string>
- ;
- ; Message examples (also see Messages at the end of this file/module):
- ; msg_1: .db 1,"First Message ", 0 ; loads msg into buff 1, eom=0
- ; msg_2: .db 1,"Another message ", 0 ; loads msg into buff 1, eom=0
- ;
- ; Notes:
- ; a) The 1st number indicates which buffer to load (either 1, 2, or 3).
- ; b) The last number (zero) is an 'end of string' indicator.
- ; c) Y = ptr to disp_buffer
- ; Z = ptr to message (passed to subroutine)
- ;********************************************************************
- load_msg:
- ldi YH, high (dsp_buff_1) ; Load YH and YL as a pointer to 1st
- ldi YL, low (dsp_buff_1) ; byte of dsp_buff_1 (Note - assuming
- ; (dsp_buff_1 for now).
- lpm R16, Z+ ; get dsply buff number (1st byte of msg).
- cpi r16, 1 ; if equal to '1', ptr already setup.
- breq get_msg_byte ; jump and start message load.
- adiw YH:YL, 16 ; else set ptr to dsp buff 2.
- cpi r16, 2 ; if equal to '2', ptr now setup.
- breq get_msg_byte ; jump and start message load.
- adiw YH:YL, 16 ; else set ptr to dsp buff 2.
- get_msg_byte:
- lpm R16, Z+ ; get next byte of msg and see if '0'.
- cpi R16, 0 ; if equal to '0', end of message reached.
- breq msg_loaded ; jump and stop message loading operation.
- st Y+, R16 ; else, store next byte of msg in buffer.
- rjmp get_msg_byte ; jump back and continue...
- msg_loaded:
- ret
- //Line messages
- line1_message: .db 1, "0.0000V ", 0 ; test string for line #1
- line2_message: .db 2, "----------------", 0 ; test string for line #2.
- line3_message: .db 3, " autorun", 0 ; test string for line #3.
- line4_message: .db 3, "Hold ", 0 ;String for when line 3 is on hold
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement