Full Version : Pao & Yang MAN - Mini Area Network (ASM)
avr >>COMMUNICATIONS & WEB PROJECTS >>Pao & Yang MAN - Mini Area Network (ASM)


<< Prev | Next >>

Admin5- 04-20-2006
Introduction
The Mini Area Network (MAN) utilizes a token ring protocol to implement a simple chat function for a small cluster of computers (no more than four computers). Using a cost efficient MAN network costing less than a few hundred dollars allows a small group of users to chat over a secure network and not have to worry about expensive and complicated installation and network security.

Aspects of Operation

The idea is to use a single ATMEL AT90S8515 controller for each individual computer on the cluster and to connect the controllers to the computers using serial cables. The controllers themselves then connect to each other using the output and input ports on the controller boards forming the MAN. The design can be divided into six modules: Initializing the Loop, Transmitting the Token, Inputting Characters, Transmitting Characters, Receiving Characters and Displaying the Messages. Each module handles some form of communication between devices, either communication between a computer and a controller or communication between controllers.

Initializing the Loop

The module which initializes the loop is used to setup the communications link between the controllers and to prepare the loop for use. The basic scheme is to designate one of the controllers as the master by pressing the initialization button on one of the controllers. The master then sends out a initialization token on his output port, which he transmits until he receives it on his input port. The slave controllers each accept the initialization token and pass it on to the next controller on the ring.

Once the initialization token has been received by the slave controllers, they wait for the acknowledge character so they can jump into the main code loop to wait for the token or the identification number for new messages. When the master receives the acknowledge on his input port he begins to transmit the token. When the slaves receive a non acknowledge character on their input port they jump to either receive a token or a new character.

Transmitting the Token

The module which handles token passing uses the three signals to pass the token: the token signal, the no token signal and the acknowledge signal. The token itself is used to decide who has permission to send a message at any one time. The decision of who gets to speak next in this case is not arbitrary, the token is simply given to the next person clockwise from the last sender. It is important to note though that once the user is done sending text, he must pass on the token to give others the opportunity to speak.

The token is passed by the current token holder by transmitting the token to the next controller. Upon receiving the token, the controller sends out a no token signal to tell the following computers that there is no token. Controllers not transmitting the token or receiving the token automatically pass on the no token signal. This no token signal ensures the current token holder that the token receiver has definitely received the token. Once the current token holder receives the no token on his input port, he sends an acknowledge character to the controller receiving the token for a set time period to ensure the token receiver will have adequate time receive the signal. Once the set time is over, he sends the no token signal. This signal tells token receiver that he is free to send the next controller the token if he does not need it.

Inputting Characters

The module which takes in input characters from the user interface communicates between the computer and individual controller using the UART protocol through the RS-232 port. To get the user's keyboard character entries, every time a key is pressed the controller throws a UART receive interrupt and appends the new character onto the current message to be sent. In the interrupt code, each character received from the computer over the serial cable is echoed back onto the screen and at the same time is stored into the user buffer.

When the first character is received from the UART, the not done flag is set. This flag indicates that a message is in progress so the user's message must be printed out when the display is refreshed (refer to the documentation on displaying messages). Whenever this not done flag is set a null character is also appended to the end of the user's own message every time a new character is added to the user's message buffer. This null character marks the end of the incomplete message and prevents remnants of the old message from being printed when the display is refreshed. The message is concluded when the user presses the enter key, which clears the not done flag and sets the full buffer flag. The cleared not done flag tells the controller not to print the user's completed message anymore because it should be in the main message buffer by this time and the full buffer flag reminds the controller that the user buffer is full and ready for transmission next time the controller receives the token. Additionally, after the enter key is pressed the ASCII characters for a carriage return, a line feed and a terminating null are also appended to the end of the message.

Transmitting Characters

