📄 videodvm.asm
字号:
;***************************************************************************
;* VIDEODVM.ASM
;*
;* a digital volt meter that displays data directly on your TV!
;* ---> composite video output showing giant digits
;* ---> comprehensive data display with current reading, max and min
;* ---> analog type bar-display, graduated from 0 to 100%, for quick checks
;* ---> selectable decimal point position
;* ---> 1200 baud, ASCII serial output is also provided
;*
;* Copyright (c) 1997 by Alberto Ricci Bitti
;* e-mail: a.riccibitti@ra.nettuno.it
;***************************************************************************
.DEVICE AT90S1200
;***************************************************************************
;* a few, very useful macros
;***************************************************************************
.LISTMAC
.MACRO ADDI
subi @0, -@1
.ENDMACRO
;skip next instruction if not equal to zero
.MACRO skipne
.SET _skipne = PC+2
brne _skipne
.ENDMACRO
;skip next instruction if carry set
.MACRO skipcs
.SET _skipcs = PC+2
brcs _skipcs
.ENDMACRO
;skip next instruction if carry clear
.MACRO skipcc
.SET _skipcc = PC+2
brcc _skipcc
.ENDMACRO
;skip next instruction if equal to zero
.MACRO skipeq
.SET _skipeq = PC+2
breq _skipeq
.ENDMACRO
;skip next instruction if lower
.MACRO skiplo
.SET _skiplo = PC+2
brlo _skiplo
.ENDMACRO
;wait two cycles but waste only one instruction
.MACRO doublenop
.SET _doublenop = PC+1
rjmp _doublenop
.ENDMACRO
;***************************************************************************
;include AT90S1200 registers definitions
.include "1200def.inc"
;include font definitions
;fonts are stored in a EEPROM table, edit fonts.inc to modify it as you wish
.include "fonts.inc"
;***************************************************************************
;* VARIABLE ASSIGNEMENTS
;***************************************************************************
.CSEG
.def SaveStatus = r0 ;status buffer during interrupts
.def CharHeight = r1 ;character height (reload value)
.def CurrHeight = r2 ;character height (running counter)
.def PrintAB = r3 ;print buffer for BCD charachters,
.def PrintCD = r4 ;the sequence is A B C D + measurement unit
.def PointPosition = r5 ;decimal point position
.def BarLenghtHigh = r6 ;lenght of analog bar, high byte
.def BarLenght = r7 ;lenght of analog bar, low byte
.def AdcLow = r8 ;ADC result, low byte
.def AdcHigh = r9 ;ADC result, high byte
.def AdcLowMax = r10 ;max value memory
.def AdcHighMax = r11
.def AdcLowMin = r12 ;min value memory
.def AdcHighMin = r13
.def TimeLow = r14 ;time counter
.def TimeHigh = r15
.def Position = r16 ;identifies wich of the four line position
;we are on (3=end of line, 0=sync pulse) when
;an interrupt occurs
;used by timer interrupt routine
.def Delay = r17 ;used for delays - decremented each field
.def RowsModFour = r18 ;raster rows counter, split in two for
.def RowsDivFour = r19 ;convenience of use. Counts up to 312
;common data space used under interrupt
.def Arg1 = r20 ;Temp variable used by interrupt
.def Arg2 = r21 ; " " " "
.def Arg3 = r22 ; " " " "
.def Arg4 = r23 ; " " " "
;common data space used by main program
.def Main1 = r24 ;Temp variable used by main program
.def Main2 = r25 ; " " " " "
.def Main3 = r26 ; " " " " "
.def Main4 = r27 ; " " " " "
.def MiscFlags = r28 ;Assorted flags
;flags bit definition
.equ SerialReqBit = 1 ;
;***************************************************************************
;* Constants
;***************************************************************************
.equ DisplayRow = 150/4 ;counter value for display start
.equ StartRetrace = (312/4)-1;counter value at wich retrace starts
.equ StopRetrace = (312/4) ;maximum counter value
.equ BAUDRATE = 13 ;lines between bits: 15625/13=1200 baud
;useful global constants
.equ ASCII_ZERO = 48 ;ascii value for '0'
.equ ASCII_RETURN = 13 ;ascii value for carriage return
;***************************************************************************
;* Port Pin Assignements
;***************************************************************************
;port D bit definitions (OUTPUTS)
.equ CSyncBit = 0 ;set video output to composite sync level when low
.equ VideoBit = 1 ;set video level to white when high, black when low
.equ SCLKBit = 2 ;ADC serial clock output
.equ CSBit = 3 ;ADC chip select (active low)
.equ DINBit = 4 ;ADC serial data
.equ LedBit = 5 ;board led, 0 = ON
.equ RS232Bit = 6 ;1200 baud TTL level serial data
;port B bit definitions (INPUTS)
.equ DOUTBit = 0 ;ADC serial data
.equ DPSel1Bit = 1 ;board jumpers: 1, 2 & 3, select
.equ DPSel2Bit = 2 ;decimal point position
.equ DPSel3Bit = 3 ;
.equ ClearButton = 4 ;Max Min Clear pushbutton
;***************************************************************************
;* VECTORS
;***************************************************************************
rjmp RESET ;Reset Handle
rjmp RESET ;Ext. interrupt request 0
rjmp TIMER ;Timer
;***************************************************************************
;*
;* MAIN PROGRAM
;*
;***************************************************************************
;* INITAILIZATION
;***************************************************************************
RESET:
clt ;clear T bit flag ;VERY important:
;clear T flag, here used to
;verify if we were sleeping
ldi Arg1, 0b01111111 ;set port D bits to outputs
out DDRD, Arg1
ldi Arg1, 0b00000001 ;preset output state
out PortD, Arg1
ldi Arg1, 0b00000000 ;set port B to inputs
out DDRB, Arg1
ldi Arg1, 0b11111111 ;turn on pullups on inputs
out PortB, Arg1
ldi Arg1, 1
out TCCR0, Arg1 ;dont'use timer prescaler
ldi Arg1, 32
out MCUCR, Arg1 ;enable sleep idle mode
ldi Arg1, 2
out TIMSK, Arg1 ;enable timer interrupt
ldi Arg1, 10 ;preset display variables
mov CharHeight, Arg1
mov CurrHeight, CharHeight
ldi Delay, 5
ldi Arg1, 0x99 ;preset minimum to "9999"
mov ADCLowMin, Arg1
mov ADCHighMin, Arg1
clr ADCLowMax ;preset maximum to "0000"
clr ADCHighMax
clr Arg1 ;and clear interrupt variables
clr Arg2
clr Arg3
clr Arg4
clr MiscFlags
sei ;at last, allow interrupts
;***************************************************************************
;* MAIN LOOP
;***************************************************************************
FOREVER:
j2: cpi Delay, 1
brne j1
cbi PortD, LedBit
j1: cpi Delay, 0
brne FOREVER
ldi Delay, 10
sbi PortD, LedBit
inc TimeLow
skipne
inc TimeHigh
;*******************************
;* ADC CONVERSION
;*******************************
ldi Main1, 0 ;select ADC channel 0
rcall READADC ;ADC conversion, return result in Main3-4
;*******************************
;* BAR DISPLAY
;*******************************
mov BarLenght, Main3 ;get binary data into
mov BarLenghtHigh, Main4 ;BarLenght storage area
lsr BarLenghtHigh ;and divide by 32 (shift 5
ror BarLenght ;times) to adapt to our
lsr BarLenghtHigh ;routine range (0-127)
ror BarLenght
lsr BarLenghtHigh
ror BarLenght
lsr BarLenghtHigh
ror BarLenght
lsr BarLenghtHigh
ror BarLenght
;*******************************
;* CONVERSION TO BCD
;*******************************
mov Main1, Main3 ;set up parameters
mov Main2, Main4
rcall BIN_BCD ;and go convert
mov ADCLow, Main3 ;store result
mov ADCHigh, Main4
;*******************************
;* PEAK RECORDER
;*******************************
cp ADCLowMax, ADCLow ;if current ADC value
cpc ADCHighMax, ADCHigh
brsh CHECK_MIN ;is greater than Max value
mov ADCLowMax, ADCLow ;then Max = Current value
mov ADCHighMax, ADCHigh ;
CHECK_MIN:
cp ADCLowMin, ADCLow ;if current ADC value
cpc ADCHighMin, ADCHigh
brlo CHECK_CLEAR ;is less than Max value
mov ADCLowMin, ADCLow ;then Min = Current value
mov ADCHighMin, ADCHigh ;
CHECK_CLEAR:
sbic PinB, ClearButton ;if Max-Min clear button pressed
rjmp DONTCLEAR ;
mov ADCLowMax, ADCLow ;then reset Max value
mov ADCHighMax, ADCHigh ;
mov ADCLowMin, ADCLow ;and Min value
mov ADCHighMin, ADCHigh ;
DONTCLEAR:
;*******************************
;* SERIAL COMMUNICATIONS
;*******************************
mov Main1, ADCHigh ;send data to serial interface
swap Main1 ;select first BCD digit
andi Main1, 0b00001111 ;cut out unwanted bits
ADDI Main1, ASCII_ZERO ;convert to ASCII
rcall SENDSERIAL ;and send to serial
mov Main1, ADCHigh ;do the same for the following 3 digits
andi Main1, 0b00001111 ;hundreds...
ADDI Main1, ASCII_ZERO
rcall SENDSERIAL
mov Main1, ADCLow ;tens...
swap Main1
andi Main1, 0b00001111
ADDI Main1, ASCII_ZERO
rcall SENDSERIAL
mov Main1, ADCLow ;and units
andi Main1, 0b00001111
ADDI Main1, ASCII_ZERO
rcall SENDSERIAL
ldi Main1, ASCII_RETURN ;add a carriage return (perfect
rcall SENDSERIAL ;for QBasic programs...)
rjmp FOREVER ;loop forever
;****************************************************************************
;*
;* Timer Interrupt: occurs four times in each video line.
;* The Position records which of four interrupt stages we are on.
;* At the third stage, we go in sleep mode, in order to have constant
;* servicing time for the fourth, crucial interrupt were we will output
;* the sync waveform.
;* This is the very heart of sync generation.
;*
;****************************************************************************
TIMER:
;if we were sleeping, then this is a synchronizing cycle: just return
brtc TIMER_NOSLEEP ;if T cleared, do timer routine
reti ;otherwise return (sync sleep)
TIMER_NOSLEEP:
in SaveStatus, SREG
;if not at line start,simply decrement position counter and return
;otherwise do the real thing...
dec Position ;Position range from 2 down to -1
breq WAITNEWLINE ;if Position = 0 then wait (sleeping) for new line
TIMER_EXIT:
out SREG, SaveStatus ;otherwise do nothing
reti
WAITNEWLINE:
set ;set T flag to tell we are sleeping
sei ;re-enable interrupts, otherwise we sleep forever!
sleep ;wait sleeping the next timer interrupt
nop ;(during sleep we have constant interrupt recovery time)
cli ;disable further interrupts
clt ;clear T, used for sleep flag
NEWLINE: ;when awaken, we will restart here
ldi Position, 3 ;reset position index
in Arg1, PortD ;read previous CSync level
ldi Arg2, 0b00000001 ;load mask for toggle csync bit
TOGGLESYNC:
eor Arg1, Arg2 ;toggle CSync output
out PortD, Arg1 ;now we are in the horizontal sync pulse
HOUSEKEEPING:
;once in horizontal sync pulse space, we have
;enough time to do some housekeepying routines...
inc RowsModFour ;increment row counters
cpi RowsModFour, 4 ;if RowsModFour == 4
brne _1
inc RowsDivFour ;then increment RowsDivFour
_1: andi RowsModFour, 0b00000011 ;clear anyway bit 2
CHECKLASTLINE:
;when at last line, the sync pulse is stretched to half line
cpi RowsDivFour, StopRetrace ;if at last line
brsh LASTLINE ;the sync pulse stops at half line
WAITSYNCEND: ;else wait sync pulse end
in Arg1, TCNT0 ;note that now we are perfectly in
cpi Arg1, 64 ;sync with timer. 64 = 4 uS
brlo WAITSYNCEND
ldi Arg2, 0b00000001 ;prepare mask for csync toggle
VERTICALRETRACE:
ldi Arg1, StartRetrace
cpi RowsModFour, 0 ;if at start of vertical retrace pulse
cpc RowsDivFour, Arg1
brne HSYNCEND ;then leave CSync low
ldi Arg2, 0
HSYNCEND:
in Arg1, PortD ;
eor Arg1, Arg2 ;toggle CSync output
out PortD, Arg1 ;now we are in the color burst space
rcall SERIALDRIVER
WAITVISIBLEPORTION:
in Arg1, TCNT0 ;wait for start of visible area
cpi Arg1, 220 ;decreasing this moves display left
brlo WAITVISIBLEPORTION
rjmp BUILDSCREEN ;go to display building table
LASTLINE:
;this is a good time to make slow (1/50 sec.) housekeepings,
;or to do things that are made once every screen picture
clr RowsDivFour ;reset row counters
clr RowsModFour
tst Delay ;if Delay <> 0
breq WAITHALFLINE
dec Delay ;then decrement it
WAITHALFLINE:
in Arg1, TIFR ;wait for two timer overflows
sbrs Arg1, TOV0 ;(each overflow is a quarter line)
rjmp WAITHALFLINE
ldi Arg1, EXP2(TOV0) ;reset timer overflow bit
out TIFR, Arg1
SECONDQUARTER:
in Arg1, TIFR ;wait second overflow
sbrs Arg1, TOV0 ;
rjmp SECONDQUARTER
ldi Arg1, EXP2(TOV0) ;reset timer overflow bit
out TIFR, Arg1
ldi Position, 1 ;and align position counter
sbi PortD, CSyncBit ;and end composite sync
rjmp TIMER_EXIT
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -