Pages

Digital Oscilloscope


 
Introduction
Rational
We chose to construct a digital oscilloscope as our final project for this course.  The skills involved in this project proved a natural extension of some of the topics we covered in class.  We were able to learn more about the UART, AD sampling, memory indexing, keypad usage and many other topics we touched on earlier.
Functionality
Our Oscilloscope samples signals on a range of 0-4 V and displays them through HyperTerminal  using VT 100 codes.  The user, through the keypad, can select time per division calibrations ranging from .1sec to 2ms. The waveform is displayed along the user defined grid and peak-to-peak voltage and average voltage are printed out.

Organization
The basic program design flows from the keyboard denouncer.  By pressing C or D the user can increase or decrease the time per division on the scope.  Each press increases the calibration by one level (We used same levels as on the scopes in the lab ie. 50ms 20ms 10ms..).  By pressing A, the user initiates a a sampling cycle. The screen background with gridlines and labels is drawn and then Timer1 ISR is turned on with the appropriate compare match based on the time per division setting.  Each time Timer 1 triggers it reads in a sample and appends it to an array in memory.  It also updates some key registers used in calculating statistics such as Vpp and Vave. Note we could have added more functionality to Timer1 ISR, but we chose to keep it short and put the majority of our manipulations in the main code to avoid hard to find bugs and conflicts. We proceed to calculate Vpp and Vave.  Next we translate our voltage array into a number discrete levels so that it will map to the screen well.  A key part of this translation is a rounding we do by performing an integer division and then a multiplication and purposefully drop some less  significant bits.  We complete execution by drawing the waveform and some text labels and data to HyperTerminal using VT100 command codes for formating.

Block Diagram








To give the user a better explanation of our project we will discuss in detail some of the more interesting parts of our code.  All topics will not be covered since many, like the denouncer, have been well covered in previous labs and others are more tedious than illustrative.  Refer to the  Appendix 1 for code comments and listing.
VT 100 Formatting
The display to Hyper Terminal can be extensively formatted using the VT100 code set.  These codes, usually beginning with the escape character can do everything from setting screen location, to changing display modes, to erasing the screen.  We wrote a number of macros at the beginning of our code to execute these commands.  They are all very similar so lets look at BigDowner as an illustration.  This macro moves the curser a  distance down and to the right of the home position according to macro input.  We load the characters 0x27 and '[' into a string and follow it by the x and y change represented in ASCII and separated by character code 0x59.  We then terminate this string with the character code 'H'.  This will move the curser x spaces to the right from home position and y lines down.  
Time / Division Setting
This fairly complicated section of our code is used to set the time per division calibration as one of 6 predefined values .1 sec, 50 ms, 20 ms, 10 ms, 5ms,or 2 ms. Note we could to have gone to ever faster times but that would have required a faster CPU.  Set at 2 ms we have a compare match on Timer 1 or about 80 cycles, close to our Timer 1 length. Though we could have acquired one we felt we the change would not have allowed us to demonstrate anything new.
We used two strings to store Timer1 compare match values, low and high, for the 6 time per division calibrations and index through them by the appropriate factor depending on user selection.  Note we do not have to change the Timer1 prescaler since we do not see a time per division of less than .1 sec being useful for practical purposes.  The Timer 1 compare match values were calculated on the premise that if we had a input signal with wavelength equal to the time/division setting we would want a minimum of 10 samples per division.  These calculations yielded the following results

  OCR1AHOCR1AL
.1 sec / division9C40
.50 ms / division4E20
20 ms / division1F40
10 ms / division0FA0
5 ms / division07D0
2 ms / division0320

To display the appropriate text labels to the screen we created an array with a all the labels, 6 characters follows by a null terminator, in it.  We then indexed through this array by 7 and outputted to the UART, the UART code detecting the end of the label based on null termination
Sampling
We choose to sample all data in the Timer1 ISR and write it to RAM for manipulation later.  The main reason we did this is that the data manipulation, especially the formatting for the screen which involves a divide, takes many clock cycles.  If we tried to do this on the fly in the ISR we would have a dramatically higher minimum time per division.  
The data takes up 160 blocks since we have 80 samples and each point i has both a high and a low bit.  We would have liked to have taken more samples (we could have easily used external RAM to store them) but the limiting factor was the width of the Hyper Terminal screen under the VT100 instruction set.  We would have had t create a Java applet to obtain better resolution and given the complexity of the project already it would be too much.
Waveform Generation
In order to generate the waveform we took the extended data set of 80 points  from RAM which range from 0-4 volts and did an integer division to  by 200mv to receive a value between 0 and 20.  We then called our Convert_Screen subroutine and converted this number into two separate ASCII values. This subroutine is very similar to the decimal to ASCII subroutine we wrote in earlier labs, but writes to registers rather than memory.   These register values are fed into the Bigdowner macro along with an x index (also converted to ASCII) value to determine our screen location to print a point. We print a point using the standard UART commands and then loop back.  
Screen Background
The screen was drawn through extensive use of theVT100 formatting macros we wrote and described above.  We looped through and to draw the grid separating by 10 lines each way.  This gives us a resolution of 200mv and a range of 0-4 volts in the y direction and a resolution of up to .2 ms in the x direction with our lowest time per division setting.  Note we would have liked to have used the extended ASCII instruction set since it contains a number of boarder and line characters much cleaner looking than the basic ASCII set.  Unfortunately we determined that they are not compatible with the UART and the VT100 instruction set.  We were limited to ASCII character lower than 128. 
Vpp and Vave
We calculated Vpp and Vave to make the output of our oscilloscope more illustrative.  Vpp was found by updating a max and min register in the TIMER1 ISR each time a sample was taken and later subtracting the two values.  Similarly Vave was found by summing all the data points and then performing a divide.  We considered displaying Vrms, but it would contain divides, multiplies and a square root.  This would add a large amount of code overhead and our scope already has a significant delay due to the 9600 baud of the UART and the extensive output across it.  It was a question of how long we wanted the user to wait from pressing the sample button to seeing the waveform.

Our oscilloscope works well within a good accuracy. In general, on the x-axis our results are as good as our choice for our timer1 compare match value, which we chose rather carefully. The y-axis is divided into 200 mv, which gives us results within 100mv of the correct value. This is because if the result is in between a 200mv value, if it is 100mv, it goes to the next highest 200mv level, and it is under 100mv, it goes to the closest lower level. In terms of our time/division findings, we worked with a range between .1 sec/division to 2ms/division because, at best, the lowest we could go was 2 ms/division. This was due to the fact that during our ADC conversion we need 13 cycles to complete it. Hence, we needed to take this time into account, so that all the samples could be retrieved during the conversion. To be sure, of course, we double-checked our results with our findings on the computer scope card. For instance, for the same sine wave, we measured our Vpp to be about 2.69 volts, and the scope measured 2.72 volts for its Vpp, approximately a 1.1% error. A more illustrative look is presented below for your convenience.
Here is a sine wave on the Scope Card: Note Vpp of 1.9 Volts and wavelength of 10ms. And Below is the same sine wave on our Scope with Vpp 1.828 and wavelength of 10ms
Above is  a sine wave of  250 ms wavelength and Vpp of 1.9V.  And below is the same wave on our scope with 250 ms wavelength and 1.826 Vpp.
 
In terms of procedures and methods we would do differently, we would definitely
try to implement the LCD screen that we had originally intended to display the waveform. Due to time constraints and problems addressing the screen, of course, we decided on using HyperTerminal. Also, we would have tried adding some more features. For instance, we would have like to have determined the frequency of the  wave. This was extremely difficult to implement , and we felt that we had done our scope justice considering where we started, with an LCD screen that displayed garbage, and ending with a hyper term window displaying an almost exact replica of a waveform shown on the computer scope card. This project was truly challenging, yet it was also very rewarding feeling when you see the product you created does indeed work!
.

SOURCE CODE

Appendix 1: Code Listing
;***EE 476 FINAL PROJECT*****
;***DIGITAL OSCILLOSCOPE*****
;James Blanchette & Rahul Mukerjee

.include "c:\avrtools\appnotes\8535def.inc"


.def AnaLo =r25 ;holds low byte of ADC conversion value
.def AnaHi =r26 ;holds high byte of ADC conversion value
.def loop =r27 ;counter to see if we've done all the samples

.def set_time =r13 ;track to see if we've gone past all possible time/divs