The module which transmits the characters to be sent to the other controllers is similar to the initialization scheme of the sender transmitting bytes until he receives them on his input port. The transmission of characters is initiated by transmitting the sender's user identification number. An integral part of the transmission scheme is also to input the user's inputted message into the local buffer of messages. The transmit stage is started when the user receives the token from the person before him on the ring. If the full buffer flag indicates that the user's message buffer is full, the user will begin to transmit the contents of his buffer to the other users. Otherwise the user will pass the token on to the next user to allow him to send a message.

In order to setup the sequence for transmitting characters, the user downloads the locations of string buffer and the user buffer. The new buffer pointer is added to the general string buffer address to get the location of the newest message in the circularly referenced string buffer message queue. The new buffer location is where the contents of the user buffer will be stored, overwriting the oldest message in the string buffer. To save the message locally, the user identification number is saved at this new buffer pointer location in the string buffer, followed by two formatting characters. To begin sending characters, the sender transmits his identification number through the output port, where it is passed to the other users on the token ring to notify them to begin receiving and to notify them who is sending. The sender transmits the identification number until he receives it on his input port.

Once the sender receives the identification number, he then begins to transmit characters. As he transmits each character, a copy of the character is simultaneously saved locally into the new buffer pointer location. Each character is followed by an acknowledge character, which is not saved locally. The purpose of this acknowledge is to differentiate when the transmission of one character ends and the next begins. Otherwise, sending two identical characters in a row would result in only one reaching the other users. Once the null terminating character has been received by all of the users and saved locally, the screen is refreshed, and the token holder passes the token to the next user in the ring.

Receiving Characters

The module which receives the characters sent by another controller is closely meshed with the module for transmitting the characters and much of the components are similar. The receiving module differentiates between two transmissions of the same characters through the acknowledge signal sent in between each character transmission. When the controller receives one of the three other user identification numbers on his input port, he jumps into receiving mode.

The first task of receiving the characters is to allocate a place in the string buffer in which to store the incoming message by downloading the address of the string buffer and then adding the value of new buffer pointer to it to place the message into the string buffer by overwriting the oldest message. The receiving controller then places the identification number of the sending controller into this buffer along with two formatting characters. The receiver then waits for the next character, stores this character in the new buffer, and passes it to the next controller. The receiver then waits for the accompanying acknowledgment and passes it on with out storing it. Upon receiving the terminating null character of the message, the receiver refreshes his own screen updated with this new message appended and the oldest message overwritten. Once the screen is refreshed the code prepares to receive either new messages or the token.

Displaying Text

The module which displays the messages is handled by the refresh function and the UART data register empty interrupt. The refresh function points the printer pointer to the correct characters to print the messages in the proper order and the UART interrupt transmits the data to the user's computer screen. The refresh function uses three pointers, old buffer, new buffer and print buffer to print out the current list of messages and also uses the address of the user's message buffer to print out the message the user is currently typing.

