;***************************************************************** ; ; File Name : 'echo2.asm' ; Title : Interrupt-Driven Serial Echo Test ; Date : 2001.10.08 ; Version : 0.01 ; Author : Pascal Stang ; Target MCU : AT90S8515 ; ;***************************************************************** .nolist .include "8515def.inc" .list ; define constants .equ BEL =0x07 .equ BS =0x08 .equ CR =0x0D .equ LF =0x0A .equ SPACE =0x20 .equ BAUD_RATE =9600 .equ CPU_CLOCK =3686000 .equ UART_BAUD_DIV =(CPU_CLOCK/(16*BAUD_RATE))-1 .equ TMR0_PRESCL =0x05 ; this is the prescaler setting for CK/1024 .equ QSIZE =50 ; define register aliases .def ARG0 =r0 ; function argument 0 .def ARG1 =r1 ; function argument 1 .def RETURN0 =r2 ; function return 0 .def RETURN1 =r3 ; function return 1 .def TEMP0 =r16 ; temporary register 0 .def TEMP1 =r17 ; temporary register 1 .def QHEAD =r22 ; index of beginning of queue buffer .def QTAIL =r23 ; index of end of queue buffer ; and as a reminder ; 8515def.inc contains ;.def XL =r26 ;.def XH =r27 ;.def YL =r28 ;.def YH =r29 ;.def ZL =r30 ;.def ZH =r31 ; data segment .dseg qbuf: .byte QSIZE ; define an array of bytes qbuf[QSIZE] ; code segment .cseg .org $000 rjmp reset ; $000 HW reset or watchdog rjmp reset ; $001 External IRQ 0 rjmp reset ; $002 External IRQ 1 rjmp reset ; $003 Timer/Counter1 capture event rjmp reset ; $004 Timer/Counter1 compare match A rjmp reset ; $005 Timer/Counter1 compare match B rjmp reset ; $006 Timer/Counter1 overflow rjmp reset ; $007 Timer/Counter0 overflow rjmp reset ; $008 SPI/SCT serial transfer complete rjmp RxC_Isr ; $009 UART Rx complete rjmp reset ; $00A UART data register empty rjmp reset ; $00B UART Tx complete rjmp reset ; $00C Analog comparator ; begin main code reset: ldi TEMP0, low(RAMEND) ; initialize stack pointer out SPL, TEMP0 ldi TEMP0, high(RAMEND) out SPH, TEMP0 rcall clrqueue ; initialize queue ldi TEMP0, $FF ; port B all outputs out DDRB, TEMP0 out PORTB, TEMP0 ; initially all LEDs off ldi TEMP0, TMR0_PRESCL ; initialize timer0 out TCCR0, TEMP0 ; prescale 1024 ldi TEMP0, UART_BAUD_DIV ; set baud rate out UBRR, TEMP0 sbi UCR, TXEN ; enable serial transmitter sbi UCR, RXEN ; enable serial receiver sbi UCR, RXCIE ; enable receiver interrupts sei ; enable interrupts loop: rcall getchar ; get next character from queue mov TEMP0, RETURN0 cpi TEMP0, $20 ; test for nonprinting character brlo beep ; drop characters lower than 0x20 cpi TEMP0, $7f brsh beep ; drop characters higher than 0x7F mov ARG0, RETURN0 rcall putchar ; transmit character rcall delay ; delay for fun rcall delay ; use the delay to help show buffering effect rjmp loop beep: ldi TEMP0, BEL ; load CTRL-G terminal bell code mov ARG0, TEMP0 rcall putchar ; ding!!! rcall delay ; delay for fun rjmp loop ; begin subroutines and functions ; function getchar ; get character from receive serial queue ; if none available, wait until one arrives getchar: rcall dequeue brcc getchar ret ; function putchar ; output character in ARG0 to serial port, non-interrupt driven putchar: sbis USR, UDRE ; loop until USR:UDRE is 1 rjmp putchar out UDR, ARG0 ; write ARG0 to transmitter buffer ret ; Interrupt Handler RxC_Isr ; UART receive character interrupt service routine RxC_Isr: push TEMP0 ; save registers in TEMP0, SREG push TEMP0 push ARG0 in ARG0, UDR ; read UART receive data mov TEMP0, ARG0 com TEMP0 out PORTB, TEMP0 ; display data on LEDs rcall enqueue ; place data in the queue pop ARG0 ; restore registers pop TEMP0 out SREG, TEMP0 pop TEMP0 reti ; return from interrupt ; function clrqueue ; initialize the queue buffer to empty (beginning=end=0) clrqueue: clr QHEAD ; make pointers equal clr QTAIL ret ; function enqueue ; add byte in ARG0 to end of queue buffer enqueue: push YH ; save registers push YL push TEMP0 clr TEMP0 ldi YL, low(qbuf) ; Y = base of queue ldi YH, high(qbuf) add YL, QTAIL ; add offset of tail adc YH, TEMP0 ; to get to end of queue st Y, ARG0 ; store data in queue inc QTAIL ; update queue end (tail) pointer cpi QTAIL, QSIZE brlo enq2 clr QTAIL ; wraparound when needed enq2: pop TEMP0 ; restore registers pop YL pop YH ret ; return ; function enqueue ; remove byte from beginning of queue and return in RETURN0 ; if data was available, carry is set ; if no data was available, carry is clear dequeue: cp QHEAD, QTAIL ; check for empty queue brne deq1 clc ; clear carry flag when empty ret deq1: push YH ; save registers push YL clr RETURN0 ldi YL, low(qbuf) ; Y = base of queue ldi YH, high(qbuf) add YL, QHEAD ; add offset to get to adc YH, RETURN0 ; the beginning (head) of queue ld RETURN0, Y ; fetch first element in queue inc QHEAD ; update queue head pointer cpi QHEAD, QSIZE brlo deq2 clr QHEAD ; wraparound to 0 when needed deq2: pop YL ; restore registers pop YH sec ; set carry to indicate data ret ; function delay ; delay 1/16 second delay: push TEMP0 push TEMP1 in TEMP1, TCNT0 ; get current timer count delay1: in TEMP0, TCNT0 cp TEMP1, TEMP0 breq delay1 ; wait until timer increments delay2: in TEMP0, TCNT0 cp TEMP1, TEMP0 brne delay2 ; wait until original count pop TEMP1 pop TEMP0 ret ; return