.def temp2 =r22 ;second temp register for misc. calculations
.def isrdone =r24 ;register keeping track of when sampling is done
.def vrmshi =r9 ;holds high byte of the average voltage value
.def vrmslo =r14 ;holds low byte of the average voltage value
.def save =r1 ;used for saving the status register during ISR
.def isrcount=r2 ;counter in ISR for tracking number of samples taken
.def YLo =r3 ;holds low byte of y position during screen conversion
.def YHi =r4 ;holds high byte of y position during screen conversion
.def vrmsmed =r5
.def minL =r6 ;temp storage for (low) minimum voltage
.def minH =r7 ;temp storage for (high) minimum voltage
.def maxL =r8 ;temp storage for (low) maximum voltage
.def maxH =r12 ;temp storage for (high) maximum voltage

.def key =r16 ;encoding for the button pressed on the keypad
.def state =r20 ;current state of the debouncer
.def press =r19 ;hold raw press value
.def butnum =r18 ;final press value
.def time1 =r17 ;counts till 30 ms interrupt
.def temp =r21 ;temporary register


.def TXflash =r23 ;flag to check if string is in flash
.def TXbusy =r29 ;trans busy flag;note duplicate but pushed
.def RXchar =r28 ;received char

;;***** Subroutine Register Variables
;DIVIDE
.def d16s =r28 ;sign register
.def drem16sL =r9 ;remainder low byte
.def drem16sH =r11 ;remainder high byte
.def drem16s3 =r10
.def dres16s2=r16 ;result middle byte
.def dres16s1=r18 ;result highest byte
.def dres16s3=r17 ;result lowest byte
.def dd16s2 =r16 ;dividend low byte
.def dd16s1 =r18 ;dividend high byte
.def dd16s3 =r17 ;dividend zero byte
.def dv16sL =r19 ;divisor low byte
.def dv16sH =r20 ;divisor high byte
.def dcnt16s =r31 ;loop counter



;Equates
.equ nopress=1 ;state when no button has been pressed
.equ maybe=2 ;detected a button once so far
.equ press=3 ;detected a button pressed at least twice in a row
.equ bounce=4 ;detected a button release once

.equ key0 =0b11101011
.equ key1 =0b01110111
.equ key2 =0b01111011
.equ key3 =0b01111101
.equ key4 =0b10110111
.equ key5 =0b10111011
.equ key6 =0b10111101
.equ key7 =0b11010111
.equ key8 =0b11011011
.equ key9 =0b11011101
.equ keyA =0b01111110
.equ keyB =0b10111110
.equ keyC =0b11011110
.equ keyD =0b11101110
.equ keyILL =0xff ;no button (or multiple buttons) pressed on keypad

.equ baud96 =25 ;Specify constants for switches:
.equ azero ='0'
.equ maxtime =5
.equ mintime=0

.MACRO downer
ldi ZH, high(xy)
ldi ZL, low(xy)
ldi temp, 27
st Z, temp
adiw ZL,1
ldi temp, '['
st Z,temp
adiw ZL,1
ldi temp, @0
st Z,temp
adiw ZL,1
ldi temp,59
st Z,temp
adiw ZL,1
ldi temp, @1
st Z,temp
adiw ZL,1
ldi temp,'H'
st Z,temp
adiw ZL,1
ldi temp,0x00
st Z,temp
.ENDMACRO

;macro that sets position on screen based on x and y coordinates
;and places cursor relative to the upper left-hand corner of the
;hyperterminal, (i.e. relative to (0,0))
;this macro takes parameters in registers
.MACRO bigdowner
ldi ZH, high(xy)
ldi ZL, low(xy)
ldi temp, 27
st Z, temp
adiw ZL,1
ldi temp, '['
st Z,temp
adiw ZL,1
mov temp, @0
st Z,temp
adiw ZL,1
mov temp, @1
st Z,temp
adiw ZL,1
ldi temp,59
st Z,temp
adiw ZL,1
mov temp, @2
st Z,temp
adiw ZL,1
mov temp, @3
st Z,temp
adiw ZL,1
ldi temp,'H'
st Z,temp
adiw ZL,1
ldi temp,0x00
st Z,temp

.ENDMACRO


;macro that sets position on screen based on x and y coordinates
;and places cursor relative to the upper left-hand corner of the
;hyperterminal, (i.e. relative to (0,0))
;this macro takes in hard-coded values as opposed to values
;already pre-defined in registers

.MACRO bigdowners
ldi ZH, high(xy) ;store macro in text string
ldi ZL, low(xy) ;for the UART to understand
ldi temp, 27 ;27 is 'Esc'
st Z, temp
adiw ZL,1
ldi temp, '['
st Z,temp
adiw ZL,1
ldi temp, @0
st Z,temp
adiw ZL,1
ldi temp, @1
st Z,temp
adiw ZL,1
ldi temp,59
st Z,temp
adiw ZL,1
ldi temp, @2
st Z,temp
adiw ZL,1
ldi temp, @3
st Z,temp
adiw ZL,1
ldi temp,'H'
st Z,temp
adiw ZL,1
ldi temp,0x00
st Z,temp

.ENDMACRO

;macro used to completely erase the hyperterminal window
.MACRO clearscreen
ldi ZH, high(clr)
ldi ZL, low(clr)
ldi temp, 27
st Z, temp
adiw ZL,1
ldi temp, '['
st Z,temp
adiw ZL,1
ldi temp,'2'
st Z,temp
adiw ZL,1
ldi temp,'J'
st Z,temp
adiw ZL,1
ldi temp,0x00
st Z,temp

.ENDMACRO
;*********************************************************
.dseg

;string of sampled wave generation samples
samples: .byte 160 ;array storing our digital samples
xy: .byte 10 ;array storing xy coordinate string off macro
value: .byte 5 ;temp array for storage voltages after hex-to-ascii conv.
clr: .byte 5 ;array storing clr string off macro


;**********************************************************
.cseg
.org $0000
rjmp RESET
reti
reti
reti
reti
reti
rjmp TIMER1A
reti
reti
rjmp TIMER0
reti
rjmp RXdone
rjmp TXempty
rjmp TXdone
reti
reti
reti

;Here are the various characters and labels seen
;on the screen; the "star" is what we use for our
;voltage samples. The other labels are pretty
;self-explanatory.

star : .db "*",0x00
timediv : .db "Time/Div:", 0x00
amp : .db "Vave:", 0x00
vpp : .db "Vpp:", 0x00
dc: .db "DC off:",0x00
grid1: .db "|",0x00
grid2: .db "+",0x00
grid3: .db "-",0x00
mv: .db " mv",0x00

;time1 and time2 store the high and low byte, respectively
;of the sampling rate, which we use in our OCR1A compare match

time1: .db 0x9C,0x4E,0x1F,0x0F,0x00,0x00
time2: .db 0x40,0x20,0x40,0xA0,0xC8,0x50
time_labels:.db ".1 sec",0x00
.db " 50 ms",0x00
.db " 20 ms",0x00
.db " 10 ms",0x00
.db " 5 ms",0x00
.db " 2 ms",0x00

RESET: ldi temp, low(RAMEND)
out SPL, temp
ldi temp, high(RAMEND)
out SPH, temp

;setup timer 1 for reset on compare match at full clock rate
ldi temp,0b00001000
out TCCR1B,temp
ldi temp, 0xf4 ;set the match A register to
out OCR1AH, temp ;62500 since 62500*16microsec=1sec
ldi temp, 0x24 ;62500 = 0xf424
out OCR1AL, temp
;setup timer 0 for overflow once every mSec
ldi temp, 3 ;prescale timer by 64
out TCCR0, temp
ldi temp,256-62 ;preload timer since
out TCNT0, temp ;62.5 x (64x.25) microSec = 1.0 mSec.


ldi temp, 0b00010001
out TIMSK, temp

;setup UART
ldi temp, 0b10111000
out UCR, temp

;set baud rate to 9600 baud
ldi temp, baud96
out UBRR, temp

clr TXbusy ;start our not busy on transmission
clr RXchar


;initial conditions
ldi time1, 30 ;set time1 for 30 mSec
ldi state, nopress ;set initial state to nopress
clr temp
mov set_time,temp
clr press


;set up analog converter to read channel zero
ldi temp, 0
out ADMUX, temp


;set up PORTB for diagnostic LEDs
ldi temp,0b10101010 ;Turn on four LEDs
out PORTB,temp
ldi temp,0xFF ;Make port B pin all outputs
out DDRB,temp
ldi temp,0xFF ;Turn off all LEDs
out PORTB,temp

