RF12 Driver

NOTE: This is not a complete work. I believe an SPIreadWrite method needs to be used to shift in the bytes at the same time as it is shifting out. I’m new to the spin language. But this is what i came up with. Feel free to comment suggestions

CON
{
  .:RF12B wireless tranciever driver V1.0:.
  .:Written by Bill Heaster:.
  .:TheCreator at ApexLogic D0t net
  .:Adapted from JeeLabs Arduino RF12Driver;.
}


  #0, TXIDLE, TXRECV, TXSEND, TXFULL

  'Frequency Constants
  F433 = 0
  F868 = 1
  F915 = 2

  'RF12 Bits

  RF_RECEIVER_ON = $82DD
  RF_XMITTER_ON  = $823D
  RF_IDLE_MODE   = $820D
  RF_SLEEP_MODE  = $8205
  RF_WAKEUP_MODE = $8207
  RF_TXREG_WRITE = $B800
  RF_RX_FIFO_READ= $B000
  RF_WAKEUP_TIMER= $E000

  'max buffer size

  MAXPACKET = 128
  'Status Bits
  RF_LBD_BIT  =    $0400
  RF_RSSI_BIT =    $0100

  'Node ID stuff
  NODE_BAND    =   $C0
  NODE_ACKANY  =   $20
  NODE_ID      =   $1F

OBJ

Debug           :               "SimpleDebug"
VAR

  'Pins , state flags, stacks, and buffers
  long SDI, SDO, CP, SS, nIRQ, ID, MHZ
  long intStack[32]
  long rxfill, rxstate, packetSize
  byte rf12Buff[MAXPACKET]
  long  in , out, pos, temp, temp2
  byte cog
  byte hdr[3]
  long count
  byte tempCheck, checksum, low, high



PUB Start(DIpin, DOpin, Cpin, SSpin, nIRQpin, NodeIDarg, MHZarg)


   Debug.start(115200)
   Debug.str(STRING("Debug Started"))
    Debug.newline

  'Assign global variables with the methods argument
  SDI :=DIpin
  SDO := DOpin
  CP := Cpin
  SS := SSpin
  nIRQ := nIRQpin
  ID := NodeIDarg
  MHZ := MHZarg

  'Set pin direction of slave select pin, set it to high
  dira[SS]~~
  dira[SDI]~~
  dira[SDO]~
  dira[CP]~~
  dira[nIRQ]~

  outa[SS]~~


  'When the interupt pin on the rf12 is pulled low clear it with a fake status read command
  'repeat until(ina[nIRQ] == 1)
  WriteCommand($0000)

  'What is our Mhz
  if(MHZ == F433)
    WriteCommand($80D8)         'Set registers, 433mhz, 12.5pf
    WriteCommand($A4B0)         'Set Frequency Center
  if(MHZ == F868)
    WriteCommand($80E8)
    WriteCommand($A640)
  if(MHZ == F915)
    WriteCommand($80F8)
    WriteCommand($A7D0)

  WriteCommand(RF_IDLE_MODE) 'disable Clock , Enable LBD
  WriteCommand($C606) '49.2kb/s data rate
  WriteCommand($94A0) 'fast VDI, -103Dbm, !gain, 134khz BaseBand
  WriteCommand($C2AC) 'DigitalFilter, clock auto lock
  WriteCommand($CA8B) 'FIFO8, 1-sync, !ff, DR
  WriteCommand($C483) 'AFC
  WriteCommand($9850) '90khz, !MP, !OP
  WriteCommand($CC77) 'PLL
  WriteCommand($E000) 'not using low duty cycle
  WriteCommand($C800) ' not using wakeup timer
  WriteCommand($C000) '1.0mhz, 2.2v


  rxState := TXIDLE
  Debug.str(STRING("txidle"))
  Debug.newline

  'Setup a new COG to handle interrupts
  cog := cognew(RX(nIRQ), @intStack)

  return cog

PUB Stop
  if(cog)
    cogstop(cog~-1)


PRI WriteCommand(cmd) | ltemp

  Debug.hex(cmd,4)
  Debug.newline
  outa[CP]~
  outa[SS]~
  repeat ltemp from 17 to 0
    if(cmd & $8000)
      write1
    else
      write0
    cmd<=1

  outa[CP]~
  outa[SS]~~


PUB canSend

    if(rxstate==TXRECV & rxfill ==0)
      WriteCommand(RF_IDLE_MODE)
      rxstate := TXIDLE
      return 1
    if(rxstate == TXIDLE)
      return 1


