JDCogSerial Object Version 09.03.19

JDCogSerial.spin :
  V08.07.01 - Initial Release
  V09.01.10 - Added MIT license for OBEX
  v09.03.19 - Fixed some bugs in the spin code

Introduction

It has the following features: 1) The buffer memory is in the cog! The buffers have been set to use the maximum amount of cog space. They may be easily changed - see the CON section. There are over 1300 bytes of buffer space available. 2) The VAR footprint is tiny - only 6 longs!!! 3) In only supports FullDuplexSerial mode %0000. 4) It supports standard baud rates up to 230400 and non-standard up to above 345600 (345600 = 115200 * 3). 5) The nature of the object allows easy use of the object from other modules. For Example:
   
   ────────────────────────────────────────────────────────────────────
     JDCogSerialDemo.spin                                              
   ────────────────────────────────────────────────────────────────────

SOURCE CODE...
   CON                                                                 
     _clkmode      = xtal1 + pll16x                                    
     _xinfreq      = 5_000_000
                                                                                                                     │
     BaudRate      = 38400                                             
     RxPin         = |<31                                              
     TxPin         = |<30                                              
                                                                       
   OBJ                                                                 
     Comms : "JDCogSerial"                                             
     Sub : "SubObject"                                                 
                                                                       
   VAR                                                                 
     long CommPtr                                                      
                                                                       
   PUB Start                                                           
     CommPtr := Comms.start(RxPin, TxPin, BaudRate)                    
     if CommPtr then                                                   
       Sub.Init(CommPtr)                                               
       Sub.SendGreeting

                                        
   ────────────────────────────────────────────────────────────────────
     SubObject.spin                                                    
   ────────────────────────────────────────────────────────────────────

SOURCE CODE...
   VAR                                                                 
     long RxPtr, TxPtr                                                 
                                                                       
   PUB Init(CommPtr)                                                   
     RxPtr := CommPtr                                                  
     TxPtr := CommPtr + 4                                              
                                                                       
   PUB Rx : RxByte                                                     
     repeat while (RxByte := long[RxPtr]) < 0                          
     long[RxPtr] := -1                                                 
                                                                       
   PUB Tx(TxByte)                                                      
     repeat while long[TxPtr] => 0                                     
     long[TxPtr] := TxByte                                             
                                                                       
   PUB Str(StringPtr)                                                  
     repeat strsize(StringPtr)                                         
       Tx(byte[StringPtr++])                                           
                                                                       
   PUB SendGreeting                                                    
     Str(string("Hello",13,10))

                                             
   ────────────────────────────────────────────────────────────────────
     Read and Write a byte using PASM                 
   ────────────────────────────────────────────────────────────────────

SOURCE CODE...

   ' Receive a byte into rx                                            
   aRx      rdlong  rx,RxPtr        'Check the receiver                                   
            shl     rx,#1  WC       'Carry set if no byte available       
     if_c   jmp     #aRx            'Wait for a byte to be received                                   
            wrlong  MinusOne,RxPtr  'Mark the byte as having been read                                     
            ...
   RxPtr    long    0               'Initialized in spin or at cog start                                                           
   rx       long    0
   MinusOne long    -1

   ' Transmit a byte from tx, also need a temporary variable         
   aTx      rdlong  temp,TxPtr      'Check the transmitter                                   
            shl     temp,#1  WC     'Carry set if transmitter is idle     
     if_nc  jmp     #aTx            'Wait til the transmitter is idle                                   
            wrlong  tx,TxPtr        'Write the byte into the transmitter                                   
            ...                                                        
   TxPtr    long    0               'Initialized in spin or at cog start                                                           
   tx       long    0                                                  
   temp     long    0
   ────────────────────────────────────────────────────────────────────

Connection Diagram

