Quote:
I'm very uncomfortable with the way that you're not ding any sort of timing synchronization at all there. What if you start reading in the middle of a frame? How do you know you'll have any sync?
I went back and tried to fix the synchronization. I am not sure if this is what you meant.
Send:
Code: sendData:
DI
LD B, 3 ;number of retries
IOStatus:
PUSH BC ;save counter
CALL checkIO ;see if IO is busy or not
POP BC
OR 0 ;checkIO returns 0 if busy
JR NZ, sendSynch ;not busy, begin synch
DJNZ IOStatus ;retry
;error message
EI
RET ;go back to main
sendSynch:
LD A, 1
OUT (0), A ;pull clock line low
sendSynchWait:
IN A, (0) ;check for data line to be pulled low
RRA
RRA
JR C, sendSynchWait
XOR A
OUT (0), A ;release the clock line
sendSynchWait2:
IN A, (0) ;check for data line to be released
RRA
RRA
JR NC, sendSynchWait2
Receive:
Code: Main:
IN A, (0) ;read link port status
CP 2
CALL Z, getData
...
getData:
DI
LD A, 2
OUT (0), A ;pull data line low
waitSynch:
IN A, (0) ;check for clock line to be released
RRA
JR NC, waitSynch
XOR A
OUT (0), A ;release data line
I also added a check on the sending side to see if the IO port was busy or not. I was hoping you could give me some more specific suggestions. The full code is below.
Code: .nolist
#include "ti83plus.inc"
.list
.org $9D93
.db t2ByteTok, tAsmCmp
Main:
IN A, (0) ;read link port status
CP 2
CALL Z, getData
b_call(_GetCSC) ;get keypress
CP skEnter
CALL Z, sendData ;send data if enter was pressed
CP skClear ;check if clear was pressed
JR NZ, Main
RET ;Exit the program
;--------------------------------------------------------------------------------
; Send Routines
;--------------------------------------------------------------------------------
;----------------------------------sendData--------------------------------------
sendData:
DI
LD B, 3 ;number of retries
IOStatus:
PUSH BC ;save counter
CALL checkIO ;see if IO is busy or not
POP BC
OR 0 ;checkIO returns 0 if busy
JR NZ, sendSynch ;not busy, begin synch
DJNZ IOStatus ;retry
;error message
EI
RET ;go back to main
sendSynch:
LD A, 1
OUT (0), A ;pull clock line low
sendSynchWait:
IN A, (0) ;check for data line to be pulled low
RRA
RRA
JR C, sendSynchWait
XOR A
OUT (0), A ;release the clock line
sendSynchWait2:
IN A, (0) ;check for data line to be released
RRA
RRA
JR NC, sendSynchWait2
LD HL, Length ;location of byte
LD B, 1 ;number of bytes to send
CALL sendByte
LD B, 2 ;number of times to resend
sendPayload:
PUSH BC ;save resend value
LD A, (Length) ;number of bytes to send
LD B, A
LD HL, Data ;location of data
CALL sendByte
getACKNAK:
IN A, (0)
CP 3
JR NZ, getACKNAK
OR 0
POP BC ;restore resend counter
JR Z, sendSuccess
;error message for resend
DJNZ sendPayload
;error message for failed transmission
sendSuccess:
EI
RET
;-----------------------------------sendByte-------------------------------------
sendByte:
PUSH BC ;save
LD B, 4 ;8 bits, 2 bits/loop, 4 loops
LD C, (HL) ;get byte from memory
CALL delay
transmitdata:
XOR A ;clear A
RRC C ;put LSB of C in carry
CCF ;invert the carry
RLA ;put carry in LSB of A
SLA A
INC A ;A is %0000 00X1 (X is data bit)
OUT (0), A
CALL delay
XOR A
RRC C
CCF
RLA
SLA A ;A is %0000 00X0
OUT (0), A
CALL delay
DJNZ transmitData
POP BC ;restore number of bytes to send
INC HL ;go to next byte location
DJNZ sendByte
DEC HL ;HL points to location of last byte
XOR A
OUT (0), A ;release data line
RET
;----------------------------------checkIO---------------------------------------
checkIO:
LD DE, $FFFF ;set a long counter
checkIOLoop:
IN A, (0) ;read in IO port
LD C, A ;save in C
RRA ;check LSB of A
JR NC, checkNoise
noise:
DEC DE
LD A, D ;"convert" to 8 bits
OR E
JR NZ, checkIOLoop ;check if counter reached zero
LD A, 1 ;IO port is clear
RET
checkNoise:
LD B, 15 ;counter for noise loop
checkNoiseLoop:
IN A, (0) ;read in IO port
CP C ;check with saved IO read
JR NZ, noise ;noise will set NZ
DEC DE
DJNZ checkNoiseLoop ;not noise, repeat
XOR A ;IO line is busy
RET
;--------------------------------------------------------------------------------
; Receive Routines
;--------------------------------------------------------------------------------
;-----------------------------------getData--------------------------------------
getData:
DI
LD A, 2
OUT (0), A ;pull data line low
waitSynch:
IN A, (0) ;check for clock line to be released
RRA
JR NC, waitSynch
XOR A
OUT (0), A ;release data line
LD HL, LengthSave
LD B, 1 ;number of bytes to receive
CALL getByte
LD (HL), C ;save length
LD B, 2 ;number of times to rereceive
receivePayload:
PUSH BC ;save retry counter
LD A, (LengthSave)
LD B, A ;length of data to receive
LD HL, DataSave
CALL getByte
LD A, (CheckSum)
POP BC
OR 0
JR Z, receiveSuccess
LD A, 1
OUT (0), A ;send checksum fail
CALL delay
XOR A
OUT (0), A ;release lines
DJNZ receivePayload
receiveSuccess:
LD A, 3
OUT (0), A ;send checksum success
CALL delay
XOR A
OUT (0), A ;release lines
EI
RET
;----------------------------------getByte---------------------------------------
getByte:
PUSH BC
XOR A
LD C, A ;C contains byte to recieve
LD B, 4
receiveClockLow:
IN A, (0)
RRA ;check if clock line is low
JR C, receiveClockLow
RRA ;put data bit in carry
RR C ;put carry in MSB of C
receiveClockHigh:
IN A, (0)
RRA
JR NC, receiveClockHigh
RRA ;put data bit in carry
RR C ;put carry in MSB of C
DJNZ receiveClockLow
LD (HL), C ;save data
LD A, (CheckSum)
ADD A, C ;add new byte to check sum
LD (checkSum), A
POP BC ;restore data length counter
INC HL ;HL points to next data save location
DJNZ getByte
DEC HL ;HL points to last data save location
RET
;--------------------------------------------------------------------------------
; Common Routines
;--------------------------------------------------------------------------------
delay:
CALL $000B
RET
;--------------------------------------------------------------------------------
; Hard Coded Data
;--------------------------------------------------------------------------------
Length:
.DB 2
Data:
.DB 8, 248
LengthSave:
.DB 0
DataSave:
.DB 0
CheckSum:
.DB 0
.end
.end
I was also wondering how you connected to the arduino. Did you just break out the TRS connecter directly to pins or did they control transistors to let power through to the pins? Also, how much current can the IO port source/sink?
Below is the program I wrote that the linking code for. I want to connect to a linux server and use the calculator as a quick terminal window. So far, I can type letters, numbers, hit enter for newline and clear to exit. I know you have done variable width char stuff before. Is it better to save a width table or use the b_call() to load the char?
Code: .nolist
#include "ti83plus.inc"
.list
.org $9D93
.db t2ByteTok, tAsmCmp
#define BUFSIZE 100
buffer .EQU appBackUpScreen ;holds the entered chars
buf_ptr .EQU buffer + BUFSIZE + 1
wrapmem .EQU buffer + BUFSIZE + 3 ;holds the position of each char
wrap_ptr .EQU buffer + BUFSIZE + BUFSIZE + 4
GetStr:
b_call(_RunIndicOff)
b_call(_ClrScrnFull)
b_call(_HomeUp)
RES AppTextSave, (IY + AppFlags)
SET ShiftAlpha, (IY + ShiftFlags) ;Turn on alphabet
RES ShiftLwrAlph, (IY + ShiftFlags)
LD HL, wrapmem ;initialize pointer to position data
LD (wrap_ptr), HL
LD DE, buffer ;initialize pointer to char data
LD (buf_ptr), DE
XOR A
LD (PenCol), A ;set the location to (0,0)
INC A
LD (PenRow), A
Startup:
LD HL, strtmsg ;HL points to the startup message
LD B, (HL) ;B now contains the string length
INC HL ;HL points the first letter
startLoop:
LD A, (HL) ;put a char in A
PUSH BC ;save the counter
PUSH HL ;save the pointer
CALL displayChar ;display the char
POP HL ;restore the pointer
POP BC ;restore the counter
INC HL ;point to the next char
DJNZ startLoop ;repeat
XOR A
LD HL, CharCount ;reset the character counter
LD (HL), A
LD HL, wrapmem ;initialize pointer to position data
LD (wrap_ptr), HL
LD DE, buffer ;initialize pointer to char data
LD (buf_ptr), DE
Main:
LD HL, CursorState + 1
LD A, (HL)
OR A
JR NZ, noCursor
CALL toggleCursor
noCursor:
DEC A
LD (HL), A
;CALL LinkStatus ;check for incoming data
CALL GetKey
OR A ;check if a key was pressed
JR Z, Main
CP skClear
JR Z, Exit
PUSH AF
LD HL, CursorState
LD A, (HL)
CP 1
CALL Z, toggleCursor
POP AF
CALL ReadKey
JR Main
Exit:
RES ShiftAlpha, (IY + ShiftFlags) ;turn off alpha characters
b_call(_RunIndicOn)
b_call(_ClrScrnFull)
RET ;exit program
GetKey:
b_call(_GetCSC) ;scan for keypress
RET ;return to Main
ReadKey:
CP sk2nd ;check if 2nd key was pressed
JR NZ, Not2nd
LD HL, Flags + ShiftFlags ;toggle uppercase/lowercase
LD A, (HL)
XOR 32
LD (HL), A
RET ;go to main
Not2nd:
CP skAlpha ;check if alpha was pressed
JR NZ, NotAlpha
LD HL, Flags + ShiftFlags ;toggle alphabet/numberic
LD A, (HL)
XOR 16
LD (HL), A
RET ;go to main
NotAlpha:
CP skDel ;check if delete was pressed
JR NZ, NotDel
LD HL, CharCount
LD A, (HL) ;check if there are any chars
OR A
JR NZ, delChar
RET ;return to main
delChar:
DEC A ;decrement char counter
LD (HL), A
LD HL, (wrap_ptr) ;get the position data pointer
DEC HL ;go back one position in the array
LD C, (HL) ;get the previous char position
LD A, (PenCol) ;get the current location
OR A ;check if current position is left edge
JR NZ, sameRow
LD A, 95 ;See how close to right edge the previous char was
SUB C
LD B, A ;make the difference a loop counter
LD A, (HL) ;get previous location
LD (PenCol), A ;move cursor back one char
LD A, (PenRow) ;move cursor up one row
ADD A, -7
LD (PenRow), A
CALL delLoop ;"erase" the char
LD A, (HL) ;the cursor moved forward to "erase"
LD (PenCol), A ;move it back
LD (wrap_ptr), HL ;save the current pointer position
RET ;return to main
sameRow:
SUB C ;figure out the number of pixels between the two chars
LD B, A ;save difference as loop counter
LD A, (HL) ;get previous location
LD (PenCol), A ;move cursor back one char
CALL delLoop ;"erase" the char
LD A, (HL) ;move cursor back again
LD (PenCol), A
LD (wrap_ptr), HL ;save current pointer position
RET ;return to main
delLoop:
LD A, $20 ;A holds the <space> token
b_call(_VPutMap) ;draw a <space>
DJNZ delLoop ;repeat until char is "erased"
RET
NotDel:
CP skEnter ;check if clear was pressed
JR NZ, NotEnter
XOR A
LD HL, CharCount ;reset char counter
LD (HL), A
LD HL, wrapmem ;reset pointer to position data
LD (wrap_ptr), HL
LD DE, buffer ;reset pointer to char data
LD (buf_ptr), DE
LD A, $FE ;display a newline char
JR validChar
NotEnter:
LD C, A ;save A for awhile
LD HL, CharCount
LD A, (HL) ;check if buffer is full
CP BUFSIZE
RET Z ;return to main
LD A, C ;restore A
SUB skAdd ;check if A is a keypress in the range we want
RET C ;return to main
CP skMath - skAdd + 1
RET NC ;return to main
LD DE, CharTable ;DE points to uppercase char LUT
BIT ShiftAlpha, (IY + ShiftFlags) ;check if alpha is mode
JR NZ, AlphaMode
LD DE, NormalTable ;DE points to numeric char LUT
JR lowerMode
AlphaMode:
BIT ShiftLwrAlph, (IY + ShiftFlags) ;check if lowercase alphabet
JR NZ, lowerMode
LD DE, LowerTable ;DE points to lowercase char LUT
lowerMode:
LD H, 0 ;put keypress in HL
LD L, A
ADD HL, DE ;add keypress offset to the pointer
LD A, (HL) ;to the char data table and save it in A
CALL displayChar
RET ;return to main
displayChar:
LD C, A ;save char for awhile
LD H, 0 ;put char in HL
LD L, A
LD DE, WidthTable - $20 ;DE points to WidthTable
CP $FF
JR NZ, validChar
RET
validChar:
CP $FE
JR NZ, notReturn
LD A, (PenRow)
ADD A, 7
CP 64
CALL Z, scrollUp
LD (PenRow), A
XOR A
LD (PenCol), A
RET
notReturn:
CP $c1
JR NZ, notLBracket
LD A, 3
JR checkEdge
notLBracket:
ADD HL, DE ;add the offset and store in HL
LD A, (HL) ;store the width pointed to in the table
checkEdge:
LD HL, Pencol ;add the next char width to the current
ADD A, (HL) ;cursor position
CP 95 ;check if past right edge of screen
JR C, sameLine
LD A, (PenRow) ;move cursor down a row
ADD A, 7
CP 64
CALL Z, scrollUp
LD (PenRow), A
XOR A ;move cursor to left edge
LD (PenCol), A
sameLine:
LD HL, (wrap_ptr) ;store the position of the cursor into
LD A, (PenCol) ;the position data pointed by HL
LD (HL), A
INC HL ;move pointer to next memory address
LD (wrap_ptr), HL ;save pointer
LD A, C ;restore char to A
PUSH AF ;save char for later
b_call(_VPutMap) ;display char
POP AF ;restore char
LD DE, buf_ptr ;DE points to buffer
LD (DE), A ;store char in buffer
INC DE ;move pointer to next memory address
LD (buf_ptr), DE
LD HL, CharCount
LD A, (HL)
INC A ;increment char counter
LD (HL), A
RET
scrollUp:
PUSH HL
PUSH BC
PUSH DE
PUSH AF
DI
LD A, $07
OUT ($10), A ;set column auto-increment mode
CALL lcdBusy
LD A, $20 ;set column to zero
OUT ($10), A
LD C, $87 ;C holds read row
column:
INC C
LD A, C
CP $BF
JR Z, scrollDone
CALL lcdBusy
OUT ($10), A ;set read row
CALL lcdBusy
IN A, ($11) ;dummy read
LD B, 12 ;12 columns per row
LD HL, ScrollData
readRow:
CALL lcdBusy
IN A, ($11) ;read a byte
LD (HL), A ;save it away
INC HL
DJNZ readRow
LD A, C
SUB 7
CALL lcdBusy
OUT ($10), A ;set write row
CALL lcdBusy
LD A, $20
OUT ($10), A ;reset column to zero
LD B, 12
LD HL, ScrollData
writeRow:
LD A, (HL) ;load a byte
INC HL
CALL lcdBusy
OUT ($11), A ;write a byte
DJNZ writeRow
CALL lcdBusy
LD A, $20
OUT ($10), A ;reset column to zero
JR column
scrollDone:
LD C, $B7
column2:
INC C
LD A, C
CP $BF
JR Z, exitScroll
CALL lcdBusy
OUT ($10), A ;set read row
LD B, 12
LD HL, ScrollData
clearLowest:
LD A, 0 ;load a byte
CALL lcdBusy
OUT ($11), A ;write a byte
DJNZ clearLowest
CALL lcdBusy
LD A, $20
OUT ($10), A ;reset column to zero
JR column2
exitScroll:
CALL lcdBusy
LD A, $05
OUT ($10), A ;set row auto increment mode
POP AF
POP DE
POP BC
POP HL
SUB 7
EI
RET
toggleCursor:
PUSH HL
PUSH BC
PUSH DE
PUSH AF
DI
LD HL, CursorState
LD A, (HL)
XOR 1 ;flip the state flag
LD (HL), A
LD A, (PenRow) ;set row to current position
ADD A, $80
OUT ($10), A
LD A, (PenCol) ;A holds read column
SRL A
SRL A ;divide by 8 to get byte column
SRL A
ADD A, $20 ;A holds correct LCD column
LD HL, CursorCol
LD (HL), A ;save column for later
CALL lcdBusy
OUT ($10), A ;set column
LD A, (PenCol)
AND 7
LD B, A
LD A, $80 ;A is %1000 0000
JR Z, skipMask
maskLoop:
RRCA ;move the 1 to the correct spot
DJNZ maskLoop
skipMask:
LD C, A ;save the bitmask
CALL lcdBusy
IN A, ($11) ;dummy read
LD B, 7 ;read seven lines
LD HL, ScrollData
toggleRow:
CALL lcdBusy
IN A, ($11) ;read a byte
LD (HL), A ;save the row
INC HL
DJNZ toggleRow
CALL lcdBusy
LD A, (PenRow) ;move back to correct row
ADD A, $80
OUT ($10), A
CALL lcdBusy
LD HL, CursorCol
LD A, (HL) ;move back to correct column
OUT ($10), A
LD B, 7
LD HL, ScrollData
drawCursor:
LD A, (HL) ;draw the cursor
INC HL
XOR C ;toggle the cursor bit
CALL lcdBusy
OUT ($11), A
DJNZ drawCursor
CALL lcdBusy
POP AF
POP DE
POP BC
POP HL
EI
RET
lcdBusy:
PUSH AF
CALL $000B
POP AF
RET
ScrollData:
.DB 0,0,0,0,0,0,0,0,0,0,0,0
CursorCol:
.DB 0
CursorState:
.DB 0,0
CharCount:
.DB 0
CharTable: ;LUT for uppercase chars
.DB $22, "WRMH", $FF, $FF
.DB "?@VQLG", $FF, $FF
.DB ":ZUPKFC", $FF
.DB " YTOJEB", $FF, $FF
.DB "XSNIDA"
LowerTable: ;LUT for lowercase chars
.DB $27, "wrmh", $FF, $FF
.DB "_/vqlg", $FF, $FF
.DB $7C, "zupkfc", $FF
.DB " ytojeb", $FF, $FF
.DB "xsnida"
NormalTable: ;LUT for numeric/symbolic chars
.DB "+-*/^", $FF, $FF
.DB $7E, "369)", $C1, "]", $FF
.DB ".258({};"
.DB "0147,<>",$5B, $FF
.DB $24, "!=#%&"
WidthTable:
.DB 1,2,4,6,6,4,6,2,3,3,6,4,3,4,2,4
.DB 4,4,4,4,4,4,4,4,4,4,2,3,4,4,4,4
.DB 6,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4
.DB 4,4,4,4,4,4,4,4,4,4,4,4,4,3,4,4
.DB 3,4,4,4,4,4,3,4,4,2,4,4,3,6,4,4
.DB 4,4,4,3,3,4,4,6,4,4,5,4,2,4,5,4
strtmsg:
.DB 64
.DB " =Terminal Connect Alpha=", $FE
.DB " (c) 2011 Frozen Flame", $FE, $FE
save_sp:
.DW 0
.end
.end