;Dick Cappels' project pages sap@cappels.org - you should be able to copy and paste this text into an assembler file.
;©2002 Richard Cappels All Rights Reserved. Email projects@cappels.org
;Use of information presented on this page is for personal, nonprofit educational
;and noncommercial use only. This material (including object files) is copyrighted
;by Richard Cappels and may not be republished or used directly for commercial
;purposes without explicit permission For commercial license, click HERE.
;
;HOME;
.include "tn12def.inc"
;Note: Storage of input values in r3 thorugh r18 are not important to the DAC function.
;serial version major and minor revision leves.
.equ version = $0 ;0 sstarting August 20, 2002
.equ revision= $1 ;seems completely funcitonal settle time set to $02
;I/O pin descriptions
.equ dataline= 0 ;B0 Data line
.equ attline = 1 ;B1 Attention line
.equ pwm0out = 2 ;B2 pwm0 output
.equ pwm0or = 4 ;value to set channel 0
.equ pwm1out = 3 ;B3 pwm1 output
.equ pwm1or = 8 ;value to set channel 1
.equ pwm2out = 4 ;B4 pwm2 output
.equ pwm2or = $10 ;value to set channel 2
;Note: Pin one of Attiny12 is used as reset input
.equ checksum =$0F
.equ initialddrb = $3C; Inital value for DDRB (pin 1 default open)
;parameters
.equ setltime =$40 ;How long to wait for data and I/O lines to settle
.def sregtemp= r1 ;temporary storage of status register during interrupt time
.def datareg = r2 ;data register
;Note: user 4,5,6, and 7 as well as pwmvalue 4,5,6, and 7 are not used in ATtiny12.
;////start user accessible regisers:
.def pwm0 = r3 ;pwm value for pwm channel 0 (user register 0)
.def pwm1 = r4 ;user 1
.def pwm2 = r5 ;user 2
.def user3 = r6 ;user 3
.def pwmvalue0= r7 ;duty cycle user register 4
.def pwmvalue1 = r8 ;duty cycle user register 5
.def pwmvalue2 = r9 ;duty cycle user register 6
.def pwmvalue3 = r10 ;duty cycle user register 7
.def spare1 = r11
.def spare2 =r12
.def spare3 =r13
.def spare4 =r14
.def spare5 =r15
.def spare6 =r16
.def eeprompointer =r17
.def spare18 =r18 ;Scratch register
;/////end of user accessible registers
.def ticks = r19 ; interrupt number modulus 8
.def bout = r20 ; interrupt only temporary buffer for output port b
.def temp = r21 ;scratch
.def tempi = r22 ;scratch for use during interrupt
.def rxtxbyte= r23 ;byte to be sent -can be lower register
.def delayc = r24 ;counter used for timing delay
.def bitcount= r25 ;counter for number of bits transfered
.def tempcounter= r26 ;variable used to reverse order of bits
.def pwmvalue = r27 ;variable used to reverse order of bits
.def oldticks = r28 ;variable used to decide when to make a pulse
.def index = r29 ;used to index registers
;note that r30 and r31 used as Z register
.ORG $0000
rjmp reset
reti ;External interrupt handler - not used
reti ;Pin change interrupt -not used
rjmp tovflo ;timer overflow interrupt handler
reset:
ldi temp,$00 ;Aero as initial value for pwm channels
mov pwmvalue0,temp
mov pwmvalue1,temp
mov pwmvalue2,temp
mov pwmvalue3,temp
ldi temp,initialddrb ;load data direction value for PORTB
out DDRB,temp ;no pullups on B0 and B1 ***
out PORTB,temp
ldi temp,$80
out ACSR,temp ;Turn off comparitor (save a little power).
ldi temp,$01
out TCCR0,temp ;Set timer prescaler to clock/1
ldi temp,0 ;preload timer counter
out TCNT0,temp
ldi temp,$02 ;enable timer overflow interrupts
out TIMSK,temp
clr pwm0
clr pwmvalue0
clr pwm1
clr pwmvalue1
clr pwm2
clr pwmvalue2
sei ;enable interrupts in general
rjmp checkandloadeeprom
sendrevisoin:
ldi rxtxbyte,version
rcall sendbyte
ldi rxtxbyte,revision
rcall sendbyte
rjmp main
CopyEEPROMtopwmvalues:
ldi eeprompointer,$00
clr ZH
ldi ZL,3 ;Point to highest register to be transferred
wratzC: sbic eecr,eewe ;Wait for EEPROM write to not be busy
rjmp wratzC
wyloop:
out eear,eeprompointer ;move to EEPROM address register
sbi eecr,eere ;Trigger the read
in temp,eedr ;Get the data into temp
st Z,temp ;Get data to register
inc eeprompointer
inc ZL
cpi ZL,$0F
brne wyloop
rjmp main
checkandloadeeprom:
clr temp ;Uses ZL,ZH,temp. Locatoin [checksum] is not used in the calculation.
clr ZH ;checksum kept here
ldi ZL,checksum-1 ;pointer
wratzD: sbic eecr,eewe ;Wait for EEPROM write to not be busy
rjmp wratzD
moretosum2:
out eear,ZL ;move to EEPROM address register
sbi eecr,eere ;Trigger the read
in temp,eedr ;Get the data into temp
add ZH,temp
dec ZL
cpi ZL,$FF
brne moretosum2
ldi temp,checksum
out eear,temp ;move to EEPROM address register
sbi eecr,eere ;Trigger the read
in temp,eedr ;Get the data into temp
cp ZH,temp
breq CopyEEPROMtopwmvalues
rjmp main
main:
rjmp ReceiveByte
recrtn:
brcc CarryIsClear ;if carry is not set, it is data, so place in datareg.
; //////Interpret DataShake input as an instruction//////
;If carry is not set, interpret as an instruction.
;make lower nybble register pointer just in case its needed
mov ZL,rxtxbyte ;move pointer into ZL register
andi ZL,$0F ;mask off upper nybble
ldi ZH,$00
andi rxtxbyte,$F0 ;mask off lower nybble in rxtxbyte
;and interpret the command
cpi rxtxbyte,$10 ;test for store instruction
breq storereg
cpi rxtxbyte,$20 ;test for read instruction
breq readreg ;finished fetching and interpreting. go for another byte
cpi rxtxbyte,$40 ;test for command to write to EEPROM (only lower 16 bytes accessible)
breq WriteEEPROM
cpi rxtxbyte,$50 ;test for command to read to EEPROM (only lower 16 bytes accessible)
breq ReadEEPROM
cpi rxtxbyte,$60 ;test for command to copy registers to EEPROM
breq CopypwmvaluesToEEPROM
cpi rxtxbyte,$70 ;test for command to write EEPROM checksum in [checksum]-1
breq Seteepromchecsum
cpi rxtxbyte,$80 ;test for command to write revision to host
breq sendrevisoin
cpi rxtxbyte,$90 ;test for command to copy EEPROM to registers
breq CopyEEPROMtopwmvalues
storereg: ;STORE INCOMING BYTE INTO DATAREG
clr ZH ;
ldi index,03 ;
add ZL,index ;point to registers 3 thrugh 18
st Z,datareg
mov pwmvalue,datareg ;REverse the bit order and store in pwmvalue registers
rcall reversepwmvalue ;for the interrupt pwm routine to use.
ldi index,$04
add ZL,index
st Z,pwmvalue
rjmp main
readreg:
clr ZH
ldi temp,03 ;
add ZL,temp ;point to registers 3 thrugh 18
ld rxtxbyte,Z ;READ BYTE FROM REGISTER
clc ;clear the carry becuase data is being sent
rcall SendByte
rjmp main
CarryIsClear: ;If carry is clear, its data -store in data register
mov datareg,rxtxbyte
rjmp main
;/////EEPROM WRITE, READ, AND CHECKSUM ROUTINES.
;ENTER WITH ZL POINTING TO EEPROM ADDRESS, WRITE DATA IN datareg, READ ROUTINE CALLS SENDBYTE.
;CHECKSUM STORED IN EEPROM LOCATOIN [checksum] Checksum is from EEPROM location 0 to [checkusm]-1
;WRITE ROUTINE TURNS OF INTERRUPTS WHILE EEPROM IS BURNT -MAY CAUSE GLITCH IN PWM OUTPUT
WriteEEPROM: ;Write contents of datareg into EEPROM register pointed to by ZL
wrat: sbic eecr,eewe ;Wait for EEPROM write to not be busy
rjmp wrat
out eear,ZL
out eedr,datareg ;Set up the write data
cli ;Inhibit interrupts while triggering EPROM write
sbi eecr,eemwe
sbi eecr,eewe ;Trigger the write
sei ;re-enable interrupts now that EEPROM write has been triggered
rjmp main
ReadEEPROM: ;Read contents of EEPROM register pointed to by ZL to datareg
wratzE: sbic eecr,eewe ;Wait for EEPROM write to not be busy
rjmp wratzE
out eear,ZL ;move to EEPROM address register
sbi eecr,eere ;Trigger the read
in rxtxbyte,eedr ;Get the data into temp
rcall SendByte
rjmp main ;This rjmp instruction is being kept incase this routine is relocated
Seteepromchecsum: ;Calculate EERPOM checksum and store in location [checksum].
clr temp ;Uses ZL,ZH,temp. Locatoin [checksum] is not used in the calculation.
clr ZH ;checksum kept here
ldi ZL,checksum-1 ;pointer
wratzB: sbic eecr,eewe ;Wait for EEPROM write to not be busy
rjmp wratzB
moretosum:
out eear,ZL ;move to EEPROM address register
sbi eecr,eere ;Trigger the read
in temp,eedr ;Get the data into temp
add ZH,temp
dec ZL
cpi ZL,$FF
brne moretosum
;Store checksum stored in ZH into EEPROM location [checksum]
wrat1: sbic eecr,eewe ;Wait for EEPROM write to not be busy
rjmp wrat1
ldi ZL,checksum
out eear,ZL
out eedr,ZH ;Set up the write data
cli ;Inhibit interrupts while triggering EPROM write
sbi eecr,eemwe
sbi eecr,eewe ;Trigger the write
sei ;re-enable interrupts now that EEPROM write has been triggered
rjmp main
;//////END OF GENERIC EEPROM ROUTINES/////
CopypwmvaluesToEEPROM:
ldi eeprompointer,$00
clr ZH
ldi ZL,3 ;Point to highest register to be transferred
wzloop:
ld temp,Z ;Get data pointed to by ZL
wratzA: sbic eecr,eewe ;Wait for EEPROM write to not be busy
rjmp wratzA
out eear,eeprompointer
out eedr,temp ;Set up the write data
cli ;Inhibit interrupts while triggering EPROM write
sbi eecr,eemwe
sbi eecr,eewe ;Trigger the write
sei ;re-enable interrupts now that EEPROM write has been triggered
inc eeprompointer
inc ZL
cpi ZL,$0F
brne wzloop
rjmp Seteepromchecsum
;//////////SEND A BYTE
; Send carry bit as first bit, then send rxtxbyte bit-by-bit. rxtxbyte destroyed.
SendByte:
ldi bitcount,$09
S1:
sbis PINB,attline ;Wait for Attention line to go high =--SEND A BIT
rjmp S1
;Put data on data line
cbi DDRB,dataline
brcs R1
cbi PORTB,dataline
sbi DDRB,dataline
R1:
cbi PORTB,attline ;Set Attention line low
sbi DDRB, attline
rcall shortdelay ;Wait a short time so other chip can see Attnetion line is low
cbi DDRB,attline ;Release Attention line for a peak
rcall shortdelay ;Short delay to allow settling of lines
sbic PINB,attline ;If Attention line isn't low, go back and put it low again, else continue
rjmp R1
brcs szero ;Invert data line
cbi DDRB,dataline
rjmp R3
szero: cbi PORTB,dataline
sbi DDRB,dataline
R3:
sbis PINB,attline ;Wait for Attention line to go high
rjmp R3
cbi DDRB,dataline ;Let data line float
;-finished sending a bit
rol rxtxbyte ;Shift rxtxbyte through carry
dec bitcount
brne S1 ;Continue until all bits sent
ret
ReceiveByte:
ldi bitcount,$09
W3A:; ;Get a bit from the input into the carry
W3:
sbic PINB,attline ;Wait for Attention line to go low
rjmp w3
cbi PORTB,attline ;ACK by pulling attention line low
sbi DDRB, attline
clc ;Latch dataline into carry bit
sbis PINB,dataline
rjmp NotaOne
sec
W1: ;Wait for data line to go low
sbic PINB,dataline
rjmp W1
rjmp RelesaseAttLineAndGo
NotaOne:
W2: ;Wait for data line to go high
sbis PINB,dataline
rjmp W2
RelesaseAttLineAndGo: ;Release attention line
cbi DDRB, attline
;DONE RECEIVING BIT
rol rxtxbyte ;Shift rxtxbyte through carry
dec bitcount
brne W3A ;Continue until all bits received
rjmp recrtn
shortdelay:
ldi delayc,setltime
D1: dec delayc
brne D1
ret
;REVERSE BIT ORDER OR PWMVALUE. Midifies pwmvalue, uses temp
reversepwmvalue: ; reverse bit order in pwmvale.
ldi temp,$08 ; 8 times through loop because there are 8 bits
mov tempcounter,temp
rotateagain:
rol pwmvalue
ror temp
dec tempcounter
brne rotateagain
mov pwmvalue,temp
ret
;///////////////TIMER OVERFLOW SERVICE//////////////////
tovflo:
in sregtemp,SREG ;save the status register contents
ldi tempi,$E ; **** set 32 clocks until next interrupt
out TCNT0,tempi
in tempi,portb ;preserve two lower bits of portb
andi tempi,$03
andi bout,$FC
or bout,tempi
out portb,bout ;transfer portb buffer to port b
andi bout,$03 ;preset all PWM bits to zero
mov oldticks,ticks ;copy interrupt counter to temp
inc ticks ;increment interrupt timer
eor oldticks,ticks ;do X-OR and AND to find bits that changed to 1 (result in temp)
and oldticks,ticks ;do AND to find bits that just changes to a 1
; OUTPUT PWM COMB WAVEFORM TO PORTB BIT 0
mov tempi,oldticks
and tempi,pwmvalue0 ;AND to find if ones in data correspond to this interrupt count change
breq dontset0 ; if ones present, set lsb in portb buffer
ori bout,pwm0or
dontset0:
; OUTPUT PWM COMB WAVEFORM TO PORTB BIT 1
mov tempi,oldticks
and tempi,pwmvalue1 ;AND to find if ones in data correspond to this interrupt count change
breq dontset1 ; if ones present, set lsb in portb buffer
ori bout,pwm1or
dontset1:
; OUTPUT PWM COMB WAVEFORM TO PORTB BIT 2
mov tempi,oldticks
and tempi,pwmvalue2 ;AND to find if ones in data correspond to this interrupt count change
breq dontset2 ; if ones present, set lsb in portb buffer
ori bout,pwm2or
dontset2:
out SREG,sregtemp ;restore the status register contents
reti
;end of assembly souce listing
;http://projects.cappels.org/
;HOME