; ************************************************************** ; * Pulse Width Generator, programmable via Serial I/O 9k6 8N1 * ; * Input cycle length in µs first, then active cycle in µs * ; * Default cycle length is 25,000 µs, default active cycle is * ; * 2,000 µs. Output is on Port D, Bit 2 * ; * Written for the STK200 board, AT90S8515 * ; * (C)2000 by info@avr-asm-tutorial.net, error reports welcome* ; ************************************************************** ; .NOLIST .INCLUDE "C:\avrtools\appnotes\8515def.inc" .LIST ; ; Used registers ; .DEF rlpm=R0; Used for LPM commands .DEF rchar=R1; Character buffer for SIO communications .DEF rilo=R2; Low byte word input/active loop .DEF rihi=R3; High byte word input/active loop .DEF rjlo=R4; Low byte for multiplication/inactive loop .DEF rjhi=R5; High byte for multiplication/inactive loop ; .DEF rmpr=R16; A multipurpose register, byte/word .DEF rcl=R18; Desired cycle time, word .DEF rch=R19 .DEF ral=R20; Desired active time, word .DEF rah=R21 ; X=R26/27: Counter for Active time ; Y=R28/29: Counter for inactive time ; Z=R30/31: Pointer for program memory read operation ; ; Constants ; .EQU OutPort=PortD; Desired output port .EQU DataDir=DDRD; Data direction register of that port .EQU ActivePin=2; Desired output pin .EQU ModeControl=0b00000100; Control word for port .EQU cDefCyc=25000; Default cycle length in µs .EQU cDefAct=2000; Default active high length in µs .EQU fq=4000000; Xtal frequency on board in Hz .EQU baud=9600; Baudrate for SIO communication .EQU bddiv=(fq/(16*baud))-1; Baudrate divider .EQU ccr=0x0D; Carriage return character .EQU clf=0x0A; Line feed character .EQU cnul=0x00; NUL character .EQU cesc=0x1B; ESCAPE character .EQU cbel=0x07; Bell character ; ; Macro Check input value for default and transfer to desired register ; .MACRO Default mov @0,rilo; Copy result to desired register pair mov @1,rihi mov rmpr,rilo; Test if input is zero or rmpr,rihi brne nodef; Not zero, don't set default value ldi @0,LOW(@2); Set to default value ldi @1,HIGH(@2) nodef: .ENDM ; ; Code segment start ; .CSEG ; ; Reset- and interrupt-vectors, interrupts not used here ; rjmp Start; Reset vector reti; Ext Int 0 reti; Ext Int 1 reti; Timer 1 Capt reti; Timer 1 CompA reti; Timer 1 CompB reti; Timer 1 OVF reti; Timer 0 OVF reti; Serial Transfer Complete reti; UART Rx Complete reti; UART Data register empty reti; UART Tx Complete reti; Analog Comparator ; ; Subroutine for string transmit ; TxStr: sbis USR,UDRE; Wait until transmit buffer empty rjmp TxStr lpm; Read next character from program memory and rlpm,rlpm; NUL = end of string brne txsend ret txsend: lpm; Read the same char again out UDR,rlpm; Tx character read adiw ZL,1; point to next char im memory rjmp TxStr ; ; Subroutine for receiving a number (word, 0..65535) ; RxWord: clr rilo; Set buffer to zero clr rihi rxw1: sbis USR,RXC; Test if rx buffer empty rjmp rxw1; receive char not available, repeat in rmpr,UDR; Get the char from the SIO out UDR,rmpr; Echo this character cpi rmpr,ccr; Return char = end of input breq rxwx subi rmpr,'0'; Subtract 48 brcs rxwerr; not a decimal number, return wit carry set cpi rmpr,10; number >9? brcs rxwok; legal decimal number rxwerr: ldi ZL,LOW(2*ErrorStr); Echo error string ldi ZH,HIGH(2*ErrorStr) rcall TxStr sec; Set carry flag, not a legal number ret rxwok: mov rjlo,rilo; Copy word for multiplicaton mov rjhi,rihi lsl rilo; Multiply with 2 = 2* rol rihi brcs rxwerr; Overflow, return with carry set lsl rilo; Multiply again by 2, = 4* rol rihi brcs rxwerr; Overflow, return with carry set add rilo,rjlo; Add copy, = 5* adc rihi,rjhi brcs rxwerr; Overflow, return with carry set lsl rilo; Multiply by 2, = 10* rol rihi brcs rxwerr; Overflow, return with carry set clr rjhi; Add the decimal number add rilo,rmpr adc rihi,rjhi brcs rxwerr; Overflow, return with carry set rjmp rxw1; Get next character rxwx: sbis USR,UDRE; Wait until transmit buffer empty rjmp rxwx ldi rmpr,clf; Transmit additional line feed char out UDR,rmpr clc; Clear carry, no errors ret ; ; Start of program ; Start: ; ; Setup the stack for the use of subroutines ; ldi rmpr,HIGH(RAMEND); Stack setting to highest RAM adress out SPH,rmpr ldi rmpr,LOW(RAMEND) out SPL,rmpr ; ; Initiate Port D to output on Bit 2 ; ldi rmpr,ModeControl; Set output pin out DataDir,rmpr; to Data Direction register ; ; Initiate SIO communication ; ldi rmpr,bddiv; Set baud rate out UBRR,rmpr ldi rmpr,0b00011000; Enable TX and RX out UCR,rmpr ; ; Transmit the hello sequence ; hello: ldi ZH,HIGH(2*InitStr); Point Z to string ldi ZL,LOW(2*InitStr) rcall TxStr ; ; Get value for total cycle length ; getcycle: ldi ZH,HIGH(2*CycleStr); Point Z to string ldi ZL,LOW(2*CycleStr) rcall TxStr rcall RxWord brcs getcycle; Repeat if error default rcl,rch,cDefCyc ; ; Get value for active cycle length ; getactive: ldi ZH,HIGH(2*ActiveStr); Point Z to string ldi ZL,LOW(2*ActiveStr) rcall TxStr rcall RxWord brcs getactive; Repeat if error default ral,rah,cDefAct ; ; Calculate counter value for active time ; mov XL,ral; Calculate active time mov XH,rah sbiw XL,5; at least 4 cycles brcs getcycle; illegal active time adiw XL,1; At least one cycle required mov rilo,XL mov rihi,XH ; ; Calculate counter value for inactive time ; mov YL,rcl; Calculate inactive time mov YH,rch sub YL,XL; Subtract active time sbc YH,XH brcs getcycle; Active time longer than cycle time sbiw YL,5; Subtract loop delays brcs getcycle; Less than 3 loop cycles are illegal adiw YL,1; minimum 1 loop mov rjlo,YL mov rjhi,YH ldi ZH,HIGH(2*WaitStr); Output operating string ldi ZL,LOW(2*WaitStr) rcall TxStr ; ; Counting loop starts here, check char at SIO available? ; ctloop: sbi OutPort,ActivePin; Start active phase ActLoop: sbiw XL,1; 0.5 µs brne ActLoop; 0.5 µs sbic USR,RXC; Test if rx buffer empty rjmp getcycle; get new cycle time cbi Outport,ActivePin; Start Inactive phase InactLoop: sbiw YL,1; 0.5 µs brne InactLoop; 0.5 µs mov XL,rilo; Refresh counters mov XH,rihi mov YL,rjlo mov YH,rjhi nop nop rjmp ctloop; start a new cycle ; ; Text strings for transmit, ANSI screen encoded! ; ErrorStr: .DB cbel,ccr,clf,cesc,'[','3','3','m' .DB "Error in input! Repeat input! " .DB ccr,clf,cnul,cnul ActiveStr: .DB cesc,'[','3','2','m','*' .DB "Input active time (default = 2,000):" .DB cesc,'[','3','1','m',' ' .DB cnul,cnul CycleStr: .DB cesc,'[','3','2','m','*' .DB "Input cycle time (default = 25,000):" .DB cesc,'[','3','1','m',' ' .DB cnul,cnul WaitStr: .DB cesc,'[','3','5','m','C' .DB "ontinued operation. " .DB ccr,clf,cesc,'[','3','7','m','E' .DB "nter new cycle length to stop and restart generator." .DB ccr,clf,cnul,cnul InitStr: .DB cesc,'[','0',59,'1',59,'3','7',59,'4','0','m'; Set screen colours .DB cesc,'[','H',cesc,'[','J' ; ANSI Clear screen .DB ccr,clf,ccr,clf .DB "Hello world!" .DB ccr,clf .DB "This is the pulse width generator at work." .DB ccr,clf .DB "All times in micro-seconds, accepted range 5..65535." .DB ccr,clf .DB "New value stops operation until all input is completed. " .DB ccr,clf,cnul,cnul ; Check: .DW check ; ; End of code segment ;