┌─────────┐ │ │ │ rxPin├─── TTL level RX line │ txPin├─── TTL level TX line │ │ └─────────┘ Propeller MCU (P8X32A) EXAMPLES: Standard Two-Pin Bi-Directional True/Inverted Modes Standard One-Pin Uni-Directional True/Inverted Mode Ex: serial.init(0, 1, ±9600) Ex: serial.init(0, -1, ±9600) -or- serial.init(-1, 0, ±9600) ┌────────────┐ ┌──────────┐ ┌────────────┐ ┌──────────┐ │Propeller P0├─────────────┤I/O Device│ │Propeller P0├───────────────┤I/O Device│ │ P1├─────────────┤ │ └────────────┘ └──────────┘ └────────────┘ └──────────┘ Same-Pin (Bi-Directional) True Mode Same-Pin (Bi-Directional) Inverted Mode Ex: serial.init(0, 0, 9600) Ex: serial.init(0, 0, -9600)  ┌────────────┐ ┌──────────┐ │ │Propeller P0├─────┳─────┤I/O Device│  4.7 kΩ └────────────┘ │ └──────────┘ ┌────────────┐ │ ┌──────────┐  4.7 kΩ │Propeller P0├─────┻─────┤I/O Device│ │ └────────────┘ └──────────┘ 

Global CONstants


SOURCE CODE...
                                                                    
' The buffer sizes may be freely adjusted. As long as the code still compiles
' to fit into the cog. The sizes are currently at their maximum. The sizes are
' number of longs, so the number of bytes stored is four times as many!

  RX1FIFO_SIZE = 167    '668 bytes
  TX1FIFO_SIZE = 168    '672 bytes


Global VARiables


SOURCE CODE...
  long cog                             'cog flag/id

  long rx1_buf                         '5 longs
  long tx1_buf
  long rx1_mask
  long tx1_mask
  long baud1_ticks


PUBlic Spin Methods

Start and Stop

Start and Stop methods are used for starting and stopping this object. This uses/frees one cog and the rx/tx-pins used.


Stop

Stop serial driver - frees the cog that was used

SOURCE CODE...
PUB Stop
  if cog
    cogstop(cog~ - 1)


Start(rxpin, txpin, baudrate)

  ┌─────────────────────────────────────────────────────────────────────┐
  │Purpose:                                                             │
  │  Start a single port serial driver - uses a single cog.             │
  ├─────────────────────────────────────────────────────────────────────┤           
  │Inputs:                                                              │
  │  rxpin and txpin are passed in as pin masks. The transmitter        │
  │    may be disabled by passing in a mask of 0. The receiver may be   │
  │    disabled by simply ignoring it.                                  │
  │  baudrate etc Is passed in as the desired baudrate. The transmit    │
  │    / receive pair operate at the same baudrate.                     │
  ├─────────────────────────────────────────────────────────────────────┤           
  │Output:                                                              │
  │  Returns the address of the shared memory space if a cog was        │
  │  successfully started, or 0 if no cog available.                    │
  └─────────────────────────────────────────────────────────────────────┘

SOURCE CODE...
PUB Start(rxpin, txpin, baudrate) : addr
  Stop
  rx1_buf     := -1                    'Set receiver to idle
  tx1_buf     := -1                    'Set transmitter to idle
  rx1_mask    := rxpin                 'Set receive pin
  tx1_mask    := txpin                 'Set transmit pin
  baud1_ticks := CLKFREQ / baudrate    'Set baudrate
  cog := cognew(@entry, @rx1_buf) + 1  'Start the cog
  if cog
    addr := @rx1_buf


Basic send/receive

Here you find basic send/receive-methods for single bytes.


RxFlush

Flush receive buffer

SOURCE CODE...
PUB RxFlush
  repeat until rxcheck < 0


RxCheck

Check if byte received (never waits) returns < 0 if no byte received, $00..$FF if byte

SOURCE CODE...
PUB RxCheck : rxbyte
  if (rxbyte := rx1_buf) => 0
    rx1_buf := -1


RxTime(ms)

Wait ms milliseconds for a byte to be received returns -1 if no byte received, $00..$FF if byte

SOURCE CODE...
PUB RxTime(ms) : rxbyte | t
  t := cnt
  repeat until (rxbyte := rxcheck) => 0 or (cnt - t) / (clkfreq / 1000) > ms