sei
rcall SET_TIME_INITIAL ;sets the initial time/div to 0.1 sec

;***************************
Sched:
tst time1 ;test for first task ready
brne Sched ;if not then loop
rcall debounce

tskend: rjmp Sched


;***************************
debounce:
ldi time1, 30 ;reset for another 30 mSec
cpi state, nopress
breq _nopress
cpi state, maybe
breq _maybe
cpi state, press
breq _press
cpi state, bounce
breq _bounce
error:
rjmp error


_nopress:
rcall keybd
cpi butnum, keyILL ;check if nothing is pressed
breq _nopressY
mov press, butnum
ldi state, maybe
_nopressY:
ret


_maybe: rcall keybd
mov temp,butnum
com temp
cp press, butnum
breq _maybeY
ldi state, nopress
ret
_maybeY:rcall gotkey
ldi state, press
ret


_press: rcall keybd
cp press, butnum
breq _pressY
ldi state, bounce
_pressY:ret


_bounce:rcall keybd
cp press, butnum
breq _bounceY
ldi state, nopress
ret
_bounceY:
ldi state, press
ret


;***************************
GOTKEY: cpi press,0x0a
breq TAKE_SAMPLE ;ready to take a sample
cpi press,0x0c
breq SET_TIME_UP_NOW ;set time/div higher
cpi press,0x0d
breq SET_TIME_DOWN_NOW ;set time/div lower
GOTEND: ret

SET_TIME_UP_NOW:
rjmp SET_TIME_UP ;used because branch was too long


SET_TIME_DOWN_NOW:
rjmp SET_TIME_DOWN ;again branch jump was too long


TAKE_SAMPLE:
clearscreen
clr vrmslo
clr vrmsmed


ldi ZL, low(clr)
ldi ZH, high(clr)
ld r0, Z
out UDR, r0
clr TXflash ;string is NOT in flash memory
ser TXbusy ;and set the TX busy flag
sbi UCR, UDRIE ;enable the TXempty interrupt
rcall TXwait ;chill until done

rcall DRAW_SCREEN
rcall set_time_initial
ldi ZL, low(samples)
ldi ZH, high(samples)

clr isrdone
ldi temp,80 ;set isrdone=0
mov isrcount,temp ;set isr count=80

ldi temp,0x0b00111111
mov minH,temp
mov minL,temp
clr maxH
clr maxL
;setup timer 1 for reset on compare match at full clock rate
ldi temp, 0b00001001
out TCCR1B, temp ;turn on timer1 isr

S_LOOP: ldi temp,1 ;loop until isrdone=1
cp isrdone,temp ;if temp equals isrdone, it means we're done sampling
brne S_LOOP ;so we jump out of the S-LOOP and turn off timer1a

ldi temp, 0b00001000 ;timer1a is off
out TCCR1B, temp

ldi loop,1

rjmp S_START

S_EXIT: rcall OUT_VPP ;once finished sampling, we are ready to get the
rcall OUT_RMS ;peak-to-peak and average voltages of the waveform
;rcall OUT_DC
ret


S_START:cpi loop,79 ;here we check to see if the all samples have been
breq S_EXIT ;read out of our samples array
clr temp
cli


ldi ZL, low(samples) ;load the samples into Z; these samples are already
ldi ZH, high(samples) ;stored in memory after the ADC conversion
add ZL,loop
adc ZH,temp
add ZL,loop
adc ZH,temp
ld AnaHi,Z
adiw ZL,1
ld AnaLo,Z


inc loop
push r9
push d16s ;due to duplicate usage of registers we decided
push dres16s2 ;to push our divide routine registers noticing
push dres16s1 ;that they are only used here to format
push dres16s3 ;the screen to the correct voltage.
push dd16s2
push dd16s1
push dd16s3
push dv16sL
push dv16sH
push dcnt16s
ldi dd16s1,0
mov dd16s2,AnaHi
mov dd16s3,AnaLo

ldi dv16sL,200 ;divide by 200, based upon the number voltage
ldi dv16sH,0 ;divisions we could fit on the hyperterminal window

rcall div16s
mov temp, dres16s3
neg temp
subi temp,-20
pop d16s
pop dres16s2 ;pop the divide registers once complete with division
pop dres16s1
pop dres16s3
pop dd16s2
pop dd16s1
pop dd16s3
pop dv16sL
pop dv16sH
pop dcnt16s
pop r9

;cli

rcall CONVERT_SCREEN ;converts the screen to the right y-coordinates
mov YLo,AnaLo ;based on our "bigdowner" macro
mov YHi,AnaHi

mov temp,loop
rcall CONVERT_SCREEN ;converts the screen to the right x-coordinates


;now call "bigdowner" to place voltage sample at the proper position
bigdowner YHi,YLo,AnaHi,AnaLo
ldi ZL, low(xy)
ldi ZH, high(xy)
ld r5, Z
out UDR, r5
clr TXflash ;string is NOT in flash memory
ser TXbusy ;and set the TX busy flag
sbi UCR, UDRIE ;enable the TXempty interrupt
sei
rcall TXwait ;chill until done


ldi ZL, LOW(star<<1) ;shifted becuase pgm memory is words
ldi ZH, HIGH(star<<1)
lpm ;put the char in r0
out UDR, r0 ;put the character in the UART buffer
ser TXflash ;string is in flash memory
ser TXbusy ;and set the TX busy flag
sbi UCR, UDRIE ;enable the TXempty interrupt

rcall TXwait ;chill until done

rjmp S_START


SET_TIME_UP:

ldi temp,5
cp set_time,temp ;check to see if we can go to a lower
;time/division value
brne SET_UP_SKIP ;if so, we go display that value
ret

SET_UP_SKIP:
inc set_time
ldi ZL, low(time1<< 1) ;display current time/divison
ldi ZH, high(time1<< 1) ;based on the index provided by "set_time"
ldi temp,0
add ZL,set_time
adc ZH,temp
lpm
mov temp,r0 ;the index provided by "set_time" corresponds
out OCR1AH,temp ;to the address pointed to by Z, which is the
;compare-match value of the time/division
ldi ZL, low(time2<< 1)
ldi ZH, high(time2<< 1)
ldi temp,0
add ZL,set_time ;As a result, OCR1AH and OCR1AL get the
adc ZH,temp ;the values stored in time2 and time1, respec.
lpm
mov temp,r0
out OCR1AL, temp

in temp,OCR1AL
com temp
out PORTB,temp

;note, here is an example where we hard-code in the position we want
bigdowners 0x32,0x34,0x31,0x31
ldi ZL, low(xy)
ldi ZH, high(xy)
ld r0, Z
out UDR, r0
clr TXflash ;string is NOT in flash memory
ser TXbusy ;and set the TX busy flag
sbi UCR, UDRIE ;enable the TXempty interrupt
rcall TXwait ;chill until done


ldi ZL, low(time_labels<< 1) ;routine to display the time_label
ldi ZH, high(time_labels<< 1) ;we chose from "SET_UP_SKIP"
mov temp,set_time
clc
rol temp
rol temp
rol temp
subi temp,1

clr temp2
add ZL,temp
adc ZH,temp2

lpm ;put the char in r0
out UDR, r0 ;put the character in the UART buffer
ser TXflash ;string is in flash memory
ser TXbusy ;and set the TX busy flag
sbi UCR, UDRIE ;enable the TXempty interrupt
rcall TXwait


ret


SET_TIME_DOWN: ;this routine is similar to the
ldi temp,0 ;"SET_TIME_UP" routine mentioned above
cp set_time,temp ;refer to those comments for more detail
brne SET_UP_DOWN_SKIP ;essentially, here we do opposite by
ret ;checking if there are any higher time/div vals.

SET_UP_DOWN_SKIP:
dec set_time
ldi ZL, low(time1<< 1) ;if higher time/div values, then display them
ldi ZH, high(time1<< 1)
ldi temp,0
add ZL,set_time
adc ZH,temp
lpm
mov temp,r0
out OCR1AH,temp

ldi ZL, low(time2<< 1)
ldi ZH, high(time2<< 1)
ldi temp,0
add ZL,set_time
adc ZH,temp
lpm
mov temp,r0
out OCR1AL, temp

com temp
out PORTB,temp


bigdowners 0x32,0x34,0x31,0x31
ldi ZL, low(xy)
ldi ZH, high(xy)
ld r0, Z
out UDR, r0
clr TXflash ;string is NOT in flash memory
ser TXbusy ;and set the TX busy flag
sbi UCR, UDRIE ;enable the TXempty interrupt
rcall TXwait ;chill until done


