Full Version : Mehr & Briggs Modem (AVR ASM)
avr >>COMMUNICATIONS & WEB PROJECTS >>Mehr & Briggs Modem (AVR ASM)


Admin3- 04-18-2006
Analog Modem Design Project

Jason Mehr and Carter Briggs

Introduction:
We thought it would be interesting to try and construct a simple modem out of the ATMEL 8535. The main motivation for doing this was to test some DSP theories about the effects of transmission power, noise, and modulation technique on the bit error rate. Initially, we wanted to connect our modem to the public telephone network; however, the voltage, current, and bandwidth constraints over a phone line would have made the project much more complex. Instead, we chose to use a simple wire channel, and focus our efforts on actually transmitting and receiving the signal waveform.


High Level Design:
Our goal was to make our modem so that it would be full duplex and we would be able to send and receive characters over a noisy analog channel. For simplicity, we chose to use the UART and Hyperterminal on a PC to provide data to the modem, and display the output of the receiver. The characters were to be transmitted pseudo asynchronously by transmitting a high voltage synchronization pulse, one parity bit, then the eight data bits. This allowed for a high level of fault tolerance because the transmitter and receiver are synchronized at the beginning of every byte. Binary ‘1’s were to be transmitted by a waveform with values defined in a table so that we could easily change the waveform. To transmit binary ‘0,’ we simply negated the values in the table. We varied the number of samples per bit transmitted as well. To receive the signal the MCU waits for expected high voltage of the sync pulse. It then waits for the end of the sync pulse to begin sampling the bits. The reciever uses a correlator algorithm to detect whether the received signal was a binary '0' or '1.' We varied the number of samples per bit to evaluate the tradeoffs between speed and noise tolerance.




Transmitter
When a byte is received from the UART, the transmitter starts the transmit sequence begins by with a sync pulse. This is a short spike of five volts that signals to the receiver that a byte is coming. Following the sync pulse, the transmitter calculates a parity bit to ensure that the number of ‘1’s transmitted is even. The parity bit is transmitted as a waveform over the channel, followed by the eight data bits. To control the speed of the transmitted, we used the timer one compare match interrupt. Every interrupts, a new sample of the waveform corresponding to the bit is loaded from flash and sent to the DAC. Since we were using BPSK, we only needed to store one waveform in flash, since we could just negate the value to produce the waveform for a binary zero. Also, since we didn’t want to deal with negative voltages, we introduced a digital DC offset into out transmission. The DC offset was added to the value to be transmitted at the transmitter and subtracted at the receiver. This allowed us to avoid the added complexity of more external hardware, while still being able to use BPSK.




Receiver
At the receiver, we sample the channel periodically using the timer one compare match interrupt to control the timing. When the analog to digital conversion produces a value the is in the range of the sync pulse, we know that a new byte will arrive at the next sample period, and we reset all of the counters that control where we are within the process of receiving a byte. In the sample period after the sync pulse, we take the sampled value and subtract off the artificial zero level, and multiply the result with a value that comes from flash. This value from flash is one of the samples of the transmitted signal. The result is then added into a sum register. This is the basis of the receiver algorithm that determines whether a waveform corresponds to a ‘0’ or ‘1.’ After eight samples, we check the sign bit of the sum. If the sign bit was ‘1,’ then the sum was negative, indicating that a zero was transmitted. If the sign bit was ‘0,’ then we interpret the signal as a binary one. We then had two registers that acted as a bit buffer to store bits as they arrived. After receiving each new bit, we shifted the registers to the left.




Waveform of "101110011"

After receiving nine bits, we did a parity check on the bits by counting the number of ‘1’s we received. If the number of ‘1’s was odd, then an odd number of bits were received incorrectly. Finally, we wrote the eight data bits to the UART.

Since the modem was full duplex, each modem had to both transmit and receive at the same time. Merging the two pieces of code became the most difficult part of the project, as we had to preserve the timing, and make sure that the two functions did trample over each other. To do this; at every sample period we transmitted the new waveform value first because the transmit code is short. All we had to do was transfer the sample value to the output ports. The amount of time that the receive code took was variable but always long, since the amount of processing depended on what part of the receive cycle the micro-controller was in. For instance, at the last sample period of the last bit, the receiver has to do an analog to digital conversion, an eight bit signed multiply, a parity check, and write the byte out to the UART. This sequence of actions proved to be the limiting factor in the speed of our modem.