Rx

Receive byte (may wait for byte) returns $00..$FF

SOURCE CODE...
PUB Rx : rxbyte
  repeat while (rxbyte := rx1_buf) < 0
  rx1_buf := -1


Tx(txbyte)

Send byte (may wait for room in buffer)

SOURCE CODE...
PUB Tx(txbyte)
  repeat while tx1_buf => 0
  tx1_buf := txbyte


Extended send/receive

Here you find extended send/receive-methods. This serial object has just one extended send-method.
You can use it for output of zero termiated strings.


Str(stringptr)

Send a string

SOURCE CODE...
PUB Str(stringptr)
  repeat strsize(stringptr)
    tx(byte[stringptr++])


Generic Interface

this Interface provides generic access with different objects.

I_ReadByteCheck

   Check if a byte is waiting in the receive buffer and return the byte if one is there,
   does NOT block (never waits).
   return:     If no byte, then return(-1).  If byte, then return(byte).

SOURCE CODE...
PUB I_ReadByteCheck
RETURN RxCheck


I_ReadByte

reads a byte

SOURCE CODE...
PUB I_ReadByte
RETURN Rx


I_WriteByte(outByte)

writes a byte

SOURCE CODE...
PUB I_WriteByte(outByte)
RETURN Tx(outByte.Byte[0])


I_ReadWord

reads a word

SOURCE CODE...
PUB I_ReadWord
RESULT.Byte[0]:=Rx
RESULT.Byte[1]:=Rx


I_WriteWord(outWord)

writes a word

SOURCE CODE...
PUB I_WriteWord(outWord)
Tx(outWord.Byte[0])
RETURN Tx(outWord.Byte[1])


I_ReadLong

reads a long

SOURCE CODE...
PUB I_ReadLong
RESULT.Byte[0]:=Rx
RESULT.Byte[1]:=Rx
RESULT.Byte[2]:=Rx
RESULT.Byte[3]:=Rx


I_WriteLong(outLong)

writes a long

SOURCE CODE...
PUB I_WriteLong(outLong)
Tx(outLong.Byte[0])
Tx(outLong.Byte[1])
Tx(outLong.Byte[2])
RETURN Tx(outLong.Byte[3])


I_ReadString(addressToPut, terminator)

reads a string terminated by terminator to addressToPut - terminator not added - returns bytes read

SOURCE CODE...
PUB I_ReadString(addressToPut, terminator) | inByte
  repeat while ((inByte:=Rx)<>terminator)
    byte[addressToPut++]:=inByte
    RESULT++


I_WriteString(addressToGet, terminator)

writes a string terminated by terminator from addressToGet - terminator not added - returns bytes written

SOURCE CODE...
PUB I_WriteString(addressToGet, terminator) | outByte
  repeat while ((outByte:=byte[addressToGet++])<>terminator)
    Tx(outByte)
    RESULT++


I_ReadBlock(addressToPut, count)

reads count bytes to addressToPut - returns bytes read

SOURCE CODE...
PUB I_ReadBlock(addressToPut, count)
  repeat count
    byte[addressToPut++]:=Rx   
RETURN count


I_WriteBlock(addressToGet, count)

writes count bytes from addressToGet- returns bytes written

SOURCE CODE...
PUB I_WriteBlock(addressToGet, count)
  repeat count
    Tx(byte[addressToGet++])   
RETURN count


Assembly Cog


SOURCE CODE...
                    ORG     0
'
' Initialisation. 
'
entry               mov     t1,PAR
                    mov     t2,#5
:en1                mov     rx1ptr,t1
                    add     :en1,c512
                    add     t1,#4
                    djnz    t2,#:en1
                    mov     t2,#3
:en2                rdlong  rx1mask,rx1mask
                    add     :en2,c513
                    djnz    t2,#:en2
                    or      OUTA,tx1mask
                    or      DIRA,tx1mask
                    jmp     dRx1

c512                long    512
c513                long    513
                    