ldi ZL, low(time_labels<< 1) ;print selected time/div label
ldi ZH, high(time_labels<< 1)
mov temp,set_time
clc
rol temp
rol temp
rol temp
cpi temp,0
breq Z_SKIP
subi temp,1

Z_SKIP: clr temp2
add ZL,temp
adc ZH,temp2

lpm ;put the char in r0
out UDR, r0 ;put the character in the UART buffer
ser TXflash ;string is in flash memory
ser TXbusy ;and set the TX busy flag
sbi UCR, UDRIE ;enable the TXempty interrupt
rcall TXwait

ret

SET_TIME_INITIAL: ;rountine that hard codes in the label
;for .1 sec as a default time/div
ldi ZL, low(time1<< 1)
ldi ZH, high(time1<< 1)
ldi temp,0
add ZL,set_time
adc ZH,temp
lpm
mov temp,r0
out OCR1AH,temp

ldi ZL, low(time2<< 1)
ldi ZH, high(time2<< 1)
ldi temp,0
add ZL,set_time
adc ZH,temp
lpm
mov temp,r0
out OCR1AL, temp

bigdowners 0x32,0x34,0x31,0x31
ldi ZL, low(xy)
ldi ZH, high(xy)
ld r0, Z
out UDR, r0
clr TXflash ;string is NOT in flash memory
ser TXbusy ;and set the TX busy flag
sbi UCR, UDRIE ;enable the TXempty interrupt
rcall TXwait ;chill until done


ldi ZL, low(time_labels<< 1)
ldi ZH, high(time_labels<< 1)
mov temp,set_time
clc
rol temp
rol temp
rol temp
cpi temp,0
breq M_SKIP
subi temp,1

M_SKIP: clr temp2
add ZL,temp
adc ZH,temp2

lpm ;put the char in r0
out UDR, r0 ;put the character in the UART buffer
ser TXflash ;string is in flash memory
ser TXbusy ;and set the TX busy flag
sbi UCR, UDRIE ;enable the TXempty interrupt
rcall TXwait

ret

OUT_VPP:cli
mov AnaHi,maxH ;move values obtained in timer1 ISR
mov AnaLo,maxL

sub AnaLo,minL ;perform peak-to-peak calculation
sbc AnaHi,minH ;based on max and min values



rcall hexascii ;do conversion from hexademical to ascii

;position of Vpp label
bigdowners 0x32,0x34,0x35,0x30
ldi ZL, low(xy)
ldi ZH, high(xy)
ld r0, Z
out UDR, r0
clr TXflash ;string is NOT in flash memory
ser TXbusy ;and set the TX busy flag
sbi UCR, UDRIE ;enable the TXempty interrupt
sei
rcall TXwait ;chill until done
cli
;write out ascii value to designated position
ldi ZL, LOW(value) ;shifted becuase pgm memory is words
ldi ZH, HIGH(value)
ld r0,Z ;put the char in r0
out UDR, r0 ;put the character in the UART buffer
clr TXflash ;string is in flash memory
ser TXbusy ;and set the TX busy flag
sbi UCR, UDRIE ;enable the TXempty interrupt
sei
rcall TXwait ;chill until done
;write out "millivolt" label
cli
ldi ZL, LOW(mv<<1) ;shifted becuase pgm memory is words
ldi ZH, HIGH(mv<<1)
lpm ;put the char in r0
out UDR, r0 ;put the character in the UART buffer
ser TXflash ;string is in flash memory
ser TXbusy ;and set the TX busy flag
sbi UCR, UDRIE ;enable the TXempty interrupt
sei
rcall TXwait ;chill until done
ret

OUT_RMS:cli ;average voltage approximation
;done through a series of right-rotations,
clc ;(i.e. divisions,)
ror vrmshi
ror vrmsmed
ror vrmslo

clc
ror vrmshi
ror vrmsmed
ror vrmslo

clc
ror vrmshi
ror vrmsmed
ror vrmslo

clc
ror vrmshi
ror vrmsmed
ror vrmslo

clc
ror vrmshi
ror vrmsmed
ror vrmslo

clc
ror vrmshi
ror vrmsmed
ror vrmslo

mov AnaLo,vrmslo
mov AnaHi,vrmsmed

com vrmsmed
out PORTB,vrmsmed

rcall hexascii ;do conversion from hexademical to ascii

;again, this is the position of Vave on screen
bigdowners 0x32,0x34,0x33,0x32
ldi ZL, low(xy)
ldi ZH, high(xy)
ld r0, Z
out UDR, r0
clr TXflash ;string is NOT in flash memory
ser TXbusy ;and set the TX busy flag
sbi UCR, UDRIE ;enable the TXempty interrupt
sei
rcall TXwait ;chill until done
cli
;obtain the appropriate ascii value from the value array
ldi ZL, LOW(value) ;shifted becuase pgm memory is words
ldi ZH, HIGH(value)
ld r0,Z ;put the char in r0
out UDR, r0 ;put the character in the UART buffer
clr TXflash ;string is in flash memory
ser TXbusy ;and set the TX busy flag
sbi UCR, UDRIE ;enable the TXempty interrupt
sei
rcall TXwait ;chill until done
;the millivolt label
cli
ldi ZL, LOW(mv<<1) ;shifted becuase pgm memory is words
ldi ZH, HIGH(mv<<1)
lpm ;put the char in r0
out UDR, r0 ;put the character in the UART buffer
ser TXflash ;string is in flash memory
ser TXbusy ;and set the TX busy flag
sbi UCR, UDRIE ;enable the TXempty interrupt
sei
rcall TXwait ;chill until done
ret

;Note that this commented out section was supposed to represent the
;DC-offset voltage. We wished to add it in the end as an extra
;feature, but were unsuccessful to completely implement it, due to
;time,hence we left our attempt at this in the commented section below.
;OUT_DC: cli
;
;
; sub maxL,minL
; sbc maxH,minH
; mov AnaHi,maxH
; mov AnaLo,maxL
;
;
; rcall hexascii ;do conversion from hexademical to ascii
;
; bigdowners 0x32,0x34,0x37,0x31
;
; ldi ZL, low(xy)
; ldi ZH, high(xy)
; ld r0, Z
; out UDR, r0
; clr TXflash ;string is NOT in flash memory
; ser TXbusy ;and set the TX busy flag
; sbi UCR, UDRIE ;enable the TXempty interrupt
; sei
; rcall TXwait ;chill until done
; cli
; ldi ZL, LOW(value) ;shifted becuase pgm memory is words
; ldi ZH, HIGH(value)
; ld r0,Z ;put the char in r0
; out UDR, r0 ;put the character in the UART buffer
; clr TXflash ;string is in flash memory
; ser TXbusy ;and set the TX busy flag
; sbi UCR, UDRIE ;enable the TXempty interrupt
; sei
; rcall TXwait ;chill until done
;
; cli
; ldi ZL, LOW(mv<<1) ;shifted becuase pgm memory is words
; ldi ZH, HIGH(mv<<1)
; lpm ;put the char in r0
; out UDR, r0 ;put the character in the UART buffer
; ser TXflash ;string is in flash memory
; ser TXbusy ;and set the TX busy flag
; sbi UCR, UDRIE ;enable the TXempty interrupt
; sei
; rcall TXwait ;chill until done
; ret


;***************************
keybd: ldi temp, 0x0f ;set lower four lines to output
out DDRC, temp
ldi temp, 0xf0 ;and turn on the pullups on the inputs
out PORTC, temp
nop ;Need some time for the pullups to
nop ;charge the port pins
nop
nop
in temp, PINC ;read the high nibble
mov key, temp ;and store it (with zeros in the low nibble)


ldi temp, 0xf0 ;set upper four lines to outputs
out DDRC, temp
ldi temp, 0x0f ;and turn on pullups on the inputs
out PORTC, temp

nop ;As before wait for the pin to charge
nop
nop
nop
in temp, PINC ;read the low nibble
or key, temp ;combine to make key code

;At the point the raw key code should have exactly one zero each in
;the lower and upper nibbles. Any other number of zeros indicates
;either no-button pressed or multiple-button pressed.

;to avoid using Z (it is used by the timer1 interrupt) we do a series
;of comparisons instead of a table lookup

;testing stuff


clr butnum
cpi key, key0
breq tbldone