PUB RX(iPin)
  dira[iPin]~


  repeat while(ina[iPin]==0 & rxstate == TXRECV)
      rf12Buff[rxfill] := ReadFIFO
      checksum += rf12Buff[rxfill]
      rxfill++
      if(rxfill>=MAXPACKET+5)
        checksum &= $0FF
        WriteCommand(RF_IDLE_MODE)
        rxstate := TXFULL

PUB checkData
  Debug.str(STRING("CheckData"))
  Debug.newline
  'If the buffer is full
  if(rxstate == TXFULL)

    if(packetSize > MAXPACKET)
      checksum := -1 'Send bad crc if the packet length is invalid

    if(rf12Buff[--rxfill] <> checksum)
      checksum := -1

    'if the packets destination was to this node, or a broadcast packet
    if((rf12Buff[0] == ID | rf12Buff[0]==31)& checksum > -1)
      'return 1 to let us know that we need to parse the buffer
      return 1

  if(rxstate == TXIDLE)
    recvStart
  return 0

PRI recvStart
  rxfill := packetSize := 0
  checksum := 0
  rxstate := TXRECV
  WriteCommand(RF_RECEIVER_ON)

PUB ReadPacket(myBuffer) | myCount

  myCount:=0
  repeat myCount from 0 to rxfill
    myBuffer[myCount] := rf12Buff[myCount]

  bytefill(@rf12Buff, 0, MAXPACKET)
  WriteCommand(RF_IDLE_MODE)
  rxstate := TXIDLE

  return myCount

PUB WritePacket(dst, buffer, size, ack) | tempTime

  bytefill(@rf12Buff, 0,MAXPACKET)
  packetSize := size
  checksum := 0
  'Assemble the packet into a buffer then send it

  'Assemble Header...Destination [0], this node ID[1], 1 for ack 0 for no ack [2]
  rf12Buff[0] := dst
  rf12Buff[1] := ID

  if(ack)
    rf12Buff[2] := 1
  else
    rf12Buff[2] := 0

  count:= 0
  repeat count from 0 to size
    checksum := checksum + buffer[count]
    rf12Buff[count+3] :=buffer[count]

  checksum &= $0FF

  rxstate := TXSEND
  WriteCommand(RF_XMITTER_ON)
  tempTime := cnt
  waitcnt(100_000 + tempTime)
  'send preamble
  WriteByte($AA)
  WriteByte($AA)
  WriteByte($AA)

  'send sync word
  WriteByte($D4)

  count:= 0
  repeat count from 0 to size+3
    WriteByte(rf12Buff[count])

  WriteByte(checksum)
  WriteByte($AA)
  WriteCommand(RF_IDLE_MODE)
  rxstate := TXIDLE

PRI Write1 | tempTime

  tempTime := cnt
  outa[CP]~
  waitcnt(10_000 + tempTime)
  outa[SDI]~~

  tempTime := cnt
  waitcnt(100_000 + tempTime)
  outa[CP]~~
  tempTime:= cnt
  waitcnt(10_000 + tempTime)


PRI Write0 | tempTime

  tempTime := cnt
  outa[CP]~
  waitcnt(10_000 + tempTime)
  outa[SDI]~

  tempTime := cnt
  waitcnt(100_000 + tempTime)
  outa[CP]~~
  tempTime := cnt
  waitcnt(10_000 + tempTime)

PRI WriteByte(outByte) | RGIT, tempOut

  RGIT := 0
  tempOut := RF_TXREG_WRITE | outByte

  repeat while RGIT == 0
    outa[CP]~
    outa[SS]~
    outa[SDI]~
    outa[CP]~~
    if(ina[SDO])
      RGIT :=1
    else
      RGIT := 0

    outa[CP]~
    outa[SDI]~~
    outa[SS]~~

    if(RGIT)
      WriteCommand(tempOut)

PRI ReadFIFO | myResult, tempTime

  'set clock and chip select
  outa[CP]~
  outa[SS]~

  'Skip status bytes
  repeat 17
    outa[CP]~~
    tempTime := cnt
    waitcnt(tempTime + 1_000_000)
    outa[CP]~
    tempTime := cnt
    waitcnt(tempTime + 1_000_000)
  myResult := 0
  repeat 9
    myResult <<= 1

    if(ina[SDO])
      myResult |= 1
    outa[CP]~~
    tempTime := cnt
    waitcnt(tempTime + 1_000_000)
    outa[CP]~
    tempTime := cnt
    waitcnt(tempTime + 1_000_000)
  outa[SS]~~
  return myResult