Old buffer is the pointer to the first character of the oldest message in the string buffer while new buffer is the pointer to the first character of the newest message, and print buffer is the pointer to the first character of the current message in the string buffer that is being printed. To refresh the text, the print buffer pointer is first set to old buffer so that the oldest message is printed up to its first terminating null character. Then the buffer size is added to print buffer so it corresponds to the beginning of the next message, and so forth. A modulo type function is applied to the print buffer so that the print buffer is reset to zero when it reaches the end of the string buffer. The pointer is moved until the pointer print buffer has made a full circuit around the string buffer and has printed the la-*test*-('") message. Once the print buffer pointer has made a full circuit, the user's buffer is added to the end if the user is currently in the middle of typing a sentence to allow the user to continue typing his message, seemingly uninterrupted.

Conclusion

The Mini Area Network performs quite satisfactorily in terms of functionality. The flicker from updating the display would probably be the first aspect in the project that should be changed as the flicker sometimes can be distracting when typing in mid-sentence. Additionally, on some occasions the display will sometimes not print the carriage return line feed and appends the following message to the same line, although this was not experienced often enough to be able to debug the situation. Most likely it can be attributed to an error in the UART transmission because on following refreshes the problem is corrected. Overall it was great to finally get a project which initially seemed impossible to actually work quite well.

Future Features

The feature that would increase the functionality of the Mini Area Network the most would be a Graphical User Interface. It was hoped that the GUI could be added to the current design instead of the Hyper-Terminal interface. A chat specific GUI would allow for a more seamless display which did not flash quite so much during display updating, possibly storing the messages in a buffer on the computer itself. After downloading the communication application programming interface from the Sun Java site, however it was decided that a chat GUI would clearly be beyond the scope of this project.

Another feature for future versions would be an increased buffer length so message length could be much longer and more messages could be saved. Since the message buffer already maximizes the RAM on the board, a larger message buffer would require an external RAM off the board or another method of organizing the message buffer. The RAM off the board option would be complicated because the board does not have available ports. Additionally the option of having user names besides user1 and user2 could be added too.


Link: http://instruct1.cit.cornell.edu/courses/e.../yang/main.html

CODE

;*************** Mini Area Network *****************************************
;intial commented version April 24, 2000 3:16 pm

.nolist
.include "c:\AVRtools\appnotes\8515def.inc"
.list

.def save   = r1  ;save SREG
.def zero  = r2  ;null value
.def temp  = r16  ;temporary variable
.def userid  = r17  ;user id  
.def newbuf  = r18  ;pointer to newest message
.def oldbuf  = r19  ;pointer to oldest message
.def count  = r20  
.def char  = r21  ;empty for now
.def rcvbuf   = r22  ;offset to receive (user) buffer
.def printbuf = r23  ;buffer being printed
.def TXbusy  = r24  ;transmitting flag for UART
.def RXchar  = r25  ;received character from UART
.def fullbuf  = r26  ;buffer full flag
.def TXflash  = r27  ;set if transmitted string is in flash, clear otherwise
.def YL  = r28
.def YH  = r29  
.def ZL  = r30
.def ZH  = r31

.equ idnum  = 17
.equ device1  = 17  ;user identification number
.equ device2  = 18  ;user identification number
.equ device3  = 19  ;user identification number
.equ device4  = 20  ;user identification number
.equ baud96  = 25  ;baud rate
.equ enter  = 13  ;enter key
.equ bkspace  = 8  ;backspace
.equ start  = 2  ;start of text
.equ null  = 0   ;end of text
.equ ack  = 6   ;acknowledge
.equ token  = 11   ;token
.equ notoken  = 12
.equ char  = ' '  ;first character in ascii table
.equ idaddend = 127  ;last character in ascii table
.equ escape  = 27  ;escape
.equ bufmax  = 150  ;biggest offset for pointer to message
.equ bufsize  = 25  ;size of each message buffer
.equ azero  = '0'

;***************************************************************************
.dseg

stringbuf:  .byte 150
userbuf:  .byte 25

;***************************************************************************
.cseg
.org $0000
rjmp RESET
reti
reti
reti
reti
reti
reti
reti
reti
rjmp UARTRX
rjmp UARTEM
reti
reti

userid:  .db "User ", 0x00
punct:  .db ": ", 0x00
crlf:  .db 0x0d, 0x0a, 0x00
chat:  .db "Chat Room -- 1", 0x00
havetoken: .db "I am the token", 0x00

;***************************************************************************
RESET: ;ALWAYS setup a stack pointer
 ldi Temp, LOW(RAMEND)  ;setup stack pointer
 out  SPL, Temp
 ldi Temp, HIGH(RAMEND)
 out SPH, Temp

;setup Transmit Port
 ser temp   ;set PORTC as output pins
 out DDRA, temp

;setup Master ID Port
 clr temp
 out DDRB, temp

;setup Receive Port
 clr  temp   ;set PORTC as input pins
 out DDRC, temp

;setup UART Port
 ser  temp   ;set PORTD as output pins
 out DDRD, temp

;setupt UART
 ldi temp, 0b10011000 ;set UART Empty and UART Recieve Interrupt
 out UCR, temp
 ldi temp, baud96  ;set UART Baud Rate
 out UBRR, temp

;initialize variables
 clr newbuf   ;clear registers
 ldi oldbuf, 200
 clr temp
 clr zero
 clr rcvbuf
 clr fullbuf
 clr count
 ldi ZL, low(stringbuf)
 ldi ZH, high(stringbuf)
 sei

;clear RAM
RAMclear: inc count
 ldi RXchar, 'w'
 rcall  putc
 st Z+, zero
 cpi count, 150
 breq  _startup
 rjmp RAMclear

;***************************************************************************
;Loop Initialization Procedure

_startup: ldi printbuf, 0
 rcall _refresh
 ldi RXchar, 'z'
 rcall  putc
 ldi ZL, low(chat<<1)
 ldi ZH, high(chat<<1)
 lpm
 out UDR, r0  ;trigger the UART TX
 ser TXflash
 ser TXbusy  ;and set the TX busy flag
 sbi UCR, UDRIE ;enable the TXempty interrupt
 rcall TXwait

 ldi ZL, low(crlf<<1)
 ldi ZH, high(crlf<<1)
 lpm
 out UDR, r0  ;trigger the UART TX
 ser TXflash
 ser TXbusy  ;and set the TX busy flag
 sbi UCR, UDRIE ;enable the TXempty interrupt
 rcall TXwait

_initialize: ldi userid, idnum ;simple user id
 sbis PINB, 0  ;identify master
 rjmp _mastertoken ;initialize as master
 in temp, PINC
 cpi temp, token
 breq _slavetoken ;initialize as slave
 rjmp _initialize

_mastertoken:   ldi RXchar, 'h'
 rcall putc
 ser temp
 out DDRB, temp
 ldi temp, token  
 out PORTA, temp ;transmit token
 in temp, PINC
 cpi temp, token
 breq _masterack ;until receive token
 rjmp _mastertoken

_masterack: ldi RXchar, 'i'
 rcall putc
 ldi temp, ack  
 out PORTA, temp ;transmit acknowledge
 in temp, PINC
 cpi temp, ack
 brne _masterack ;until receive acknowledge
 rjmp _txstart

_slavetoken: ldi RXchar, 'h'
 rcall putc
 ser temp
 out DDRB, temp
 ldi temp, token
 out PORTA, temp ;transmit token
 in temp, PINC
 cpi temp, ack
 breq _slaveack ;until receive acknowledge
 rjmp _slavetoken
 
_slaveack: ldi RXchar, 'i'
 rcall putc
 ldi temp, ack
 out PORTA, temp ;transmit acknowledge
 in temp, PINC
 cpi temp, ack
 breq _slaveack  
 rjmp _mainloop ;until receive non-acknowledge

;***************************************************************************
;Main Loop Procedure

_mainloop: in temp, PINC
 
;ldi RXchar, 'm'
;rcall putc
 
 cpi temp, device1
 breq _jmpreceive ;receive text
 cpi temp, device2
 breq _jmpreceive ;receive text
 cpi temp, device3
 breq _jmpreceive ;receive text
 cpi temp, device4
 breq _jmpreceive ;receive text
 cpi temp, token
 breq _jmptransmit ;transmit text
;out PORTA, temp
;mov RXchar, temp
;rcall putc
 rjmp _mainloop

_jmpreceive:;ldi RXchar, 'r'
;rcall putc
 rjmp  _receive ;jump to code

_jmptransmit:;ldi RXchar, 't'
;rcall putc

 rjmp _transmit ;jump to code

;***************************************************************************
;Receive Character Procedure

_receive:
_rxallocbuf: in temp, PINC  ;receive sender id
 out PORTA, temp ;transmit sender id
 subi temp, -azero ;convert sender id to simple sender id
 ldi ZL, low(stringbuf);allocate new buffer for sender message
 ldi ZH, high(stringbuf)
 add ZL, newbuf
 adc ZH, zero
 st Z+, temp ;sender id into first character of sender message

_rxchar:;ldi RXchar, 'c'
;rcall putc
 in temp, PINC
 mov RXchar, temp
 rcall putc
 out PORTA, temp
;ldi RXchar, 'p'
;rcall putc
 cpi temp, device1
 breq _rxchar  ;wait for character to start receiving
 cpi temp, device2
 breq _rxchar  ;wait for character to start receiving
 cpi temp, device3
 breq _rxchar  ;wait for character to start receiving
 cpi temp, device4
 breq _rxchar  ;wait for character to start receiving
 cpi temp, ack
 breq _rxchar  
 st Z+, temp   ;place character in buffer
 cpi temp, null
 breq _rxend  ;if end of text send handle appropriately

_rxack:  ldi RXchar, 'k'
 rcall putc
 in temp, PINC ;receive acknowledgement of recieving character
 out PORTA, temp ;transmit acknowledgement of receiving character
 cpi temp, ack  
 brne _rxack  ;receive acknowledge until non-acknowledge character
 rjmp _rxchar  ;send next character

_rxend:  ldi RXchar, 'e'
 rcall putc
 in temp, PINC
 cpi temp, null
 breq _rxend  ;wait until non-enter character to return to main loop
 mov printbuf, oldbuf
;rcall  _refresh  ;refresh screen with new message
 subi newbuf, -bufsize;update the pointer to the newest message in the buffer
 ldi temp, bufmax  
 cpse newbuf, temp  
 clr newbuf
 subi oldbuf, -bufsize;update the pointer to the oldest message in the buffer
 ldi temp, bufmax
 cpse oldbuf, temp
 clr  oldbuf
 rjmp _mainloop


;***************************************************************************
;Transmit Character Procedure

_transmit:
_rxtoken:;ldi RXchar, 'w'
;rcall putc
 ldi temp, notoken  
 out PORTA, temp ;transmit notoken
 in temp, PINC
 cpi temp, ack  
 brne _rxtoken ;until receive notoken

_txstart: out PORTB, fullbuf
 tst fullbuf  ;check if user text buffer full
 brne _txallocbuf ;if full transmit identification
 rjmp _txtoken ;else transmit token

_txallocbuf: ldi ZL, low(userbuf);prepare to read own user buffer
 ldi ZH, high(userbuf)
 ldi YL, low(stringbuf);prepare to load string buffer for received messages
 ldi YH, high(stringbuf)
 add YL, newbuf ;allocate new buffer for own user message
 adc YH, zero
 st Y, userid ;store user id in the string buffer (may over write old id)

_txid:  ldi RXchar, 'i'
 rcall putc
 mov temp, userid  
 out PORTA, temp ;transmit user identification
 in temp, PINC
 cp temp, userid  
 breq _txchar  ;until receive user identification
 rjmp _txid  ;transmit identification again

_txchar: ld char, Z  ;load character to transmit
 mov RXchar, char
 rcall putc
 out PORTA, char ;transmit character
 in temp, PINC  
 cpi temp, null ;if receive end of text
 breq _txend  ;end transmission
 cp temp, char ;if receive character back
;ldi RXchar, 'x'
;rcall putc
;mov RXchar, temp
;rcall putc
 breq _txack  ;transmit acknowledge
 rjmp _txchar  ;otherwise transmit chacter again

_txack:  ldi RXchar, 'j'
 rcall putc
 st Y, char  ;store character into own user buffer
 ldi temp, ack ;load acknowledge character
 out PORTA, temp  
 in  temp, PINC
 cpi temp, ack  
 breq _txnextchar ;if acknowledge transmit next character
 rjmp _txack  ;otherwise transmist acknowledge again

_txnextchar: ldi RXchar, 'r'
 rcall putc
 adiw ZL, 1  ;increment to transmit the next character
 adiw YL, 1  ;increment to receive the next character
 rjmp _txchar  ;transmit next character

_txend:  ldi RXchar, 'x'
 rcall putc
 mov printbuf, oldbuf
 ldi printbuf, 0
 rcall  _refresh ;refresh the screen
 clr fullbuf
;subi newbuf, -bufsize;update the pointer to the newest message in the buffer
;ldi temp, bufmax  
;cpse newbuf, temp  
 clr newbuf
 ldi RXchar, 'g'
 rcall putc
;subi oldbuf, -bufsize;update the pointer to the oldest message in the buffer
;ldi temp, bufmax
;cpse oldbuf, temp
;clr  oldbuf

_txtoken:;ldi RXchar, 'o'
;rcall putc
 ldi temp, token
 out PORTA, temp ;pass token to next user
 in temp, PINC  
 cpi temp, notoken
 brne _txtoken ;until receive notoken
 ldi temp, ack
 out PORTA, temp ;send acknowledge to next user
 
 rjmp _mainloop ;return to main loop

;***************************************************************************
;UART Interface Procedure

_refresh: push temp
 ldi RXchar, 'n'
 rcall putc
 ldi RXchar, 'n'
 rcall putc
 ldi RXchar, 'n'
 rcall putc
 ldi ZL, low(stringbuf);prepare to print out messages
 ldi ZH, high(stringbuf)
 add ZL, printbuf ;print out oldest message
 adc ZH, zero  
 ld r0, Z+
 out UDR, r0
 clr TXflash
 ser TXbusy
 sbi UCR, UDRIE
 rcall TXwait
 subi printbuf, -bufsize
;ldi temp, bufmax
;cpi printbuf, bufmax  
;brne _noreset
;clr printbuf ;clear if printbuf greater than 200 to get all messages
_noreset:;cp printbuf, oldbuf;check if printed all the messages in string buffer
 cpi printbuf, 100
 brlt _refresh ;if not repeat loop again
 pop temp
 ret   ;if so done printing the buffer

;***************************************************************************
;Interrupt Service Routines

;UART Receive Complete Interrupt
UARTRX:  in  save, SREG
 push temp
 push ZL
 push ZH
 in temp, UDR
 mov RXchar, temp
 rcall putc  ;transmit one char
 ldi ZL, low(userbuf);download pointer to user buffer
 ldi ZH, high(userbuf)
 add ZL, rcvbuf ;add offset to pointer
 adc ZH, zero
 st Z+, temp  
 inc rcvbuf
 cpi temp, enter ;if "enter" character message end
 brne exit
 ser fullbuf
;ldi RXchar, 'f'
;rcall putc
 ldi temp, 0x0d ;store the carriage return line feed and a
 st Z+, temp ;null character
 ldi temp, 0x0a
 st Z+, temp
 ldi temp, 0x00
 st Z+, temp
 clr rcvbuf

exit:  pop ZH
 pop ZL
 pop temp
 out  SREG, save
 reti

;UART Transmit Register Empty Interrupt
UARTEM:  in save, SREG ;save SREG
 tst TXflash
 breq TXram
 inc ZL  ;output next character
 lpm   ;and put it in r0
 rjmp TXfls
TXram:  inc ZL
 ld r0, Z
TXfls:  tst r0  ;check if terminating character
 breq TXend
 out UDR, r0
 rjmp TXexit
TXend:  clr TXbusy  ;exit wait loop if terminating character
 cbi UCR, UDRIE ;disable UART UDR empty interrupt
TXexit:  out SREG, save ;load SREG
 reti

;***************************************************************************
;Subroutines

TXwait:  tst TXbusy  ;wait in loop until terminating character
 ldi RXchar, 'f'
 rcall putc
 brne TXwait
 ret

putc:  sbis USR, UDRE ;check empty flag
 rjmp putc
 out UDR, RXchar ;transmit one char
 ret

getc:  sbis USR, RXC ;check receive flag
 rjmp getc
 in RXchar, UDR ;receive one char
 ret





Free Forum Hosting by Forumer.comTM!