inc butnum
cpi key, key1
breq tbldone

inc butnum
cpi key, key2
breq tbldone

inc butnum
cpi key, key3
breq tbldone

inc butnum
cpi key, key4
breq tbldone

inc butnum
cpi key, key5
breq tbldone

inc butnum
cpi key, key6
breq tbldone

inc butnum
cpi key, key7
breq tbldone

inc butnum
cpi key, key8
breq tbldone

inc butnum
cpi key, key9
breq tbldone

inc butnum
cpi key, keyA
breq tbldone

inc butnum
cpi key, keyB
breq tbldone

inc butnum
cpi key, keyC
breq tbldone

inc butnum
cpi key, keyD
breq tbldone
ldi butnum, keyILL

tbldone:ret
;********************************************************
CONVERT_SCREEN:
push temp2
ldi temp2,0
CONVERT_LOOP:
cpi temp,10 ;check if current position is less than ten
brlt CONVERT_DONE
subi temp,10
inc temp2
rjmp CONVERT_LOOP

CONVERT_DONE: subi temp,-0x30 ;conversion to ascii so it can be interpretted by
subi temp2,-0x30 ;UART for correct display to screen
mov AnaLo,temp
mov AnaHi,temp2
pop temp2
ret
;***************************************************************
;TIMER0 ISR
;Enters every 1.0 mSec

TIMER0:push temp
push save
in save, SREG
ldi temp,256-62
out TCNT0, temp ; keeps clock ticking at 1 mSec
tst time1
breq TIMEREXIT
dec time1


TIMEREXIT:
out SREG, save
pop temp
pop save
reti ;back to backgound tasks


;*****************************************************

TIMER1A:
;**** timer 1 compare A match 1/sec

in save, SREG
push temp

ldi temp, 0b11000101 ;start A to D conversion
out ADCSR, temp


ISRLOOP:in temp, ADCSR ;wait for A to D done
andi temp, 0b01000000 ;by checking ADSC bit
brne ISRLOOP ;this bit is cleared by the AtoD hardware
in AnaLo, ADCL ;read the voltage
in AnaHi, ADCH
lsl AnaLo ;shift to the left twice
rol AnaHi ;to multiply by four so
lsl AnaLo ;final hex value represents
rol AnaHi ;number in mV
mov temp,isrcount



cpi temp,75
brge ISR_J
cpi temp,11
brlt ISR_J

ldi temp,0
add vrmslo,AnaLo
adc vrmsmed,AnaHi
adc vrmshi,temp

cp maxL,AnaLo ;test to see whether or not
cpc maxH,AnaHi ;there is a new maximum or
brge MAX_SKIP ;new minimum sample
mov maxL,AnaLo
mov maxH,AnaHi
MAX_SKIP:
cp minL,AnaLo
cpc minH,AnaHi
brge MIN_SKIP
rjmp ISR_J
MIN_SKIP:mov minL,AnaLo
mov minH,AnaHi

ISR_J:
st Z, AnaHi ;store the high ADC value into Z
adiw ZL,1 ;increment the address by 1
st Z, AnaLo ;and store the low ADC value into Z
adiw ZL,1

dec isrcount
tst isrcount ;check to see if you have taken the
brne EXIT ;final sample
ldi temp,1
mov isrdone,temp

EXIT: pop temp
out SREG, save
reti

;**********************************************
div16s: clr d16s ;clear sign register
tst dd16s1 ;if dividend negative, set sign register
brge div_0
ser d16s
com dd16s1 ; change sign of dividend
com dd16s2
subi dd16s2,low(-1)
sbci dd16s2,high(-1)


div_0: clr dd16s3 ;make 24-bit #

clr drem16sL ;sets remainder to 0
clr drem16sH

ldi dcnt16s, 0x18 ;sets counter at 24

div_1: clc

rol dres16s3 ;shift dividend left
rol dres16s2
rol dres16s1

rol drem16sL ;shift remainder left + carry in from dividend
rol drem16sH

sub drem16sL, dv16sL ;remainder = remainder - divisor
sbc drem16sH, dv16sH

brcc div_2 ;negative?
add drem16sL, dv16sL ; restore remaidner
adc drem16sH, dv16sH
rjmp div_3

div_2: ori dres16s3, 0x01 ;positive --> Q0 = 1

div_3: dec dcnt16s ;count--
brne div_1

tst d16s
brge div_end
com dres16s2 ; change sign of result
com dres16s3
subi dres16s3,low(-1)
sbci dres16s2,high(-1)


div_end: ret

;*****************************
;interrupt routines

; UART needs a character
TXempty:in save, SREG ;save processor status
tst TXflash ;Is the string in flash memory?
breq TXram ;If not, it is in RAM
inc ZL ;get the next char from flash
lpm ;and put it in r0
rjmp TXfls
TXram: inc ZL ;get the next char from RAM
ld r0,Z
TXfls: tst r0 ;if char is zero then exit
breq TXend
out UDR, r0 ;otherwise transmit it
rjmp TXexit ;exit until next char
TXend: clr TXbusy ;no more chars
cbi UCR, UDRIE ;clear the TXempty interrupt
TXexit: out SREG, save ;restore proc status
reti ;back to pgm

; TX done -- buffer is empty -- unused here
TXdone: in save, SREG ;save processor status
out SREG, save ;restore proc status
reti ;back to pgm

; UART read a character
RXdone: in save, SREG ;save processor status
in RXchar, UDR ;get the character
out SREG, save ;restore proc status
reti ;back to pgm

;*****************************
;subroutine

TXwait: tst TXbusy ;now wait for the tranmission to finish
brne TXwait
ret

;********************************************************************************


;These next few routines draws the grid for the scope, so that voltage readings
;can be seen more clearly and accurately. The screen is drawn using the "grid"
;1,2,and 3 characters as listed in the labels at the beginning. The "FIRST_LINE"
;through to the "FIFTH_LINE" draw the horizontal grid lines and the "FIRST_VLINE"
;to the "NINTH_VLINE" draw the vertical grid lines, all at their respective positions.

;Notice how in between reading grid lines onto the screen that we clear and set
;interrupts repeatedly, because without these cli's and sei's, other routines were
;apparently interrupting as the grid line was printed, which resulted in random
;junk being displayed, occasionally. The appropriate calls of cli and sei remedies
;this minor problem

DRAW_SCREEN:

clr r5 ;used r5 because temp kept on getting changed
mov temp,r5
ldi temp2,0x30
mov YHi,temp2
ldi temp2,0x30
mov YLo,temp2

FIRST_LINE:
cli
rcall CONVERT_SCREEN

bigdowner YHi,YLo,AnaHi,AnaLo
ldi ZL, low(xy)
ldi ZH, high(xy)
ld r0, Z
out UDR, r0
clr TXflash ;string is NOT in flash memory
ser TXbusy ;and set the TX busy flag
sbi UCR, UDRIE ;enable the TXempty interrupt
sei
rcall TXwait ;chill until done

cli
ldi ZL, LOW(grid3<<1) ;shifted becuase pgm memory is words
ldi ZH, HIGH(grid3<<1)
lpm ;put the char in r0
out UDR, r0 ;put the character in the UART buffer
ser TXflash ;string is in flash memory
ser TXbusy ;and set the TX busy flag
sbi UCR, UDRIE ;enable the TXempty interrupt
sei
rcall TXwait ;chill until done

cli

inc r5 ;increment until whole horizontal line is filled up
mov temp,r5
cpi temp,80
brne FIRST_LINE

;next line
clr r5
mov temp,r5
ldi temp2,0x30
mov YHi,temp2
ldi temp2,0x35
mov YLo,temp2

SECOND_LINE:
cli
rcall CONVERT_SCREEN
bigdowner YHi,YLo,AnaHi,AnaLo
ldi ZL, low(xy)
ldi ZH, high(xy)
ld r0, Z
out UDR, r0
clr TXflash ;string is NOT in flash memory
ser TXbusy ;and set the TX busy flag
sbi UCR, UDRIE ;enable the TXempty interrupt
sei
rcall TXwait ;chill until done

cli
ldi ZL, LOW(grid3<<1) ;shifted becuase pgm memory is words
ldi ZH, HIGH(grid3<<1)
lpm ;put the char in r0
out UDR, r0 ;put the character in the UART buffer
ser TXflash ;string is in flash memory
ser TXbusy ;and set the TX busy flag
sbi UCR, UDRIE ;enable the TXempty interrupt
sei
rcall TXwait ;chill until done