Performance Analysis:

Accuracy
In the presence of noise, there is always the possibility that the receiver will not correctly identify a bit. This error is a function of the energy of the transmitted signal. The larger the signal energy, the less likely the receiver will confuse a ‘1’ for a ‘0,’ or vice versa. In order to measure the probability of error for our modem, we appended a parity bit to each transmitted byte. This allowed us to empirically measure the probability of an odd number of bit errors out of the nine transmitted bits. We could then interpolate the probability that a single bit was in error. We were interested in calculating the probability of error as a function of signal energy by varying the shape of the signal. In our code, we had registers that kept track of how many bytes were received, and how many of those bytes failed a parity check. We intented to use this to produce a plot of error probabilty as a function of signal energy, but we ran out of time, and were not able to perform these tests.


Speed
The fastest we were able to reliably transmit was when we set one sample period to be 1000 clock cycles. Since our signal waveforms contained eight samples, this resulted in a transmission speed of 2 msec. per bit, or 500 bits per second. When we attempted to speed up the modem by decreasing the sample period to 500 clock cycles, the result was unreliable transmission. From looking at our code, we suspect that this is due to the fact that the code required to receive data took longer than 500 clock cycles. This means that the transmitter begins to send a new bit before the receiver is done processing the previous bit. At 500 bits per second our modem pales compared to modern modems, which can obtain throughputs two orders of magnitude better. We did; however, manage to outperform a 300 bps modem ignoring any sort of bandwidth analysis.

Link: http://instruct1.cit.cornell.edu/courses/e...cts/s2000/mehr/

CODE

;********************************
;You will need to CHANGE this path
.nolist
.include "c:\avrtools\appnotes\8535def.inc"
.list

;********************************

.def save =r1
.def free1 =r2
.def bcount =r3
.def sumL =r4
.def sumH =r5
.def TXbuffL =r6
.def TXbuffH =r7
.def nBytesL =r8
.def nBytesH =r9
.def nErorsL =r10
.def nErorsH =r11
.def midL =r12
.def midH =r13
.def txL =r14
.def txH =r15
.def rxL =r16
.def rxH =r17
.def timeout =r19
.def temp =r20
.def tempH =r21
.def RXbuffL =r22
.def RXbuffH =r23
.def RXbitcnt=r24
.def TXbitcnt=r25
.def TXphase =r26
.def RXphase =r27
.def RXchar =r28
.def MODREG =r29






.equ Nsamples= 8
.equ samptime= 5000; ie every 10 msec
.equ midline = 256
.equ baud96 =25;9600 baud constant for 4Mhz crystal
.equ MPS0 = 0
.equ MPS1 = 1

.equ TXALN = 5
.equ UARTRX = 6
.equ DEBUG = 7
.equ prescal = 1

.macro msbr
push temp
mov temp, @0
sbr temp, @1
mov @0, temp
pop temp
.endmacro

.macro mcbr
push temp
mov temp, @0
cbr temp, @1
mov @0, temp
pop temp
.endmacro

.macro incWord
push temp
ldi temp, 1
add @0L, temp
clr temp
adc @0H, temp
pop temp
.endmacro


;*******************************
; Initialization
.cseg
.org $0000
rjmp  RESET;reset entry vector  
reti    ;2
reti    ;3
reti    ;4
reti    ;5
reti    ;6
rjmp compA; timer 1 compare match A
reti    ;8
reti    ;9
reti    ;10
reti    ;11
rjmp RXdone    ;12
reti    ;13
reti    ;14
reti    ;15
reti    ;16
reti    ;17




