;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