; Dick Cappels' project pages projects@cappels.org 
; 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).
; 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

;Richard Cappels