cli

inc r5
mov temp,r5
cpi temp,80
brne SECOND_LINE

clr r5
mov temp,r5
ldi temp2,0x31
mov YHi,temp2
ldi temp2,0x30
mov YLo,temp2

THIRD_LINE:
cli
rcall CONVERT_SCREEN

bigdowner YHi,YLo,AnaHi,AnaLo
ldi ZL, low(xy)
ldi ZH, high(xy)
ld r0, Z
out UDR, r0
clr TXflash ;string is NOT in flash memory
ser TXbusy ;and set the TX busy flag
sbi UCR, UDRIE ;enable the TXempty interrupt
sei
rcall TXwait ;chill until done

cli
ldi ZL, LOW(grid3<<1) ;shifted becuase pgm memory is words
ldi ZH, HIGH(grid3<<1)
lpm ;put the char in r0
out UDR, r0 ;put the character in the UART buffer
ser TXflash ;string is in flash memory
ser TXbusy ;and set the TX busy flag
sbi UCR, UDRIE ;enable the TXempty interrupt
sei
rcall TXwait ;chill until done

cli

inc r5
mov temp,r5
cpi temp,80
brne THIRD_LINE


clr r5
mov temp,r5
ldi temp2,0x31
mov YHi,temp2
ldi temp2,0x35
mov YLo,temp2

FOURTH_LINE:
cli
rcall CONVERT_SCREEN

bigdowner YHi,YLo,AnaHi,AnaLo
ldi ZL, low(xy)
ldi ZH, high(xy)
ld r0, Z
out UDR, r0
clr TXflash ;string is NOT in flash memory
ser TXbusy ;and set the TX busy flag
sbi UCR, UDRIE ;enable the TXempty interrupt
sei
rcall TXwait ;chill until done

cli
ldi ZL, LOW(grid3<<1) ;shifted becuase pgm memory is words
ldi ZH, HIGH(grid3<<1)
lpm ;put the char in r0
out UDR, r0 ;put the character in the UART buffer
ser TXflash ;string is in flash memory
ser TXbusy ;and set the TX busy flag
sbi UCR, UDRIE ;enable the TXempty interrupt
sei
rcall TXwait ;chill until done

cli
inc r5
mov temp,r5
cpi temp,80
brne FOURTH_LINE


clr r5
mov temp,r5
ldi temp2,0x32
mov YHi,temp2
ldi temp2,0x30
mov YLo,temp2

FIFTH_LINE:
cli
rcall CONVERT_SCREEN
bigdowner YHi,YLo,AnaHi,AnaLo
ldi ZL, low(xy)
ldi ZH, high(xy)
ld r0, Z
out UDR, r0
clr TXflash ;string is NOT in flash memory
ser TXbusy ;and set the TX busy flag
sbi UCR, UDRIE ;enable the TXempty interrupt
sei
rcall TXwait ;chill until done

cli
ldi ZL, LOW(grid3<<1) ;shifted becuase pgm memory is words
ldi ZH, HIGH(grid3<<1)
lpm ;put the char in r0
out UDR, r0 ;put the character in the UART buffer
ser TXflash ;string is in flash memory
ser TXbusy ;and set the TX busy flag
sbi UCR, UDRIE ;enable the TXempty interrupt
sei
rcall TXwait ;chill until done

cli
inc r5
mov temp,r5
cpi temp,80
brne FIFTH_LINE

;end of horizontal lines, now we're ready for the vertical lines

clr r6
mov temp,r6

FIRST_VLINE:
cli
rcall CONVERT_SCREEN

mov YHi,AnaHi
mov YLo,AnaLo
ldi temp2,0x30
mov AnaHi,temp2
ldi temp2,0x30
mov AnaLo,temp2

bigdowner YHi,YLo,AnaHi,AnaLo
ldi ZL, low(xy)
ldi ZH, high(xy)
ld r0, Z
out UDR, r0
clr TXflash ;string is NOT in flash memory
ser TXbusy ;and set the TX busy flag
sbi UCR, UDRIE ;enable the TXempty interrupt
sei
rcall TXwait ;chill until done

cli
ldi ZL, LOW(grid1<<1) ;shifted becuase pgm memory is words
ldi ZH, HIGH(grid1<<1)
lpm ;put the char in r0
out UDR, r0 ;put the character in the UART buffer
ser TXflash ;string is in flash memory
ser TXbusy ;and set the TX busy flag
sbi UCR, UDRIE ;enable the TXempty interrupt
sei
rcall TXwait ;chill until done
cli
inc r6
mov temp,r6
cpi temp,20
brne FIRST_VLINE


clr r6
mov temp,r6

SECOND_VLINE:
cli
rcall CONVERT_SCREEN

mov YHi,AnaHi
mov YLo,AnaLo
ldi temp2,0x31
mov AnaHi,temp2
ldi temp2,0x30
mov AnaLo,temp2


bigdowner YHi,YLo,AnaHi,AnaLo
ldi ZL, low(xy)
ldi ZH, high(xy)
ld r0, Z
out UDR, r0
clr TXflash ;string is NOT in flash memory
ser TXbusy ;and set the TX busy flag
sbi UCR, UDRIE ;enable the TXempty interrupt
sei
rcall TXwait ;chill until done

cli
ldi ZL, LOW(grid1<<1) ;shifted becuase pgm memory is words
ldi ZH, HIGH(grid1<<1)
lpm ;put the char in r0
out UDR, r0 ;put the character in the UART buffer
ser TXflash ;string is in flash memory
ser TXbusy ;and set the TX busy flag
sbi UCR, UDRIE ;enable the TXempty interrupt
sei
rcall TXwait ;chill until done
cli
inc r6
mov temp,r6
cpi temp,20
brne SECOND_VLINE


clr r6
mov temp,r6

THIRD_VLINE:
cli
rcall CONVERT_SCREEN

mov YHi,AnaHi
mov YLo,AnaLo
ldi temp2,0x32
mov AnaHi,temp2
ldi temp2,0x30
mov AnaLo,temp2


bigdowner YHi,YLo,AnaHi,AnaLo
ldi ZL, low(xy)
ldi ZH, high(xy)
ld r0, Z
out UDR, r0
clr TXflash ;string is NOT in flash memory
ser TXbusy ;and set the TX busy flag
sbi UCR, UDRIE ;enable the TXempty interrupt
sei
rcall TXwait ;chill until done

cli
ldi ZL, LOW(grid1<<1) ;shifted becuase pgm memory is words
ldi ZH, HIGH(grid1<<1)
lpm ;put the char in r0
out UDR, r0 ;put the character in the UART buffer
ser TXflash ;string is in flash memory
ser TXbusy ;and set the TX busy flag
sbi UCR, UDRIE ;enable the TXempty interrupt
sei
rcall TXwait ;chill until done
cli
inc r6
mov temp,r6
cpi temp,20
brne THIRD_VLINE

clr r6
mov temp,r6

FOURTH_VLINE:
cli
rcall CONVERT_SCREEN
mov YHi,AnaHi
mov YLo,AnaLo
ldi temp2,0x33
mov AnaHi,temp2
ldi temp2,0x30
mov AnaLo,temp2


bigdowner YHi,YLo,AnaHi,AnaLo
ldi ZL, low(xy)
ldi ZH, high(xy)
ld r0, Z
out UDR, r0
clr TXflash ;string is NOT in flash memory
ser TXbusy ;and set the TX busy flag
sbi UCR, UDRIE ;enable the TXempty interrupt
sei
rcall TXwait ;chill until done

cli
ldi ZL, LOW(grid1<<1) ;shifted becuase pgm memory is words
ldi ZH, HIGH(grid1<<1)
lpm ;put the char in r0
out UDR, r0 ;put the character in the UART buffer
ser TXflash ;string is in flash memory
ser TXbusy ;and set the TX busy flag
sbi UCR, UDRIE ;enable the TXempty interrupt
sei
rcall TXwait ;chill until done
cli
inc r6
mov temp,r6
cpi temp,20
brne FOURTH_VLINE

clr r6
mov temp,r6

FIFTH_VLINE:
cli
rcall CONVERT_SCREEN

mov YHi,AnaHi
mov YLo,AnaLo
ldi temp2,0x34
mov AnaHi,temp2
ldi temp2,0x30
mov AnaLo,temp2

