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
OCR1AH | OCR1AL | |
.1 sec / division | 9C | 40 |
.50 ms / division | 4E | 20 |
20 ms / division | 1F | 40 |
10 ms / division | 0F | A0 |
5 ms / division | 07 | D0 |
2 ms / division | 03 | 20 |
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!
.
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
|
0 comments:
Post a Comment