sigtbl: .db 0x00, 0x20, 0x40, 0x60, 0x60, 0x40, 0x20, 0x00
asctbl: .db "0123456789ABCDEF"
crlf: .db 0x0d, 0x0a, 0x00
qt: .db "Quitting debug mode....", 0x00
dbgstr: .db "debug>", 0x00
clrstat:.db "Clearing stats.", 0x00
nbstr: .db "Number of bytes: ", 0x00
nestr: .db "Number of errors: ", 0x00
hello: .db "testing.....", 0x00
foo: .db "..entering ISR..", 0x00
curExp: .db "Curr exp: ", 0x00
fbyte: .db "Final byte: ", 0x00
gotaone:.db "Got a one", 0x00
gotazer:.db "Got a zero",0x00
gotasyn:.db "Gotsync", 0x00
rsetstr:.db "Clearing statistics", 0x00
dbgpmt: .db "debug>", 0x00
tunedn: .db " -> Turn Aref down", 0x00
tuneup: .db " -> Turn Aref up", 0x00
errfnd: .db "error found", 0x00
gbb: .db "**", 0x00
star: .db "*"
pps: .db "+", 0x00


.macro macputF
ldi ZH, HIGH(@0*2)
ldi ZL, low(@0*2)
rcall putFstr
.endmacro


;*******************************
RESET:
ldi Temp, LOW(RAMEND) ;setup stack pointer
out  SPL, Temp
ldi Temp, HIGH(RAMEND)
out SPH, Temp
;set up the PORTs
ldi temp, 0x7F
out DDRD, temp
ser temp ;set PORTB to be
out DDRC,temp;all outputs
ser temp
out DDRB, temp
ldi temp, midline
out PortC, temp
out PortB, temp
;setup timer 1 for reset on compare match and disable for now
ldi temp, 8 + prescal
out TCCR1B, temp
ldi Temp,0b00010000
out TIMSK, Temp

;setup UART -- enable RX, TX pins & RX int.
ldi  temp, 0b10011000
out  UCR, temp
;set baud rate to 9600
ldi temp, baud96
out UBRR, temp

clr MODREG
;setup ADC
clr temp
out ADMUX, temp ; set channel 0


ldi temp, HIGH(samptime)
out OCR1AH, temp
ldi temp, LOW(samptime)
out OCR1AL, temp

clr RXbuffL
clr RXbuffH
clr nBytesL
clr nBytesH
clr nErorsL
clr nErorsH
ldi temp, high(midline)
out PortC, temp
ldi temp, low(midline)
out PortB, temp

sei  ;enable all interrupts

clr bcount



mloop: sbrs MODREG, DEBUG
rjmp mnorm
; macputF gbb
; macputF crlf
sbrs MODREG, UARTRX
rjmp mloop
cbr MODREG, exp2(UARTRX)
rcall procDBG
rjmp mloop
mnorm:
; rcall perfTst
sbrs MODREG, UARTRX
rjmp mloop
cbr MODREG, exp2(UARTRX)
rcall procRX
rjmp mloop


getAna: push temp
ldi temp, 0b11000101
out ADCSR, temp
swait: in temp, ADCSR;wait for A to D start
andi temp, 0b01000000;by checking if ADSC bit is set
breq swait ;this bit is set by t1match interrupt
await: in temp, ADCSR;wait for A to D done
andi temp, 0b01000000;by checking ADSC bit
brne await ;this bit is cleared by the AtoD hardware
pop temp
ret



tunemid:clr temp
clr midL
clr midH
_tuntst:cpi temp, 4
breq _tunend
rcall getAna
rcall getAna
in rxL, ADCL
in rxH, ADCH
add midL, rxL
adc midH, rxH
ldi tempH, low(midline)
sub midH, tempH
ldi tempH, high(midline)
sbc midH, tempH
inc temp
rjmp _tuntst
_tunend:clc
ror midH
ror midL
clc
ror midH
ror midL
ldi temp, low(midline)
ldi tempH, high(midline)
add midL, temp
adc midH, tempH
ret


procDBG:cpi RXchar, 'q'
breq _bug0
cpi RXchar, 'u'
breq _bug1
cpi RXchar, 'd'
breq _bug2
cpi RXchar, 'r'
breq _bug3
cpi RXchar, 's'
breq _bug4
cpi RXchar, 't'
breq _bug5
_enddbg:macputF crlf
macputF dbgstr
ret

_bug0: macputF qt
macputF crlf
cbr MODREG, exp2(DEBUG)
ret
rjmp _enddbg

_bug1: ldi temp, 1
rcall setPre
rjmp _enddbg

_bug2: ldi temp, 0xFF
rcall setPre
rjmp _enddbg

_bug3: clr nBytesL
clr nBytesH
clr nErorsL
clr nErorsH
macputF crlf
macputF clrstat
macputF crlf
rjmp _enddbg