bigdowner YHi,YLo,AnaHi,AnaLo
ldi ZL, low(xy)
ldi ZH, high(xy)
ld r0, Z
out UDR, r0
clr TXflash ;string is NOT in flash memory
ser TXbusy ;and set the TX busy flag
sbi UCR, UDRIE ;enable the TXempty interrupt
sei
rcall TXwait ;chill until done

cli
ldi ZL, LOW(grid1<<1) ;shifted becuase pgm memory is words
ldi ZH, HIGH(grid1<<1)
lpm ;put the char in r0
out UDR, r0 ;put the character in the UART buffer
ser TXflash ;string is in flash memory
ser TXbusy ;and set the TX busy flag
sbi UCR, UDRIE ;enable the TXempty interrupt
sei
rcall TXwait ;chill until done
cli
inc r6
mov temp,r6
cpi temp,20
brne FIFTH_VLINE

clr r6
mov temp,r6

SIXTH_VLINE:
cli
rcall CONVERT_SCREEN
mov YHi,AnaHi
mov YLo,AnaLo
ldi temp2,0x35
mov AnaHi,temp2
ldi temp2,0x30
mov AnaLo,temp2

bigdowner YHi,YLo,AnaHi,AnaLo
ldi ZL, low(xy)
ldi ZH, high(xy)
ld r0, Z
out UDR, r0
clr TXflash ;string is NOT in flash memory
ser TXbusy ;and set the TX busy flag
sbi UCR, UDRIE ;enable the TXempty interrupt
sei
rcall TXwait ;chill until done
cli

ldi ZL, LOW(grid1<<1) ;shifted becuase pgm memory is words
ldi ZH, HIGH(grid1<<1)
lpm ;put the char in r0
out UDR, r0 ;put the character in the UART buffer
ser TXflash ;string is in flash memory
ser TXbusy ;and set the TX busy flag
sbi UCR, UDRIE ;enable the TXempty interrupt
sei
rcall TXwait ;chill until done
cli
inc r6
mov temp,r6
cpi temp,20
brne SIXTH_VLINE

clr r6
mov temp,r6
SEVENTH_VLINE:
cli
rcall CONVERT_SCREEN

mov YHi,AnaHi
mov YLo,AnaLo
ldi temp2,0x36
mov AnaHi,temp2
ldi temp2,0x30
mov AnaLo,temp2

bigdowner YHi,YLo,AnaHi,AnaLo
ldi ZL, low(xy)
ldi ZH, high(xy)
ld r0, Z
out UDR, r0
clr TXflash ;string is NOT in flash memory
ser TXbusy ;and set the TX busy flag
sbi UCR, UDRIE ;enable the TXempty interrupt
sei
rcall TXwait ;chill until done

cli
ldi ZL, LOW(grid1<<1) ;shifted becuase pgm memory is words
ldi ZH, HIGH(grid1<<1)
lpm ;put the char in r0
out UDR, r0 ;put the character in the UART buffer
ser TXflash ;string is in flash memory
ser TXbusy ;and set the TX busy flag
sbi UCR, UDRIE ;enable the TXempty interrupt
sei
rcall TXwait ;chill until done
cli
inc r6
mov temp,r6
cpi temp,20
brne SEVENTH_VLINE

clr r6
mov temp,r6
EIGHTH_VLINE:
cli
rcall CONVERT_SCREEN

mov YHi,AnaHi
mov YLo,AnaLo
ldi temp2,0x37
mov AnaHi,temp2
ldi temp2,0x30
mov AnaLo,temp2

cli
bigdowner YHi,YLo,AnaHi,AnaLo
ldi ZL, low(xy)
ldi ZH, high(xy)
ld r0, Z
out UDR, r0
clr TXflash ;string is NOT in flash memory
ser TXbusy ;and set the TX busy flag
sbi UCR, UDRIE ;enable the TXempty interrupt
sei
rcall TXwait ;chill until done

cli
ldi ZL, LOW(grid1<<1) ;shifted becuase pgm memory is words
ldi ZH, HIGH(grid1<<1)
lpm ;put the char in r0
out UDR, r0 ;put the character in the UART buffer
ser TXflash ;string is in flash memory
ser TXbusy ;and set the TX busy flag
sbi UCR, UDRIE ;enable the TXempty interrupt
sei
rcall TXwait ;chill until done
cli
inc r6
mov temp,r6
cpi temp,20
brne EIGHTH_VLINE

clr r6
mov temp,r6

NINTH_VLINE:
cli
rcall CONVERT_SCREEN

mov YHi,AnaHi
mov YLo,AnaLo
ldi temp2,0x38
mov AnaHi,temp2
ldi temp2,0x30
mov AnaLo,temp2

bigdowner YHi,YLo,AnaHi,AnaLo
ldi ZL, low(xy)
ldi ZH, high(xy)
ld r0, Z
out UDR, r0
clr TXflash ;string is NOT in flash memory
ser TXbusy ;and set the TX busy flag
sbi UCR, UDRIE ;enable the TXempty interrupt
sei
rcall TXwait ;chill until done

cli
ldi ZL, LOW(grid1<<1) ;shifted becuase pgm memory is words
ldi ZH, HIGH(grid1<<1)
lpm ;put the char in r0
out UDR, r0 ;put the character in the UART buffer
ser TXflash ;string is in flash memory
ser TXbusy ;and set the TX busy flag
sbi UCR, UDRIE ;enable the TXempty interrupt
sei
rcall TXwait ;chill until done
cli
inc r6
mov temp,r6
cpi temp,20
brne NINTH_VLINE

;we're done printing the main grid; this next character is just used to show a
;crossbar, (a '+') in the middle of the grid

NEXT_LINE1:
cli
bigdowners 0x31,0x30,0x34,0x30
ldi ZL, low(xy)
ldi ZH, high(xy)
ld r0, Z
out UDR, r0
clr TXflash ;string is NOT in flash memory
ser TXbusy ;and set the TX busy flag
sbi UCR, UDRIE ;enable the TXempty interrupt
sei
rcall TXwait ;chill until done

cli
ldi ZL, LOW(grid2<<1) ;shifted becuase pgm memory is words
ldi ZH, HIGH(grid2<<1)
lpm ;put the char in r0
out UDR, r0 ;put the character in the UART buffer
ser TXflash ;string is in flash memory
ser TXbusy ;and set the TX busy flag
sbi UCR, UDRIE ;enable the TXempty interrupt
sei
rcall TXwait ;chill until done
cli

;Time/Div label
bigdowners 0x32,0x34,0x30,0x30
ldi ZL, low(xy)
ldi ZH, high(xy)
ld r0, Z
out UDR, r0
clr TXflash ;string is NOT in flash memory
ser TXbusy ;and set the TX busy flag
sbi UCR, UDRIE ;enable the TXempty interrupt
sei
rcall TXwait ;chill until done
cli

ldi ZL, LOW(timediv<<1) ;shifted becuase pgm memory is words
ldi ZH, HIGH(timediv<<1)
lpm ;put the char in r0
out UDR, r0 ;put the character in the UART buffer
ser TXflash ;string is in flash memory
ser TXbusy ;and set the TX busy flag
sbi UCR, UDRIE ;enable the TXempty interrupt
sei
rcall TXwait ;chill until done
cli

;Average voltage position
bigdowners 0x32,0x34,0x32,0x34
ldi ZL, low(xy)
ldi ZH, high(xy)
ld r0, Z
out UDR, r0
clr TXflash ;string is NOT in flash memory
ser TXbusy ;and set the TX busy flag
sbi UCR, UDRIE ;enable the TXempty interrupt
sei
rcall TXwait ;chill until done
cli
;Average voltage label
ldi ZL, LOW(amp<<1) ;shifted becuase pgm memory is words
ldi ZH, HIGH(amp<<1)
lpm ;put the char in r0
out UDR, r0 ;put the character in the UART buffer
ser TXflash ;string is in flash memory
ser TXbusy ;and set the TX busy flag
sbi UCR, UDRIE ;enable the TXempty interrupt
sei
rcall TXwait ;chill until done
cli

;V Peak to Peak Label
bigdowners 0x32,0x34,0x34,0x34
ldi ZL, low(xy)
ldi ZH, high(xy)
ld r0, Z
out UDR, r0
clr TXflash ;string is NOT in flash memory
ser TXbusy ;and set the TX busy flag
sbi UCR, UDRIE ;enable the TXempty interrupt
sei
rcall TXwait ;chill until done
cli

