?? avr304.asm
字號:
;**** A P P L I C A T I O N N O T E A V R 3 0 4 ************************
;*
;* Title: Half Duplex Interrupt Driven Software UART
;* Version: 1.0
;* Last updated: 97.07.18
;* Target: AT90Sxxxx (All AVR Devices)
;*
;* Support E-mail: avr@atmel.com
;*
;* Code Size :72 words
;* Low Register Usage :2
;* High Register Usage :5
;* Interrupt Usage :External Interrupt,
;* Timer/Counter0 overflow interrupt
;*
;* DESCRIPTION
;*
;* This application note describes how to make a half duplex software UART
;* on any AVR device with the 8-bit Timer/Counter0 and External Interrupt.
;* As a lot of control applications communicate in one direction at a time
;* only, a half duplex UART will limit the usage of MCU resources.
;*
;* The constants N and R determine the data rate. R selects clock frequency
;* as described in the T/C Prescaler in the AVR databook. If the T/C pre-
;* scaling factor is denoted C, the following expression yields the data rate:
;*
;* XTAL
;* BAUD = ------ min. N*C = 17
;* N*C max. N = 170
;*
;* Absolute minimum value for N*C is 17 (which causes the interrupt flag to be
;* set again before the interrupt is finished). Absolute maximum is 170.
;* (Caused by the 1.5bit-lenght that is necessary to receive bits correctly.)
;*
;* The UART uses PD2 as receive pin because it utilizes the external interrupt.
;* The transmit-pin is PD4 in this example, but it can be any other pins.
;*
;* Since the UART is half duplex, it can either send or recieve data. It can't
;* do both simoutaneausly. When idle it will automatically recieve incoming
;* data, but if it is transmitting data while incoming data arrives, it will
;* ignore it. Also, if u_transmit is called without waiting for the 'READY' bit
;* in the 'u_status' register to become cleared, it will abort any pending
;* reception or transmittal.
;*
;*
;* *** Initialization
;*
;* 1. Call uart_init
;* 2. Enable global interrupts (with 'sei')
;*
;* *** Receive
;*
;* 1. Wait until RDR in 'u_status' becomes set
;* 2. Read 'u_buffer'
;*
;* *** Transmit
;*
;* (0. Initialize the UART by executing uart_init and sei)
;* 1. Wait until READY in 'u_status' becomes clear
;* 2. Set 'u_buffer'
;* 3. Call 'u_transmit'
;*
;**************************************************************************
.include "1200def.inc"
;***** BAUD-rate settings
;BAUD-RATES @1MHz XTAL AND R=1
;.equ N=104 ; 9600
;.equ N=52 ;19200
.equ N=26 ;38400
.equ C=1 ;Divisor
.equ R=1 ;R=1 when C=1
;***** UART Global Registers
.def u_buffer =r14 ;Serial buffer
.def u_sr =r15 ;Status-register storage
.def u_tmp =r16 ;Scratchregister
.def u_bit_cnt =r17 ;Bit counter
.def u_status =r18 ;Status buffer
.def u_reload =r19 ;Reload-register (internal - do not use)
.def u_transmit =r20 ;Data to transmit
;***** Bit positions in the Status-register
.equ RDR=0 ;Receive data ready bit
.equ TD=6 ;Transmitting data (internal - read-only)
.equ BUSY=7 ;Busy-flag (internal - read-only)
;**************************************************************************
;*
;* PROGRAM START - EXECUTION STARTS HERE
;*
;**************************************************************************
.cseg
.org $0000
rjmp start ;Reset handler
.org INT0addr
rjmp ext_int0 ;External interrupt handler
.org OVF0addr
rjmp tim0_ovf ;Timer0 overflow handler
.org ACIaddr
reti ;Analog comparator handler (Not Used)
;**************************************************************************
;*
;* EXT_INT0 - External Interrupt Routine 0
;*
;*
;* DESCRIPTION
;* This routine is executed when a negative edge on the incoming serial
;* signal is detected. It disables further external interrupts and enables
;* timer interrupts (bit-timer) because the UART must now receive the
;* incoming data.
;*
;* This routine sets bits in the GIMSK, TIFR and TIMSK registers. In this
;* code when the bits are set, it overwrites all other bits. This is done
;* because of the lack of available cycles when it operates at low clock
;* rate and high baudrates.
;*
;*
;* Total number of words : 12
;* Total number of cycles : 15 (incl. reti)
;* Low register usage : 1 (u_sr)
;* High register usage : 4 (u_bit_cnt,u_tmp,u_status,u_reload)
;*
;**************************************************************************
ext_int0:
in u_sr,SREG ;Store Status Register
ldi u_status,1<<BUSY ;Set busy-flag (clear all others)
ldi u_tmp,(256-(N+N/2)+(29/C));Set timer reload-value (to 1.5
out TCNT0,u_tmp ; bit len). 29 = time delay that
; have already been used in this
; interrupt plus the time
; that will be used by the time
; delay between timer interrupt request
; and the actual sampling of the first
; data bit.
ldi u_tmp,1<<TOIE0 ;Set bit 1 in u_tmp
out TIFR,u_tmp ; to clear T/C0 overflow flag
out TIMSK,u_tmp ; and enable T/C0 overflow interrupt
clr u_bit_cnt ;Clear bit counter
out GIMSK,u_bit_cnt ;Disable external interrupt
ldi u_reload,(256-N+(8/C)) ;Set reload-value (constant).
out SREG,u_sr ;Restore SREG
reti
;**************************************************************************
;*
;* TIM0_OVF - Timer/Counter 0 Overflow Interrupt
;*
;*
;* DESCRIPTION
;* This routine coordinates the transmition and reception of bits. This
;* routine is automatically executed at a rate equal to the baud-rate. When
;* transmitting, this routine shifts the bits and sends it. When receiving,
;* it samples the bit and shifts it into the buffer.
;*
;* The serial routines uses a status register (u_status): READ-ONLY.
;* BUSY This bit indicates whenever the UART is busy
;* TD Transmit Data. Set when the UART is transmitting
;* RDR Receive Data Ready. Set when new data has arrived
;* and it is ready for reading.
;*
;* When the RDR flag is set, the (new) data can be read from u_buffer.
;*
;* This routine also sets bits in TIMSK and TIMSK. See ext_int0 description
;*
;*
;* Total number of words : 35
;* Total number of cycles : min. 18, max. 28 - depending on if it is
;* receiving or transmitting, what bit is
;* pending, etc.
;* Low register usage : 2 (u_sr,u_buffer)
;* High register usage : 4 (u_bit_cnt,u_tmp,u_status,u_reload)
;*
;**************************************************************************
tim0_ovf:
in u_sr,SREG ;Store statusregister
out TCNT0,u_reload ;Reload timer
inc u_bit_cnt ;Increment bit_counter
sbrs u_status,TD ;if transmit-bit set
rjmp tim0_receive ; goto receive
sbrc u_bit_cnt,3 ;if bit 3 in u_bit_cnt (>7) is set
rjmp tim0_stopb ; jump to stop-bit-part
sbrc u_buffer,0 ;if LSB in buffer is 1
sbi PORTD,PD4 ; Set transmit to 1
sbrs u_buffer,0 ;if LSB in buffer is 0
cbi PORTD,PD4 ; Set transmit to 0
lsr u_buffer ;Shift buffer right
out SREG,u_sr ;Restore SREG
reti
tim0_stopb:
sbi PORTD,PD4 ;Generate stop-bit
sbrs u_bit_cnt,0 ;if u_bit_cnt==8 (stop-bit)
rjmp tim0_ret ; jump to exit
tim0_complete:
ldi u_tmp,1<<INT0 ;(u_bit_cnt==9):
out GIMSK,u_tmp ;Enable external interrupt
clr u_tmp
out TIMSK,u_tmp ;Disable timer interrupt
cbr u_status,(1<<BUSY)|(1<<TD) ;Clear busy-flag and transmit-flag
tim0_ret:
out SREG,u_sr ;Restore status register
reti
tim0_receive:
sec ;Set carry
sbis PIND,2 ;if PD2=LOW <=== SAMPLE HERE
clc ; clear carry
ror u_buffer ;Shift carry into data
in u_tmp,SREG ;Store SREG
cpi u_bit_cnt,9 ;if u_bit_cnt!=9 (must sample stop-bit)
brne tim0_ret ; exit interrupt
out SREG,u_tmp ;Get old SREG
rol u_buffer ;Rotate back data (to get rid of the stop-bit)
sbr u_status,1<<RDR ;Clear busy-flag
rjmp tim0_complete
;**************************************************************************
;*
;* uart_init - Subroutine for UART initialization
;*
;*
;* DESCRIPTION
;* This routine initializes the UART. It sets the timer and enables the
;* external interrupt (receiving). To enable the UART the global interrupt
;* flag must be set (with SEI).
;*
;*
;* Total number of words : 8
;* Total number of cycles : 11 (including the RET instructions)
;* Low register usage : None
;* High register usage : 2 (u_tmp,u_status)
;*
;**************************************************************************
uart_init:
ldi u_tmp,R
out TCCR0,u_tmp ;Start timer and set clock source
ldi u_tmp,1<<INT0
out GIMSK,u_tmp ;Enable external interrupt 0
in u_tmp,1<<ISC01
out MCUCR,u_tmp ;On falling edges
clr u_status ;Erase status-byte
ret
;**************************************************************************
;*
;* uart_transmit - Subroutine for UART transmittal
;*
;*
;* DESCRIPTION
;* This routine initialize the UART to transmit data. The data to be sent
;* must be located in u_transmit.
;*
;* Warning: This routine cancels all other transmittions or receptions.
;* So be careful. If a byte is being received and the timer interrupt is
;* currently running, the tranmittion may be corrupted. By checking the
;* READY-bit and/or TD-bit in the u_status register for safe transmissions.
;*
;* This routine also sets bits in TIMSK and TIMSK. See ext_int0 description
;*
;*
;* Total number of words : 13
;* Total number of cycles : 17 (including RET)
;* Low register usage : 1 (u_buffer)
;* High register usage : 4 (u_bit_cnt,u_tmp,u_transmit,u_reload)
;*
;**************************************************************************
uart_transmit:
ldi u_status,(1<<BUSY)|(1<<TD) ;Set only busy- and transmit flag
clr u_tmp
out GIMSK,u_tmp ;Disable external interrupt
ser u_bit_cnt ;Erase bit-counter (set all bits)
mov u_buffer,u_transmit ;Copy transmit-data to buffer
ldi u_tmp,1<<TOIE0 ;Set bit 1 in u_tmp
out TIFR,u_tmp ; to clear T/C0 overflow flag
out TIMSK,u_tmp ; and enable T/C0 overflow interrupt
ldi u_reload,(256-N+(8/C)) ;Set reload-value
ldi u_tmp,(256-N+(14/C)) ;Set timer delay to first bit
out TCNT0,u_tmp ;Set timer reload-value (1 bit)
cbi PORTD,PD4 ;Clear output (start-bit)
ret
;**************************************************************************
;*
;* Test/Example Program
;*
;* This example program can be used for evaluation of the UART agains a PC
;* running a terminal emulator. If a key is pressed on the PC keyboard, the
;* message 'You typed <characher>' is sent back. The character is also
;* presented on port B.
;*
;**************************************************************************
.def tmp=r21 ;Temp. register
.def buffer=r22 ;Recieved byte
.def adr=r23 ;EEPROM Address
start: ser tmp ;Initialize
out PORTD,tmp ;Set port D as input with pullups
sbi DDRD,DDD4 ; except PD4 -> output with 1's
out DDRB,tmp ;Set port B as output with 1's (LED-off)
out PORTB,tmp
rcall uart_init ;Init UART
sei ;Enable interrupts
idle: sbrs u_status,RDR ;Wait for Character
rjmp idle
mov buffer,u_buffer ;Get recieved character
out PORTB,u_buffer ;Output the byte on port B
ldi adr,example_data ;Set/Restore pointer to EEPROM data
loop: out EEAR,adr ;Set the EEPROM's address
sbi EECR,EERE ;Send the Read strobe
in u_transmit,EEDR ;Put the data in the transmit register
rcall uart_transmit ;And transmit the data
wait: sbrc u_status,TD ;Wait until data is sent
rjmp wait
inc adr ;Increase pointer
cpi adr,example_data+12 ;Reached byte 12? (End?)
breq idle ; Yes, wait for new char.
cpi adr,example_data+10 ;Reached byte 10?
brne loop ; No, jump back
mov u_transmit,buffer ;Put data in transmit register
rcall uart_transmit ;And transmit it
wait2: sbrc u_status,TD ;Wait until data is sent
rjmp wait2
rjmp loop ;Continue sendig chars
;**************************************************************************
;*
;* Test/Example program data.
;*
;* This is the data that will be sent back when a character is recieved.
;*
;**************************************************************************
.eseg
example_data:
.db 89 ;'Y'
.db 111 ;'o'
.db 117 ;'u'
.db 32 ;' '
.db 116 ;'t'
.db 121 ;'y'
.db 112 ;'p'
.db 101 ;'e'
.db 100 ;'d'
.db 32 ;' '
.db 13 ;<CR>
.db 10 ;<LF>
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -