

    ; Dick Cappels' project pages projects@cappels.org 
    ; Copyright 2004 Richard Cappels
    ; Version LDCbutons040904C_updated 
    ; This version corrects a typo in the MoreToCopy routine that prevents
    ; assembly on later versions of the assemblers. The error was fround by Allun Bennett.
    ; You should be able to copy and paste this text into an assembler file.
    ;
    ; No liability for use of this code is assumed. See full disclaimer on web site at 
    ; www.projects.cappels.org.
    ;
    ; Please email comments or corrections to projectsxcappels.org where x is the @ sign. 
    ; (the above is to mask the address from email address gathering robots).
    ;
    ;HOME
    ;
    ; Begin assembly souce.
    ;
    ; Character receive and display for Truly MTC-C162DPLY-2N 2 line X 16 char LCD
    ; and two button which send an ASCII "R" and an ASCII carriage return.
    ; Two include files are needed."2x16lcd.inc" is available on projects.cappels.org, and
    ; "2313def.inc" is available from Atmel as part of AVR Studio.
    ; Version LDCbutons040904C Has a larger UART receive buffer and two button inputs to provid
    ; control signals vai the UART. If the two buttons are not needed, don't connect them, and thsi
    ; will be a receive-only application.
    ;
    ; Requires AT90S2313 or equivalent with 4 MHz clock.
    ;
    ; Below are the connections to the Truly Display
    ; Pin 	Function (connection)
    ; 1	GND
    ; 2	VCC (+5V)
    ; 3	Contrast (0to +5V from wiper of 10k pot)
    ; 4	RS (Register Select 1=data 0=command)
    ; 5	R/W (tie to ground for write-only)
    ; 6	OE (Enable - data clocked on neg transition)
    ; 7-10	D0 - D3 lower 4 data bits (not used - ground these)
    ; 11-14	D4-D7	upper 4 data bits (connects to AVR90S2313 PB4 through PB7 respectively).
    ; 
    ; The AT90S2313 routines to control the display, based on the Hitachi HD44780, and the initialization 
    ; code in particular, are based on code originally published by Richard Hosking. 
    ; The display is driven in the 4 bit mode. The Truly display uses a Samsung KS0070B controller
    ; that appears to differ from the Hitachi HD44780 controller in that the Samsung requires an
    ; additional 4 bit write operation during "Function set" when in 4 bit mode. This was accomplished 
    ; by writing the "Function set" command twice to the controller twice instead of once.
    ; I suspect that this would work without modification with the Hitachi
    ; controller as the second write would be a redundant command for the Hitachi controller. 
    ; 
    ; Note that 
    ; 
    ; Below re the connections for the AT90S2313
    ; Pin		Function (Connection)
    ; VCC		+5V (decoupled)
    ; GND		ground
    ; XTAL1		Clock (see data sheet)
    ; XTAL2		Clcok (see data sheet)
    ; TXD		Serial out (RS-232 inverting buffer to remote device - NOT USED)
    ; RXD		Serial in (RS-232 inverting receive buffer)
    ; PD2          "R" buton. When this pin is momentarily groudned, an ASCII "R" is sent via UART.
    ; PD3          'Return" button. When this pin is momentarily grounded, an ASCII CR is sent.
    ; PD4-6        Unassigned (not connected)
    ; PD6
    ; PB0,PB1	Unassigned (not connected)
    ; PB2		R/S pin on Truly LCD (Truly module pin 4)
    ; PB3		OE pin on Truly LCD (Truly module pin 6)
    ; PB4-PB7	D4-D7 on Truly LCD (Truly module pins 11-14 respectively)
    ; 
    ; Note that unused I/O pins are pulled up with the weak pull-up (direction bits 
    ; set to inputs, data bits written with a logic "1").
    ; 
    ; Behavior of the receive and display code:
    ; 
    ; Upon the application of power, communications protocol and code date and revision letter are
    ; displayed. "9600" Refers to 9600 baud, "1" refers to 1 stop bit being required, and 
    ; "N" refers to no parity bits are expected. The next 7 characters uniquely identify the
    ; firmware revision level.
    ; 
    ; Incoming characters are display on the LCD module on the lower of the two lines on the 
    ; display, referred to here as line two. The upper line is referred to as line 1. the first
    ; 16 characters are displayed and any additional characters are not. Control characters, 
    ; defined as those represented by ASCII values below $1F are not displayed. 
    ; 
    ; Linefeed characters cause the display to wait for the first non-control
    ; character following the linefeed before copying line 2 to line 1 (scrolling the display),
    ; clearing line 2 and positioning the cursor at the start of line 2.
    ; 
    ; The display responds to carriage return characters ($0D)by placing the cursor to the 
    ; fist (leftmost) position of line two.
    ; 
    ; The display cursor is on.
    ; 
    ; A note about how this code works:
    ; 
    ; The display is two lines of 16 characters. 
    ; 
    ; The maximum data rate for allowable operation is 9600 baud with 1 stop bit and no parity.
    ; 
    ; All display write operations are "open loop" timing. That is to say that delay loops are
    ; used to assure that display write operations don't proceed faster than the display can handle.
    ; Therefore, if the controller's clock rate is changed, the timing routines will have to be 
    ; modified accordingly. Mr. Hosking has thoughtfully indicated the delay time expected for each
    ; routine.
    ; 
    ; Upon coming out of reset, the display is initialized and the data format and firmware revision
    ; are displayed.
    ; 
    ; An 8 character circular buffer is used to capture incoming characters and a 16 line buffer 
    ; is is used to store a copy of the line two (lower line) of the display so it can be copied
    ; to line one during scrolling. The circular buffer is necessary because scrolling of the display
    ; takes 1.8 character times at 9600 baud, and without the buffer, a character would be lost each 
    ; time display content is scrolled.
    ; 
    ; Incoming characters cause and interrupt and each character is stored in an 8 character 
    ; circular buffer. 
    ; 
    ; The main loop of the program, named "forever" continuously checks to see if new characters 
    ; have been written to the buffer and processes new characters. Displayable characters, 
    ; defined as those with ASCII codes above $1F, are written to display line 2 and to a 16 
    ; character buffer in RAM. Linefeed and carriage return are the only control characters that
    ; recognized. Carriage returns cause the line buffer to be erased and its pointer set to the start
    ; of the buffer. Linefeeds cause a flag to be set, which will cause display scrolling when the first
    ; displayable character after the linefeed is read from the circular buffer
    ; 
    ; Display scrolling, in response to the first character read from the circular buffer after a
    ; linefeed calls the routine "linefeed". Linefeed scrolls the display upward one line, leaving
    ; line two clear with the cursor positioned at the start of the line. In scrolling the display
    ; linefeed copies the line buffer, which contains a copy of the contents of line two of the display
    ; to line one, resets the line buffer by clearing it and setting the pointer to the start, then it
    ; clears line two and sets the cursor to the start of line two. 
    ; 
    ; 
       
    .include "2313def.inc"
       
       
    .def	charcount	=r1	;Number of characters displayed on line being written
    .def    temp    	=r16    ;Temporary register.(May not be R17).
    .def    temp2  		=r18	;Temporary register.
    .def	outchar		=r19;	;Char to send by UART.
    .def	inchar		=r20	;Char received by UART
    .def	flagreg		=r21	;Flags
    .def	charbuf		=r22	;Char read from circular buffer
    .def	gpcount		=r23	;Genral purpose counter (was YH in earlier version)
    ;	YL			;UART circular buffer write ponter.
    ;	ZL			;UART circultar buffer read pointer
    ;	XL			;16 char line buffer pointer
       
       
    				;Assign I/O pins.
    .equ    OE		=8	;Bit 3 port B display enable (also directly addressed).
    .equ    RS		=4	;Bit 2 in port B display register select (also directly addressed).
    .equ	Returnbut	=3	;Button used to send return ($0D) via UART.
    .equ	Rbut		=2	;Button used to send ASCII "R" ($52) vai UART.
    				;Allocate buffers
    .equ	lbufsiz		=16
    .equ	circbufsiz	=64
       
    .equ	cbufbot		=$60	;Bottom of circular UART receive buffer.
    .equ	cbuftop		=cbufbot + circbufsiz   ;Top of circular UART receive buffer.
       
    .equ	lbufbot		=cbuftop + 1		;Bottom of display line buffer.
    .equ	lbuftop		=lbufbot + lbufsiz 	;Top of display line buffer.
       
       
    		;Baudrate Calculation
    .equ	clock		= 4000000		;clock frequency
    .equ	baudrate	= 9600			;choose a baud rate
    .equ	baudconstant	= (clock/(16*baudrate))-1
       
       
       
    ;Flagreg bit assignments
    ;	bit	0		Pending linefeed if high.
    ;	bit	1	
    ;	bit	3
    ;	bit	4
    ;	bit	5
    ;	bit	6
    ;	bit	7
       
    ;Memory uage:
    ;Ring buffer from $60 through $67
    ;Line buffer from $70 to $7F
       
    ;******************************
       
    .cseg
    .org	$00
       
    	rjmp	start		; Reset 
    	rjmp	start
    	rjmp	start
    	rjmp	start
    	rjmp	start
    	rjmp	start
    	rjmp	start
    	rjmp	UartRecInt	;UART interrupt 
       
       
    .include "2x16lcd.inc"
       
       
    HelloString:	;TEXT TO BE TYPED ON FIRST LINE WHEN POWER IS APPLIED
    .db	"LDCbutons040904C "
    .db	00,00
       
    	
    start: 
    	ldi 	temp,RAMEND      ;Init Stack Pointer
    	out 	SPL,temp
       
    	ldi	temp,0b00000011	;Weak pullups on inputs
    	out	PORTB,temp
    	ldi     temp,0b11111100    
            out     DDRB,temp	;PORTB = all outputs except bits 0,1 
            
            ldi	temp,0b11111111	;Weak pullups on inputs
            out	PORTD,temp
            ldi	temp,0b00000000	;PORTD - all inputs
            
    	ldi	flagreg,$00	;Set all flags to zero.
    	ldi	temp,$00
    	mov	charcount,temp
       
    	rcall	ClearLineBuffer	;Initialize line buffer.
    	rcall	InitDisplay	;Initialize LCD display module,
       
    	ldi	temp,baudconstant	
    	out	ubrr,temp	;load baudrate
    	sbi	ucr,txen	;Enable the UART transmitter
    	sbi	ucr,rxen	;Enable the receiver..
       
      	rcall	sendhello	;write line 1 power-up information
    	rcall	Hometwo		;Position cursor for input on line two.	
    	clr	XH		;Clear XH, YH, ZY for processors with 16 bit RAM addressing
    	clr	YH
    	clr	ZH
    	ldi	YL,cbufbot	;Set Y and Z circ buff pointers to bottom.
    	ldi	ZL,cbufbot
    	
    			
            sbi	UCR,7		;Emable UART Interrupt.
            sei			;Global interrupt flag set (enabled).
       
    forever:	;Waiting for new data from circular buffer, check buttons
    	rcall	checkbut	;Check to see if any buttons are down.
    	 			;Get waiting char from circular buffer if there is one.
    	cp	YL,ZL		;Is circular buffer read pointer alredy pointing to latest entry?
    	breq	Buffempty	;If so, there is no new data in the buffer.
    	ld	charbuf,Z+	;If pointers are not equal, then read next char in buffer.
    	cpi	ZL,cbuftop + 1	;Advance circular buffer read pointer to next value.
    	brne	NoZeroZL	;If end of buffer, wrap around to start of buffer.
    	ldi	ZL,cbufbot
    NoZeroZL:
    	
    		;Handle the new character as either a cotnrol char or a displaybale char.
    		;If bufchar is a control char,test for CR and LF
    	
    	cpi	charbuf,$1F	;If not a control char branch to displayable char routine..
    	brpl	ItsDisplayable
       
    				
    	cpi	charbuf,$0D	;If this is a carriage return character, 
    	brne	noCR		;Set cursor to start of bottom line.
    	ldi	XL,lbufbot	;Its a CR so set ponters back to start of line.
    	ldi	temp,$00
    	mov	charcount,temp
    	rcall	hometwo		;Put cursor back in first column of line two.	
    noCR:
    	
    	cpi	charbuf,$0A	;If its a linfeed char then
    	brne	NotALineFeed	;set linfeed pending flag.
    	ori	flagreg,0b00000001	
    NotALineFeed:
    	
    	rjmp	Buffdone
       
    ItsDisplayable:	;If not a control char then do line feed if pending then
    			;write to display and to line buffer.
    		
    	sbrc	flagreg,0	;If linefeed i spending, then do it	
    	rcall	linefeed
    		
    	cpi	XL,lbuftop+1	;Don't store if buffer at limit.
    	breq 	Xfull
    	st	X+,charbuf
    	mov	temp,charbuf
    	rcall	SendData	
    Xfull:
    	
    Buffdone:
    Buffempty:
    	rjmp	forever	
       
       
    	
    SendHello:	;Send HelloString	
      	rcall	Homeone
    	ldi     ZH,high(2*HelloString)	; Load high part of byte address into ZH
    	ldi     ZL,low(2*HelloString)	; Load low part of byte address into ZL
    moretosend:
    	lpm				; Load byte from program memory into r0
    	tst	r0			; Check if we've reached the end of the message
    	breq	finishsendstering	; If so, return
    	mov	temp,r0
    	rcall	SendData
    	adiw	ZL,1			; Increment Z registers
    	rjmp	moretosend
    finishsendstering:
        	ret
         
       
    linefeed:	;Handle a linefeed char
    		;Clear line 1 (top line), copy line two to line one, clear
    		;line two, the position the cursor in first column of line two.
    	push	gpcount
    	rcall	hometwo		;Put cursor at start of line 2 so it can be cleared.
    	ldi	gpcount,$10		;Number of chars in line.
    clearmore:
       
    	ldi	temp,$20	;Fill line with spaces (erase).
    	rcall	SendData
    	dec	gpcount
    	brne	clearmore
       
    	rcall	homeone		;Copy line buffer to line 1, reset XL to bottom.
    	ldi	XL,lbufbot
    	ldi	temp,$00
    	mov	charcount,temp
    MoreToCopy:
    	ld	temp,X+
    	rcall	SendData
    	cpi	XL,lbuftop + 1
    	brne	MoreToCopy
    	rcall	ClearLineBuffer
    	andi	flagreg,0b11111100	;Clear linefeed pending and enable flagsb.
    	rcall	hometwo
    	pop	gpcount
    	ret			;Done
       
    	
    ClearLineBuffer:	;Fill line buffer with spaces, set XL to bottom.
    	ldi	XL,lbufbot
    	ldi	temp,$00
    	mov	charcount,temp
    	ldi	temp,$20
    MoreToSpace:
    	st	X+,temp
    	cpi	XL,lbuftop + 1
    	brne	MoreToSpace
    	ldi	XL,lbufbot
    	ret
    	
       
    recchar:
    	sbis	usr,rxc		;Wait for a char.
    	rjmp	recchar
    	in	inchar,udr	;Read the char.
    	ret			
    	
    	
       
    emitchar:
    	sbis	usr,udre	;wait until the register is cleared
    	rjmp	emitchar   
    	out	udr,outchar	;send the byte
    	ret			;go back
    	
    	
    UartRecInt:	;Uart interrupt service -Write received char into a circular buffer
    	push	temp
    	in	temp,sreg
    	push	temp
    	rcall	recchar			;Get the char from the UART.
    	st	Y+,inchar
    	cpi	YL,cbuftop + 1
    	brne	NoZeroYL
    	ldi	YL,cbufbot
    NoZeroYL:
    	pop	temp
    	out	sreg,temp
    	pop	temp
    	reti
    	
       
       
       
    checkbut:				;Check for button down; if so, send by UART
       
    	sbis	PIND,Returnbut
    	rjmp	sendreturn
    	sbis	PIND,Rbut
    	rjmp	sendr
    	ret
    	
    sendreturn:
    	ldi	outchar,$0D
    	rcall	EimtAndWait
    ISRT:
    	sbis	PIND,Returnbut
    	rjmp	ISRT
    	rcall	wait20ms
    	ret
    	
    sendr:
    	ldi	outchar,'R'
    	rcall	EimtAndWait
    ISR:
    	sbis	PIND,Rbut
    	rjmp	ISR
    	rcall	wait20ms
    	ret
       
    EimtAndWait:				
    	rcall	emitchar
    	rcall	wait20ms
    	ret
    	
    wait20ms:				;20 millisecond debounce timer
    	clr	temp
    dlx0:	clr	outchar				
    dlx1:	dec	outchar
    	brne	dlx1
    	dec	temp
    	brne	dlx0
    	ret
       
    .exit
       
       
    ;HOME


    ;http://projects.cappels.org/
    ;Richard Cappels