ldi ZL, LOW(vpp<<1) ;shifted becuase pgm memory is words
ldi ZH, HIGH(vpp<<1)
lpm ;put the char in r0
out UDR, r0 ;put the character in the UART buffer
ser TXflash ;string is in flash memory
ser TXbusy ;and set the TX busy flag
sbi UCR, UDRIE ;enable the TXempty interrupt
sei
rcall TXwait ;chill until done


;DC-Offeset Label
bigdowners 0x32,0x34,0x36,0x32
ldi ZL, low(xy)
ldi ZH, high(xy)
ld r0, Z
out UDR, r0
clr TXflash ;string is NOT in flash memory
ser TXbusy ;and set the TX busy flag
sbi UCR, UDRIE ;enable the TXempty interrupt
sei
rcall TXwait ;chill until done
cli

ldi ZL, LOW(DC<<1) ;shifted becuase pgm memory is words
ldi ZH, HIGH(DC<<1)
lpm ;put the char in r0
out UDR, r0 ;put the character in the UART buffer
ser TXflash ;string is in flash memory
ser TXbusy ;and set the TX busy flag
sbi UCR, UDRIE ;enable the TXempty interrupt
sei
rcall TXwait ;chill until done

ret

hexascii:
push temp
clr temp ;use temp as our counter
ldi ZL, LOW(value) ;ptr to RAM
ldi ZH, HIGH(value)

loop1000:
cpi AnaHi,0x03 ;compare high byte to see if > or < 1000
brlo end1000 ;we're less than 3 so therefore less than 1000
breq testLo
inc1000:
inc temp ;If >= 1000, add 1 to counter
subi AnaLo,0xe8 ;Subtract 1000 from Ana
sbci AnaHi,0x03
rjmp loop1000
testLo:
cpi AnaLo,0xe8 ;test low byte of Ana
brsh inc1000

end1000:
subi temp,-48 ;convert decimal char to ascii and store
st Z, temp
adiw ZL,1
clr temp

loop100:
cpi AnaHi,0 ;make sure the upper bits are clear
brne inc100 ;if AnaHi is non-zero, we want to increment temp
cpi AnaLo,0x64 ;compare Low byte to see if > or < 100
brlo end100

inc100:
inc temp ;If >= 100, add 1 to counter
subi AnaLo,0x64 ;Subtract 100 from Ana
sbci AnaHi,0x00
rjmp loop100

end100:
subi temp,-48 ;convert decimal char to ascii
st Z, temp ;store ascii value to ram
adiw ZL,1 ;increment the pointer
clr temp

loop10:
cpi AnaLo,0x0a ;compare Low byte to see if > or < 10
brlo end10 ;we're less than 0x0a so therefore less than 10:
inc temp ;If >= 10, add 1 to counter
subi AnaLo,0x0a ;Subtract 10 from Ana
sbci AnaHi,0x00
rjmp loop10

end10:
subi temp,-48 ;convert decimal char to ascii
st Z, temp
adiw ZL,1
clr temp
subi AnaLo,-48 ;Convert the 1's digit to ASCII
st Z,AnaLo ;Store it in the string
adiw ZL,1
clr temp ;NULL terminate the string
st Z,temp
pop temp
ret

;END OF CODE

Appendix 2: Schematics

We tried to implement a differential amplifier circuit in our digital oscilloscope. The point of this device was an attempt to input a negative voltage for waves, such as sinusoids, that have voltages from -Vp to +Vp, (our range, specifically, was -2 to +2 volts,) because the ADC does not read in negative voltages. This way, by setting one of our inputs to the amplifier to -2 volts, we could add -2 to +2 volts to the other input, which would effectively provide us a range of 0 to 4 volts. Unfortunately, we had a problem with the LM358 operational amplifier in the laboratory; it seemed to be adding some kind of load impedance that we could not detect.
For instance, the -2 volt input to the amplifier was produced using a voltage divider, yet once this voltage was inputted to the amplifier it dropped all of a sudden to -1.63 volts. Also, though there was a differential voltage on the output, but it was nowhere near its expected value. An example was where the inputted voltage was around -2 volts, and another voltage was 12 volts, and the difference on the output was about 4 volts. Again, this was probably due to some loading impedance. As already mentioned, we decided to implement a simpler solution to the voltage input, by simply manually adding the DC offset. However, since a deal of effort was put into building the differential amplifier, we felt it was necessary to mention this attempt.





0 comments:

Post a Comment

Share your knowledge

Related Posts Plugin for WordPress, Blogger...

Popular Projects

program for Dual DAC 8051 Microcontroller Based DC Motor Control A Microcontroller Based Turbidity Meter A m -Controller Based Thermostat ASCII to BCD conversion in 8051 AT90LS8515 Digital Message Machine Audio Frequency Response Analyzer Audio Homing Robot Automated Juice Mixer Automated Pet Feeder Autonomous Car Autonomous Parallel Parking RC Car Autonomous Search Robot Autonomous Tank Autonomous Vehicle Contrast Following Rover Autonomous navigating robot BCD number to ASCII in 8051 Balance Bot Blind Bot Blood Pressure Monitor Bloodshed Dev-C++ 5 Compiler/IDE Breath Alcohol Tester Converters on TI MSP430 CrossStudio MSP430 IDE Design of a Real-Time Digital Guitar Tuner Digital Oscilloscope Digital Stethoscope Digital clock project using PIC16C54A microcontroller Digital thermometer ECG monitoring system GPS Data Logger with Wireless Trigger Handwriting Recognition Systm Home Security System Home energy managment IAR Embedded Workbench IDE INFRARED TRACKING SYSTEM IntelliBOT Laser Communications System Line following van MSP-EXP430FG4618 Development Tool and the eZ430 kits MSP430FG4618 device implement a Buzzer tone generator MSP430FG4618 device implement a Real Time Clock MSP430FG4618 device implement a voltage ramp generator MSP430FG4618 device present a message on the LCD Basic Microcontroller(8051) Lab Mivo- RFID based mobile payment system Multi-Zone Fire Alarm System PC based temperature control PIC 16f877 RPM Meter PIC16C54 dual dice electronic project circuit PIC16F84A digital thermometer microcontroller project PIC16F886 horn driver PWM motor contoller with MSP430 Program Block data transfer in 8051 Program to add two BCD numbers in 8051 Program to check whether a 4th bit of a byte is 1 Program to convert ASCII to hex in 8051 Program to count from 0-9 in 8051 Program to count number of 1's in a given data byte in 8051 Program to divide an 8 bit no by another 8 bit number in 8051 Program to find largest of n numbers in 8051 Program to find the LCM of two numbers in 8051 Program to find the square of an 8 bit number in 8051 Program to generate 50msec delay in 8051 Program to implement BCD counter to count from 0-99 in 8051 Program to implement BCD counter to count from 99-0 in 8051 Program to interchange two blocks of data in 8051 Program to multiply 16 bit number by 8 bit number in 8051 Program to search an element in an array in 8051 Program to sort an array of 10 elements in 8051 Programming the ez430 Proximity Security System RAMP wave in 8051 RC Car Controller RObo Dog Radio-controlled Truck Retina color tracker Robotic Arm Controller with GUI Robotic Car Traction Control Safety-sensor vehicle Security Entrance System Self-Powered Solar Data Logger Snake Arm Ultrasonic Positioning Control System Store FFh if 1 Super Train Controller TI MSP430 Microcontrollers Timers on the MSP430 TouchPad Drawing Board Ultra-Sonic Parking Assistant Ultrasonic Parking Controller Ultrasonic Range finder Voice Activated Alarm Clock Voice Recognition Robotic Car Voting Machine Weather Station Web-Monitored Thermostat Wireless Drawing Device Wireless Telemetry Wireless message Communicator Write a C program to display the code of the key pressed in 8051 Zigbee Wireless Relay Control and Power Monitoring System add two multibyte numbers in 8051 convert a decimal number to hex number in 8051 convert an 8bit Hex number to decimal number in 8051 convert hex number to ASCII number in 8051 eZ430-F2013 Development Tool use SD16_A ADC eZ430-RF2500 Development Tool use ADC10 else store 00 in the same location in 8051 find the GCF of two numbers in 8051 find the average of 10 numbers in 8051 generate Fibonacci series in 8051 metal detector project microcontroller using IAR Embedded Workbench program for Elevator Interface in 8051 program for Stepper motor interface in 8051 spectrum analyser square wave in 8051 triangle wave in 8051 voice recognition security system

Sites U missed

Hint

Open Pictures in new page by right click on it, if it is not shown full image.