| CODE |
;******************************************************************** ; ; Program: inoutbox.asm ; ; Author: Herbert Dingfelder, DL5NEG ; ; Function: serves as a universal serial interface-box ; between a PC and the outside world ; reads simple control commands from the serial ; port of the PC and sets/reads the pin on the ; in-out-box accordingly ; during active communcation a LED is switched on ; ; Hardware: AT90LS4433 Atmel 8Bit AVR Microprocessor ; crystal frequency is 3.6864 MHz ; ; History: 15.02.2001 Start of development ; RS232 communication works ; Online editing with terminal works ; Switch-on Message works ; 16.02.2001 Implementation of Commands started ; 26.02.2001 UART synchonsisation debugged ; 27.02.2001 ADconversion routine works ; 09.03.2001 Set Bit Routine included ; 12.03.2001 Clear Bit Routine included ; 13.03.2001 Read Bit Routine included ; bin2bit introduced for cleaner code ; -> Version 1.0 released ; 12.06.2001 bugfix: C5 for LED was not set as output ; -> Version 1.01 released ; 03.11.2001 Code cleanup for internet publishing ; -> Version 1.1 released ; 15.08.2002 bugfix: two ",0" as termination were ; missing in the string definitions at ; the end of the code ; (no effect on 4433 but problem if code ; is ported for 8535) ; -> Version 1.2 released ; ;******************************************************************** ;*** includes *** .include "4433def.inc" ;*** definitions, equations, constants *** .def char = r0 .def ad_high = r1 .def ad_low = r2 .def rb_value = r3 .def power = r4 .def bbres = r5 .def temp = r16 .def wait = r17 .def wait1 = r18 .def wait2 = r19 .def data = r20 .def data_received = r21 .def str_len = r22 .def command = r23 .def sendbyte = r24 .def hex_ascii = r25 .def temp_sub = r26 .def str_ptr_l = r30 .def str_ptr_h = r31 .equ val_bit7 = 128 .equ string = $0060; string start in memory ; (SRAM starts at $0060, not at $0000 !!!) .equ stringmaxlen = 40 ; max. number of char in the input string ;*** interrupt table *** .CSEG .ORG 0x00 rjmp reset ;jump to reset handler reti ;irq0 handler reti ;irq1 handler reti ;timer1 capture handler reti ;timer1 compare handler reti ;timer1 overflow handler reti ;timer0 overflow handler reti ;spi transfer complete handler rjmp uart_rx ;uart rx complete handler reti ;udr empty handler reti ;uart tx complete handler reti ;adc conversion complete interrupt handler reti ;eeprom ready handler reti ;analog comparator handler ;++++++++++++++++++++++++++ main +++++++++++++++++++++++++++++++++ main: ldi str_ptr_l, string; load the string pointer with the string clr str_ptr_h ; start address clr str_len ; reset the number of char in the string rcall sendstartmsg; send the startup-message via the UART loop: sbi PORTC, 5 ; switch of the LED (active low) sbrs data_received,0; skip the jump if bit0 is set ; i.e. if a databyte was received from UART rjmp loop ; wait for a received databyte in this loop ;a data byte was received from UART, lets handle it: clr data_received; reset my own received flag cbi PORTC, 5 ; switch on the LED (active low) cpi data, 13 ; was the databyte a CR (ASCII 13) ? brne not_cr rcall cr_received rjmp loop not_cr: cpi data, 8 ; was the databyte a Backspace (ASCII 8) ? brne not_bs rcall backspace rjmp loop not_bs: cpi data, 10 ; was the databyte a LF (ASCII 10) ? breq loop ; if yes -> do nothing and ignore it cpi str_len, stringmaxlen; ignore input if max. number of char in the breq loop ; input string is already reached ; the databyte is a normal character, add it to the input string inc str_len ; increments the number of char. in the string st z+, data ; stores the databyte in the string and inc. ; the pointer to the next addr. mov sendbyte, data; echo the received character rcall send_ser rjmp loop ;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ;---------------------- subroutine reset ------------------------- reset: init_stack: ldi temp, low(RAMEND); init stack pointer to last byte of ram out SPL, temp init_portd: ; configure Port D as input clr temp ;(Bit 2-7 will be used as general input) out DDRD, temp ;(see init_uart for bits 0 and 1) ldi temp, 0b11111100;switch on the internal pull-up resistors out PORTD, temp ; to prevent floating of the inputs init_uart: sbi DDRD,1 ; UART TX-pin (PortD, Bit1) as output cbi DDRD,0 ; UART RX-pin (PortD, Bit0) as input ldi temp, 0b10011000; enable TX, enable RX, enable RX-interrupt out UCSRB,temp ldi temp,23 ; 9600 Baud with fq=3.6864 MHz out UBRR,temp clr temp out UBRRH,temp init_portc: ldi temp, 0b00100000; configure Port C as input out DDRC, temp ; except C5 =output for LED init_portb: ser temp ; configure Port B as output out DDRB, temp enable_irq: ldi temp, val_bit7; set bit 7 in Statusregister to out SREG, temp ; enable the interupts rcall pause ; for phasing in the UART: ldi temp, 32 ; pause - send one character - pause out UDR, temp rcall pause rjmp main ; everything initialized, now lets go... ;----------------- interrupt routine uart_rx --------------------- uart_rx: in data, UDR ; read received data from UART inc data_received; increment number of received datas reti ; end of interrupt routine ;--------------------- subroutine pause -------------------------- ;(pause for about 10ms) pause: ldi wait,35 wl: ser wait1 wl1: dec wait1 brne wl1 dec wait brne wl ret ;----------------- subroutine send_ser---------------- ; sends one byte via UART and waits long enough for the ; byte to be send send_ser: out UDR, sendbyte; send the character via UART serl: sbis UCSRA, UDRE; wait till TX is ready rjmp serl ret ;------------------------subroutine rec_cmd ----------------------- ; This subroutine tries to recognize the command in the inputstring ; Returns the number of the command (0 if no command could be recog.) rec_cmd: ;*** check if at least 2 char are in the string, otherwise old ;characters may be recognized as commands ******** clr command ; preset reg command with 0 (no command rec.) cpi str_len, 2 ; check if input is at least 2 char. long brlo nocommand ; if no -> go to end of routine (returns 0) ldi str_ptr_l, string; set the string pointer to the first char clr str_ptr_h ; in the string ld temp, z+ cpi temp, 's' brne noset ld temp, z+ cpi temp, 'b' brne noset ldi command, 1 ; the command was recognized as SB set bit noset: ldi str_ptr_l, string; set the string pointer to the first char clr str_ptr_h ; in the string ld temp, z+ cpi temp, 'c' brne noclr ld temp, z+ cpi temp, 'b' brne noclr ldi command, 2 ; the command was recognized as CB clear bit noclr: ldi str_ptr_l, string; set the string pointer to the first char clr str_ptr_h ; in the string ld temp, z+ cpi temp, 'r' brne norb ld temp, z+ cpi temp, 'b' brne norb ldi command, 4 ; the command was recognized as RB read bit norb: ldi str_ptr_l, string; set the string pointer to the first char clr str_ptr_h ; in the string ld temp, z+ cpi temp, 'r' brne nora ld temp, z+ cpi temp, 'a' brne nora ldi command, 3 ; the command was recognized as RA read ADC nora: ldi str_ptr_l, string; set the string pointer to the first char clr str_ptr_h ; in the string ld temp, z+ cpi temp, 'e' brne noei ld temp, z+ cpi temp, 'i' brne noei ldi command, 5 ; the command was recognized as ei echo input noei: nocommand: ret ;-------------------------output string---------------------------- ; reads characters from the flash meomory and puts them out via the ; the uart, stops when a '\0' is read from the memory. Starts to read ; at the location Z points to outputstring: lpm ;read one byte from flash (acc. to Z-pointer) tst char ;check if the char is zero (marks end of string) breq endos ;if end-of-string -> jump to end of routine mov sendbyte,char;transver the character to sendbyte rcall send_ser ;call send_ser to transmit the char. via UART adiw ZL,1 ;increment Z for next char. (add 1 to 16bit reg.) rjmp outputstring;jump up for next character endos: ret ;---------------------------setbit--------------------------------- ; sets a bit on Output Port B according to inputstring setbit: ldi str_ptr_l, low(2*sb_ok) ldi str_ptr_h, high(2*sb_ok) rcall outputstring ldi str_ptr_l, string; set the string pointer to the first char clr str_ptr_h ; in the string ld temp, z+ ; overread the first 2 characters (sb...) ld temp, z+ ld temp, z+ ; read the 3rd char. cpi temp, ' ' ; if the 3rd char is no space -> error brne sbparametererr cpi str_len, 4 ; is the input exactly 4 character long? brne sbparametererr; if not -> error ld temp, z+ ; read the 4th char. (number of ADC) subi temp, 48 ; sub 48 to convert ASCII -> BIN cpi temp, 6 ; check if Bit-Nr. is >= 6 brsh sbparametererr; only 1-4 allowed, if >=6 -> error ; all errors excluded, lets go on with bit setting ldi sendbyte, ' '; output space rcall send_ser mov sendbyte, temp; output bit number subi sendbyte, -48; (convert to ASCII before output) rcall send_ser ldi str_ptr_l, low(2*sb_hs) ; output "has been set" ldi str_ptr_h, high(2*sb_hs) rcall outputstring mov power, temp ; convert the bitnumber to a set bit rcall bin2bit ; (calculates bbres=2^power) in temp, PORTB ; read current value of PORTB or temp, bbres ; set the chosen bit out PORTB, temp ; write the value back to PORTB rjmp sbend ; all done, go to end of subroutine sbparametererr: ldi str_ptr_l, low(2*parerr) ;write to terminal: ldi str_ptr_h, high(2*parerr);Parameter error rcall outputstring sbend: ret ;---------------------------clearbit--------------------------------- ; clears a bit on Output Port B according to input clearbit: ldi str_ptr_l, low(2*cb_ok) ldi str_ptr_h, high(2*cb_ok) rcall outputstring ldi str_ptr_l, string; set the string pointer to the first char clr str_ptr_h ; in the string ld temp, z+ ; overread the first 2 characters (sb...) ld temp, z+ ld temp, z+ ; read the 3rd char. cpi temp, ' ' ; if the 3rd char is no space -> error brne cbparametererr cpi str_len, 4 ; is the input exactly 4 character long? brne cbparametererr; if not -> error ld temp, z+ ; read the 4th char. (number of ADC) subi temp, 48 ; sub 48 to convert ASCII -> BIN cpi temp, 6 ; check if Bit-Nr. is >= 6 brsh cbparametererr; only 1-4 allowed, if >=6 -> error ; all errors excluded, lets go on with bit clearing ldi sendbyte, ' '; output space rcall send_ser mov sendbyte, temp; output bit number subi sendbyte, -48; (convert to ASCII before output) rcall send_ser ldi str_ptr_l, low(2*cb_hc) ; output "has been cleared" ldi str_ptr_h, high(2*cb_hc) rcall outputstring mov power, temp ; convert the bitnumber to a set bit rcall bin2bit ; (calculates bbres=2^power) in temp, PORTB ; read current value of PORTB com bbres ; invert all bits in bbres and temp, bbres ; clear the chosen bit out PORTB, temp ; write the value back to PORTB rjmp cbend ; all done, go to end of subroutine cbparametererr: ldi str_ptr_l, low(2*parerr) ;write to terminal: ldi str_ptr_h, high(2*parerr);Parameter error rcall outputstring cbend: ret ;---------------------------read adc------------------------------- readadc: ldi str_ptr_l, low(2*ra_ok) ;write to terminal: READ ADC - ldi str_ptr_h, high(2*ra_ok) rcall outputstring ldi str_ptr_l, string; set the string pointer to the first char clr str_ptr_h ; in the string ld temp, z+ ; overread the first 2 characters (ra...) ld temp, z+ ld temp, z+ ; read the 3rd char. cpi temp, ' ' ; if the 3rd char is no space -> error brne parametererr cpi str_len, 4 ; is the input exactly 4 character long? brne parametererr; if not -> error ld temp, z+ ; read the 4th char. (number of ADC) cpi temp, '0' ; ADC 0 is not allowed breq parametererr; if 0 -> error subi temp, 48 ; sub 48 to convert ASCII -> BIN cpi temp, 5 ; check if ADC-Nr. is >= 5 brsh parametererr; only 1-4 allowed, if >=5 -> error ; all errors excluded, lets go on with the AD conversion out ADMUX, temp ; switch the MUX to the selected ADC subi temp, -48 ; output the ADC number to the terminal mov sendbyte, temp rcall send_ser ldi sendbyte, '='; output = rcall send_ser ldi sendbyte, '$'; output $ to make clear the output is hex rcall send_ser ldi temp, 0b11100110; status for ADC: enable adc, start out ADCSR, temp ; 1st conversion, free run, ; clock prescaled with factor 64 ; ->57kHz clock with fq=3.686MHz rcall pause ; wait for ADconversion to be performed in ad_low, ADCL ; read adc-value (low byte must be in ad_high, ADCH; read first!!!) mov temp, ad_high; get the high-byte of the ADC value, cbr temp, $F0 ; mask out higher nibble (lower remains) mov hex_ascii, temp; convert the hex-number into ASCII rcall hex2ascii mov sendbyte, hex_ascii; send the ASCII character to the terminal rcall send_ser mov temp, ad_low; get the low-byte of the ADC value, cbr temp, $0F ; mask out lower nibble (higher remains) swap temp ; swap nibbles to shift the higher nibble down mov hex_ascii, temp; convert the hex number into ASCII rcall hex2ascii mov sendbyte, hex_ascii; send the ASCII character to the terminal rcall send_ser mov temp, ad_low; get the low-byte of the ADC value, cbr temp, $F0 ; mask out higher nibble (lower remains) mov hex_ascii, temp; convert the hex-number into ASCII rcall hex2ascii mov sendbyte, hex_ascii; send the ASCII character to the terminal rcall send_ser ldi sendbyte, 32; send one space to the terminal rcall send_ser rjmp endadc parametererr: ldi str_ptr_l, low(2*parerr) ;write to terminal: ldi str_ptr_h, high(2*parerr);Parameter error rcall outputstring endadc: ret ;-------------------------hex2ascii------------------------------ ;converts a byte with value 0-15 to a ascii-code in hex notation ;parameter: hex_ascii (contains the hex value before this routine ;is called and contains the ascii value afterwards) hex2ascii: subi hex_ascii, -48; add 48 for bin->ascii conversion ; (there is no addi!-> sub -48) cpi hex_ascii, 58; 10 is 58 after the add above brlo h2a_end ; if<58 ok, if not make 10->A, etc. subi hex_ascii, -7; if 10-15 -> add 6 (58->65=A etc.) h2a_end: ret ;--------------------------read bit------------------------------ readbit: ldi str_ptr_l, low(2*rb_ok) ldi str_ptr_h, high(2*rb_ok) rcall outputstring ldi str_ptr_l, string; set the string pointer to the first char clr str_ptr_h ; in the string ld temp, z+ ; overread the first 2 characters (ra...) ld temp, z+ ld temp, z+ ; read the 3rd char. cpi temp, ' ' ; if the 3rd char is no space -> error brne rbparametererr cpi str_len, 4 ; is the input exactly 4 character long? brne rbparametererr; if not -> error ld temp, z+ ; read the 4th char. (number of ADC) subi temp, 48 ; sub 48 to convert ASCII -> BIN cpi temp, 8 ; check if Bit-Nr. is >= 8 brsh rbparametererr; only 2-7 allowed, if >=8 -> error cpi temp, 2 ; check if Bit-Nr. is < 2 brlo rbparametererr; only 2-7 allowed, if <2 -> error ; all errors excluded, lets go on with the bit reading mov power, temp ; convert the bitnumber to a set bit rcall bin2bit ; (calculates bbres=2^power) in rb_value, PIND; read the physical status of the PortD pins and rb_value, bbres; mask out the chosen bit ; now r_value is zero when the bit was cleared or ; r_value is not equal to zero when the bit was set ; now we can go ahead with the output to the terminal subi temp, -48 ; output the bit number to the terminal mov sendbyte, temp rcall send_ser ldi sendbyte, '='; output = rcall send_ser tst rb_value ; test if r_value is zero or not breq bitzero ; if zero go to bitzero ldi sendbyte, '1'; output char. '1' to the terminal rcall send_ser rjmp endrb ; all done, got to end of subr. bitzero: ldi sendbyte, '0'; output char. '0' to the terminal rcall send_ser rjmp endrb ; all done, got to end of subr. rbparametererr: ldi str_ptr_l, low(2*parerr) ;write to terminal: ldi str_ptr_h, high(2*parerr);Parameter error rcall outputstring endrb: ret ;---------------------------bin2bit-------------------------------- ; converts a number (0-7) into a set bit in a byte ; e.g. the bit number 3 into 00001000, i.e. mathemtically ; it simply calulates n to the power of 2 ; parameter is reg. exp, result is stored in bbres bin2bit: ldi temp_sub, 0b00000001; preset bbres with 1 mov bbres, temp_sub bbloop: tst power ; test if power is already zero breq bbdone ; if yes go to end of subroutine lsl bbres ; logic shift left (bbres=2*bbres) dec power ; decrement power rjmp bbloop ; go up for next round bbdone: ret ;--------------------------echo input------------------------------ echoinput: ldi str_ptr_l, low(2*ei_ok) ldi str_ptr_h, high(2*ei_ok) rcall outputstring ldi str_ptr_l, string; set the string pointer to the first char clr str_ptr_h ; in the string nextchar: ld temp, z+ ; load one char and inc. the point to the next one mov sendbyte, temp; send the char out via the UART rcall send_ser dec str_len ; decrement the stringlenght brne nextchar ; stringlength=0 reached? if not, next character ret ;---------------------- subroutine cr_received -------------------- ; whenever a CR (Return = ASCII 13) is received via the UART, this ; subroutine is called cr_received: ldi sendbyte,13 ; send CR, LF to start a new line rcall send_ser ldi sendbyte,10 rcall send_ser rcall rec_cmd ; try to recognize the command tst command ; if command=0 -> no command could be brne iscommand ; recognized, send SYNTAX ERROR ldi str_ptr_l, low(2*synerr) ldi str_ptr_h, high(2*synerr) rcall outputstring iscommand: cpi command,1 ; if command=1 -> go to setbit brne cr_nosetbit rcall setbit cr_nosetbit: cpi command,2 ; if command=2 -> go to clearbit brne cr_noclearbit rcall clearbit cr_noclearbit: cpi command,3 ; if command=3 -> go to read adc brne cr_noreadadc rcall readadc cr_noreadadc: cpi command,4 ; if command=4 -> go to read bit brne cr_noreadbit rcall readbit cr_noreadbit: cpi command,5 ; if command=5 -> echo input brne cr_noechoinput rcall echoinput cr_noechoinput: clr str_len ; empty the inputstring and ldi str_ptr_l, string; reset the clr str_ptr_h ; stringpointer for the next string ldi sendbyte,13 ; send CR, LF to start a new line rcall send_ser ldi sendbyte,10 rcall send_ser ldi sendbyte,13 ; send CR, LF to start a new line rcall send_ser ldi sendbyte,10 rcall send_ser ret ;----------------------- sendstartmsg ----------------------------- sendstartmsg: .eseg on_msg: .db 13,10,13,10 .db "Universial serial PC interface, Herbert Dingfelder DL5NEG",13,10 .db "For users guide check out www.t-online.de/home/dl5neg",13,10 .db "For futher questions send a mail to dl5neg",13,10 .db "Firmware Version 1.1, built 03.11.2001",13,10,13,10,0 .cseg ldi temp,low(on_msg) ;load the EEPROM pointer with the address where out EEAR,temp ;the message (see above) is stored EERead: sbic EECR,EEWE;if EEWE not clear rjmp EERead ;wait more sbi EECR,EERE;set EEPROM Read strobe ;This instruction takes 4 clock cycles since ;it halts the CPU for two clock cycles in temp,EEDR;get data tst temp ;end of message is marked by an zerobyte breq ssm_end ;check if 0, if yes jump to end of routine mov sendbyte, temp;transver the character to register sendbyte rcall send_ser ;call send_ser to transmit the char. via UART in temp, EEAR ;increment EEAR (pointer to current address inc temp ;in the EEPROM) for next character out EEAR, temp rjmp EERead ;jump up for next character ssm_end: ret ;---------------------- subroutine backspace ---------------------------- ; whenever a Backspace (Return = ASCII 8) is received via the UART, this ; subroutine is called backspace: tst str_len ; is the stringlength=0 ? If yes ignore Backspace breq end_bs ld temp, -z ; set stringpointer one character left ; (temp is not needed, the command is only used ; to decrement Z) dec str_len ; decrease the number of char in the string ldi sendbyte, 8 ; send Backspace, Space, Backspace because rcall send_ser ; the BS alone only moves the cursor one step ; left but does not delete the char from the ldi sendbyte, 32; screen rcall send_ser ldi sendbyte, 8 rcall send_ser end_bs: ret ; return from subroutine ;*************** output messages ***************************************** synerr: .db "SYNTAX ERROR - available commands: sb, cb, rb, ra, ei",0 sb_ok: .db "SET BIT - Bit",0 sb_hs: .db " has been set",0 cb_ok: .db "CLEAR BIT - Bit",0 cb_hc: .db " has been cleared",0 rb_ok: .db "READ BIT - Bit ",0 ra_ok: .db "READ ADC - ADC",0 ei_ok: .db "ECHO INPUT - ",0 ok_out: .db "OK",0 parerr: .db " parameter not allowed or parameter syntax incorrect!",0 |