rx1ptr              long    0    
tx1ptr              long    0 
rx1mask             long    0
tx1mask             long    0
baud1               long    0
'
' Receiver 
'
aRx1_Start          mov     rx1bits,#9          '# of bits to receive
                    mov     rx1cnt,baud1
                    shr     rx1cnt,#2           'quarter of a bit tick ...
                    add     rx1cnt,baud1        '... plus a full bit tick
                    mov     dRx1,#aRx1_Wait
                    
aRx1_Wait           test    rx1mask, INA  WC    'Wait for start bit
    if_c            jmp     #aRx1_Get                                 
                    add     rx1cnt, CNT
                    mov     dRx1,#aRx1_Bits
                    
aRx1_Bits           mov     t1,rx1cnt           'Check if bit receive period done
                    sub     t1,cnt
                    cmps    t1,#0         WC    'Time to sample yet?
    if_nc           jmp     #aRx1_Get
                    test    rx1mask, INA  WC    'Receive bit into carry
                    rcr     rx1data,#1          'Carry into Rx buffer
                    add     rx1cnt,baud1        'Go to next bit period
                    djnz    rx1bits,#aRx1_Bits  'Get next bit
    if_nc           jmp     #aRx1_Start
                    and     rx1data,cRxAnd      'Mask out the unwanted bits
                    rol     rx1data,rx1rol      'Put the data into the correct byte
                    mov     dRx1,#aRx1_Put
                    jmp     dTx1                'Go do some transmitter code

                    'General variables for the receiver
cRxAnd              long    %0111_1111_1000_0000_0000_0000_0000_0000
rx1len              long    0
rx1maxlen           long    (tx1fifo - rx1fifo - 1) * 4
dRx1                long    aRx1_Start
                    'Variables used to receive a byte into the FIFO
rx1data             long    0
rx1bits             long    0
rx1cnt              long    0
rx1rol              long    9
rx1out              long    0
rx1put              long    rx1fifo
rx1putb             long    $80
                    'Variables used to grab a byte from the FIFO
rx1ror              long    0                    
rx1get              long    rx1fifo
rx1getb             long    $80

aRx1_Put            cmp     rx1len,rx1maxlen  WZ
    if_z            jmp     #(aRx1_Get+2)
                    mov     dRx1,#aRx1_Start
                    add     rx1len,#1
                    or      rx1out,rx1data      'Merge in the new data byte
arp1                mov     rx1fifo,rx1out      'Write to the FIFO memory
                    add     rx1rol,#8           'Prapare for the next byte
                    rol     rx1putb,#8  WC      'C true every 4 cycles
    if_nc           jmp     dTx1
                    add     rx1put,#1
                    cmp     rx1put,#tx1fifo   WZ
    if_z            mov     rx1put,#rx1fifo
                    movd    arp1,rx1put         'Set the new FIFO destination 
                    mov     rx1out,#0
                    jmp     dTx1

aRx1_Get            cmp     rx1len,#0  WZ
    if_z            jmp     dTx1
                    rdlong  t1,rx1ptr
                    shl     t1,#1    WC         'Is the receive buffer empty?
    if_nc           jmp     dTx1                'Jump if not
                    sub     rx1len,#1
arg1                mov     t1,rx1fifo
                    ror     t1,rx1ror
                    and     t1,#$ff
                    wrlong  t1,rx1ptr 
                    add     rx1ror,#8
                    rol     rx1getb,#8  WC      'C true every 4 cycles
    if_nc           jmp     dTx1
                    add     rx1get,#1
                    cmp     rx1get,#tx1fifo  WZ
    if_z            mov     rx1get,#rx1fifo
                    movs    arg1,rx1get
                    jmp     dTx1
'
' Transmitter 
'
aTx1_Start          cmp     tx1len,#0   WZ
    if_z            jmp     #aTx1_Put
                    mov     dTx1,#aTx1_Byte
                    sub     tx1len,#1