_bug4: macputF crlf
macputF nbstr
mov temp, nBytesH
rcall putReg
mov temp, nBytesL
rcall putReg
macputF crlf
macputF nestr
mov temp, nErorsH
rcall putReg
mov temp, nErorsL
rcall putReg
rjmp _enddbg

_bug5: rcall tunemid
macputF crlf
mov temp, midH
rcall putReg
mov temp, midL
rcall putReg
rjmp _enddbg

perfTst:mov TXbuffL, bcount
rcall TXbyte
ldi timeout, 0x14
rcall delay
ldi temp, 1
add bcount, temp
ret

procRX: mov TXbuffL, RXchar
rcall TXbyte
cpi RXchar, 0x0d
brne prend
ldi temp, 0x0a
_prx: sbis USR, UDRE
rjmp _prx
out UDR, temp
mov TXbuffL, temp
rcall TXbyte
prend: ret

delay: tst timeout
brne delay
ret



TXsync: ldi temp, 0xff
ldi tempH, 0x03
rcall TXalign
ret



genPar: mov temp, TXbuffL
clr TXbuffH
_ptest: tst temp
breq _parend
sbrc temp, 7
inc TXbuffH
lsl temp
rjmp _ptest
_parend:sbrs TXbuffH, 0
ret
msbr TXbuffH, 0b10000000
ret


TXpar: push TXbuffL
mov TXbuffL, TXbuffH
rcall TXbit
pop TXbuffL
ret



TXbit: clr TXphase
; macputF star
; macputF crlf
ldi ZL, LOW(sigtbl*2)
ldi ZH, HIGH(sigtbl*2)
_bloop: cpi TXphase, Nsamples
breq _bitend
lpm; r0
rcall doPre
; clr r2
ldi temp, low(midline)
ldi tempH, high(midline)
sbrs TXbuffL, 7
rjmp _foo
add temp, r0 ; a '1'
clr r0
adc tempH, r0
rjmp _bbb
_foo: sub temp, r0 ; a '0'
clr r0
sbc tempH, r0
_bbb: rcall TXalign
inc TXphase
adiw ZL, 1
rjmp _bloop
_bitend:ret

TXbyte: rcall TXsync
rcall genPar
rcall TXpar
ldi TXbitcnt, 0
_byloop:cpi TXbitcnt, 8
breq _byend
rcall TXbit
lsl TXbuffL
inc TXbitcnt
rjmp _byloop
_byend: ldi temp, low(midline)
ldi tempH, high(midline)
rcall TXalign
ret


setPre: mov tempH, MODREG
andi tempH, 0x03
add tempH, temp
andi tempH, 0x03
mov temp, MODREG
andi temp, 0xFC
or temp, tempH
mov MODREG, temp
ret

doPre: sbrc MODREG, MPS0
asr r0
sbrs MODREG, MPS1
ret
asr r0
asr r0
ret


TXalign:cli
cbr MODREG, exp2(TXALN)
mov txL, temp
mov txH, tempH
sei
_txa: sbrs MODREG, TXALN
rjmp _txa
ret

compA: in save, SREG
push temp
push tempH
push ZL
push ZH


out PortC, txH
out PortB, txL
sbr MODREG, exp2(TXALN)

sbrc MODREG, DEBUG
rjmp _aaa
sbic PinD, 7
rjmp _aaa
sbr MODREG, exp2(DEBUG)
ldi RXphase, Nsamples
macputF crlf
macputF dbgstr

_aaa: dec timeout
sbrs MODREG, DEBUG
rcall getInpt
pop ZH
pop ZL
pop tempH
pop temp
out SREG, save
reti

getInpt2: ret

getInpt:push r0
push r19 ; ie - timeout -> gets mauled by mpy8s
rcall getAna
in rxL, ADCL
in rxH, ADCH
andi rxH, 0x03
cpi rxH, 0x03
brlt _corr
rcall procSyn  ; found a sync pulse
rjmp _getend
_corr: cpi RXphase, nSamples
brlt _hop
rjmp _getend  ; past end of bit stop correlating
_hop: sub rxL, midL
sbc rxH, midH
; mov temp, rxH
; rcall putReg
; mov temp, rxL
; rcall putReg
; macputF crlf
ldi ZL, LOW(sigtbl*2)
ldi ZH, HIGH(sigtbl*2)
clr r0
add ZL, RXphase
adc ZH, r0
lpm; r0
mov r17, r0
rcall mpy8s
add sumL, r17
adc sumH, r18
inc RXphase
cpi RXphase, Nsamples
brne _getend
rcall gotBit
_getend:pop r19
pop r0
ret