{~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
{Test stuff below here}
{~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}


PUB getStatus | statByte, TempTime

  statByte := 0
  WriteCommand($0000)
  repeat 17
    outa[CP]~~
    tempTime := cnt
    waitcnt(tempTime + 1_000_000)
    if(ina[SDO] == 1)
      statByte |=1
    outa[CP]~
    tempTime := cnt
    waitcnt(tempTime + 1_000_000)
    statByte <<=1
CON
{
  .:RF12B wireless tranciever driver V1.0:.
  .:Written by Bill Heaster:.
  .:TheCreator at ApexLogic D0t net
  .:Adapted from JeeLabs Arduino RF12Driver;.
}


  #0, TXIDLE, TXRECV, TXSEND, TXFULL

  #0, PRE1, PRE2, PRE3, SYNC, HEADER1, HEADER2, HEADER3, SDATA, POST

  'Frequency Constants
  F433 = 0
  F868 = 1
  F915 = 2

  'RF12 Bits

  RF_RECEIVER_ON = $82DD
  RF_XMITTER_ON  = $823D
  RF_IDLE_MODE   = $820D
  RF_TXREG_WRITE = $B800
  RF_RX_FIFO_READ= $B000
  RF_WAKEUP_TIMER= $E000

  'max buffer size

  MAXPACKET = 128
  'Status Bits
  RF_LBD_BIT  =    $0400
  RF_RSSI_BIT =    $0100

  'Node ID stuff
  NODE_BAND    =   $C0
  NODE_ACKANY  =   $20
  NODE_ID      =   $1F

OBJ

Debug           :               "SimpleDebug"
SPI             :               "SPI_ASM"
VAR

  'Pins , state flags, stacks, and buffers
  long SDI, SDO, CP, SS, nIRQ, ID, MHZ
  long intStack[128]
  long rxfill, rxstate, packetSize
  byte rf12Buff[MAXPACKET]
  byte rf12hdr[2]
  long  in , out, temp, temp2
  byte cog, pos
  long count
  byte tempCheck, checksum, low, high
  long packetState



PUB Start(DIpin, DOpin, Cpin, SSpin, nIRQpin, NodeIDarg, MHZarg)

   SPI.start(1,0)
   'Debug.start(115200)
   'Debug.str(STRING("Debug Started"))
   'Debug.newline

  'Assign global variables with the methods argument
  SDI :=DIpin
  SDO := DOpin
  CP := Cpin
  SS := SSpin
  nIRQ := nIRQpin
  ID := NodeIDarg
  MHZ := MHZarg

  'Set pin direction of slave select pin, set it to high
  dira[SS]~~
  dira[SDI]~~
  dira[SDO]~
  dira[CP]~~
  dira[nIRQ]~

  outa[SS]~~


  'When the interupt pin on the rf12 is pulled low clear it with a fake status read command
  repeat while(ina[nIRQ] == 0)
    WriteCommand($0000)

  'What is our Mhz
  if(MHZ == F433)
    WriteCommand($80D8)         'Set registers, 433mhz, 12.5pf
    WriteCommand($A4B0)         'Set Frequency Center
  if(MHZ == F868)
    WriteCommand($80E8)
    WriteCommand($A640)
  if(MHZ == F915)
    WriteCommand($80F8)
    WriteCommand($A7D0)

  WriteCommand(RF_IDLE_MODE) 'disable Clock , Enable LBD
  WriteCommand($C606) '49.2kb/s data rate
  WriteCommand($94A0) 'fast VDI, -103Dbm, !gain, 134khz BaseBand
  WriteCommand($C2AC) 'DigitalFilter, clock auto lock
  WriteCommand($CA8B) 'FIFO8, 1-sync, !ff, DR
  WriteCommand($C483) 'AFC
  WriteCommand($9850) '90khz, !MP, !OP
  WriteCommand($CC77) 'PLL
  WriteCommand($E000) 'not using low duty cycle
  WriteCommand($C800) ' not using wakeup timer
  WriteCommand($C000) '1.0mhz, 2.2v

  rxstate := TXIDLE

  'Setup a new COG to handle interrupts
  stop
  cog := cognew(handleInt(nIRQ), @intStack)
  return cog

PUB Stop
  if(cog)
    cogstop(cog~-1)

PRI WriteCommand(cmd) | myHigh, myLow

 ' Debug.hex(cmd,4)
 ' Debug.newline
  outa[SS]~
  myLow := cmd & $FF
  myHigh := cmd >>8

  SPI.SHIFTOUT(SDI, CP, SPI#MSBFIRST, 8, myHigh)
  SPI.SHIFTOUT(SDI, CP, SPI#MSBFIRST, 8, myLow)
  outa[SS]~~

PUB canSend
    if(rxstate==TXRECV & rxfill ==0)
      WriteCommand(RF_IDLE_MODE)
      rxstate := TXIDLE
      return 1
    if(rxstate == TXIDLE)
      return 1
    else
      return 0


PUB handleInt(iPin)
  dira[iPin]~

  if(rxstate == TXRECV)
      SPI.SHIFTOUT(SDI,CP,SPI#MSBFIRST, 8, RF_RX_FIFO_READ)
      SPI.SHIFTIN(SDO,CP,SPI#MSBPRE, 8)
      rf12Buff[rxfill] := in
      checksum += rf12Buff[rxfill]
      rxfill++
      if(rxfill>=MAXPACKET+5)
        checksum &= $0FF
        WriteCommand(RF_IDLE_MODE)
        rxstate := TXFULL
  if(rxstate==TXSEND & packetState==SDATA)
    if(pos <= packetSize)
      txByte(rf12Buff[pos++])
    else
      packetState++

  if(rxstate == TXSEND)
    case packetState
      PRE1:                     txByte($AA)
                                packetState++
      PRE2:                     txByte($AA)
                                packetState++
      PRE3:                     txByte($AA)
                                packetState++
      SYNC :                    txByte($D4)
                                pos := 0
                                packetState++
      HEADER1:                  txByte(rf12hdr[0])
                                packetState++
      HEADER2:                  txByte(rf12hdr[1])
                                packetState++
      HEADER3:                  txByte(rf12hdr[2])
                                packetState++
      POST :                    txByte(checksum)
                                txByte($AA)
                                WriteCommand(RF_IDLE_MODE)
                                rxstate := TXIDLE
      OTHER :                   txByte($AA)



PUB checkData

  'If the buffer is full
  if(rxstate == TXFULL)

    if(packetSize > MAXPACKET)
      checksum := -1 'Send bad crc if the packet length is invalid

    'if the packets destination was to this node, or a broadcast packet
    'if(( rf12Buff[0] == ID | rf12Buff[0]==31)& checksum > -1)
      'return 1 to let us know that we need to parse the buffer
    return 1

  if(rxstate == TXIDLE)
    recvStart

  return 0

PRI recvStart
  rxfill := packetSize := 0
  checksum := 0
  rxstate := TXRECV
  WriteCommand(RF_RECEIVER_ON)

PUB txByte(myByte) | RGIT

  RGIT := 0
  outa[SS]~
  repeat while !RGIT
    if(ina[SDO])
      RGIT := 1
      SPI.SHIFTOUT(SDI, CP, SPI#MSBFIRST, 8, RF_TXREG_WRITE)
      SPI.SHIFTOUT(SDI, CP, SPI#MSBFIRST, 8, myByte)
    else
      RGIT := 0
  outa[SS]~~

PUB ReadPacket(myBuffer) | myCount


  bytemove(@myBuffer, @rf12Buff, MAXPACKET)
  WriteCommand(RF_IDLE_MODE)
  rxstate := TXIDLE

  return myCount

PUB WritePacket(dst, buffer, size, ack) | tempTime

  bytefill(@rf12Buff, 0,MAXPACKET)
  packetSize := size
  checksum := 0
  'Assemble the packet into a buffer

  'Assemble Header...Destination [0], this node ID[1], 1 for ack 0 for no ack [2]
  rf12hdr[0] := dst
  rf12hdr[1] := ID

  if(ack)
    rf12hdr[2] := 1
  else
    rf12hdr[2] := 0

  bytemove(@rf12Buff, @buffer, size)

  repeat count from 0 to size
    checksum += rf12buff[count]
  checksum &= $0FF

  rxstate := TXSEND
  packetState := PRE1
  WriteCommand(RF_XMITTER_ON)

PUB checkStatus | outByte

  outa[SS]~
  outByte := SPI.SHIFTIN(SDO, CP, SPI#MSBPRE,8)
  outa[SS]~~

  return outByte

This is a different version using SPI_ASM object.

Leave a Reply