atg1                mov     tx1data,tx1fifo
                    ror     tx1data,tx1ror
                    and     tx1data,#$ff
                    add     tx1ror,#8
                    rol     tx1getb,#8  WC
    if_nc           jmp     dRx1     
                    add     tx1get,#1
                    cmp     tx1get,#fifo1end  WZ
    if_z            mov     tx1get,#tx1fifo
                    movs    atg1,tx1get
                    jmp     dRx1

aTx1_Byte           shl     tx1data,#2
                    or      tx1data,cFixedBits  'or in a idle line state and a start bit
                    mov     tx1bits,#11
                    mov     tx1cnt,cnt
aTx1_Bits           shr     tx1data,#1   WC
                    muxc    OUTA,tx1mask        
                    add     tx1cnt,baud1        'Bit period counter
                    mov     dTx1,#aTx1_Wait
                    jmp     dRx1

aTx1_Wait           mov     t1,tx1cnt           'Check bit period
                    sub     t1,CNT
                    cmps    t1,#0       WC      'Is bit period done yet?
    if_nc           jmp     #aTx1_Put
                    djnz    tx1bits,#aTx1_Bits  'Transmit next bit
                    mov     dTx1,#aTx1_Start
                    jmp     dRx1

aTx1_Put            cmp     tx1len,#(fifo1end-tx1fifo) WZ
    if_z            jmp     dRx1
                    rdlong  t1,tx1ptr
                    shl     t1,#1    NR, WC
    if_c            jmp     dRx1
                    wrlong  cMinusOne,tx1ptr
                    add     tx1len,#1
                    and     t1,#$ff
                    shl     t1,tx1rol
                    add     tx1rol,#8
                    or      tx1out,t1
atp1                mov     tx1fifo,tx1out    
                    rol     tx1putb,#8  WC
    if_nc           jmp     dRx1
                    add     tx1put,#1
                    cmp     tx1put,#fifo1end  WZ
    if_z            mov     tx1put,#tx1fifo
                    movd    atp1,tx1put
                    mov     tx1out,#0
                    jmp     dRx1
         
                    'General variables for the transmitter
tx1len              long    0
tx1maxlen           long    (fifo1end - tx1fifo - 1) * 4
dTx1                long    aTx1_Start
                    'Variables used to grab the transmit a byte from the FIFO
tx1data             long    0
tx1bits             long    0
tx1cnt              long    0
tx1ror              long    0
tx1get              long    tx1fifo
tx1getb             long    $80
                    'Variables used to receive a byte into the FIFO
tx1rol              long    0
tx1put              long    tx1fifo
tx1putb             long    $80
tx1out              long    0

'
' Data
'
cFixedBits          long    %1_0000_0000_0_1    'Stop + 8 x Data + Start + Idle bits
cMinusOne           long    -1     

t1                  long    0
t2                  long    0

'
' The buffer sizes may be freely adjusted. As long as the code still compiles
' into the cog. The sizes are currently at their maximum, but may be adjusted
' to favour either the receiver or tranmitter.
'
rx1fifo             res     RX1FIFO_SIZE
tx1fifo             res     TX1FIFO_SIZE
fifo1end

                    FIT     496


License

┌──────────────────────────────────────────────────────────────────────────────────────┐
│                            TERMS OF USE: MIT License                                 │                                                            
├──────────────────────────────────────────────────────────────────────────────────────┤
│Permission is hereby granted, free of charge, to any person obtaining a copy of this  │
│software and associated documentation files (the "Software"), to deal in the Software │
│without restriction, including without limitation the rights to use, copy, modify,    │
│merge, publish, distribute, sublicense, and/or sell copies of the Software, and to    │
│permit persons to whom the Software is furnished to do so, subject to the following   │
│conditions:                                                                           │
│                                                                                      │
│The above copyright notice and this permission notice shall be included in all copies │
│or substantial portions of the Software.                                              │
│                                                                                      │
│THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,   │
│INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A         │
│PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT    │
│HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF  │
│CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE  │
│OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                                         │
└──────────────────────────────────────────────────────────────────────────────────────┘