procSyn:
; macputF gotasyn
; macputF crlf
clr RXphase ; sync pulse
clr sumL
clr sumH
clr RXbitcnt
clr RXbuffL
clr RXbuffH
ret


gotBit: clc
rol RXbuffL
rol RXbuffH
sbrc sumH, 7  ; <<<<<-----------------switch
ori RXbuffL, 0x01
inc RXbitcnt
cpi RXbitcnt, 9
brne _notbyt
rcall gotByte
ret
_notbyt:clr RXphase
clr sumL
clr sumH
ret

gotByte:rcall chkPar
rcall procByt
ret


procByt:incWord nBytes
mov temp, RXbuffL
_pb: sbis USR, UDRE
rjmp _pb
out UDR, temp
; rcall putReg
ret



chkPar: andi RXbuffH, 0x01
mov temp, RXbuffL
_chktst:tst temp
breq _endchk
sbrc temp, 7
inc RXbuffH
lsl temp
rjmp _chktst
_endchk:sbrs RXbuffH, 0
ret  ; even # of 1's
macputF errfnd ; odd # of 1's found
macputF crlf
incWord nErors
ret





putFstr:push r0
_fgtchr:lpm
adiw ZL, 1
tst r0
breq _flstch
_bar: sbis USR, UDRE
rjmp _bar
out UDR, r0
rjmp _fgtchr
_flstch:pop r0
ret

putReg: push r0
push temp
swap temp
andi temp, 0x0F
ldi ZL, low(asctbl*2)
ldi ZH, high(asctbl*2)
add ZL, temp
clr temp
adc ZH, temp
lpm; r0
_xa: sbis USR, UDRE
rjmp _xa
out UDR, r0
pop temp
push temp
andi temp, 0x0F
ldi ZL, low(asctbl*2)
ldi ZH, high(asctbl*2)
add ZL, temp
clr temp
adc ZH, temp
lpm; r0
_xb: sbis USR, UDRE
rjmp _xb
out UDR, r0
pop temp
pop r0
ret


RXdone: in save, SREG
push temp
in RXchar, UDR
_rxx: sbis USR, UDRE
rjmp _rxx
out UDR, RXchar
sbr MODREG, exp2(UARTRX)
pop temp
out SREG, save
reti





;***************************************************************************
;*
;* "mpy8s" - 8x8 Bit Signed Multiplication
;*
;* This subroutine multiplies signed the two register variables mp8s and
;* mc8s. The result is placed in registers m8sH, m8sL
;* The routine is an implementation of Booth's algorithm. If all 16 bits
;* in the result are needed, avoid calling the routine with
;* -128 ($80) as multiplicand
;*  
;* Number of words :10 + return
;* Number of cycles :73 + return
;* Low registers used :None
;* High registers used  :4 (mc8s,mp8s/m8sL,m8sH,mcnt8s)
;*
;***************************************************************************

;***** Subroutine Register Variables

.def mc8s =r16 ;multiplicand
.def mp8s =r17 ;multiplier
.def m8sL =r17 ;result Low byte
.def m8sH =r18 ;result High byte
.def mcnt8s =r19 ;loop counter

;***** Code

mpy8s: sub m8sH,m8sH;clear result High byte and carry
ldi mcnt8s,8;init loop counter
m8s_1: brcc m8s_2 ;if carry (previous bit) set
add m8sH,mc8s;    add multiplicand to result High byte
m8s_2: sbrc mp8s,0 ;if current bit set
sub m8sH,mc8s;    subtract multiplicand from result High
asr m8sH ;shift right result High byte
ror m8sL ;shift right result L byte and multiplier
dec mcnt8s ;decrement loop counter
brne m8s_1 ;if not done, loop more
ret






Forumer™ is Voted #1 Free Forum Hosting provider
Build your own community today with the largest message board hosting company.