Can't get spi to read data

General discussion relating to the library modules supplied with the compiler

Moderators: David Barker, Jerry Messina

garryp4
Posts: 125
Joined: Mon May 21, 2007 7:18 am
Location: Loveland, CO USA

Can't get spi to read data

Post by garryp4 » Sun Apr 26, 2020 5:23 pm

I am having a problem getting SPI data from a radio. A paired circuit with the same circuit uses the same radio and works so I know the logic is good. the other circuit has an 18F47J13 and uses SPI2 and Jerry M's PPS module (great code!). This circuit uses an 18F14K22. I use the exact same settings for the SPI as the SPI2. I also see good updated radio register data returning on the scope, but just get 0's when display the value on hyperterm. The radio has a config chip select and a data chip select

Code: Select all

'****************************************************************************
Public Sub OPEN_SPI1()
  SPI1.SetClock(spiIdleLow,spiRisingEdge)           ' spiRisingEdge  spiFallingEdge
  SPI1.Open(SPI_MODE_1,spiOscDiv64,spiSampleEnd)    ' spiSampleEnd, spiSampleMiddle 
  DelayUS(5)
End Sub
'****************************************************************************
Public Sub RADIO_RESET()
  High(r_reset)
  DelayUS(110)
  Input(r_reset)
End Sub  
'****************************************************************************
Private Sub RADIO_CONFIG_SPI()
  OPEN_SPI1
  High(r_csdata)
  Low(r_cscon)
  DelayUS(100)
  
'  TRISB.4 = 1
'  b1 = SSPSTAT
'  b2 = SSPCON1
'  b3 = TRISB
'  
'  USART.Write("SSPSTAT = ",BinToStr(b1,8),"  SSPCON1 = ",BinToStr(b2,8),"  TRISB = ",BinToStr(b3,8),10,13)
'  DelayMS(2)

(gives "SSPSTAT = 10000000  SSPCON1 = 00100010  TRISB = 10110000")
  
End Sub
'****************************************************************************
Private Sub RADIO_DATA_SPI()
  OPEN_SPI1
  High(r_cscon)
  Low(r_csdata)
  DelayUS(100)
    
End Sub
'****************************************************************************
Private Sub DISABLE_RADIO_SPI()
  High(r_cscon)
  High(r_csdata)
'  DelayUS(2)
  SPI1.Close
End Sub  
'****************************************************************************
Public Sub RADIO_INIT()

  RADIO_RESET
  OPEN_SPI1

End Sub
'****************************************************************************
'****************************************************************************
Public Sub READ_REG()

  RADIO_INIT
  
  RADIO_CONFIG_SPI  
  SPI1.Transfer(gcon_addr_r)           ' $40
  b1 = SPI1.ReadByte
  DISABLE_RADIO_SPI
  USART.Write("GCON = ",BinToStr(b1,8),"  ",HexToStr(b1),10,13)
  DelayMS(2)

  RADIO_CONFIG_SPI  
  SPI1.Transfer(gcon_addr_w)                        ' Register address write
  SPI1.Transfer($68)                                ' Register data write
  DISABLE_RADIO_SPI
  
  RADIO_CONFIG_SPI  
  SPI1.Transfer(gcon_addr_r)
  b1 = SPI1.ReadByte
  DISABLE_RADIO_SPI
  USART.Write("GCON = ",BinToStr(b1,8),"  ",HexToStr(b1),10,13)
  DelayMS(2)  

  RADIO_CONFIG_SPI  
  SPI1.Transfer(gcon_addr_w)                        ' Register address write
  SPI1.Transfer($28)                                ' Register data write
  DISABLE_RADIO_SPI
  
  RADIO_CONFIG_SPI  
  SPI1.Transfer(gcon_addr_r)
  b1 = SPI1.ReadByte
  DISABLE_RADIO_SPI
  USART.Write("GCON = ",BinToStr(b1,8),"  ",HexToStr(b1),10,13)
  DelayMS(2)  

  RADIO_CONFIG_SPI  
  SPI1.Transfer(gcon_addr_w)                        ' Register address write
  SPI1.Transfer($88)                                ' Register data write
  DISABLE_RADIO_SPI
  
  RADIO_CONFIG_SPI  
  SPI1.Transfer(gcon_addr_r)
  b1 = SPI1.ReadByte
  DISABLE_RADIO_SPI
  USART.Write("GCON = ",BinToStr(b1,8),"  ",HexToStr(b1),10,13)
  DelayMS(2)  
  
End Sub
'****************************************************************************
Any help is greatly appreciated.
Thanks

Jerry Messina
Swordfish Developer
Posts: 1469
Joined: Fri Jan 30, 2009 6:27 pm
Location: US

Re: Can't get spi to read data

Post by Jerry Messina » Mon Apr 27, 2020 10:04 am

Here are a few things to look out for...

- on the 14K22, SPI pins are also analog so be sure to add a SetAllDigital() call in your main routine

- you can change OPEN_SPI1 to remove the SetClock() call since this is done by Open()

Code: Select all

Public Sub OPEN_SPI1()
// remove SetClock since Open does this
//  SPI1.SetClock(spiIdleLow,spiRisingEdge)           ' spiRisingEdge  spiFallingEdge
  SPI1.Open(SPI_MODE_1,spiOscDiv64,spiSampleEnd)    ' spiSampleEnd, spiSampleMiddle 
  DelayUS(5)
End Sub
- In READ_REG I'd recommend just using Transfer() instead of ReadByte() since Transfer() will take care of dealing with the SSPBUF and flags.
Calling Transfer() with no argument will send a dummy byte (set via '#option SPI_DEFAULT_TRANSFER') and read the result.
Change

Code: Select all

  SPI1.Transfer(gcon_addr_r)           ' $40
  b1 = SPI1.ReadByte
to

Code: Select all

  SPI1.Transfer(gcon_addr_r)           ' $40
  b1 = SPI1.Transfer()
And finally, I never tested this on a 14K22, so if it still doesn't work let me know and I'll get some samples to test it with.

garryp4
Posts: 125
Joined: Mon May 21, 2007 7:18 am
Location: Loveland, CO USA

Re: Can't get spi to read data

Post by garryp4 » Mon May 04, 2020 7:27 pm

Jerry:

Thanks for the reply and hope all is well in these crazy times.

I have changed the SPI1.ReadByte to SPI1.Transfer(). I have also put a statement to show that TRISB.4, the SDI pin, is an input. This should satisfy the SetAllDigital() call requirement. In the parent routine only AN2 is enabled with:

ADCON0 = $09 ' AN2
ADCON1 = $00 ' Vref = Vcc

Because I can see the data sent from the radio registers changing on the scope and matching what the it should be, I know the PIC SDO and CLK are good. The SDI data is getting to B.4.


Here is the current code:

Code: Select all

'****************************************************************************
Public Sub OPEN_SPI1()
  SPI1.Open(SPI_MODE_1,spiOscDiv64,spiSampleEnd)    ' spiSampleEnd, spiSampleMiddle 
  DelayUS(5)
End Sub
'****************************************************************************
Public Sub RADIO_RESET()
  High(r_reset)
  DelayUS(110)
  Input(r_reset)
End Sub  
'****************************************************************************
Private Sub RADIO_CONFIG_SPI()
  OPEN_SPI1
  High(r_csdata)
  Low(r_cscon)
  DelayUS(100)
  
End Sub
'****************************************************************************
Private Sub RADIO_DATA_SPI()
  OPEN_SPI1
  High(r_cscon)
  Low(r_csdata)
  DelayUS(100)
    
End Sub
'****************************************************************************
Private Sub DISABLE_RADIO_SPI()
  High(r_cscon)
  High(r_csdata)
  SPI1.Close
End Sub  
'****************************************************************************
Public Sub RADIO_INIT()

  RADIO_RESET
  OPEN_SPI1

End Sub
'****************************************************************************
Public Sub READ_REG()

  RADIO_INIT
  
  RADIO_CONFIG_SPI
  
  b3 = TRISB
  USART.Write("TRISB = ",BinToStr(b3,8),".  B.4 is SDI",10,13)
  DelayMS(2)
    
  SPI1.Transfer(gcon_addr_r)           ' $40
'  b1 = SPI1.ReadByte
  b1 = SPI1.Transfer()
  DISABLE_RADIO_SPI
  USART.Write("GCON = ",BinToStr(b1,8),"  ",HexToStr(b1),10,13)
  DelayMS(2)

  RADIO_CONFIG_SPI  
  SPI1.Transfer(gcon_addr_w)                        ' Register address write
  SPI1.Transfer($68)                                ' Register data write
  DISABLE_RADIO_SPI
  
  RADIO_CONFIG_SPI  
  SPI1.Transfer(gcon_addr_r)
  b1 = SPI1.Transfer()
  DISABLE_RADIO_SPI
  USART.Write("GCON = ",BinToStr(b1,8),"  ",HexToStr(b1),10,13)
  DelayMS(2)  

  RADIO_CONFIG_SPI  
  SPI1.Transfer(gcon_addr_w)                        ' Register address write
  SPI1.Transfer($28)                                ' Register data write
  DISABLE_RADIO_SPI
  
  RADIO_CONFIG_SPI  
  SPI1.Transfer(gcon_addr_r)
  b1 = SPI1.Transfer()
  DISABLE_RADIO_SPI
  USART.Write("GCON = ",BinToStr(b1,8),"  ",HexToStr(b1),10,13)
  DelayMS(2)  

  RADIO_CONFIG_SPI  
  SPI1.Transfer(gcon_addr_w)                        ' Register address write
  SPI1.Transfer($88)                                ' Register data write
  DISABLE_RADIO_SPI
  
  RADIO_CONFIG_SPI  
  SPI1.Transfer(gcon_addr_r)
  b1 = SPI1.Transfer()
  DISABLE_RADIO_SPI
  USART.Write("GCON = ",BinToStr(b1,8),"  ",HexToStr(b1),10,13)
  DelayMS(2)

  b1 = SSPBUF
  USART.Write("SSPBUF = ",BinToStr(b1,8),"  ",HexToStr(b1),10,13)
  DelayMS(2)  
  
End Sub
This is the hyperterm display:

TRISB = 10110000. B.4 is SDI
GCON = 00000000 0
GCON = 00000000 0
GCON = 00000000 0
GCON = 00000000 0
SSPBUF = 00000000 0

Still not getting any SDI data.


Thanks a lot for your time.

Garry

Jerry Messina
Swordfish Developer
Posts: 1469
Joined: Fri Jan 30, 2009 6:27 pm
Location: US

Re: Can't get spi to read data

Post by Jerry Messina » Tue May 05, 2020 10:39 am

I assume you're using the SPI.bas library module from the latest version (2.2.3.2) and have copied it and renamed the module to SPI1.

I have also put a statement to show that TRISB.4, the SDI pin, is an input. This should satisfy the SetAllDigital() call requirement. In the parent routine only AN2 is enabled with:

ADCON0 = $09 ' AN2
ADCON1 = $00 ' Vref = Vcc
Just so we're clear, ADCON0 doesn't change the analog/digital function of the ports... that's done using ANSEL/ANSELH on the 14K22.
A PORT pin that's in analog mode will always read '0' no matter what the TRIS setting for that bit is.

Humor me. Try adding this to your main program:

Code: Select all

include "setdigitalio.bas"

SetAllDigital()
SetAnalogPort(AN2, ANA)   // set AN2 (RA2) to analog mode
That should set all IO to digital mode and then AN2 back to analog.
It will also setup registers like slew-rate control for you.

If that doesn't work then I'll have to get some samples and figure out what's up.

garryp4
Posts: 125
Joined: Mon May 21, 2007 7:18 am
Location: Loveland, CO USA

Re: Can't get spi to read data

Post by garryp4 » Tue May 05, 2020 1:37 pm

Jerry:

That definitely did make a difference! I now get:

GCON = 01000111 47
GCON = 01000111 47
GCON = 01000111 47
GCON = 01000111 47
SSPBUF = 01000111 47

I should be writing then reading a hex 68, 28, then 88. I will put the scope on it tonight and see what is happening.

Also, I did not look far into the A/D section of the data sheet as have been using the ADCON0 to disable successfully for years. Thanks for that.

And, I am running version 2.2.2.5. Are you able to send me a new code to get the latest or does that have to come from David?

Thanks

Jerry Messina
Swordfish Developer
Posts: 1469
Joined: Fri Jan 30, 2009 6:27 pm
Location: US

Re: Can't get spi to read data

Post by Jerry Messina » Tue May 05, 2020 2:34 pm

Great.

The ADCON/ANSEL/XXXXX varies from device to device. That's why it's best to use SetAllDigital
Are you able to send me a new code to get the latest or does that have to come from David?
There's a "check for updates" button in the IDE Help | About window.

If you need your registration number you'll have to get that from David.

Jerry Messina
Swordfish Developer
Posts: 1469
Joined: Fri Jan 30, 2009 6:27 pm
Location: US

Re: Can't get spi to read data

Post by Jerry Messina » Wed May 06, 2020 9:50 am

If you're using an older version of spi.bas you should check the transfer() routines.

Some versions of the function returned the value in the SSPBUF register.
They were declared something like this...

Code: Select all

public inline function transfer(b as WREG) as SSPBUF
Note the 'as SSPBUF' as the return value. The problem with that is if you don't assign the function to a value ie 'b = spi.transfer($55)' then it doesn't read the SSPBUF register and you get overrun errors.

The simplest way to fix that is to always assign the return value to something, even if you're doing a write and don't need the value

Code: Select all

  b1 = SPI1.Transfer(gcon_addr_r)           'write  $40, read dummy return
  b1 = SPI1.Transfer()
You can always assign the return value to WREG if not needed

Code: Select all

  WREG = SPI1.Transfer(gcon_addr_r)           'write  $40, read dummy return
This was changed in later versions to always read the SSPBUF and return a byte value instead of the SSPBUF directly.

garryp4
Posts: 125
Joined: Mon May 21, 2007 7:18 am
Location: Loveland, CO USA

Re: Can't get spi to read data

Post by garryp4 » Wed Jul 22, 2020 9:51 pm

Jerry:

Sorry did not get back before now. Life happens...

I am still having a problem with the 18F47J13 SPI with a Microchip radio. Here is a comment from their help:

"An important point for the 18F47J13. Specifically, for every byte SPI is transferred, the SPI (MSSP) buffered value need to be read. Therefore, there should be two writes and two reads for every set of /CSCON."

Is this done with:

Code: Select all

****************************************************************************
* Name    : begin_transfer, wait_for_transfer helper functions             *
* Purpose : wait for SPI transfer to complete                              *
*         : if #option SPI_TRANSFER_END = BF then it waits for BF status   *
*         : otherwise it waits for the SSPIF bit to be set                 *
****************************************************************************
}         
// begin an SPI transfer
// you can use this routine to perform general housekeeping (clear WCOL, etc)
// depending on the SPI2_TRANSFER_END setting it may just compile out
Inline Sub begin_transfer()
  #if (SPI2_TRANSFER_END = SSPIF)
    SSPIF = 0               // must clear SSPxIF manually
  #endif
End Sub

// wait for transfer to complete
Inline Sub wait_for_transfer()
  #if (SPI2_TRANSFER_END = BF)
    Repeat
        ClrWDT
    Until (BF = 1)                  // wait for BF bit (clears when SSPBUF read)
  #else     //   SPI2_TRANSFER_END = SSPIF
    Repeat
        ClrWDT
    Until (SSPIF = 1)               // wait for SSPxIF bit
    SSPIF = 0                       // must clear SSPxIF manually
  #endif    
End Sub
?

And thanks so much for all your help and hope all is well in these crazy times.

Garry

Jerry Messina
Swordfish Developer
Posts: 1469
Joined: Fri Jan 30, 2009 6:27 pm
Location: US

Re: Can't get spi to read data

Post by Jerry Messina » Wed Jul 22, 2020 11:27 pm

Hi Garry,

The write-read sequence is done in the '_transfer()' function:

Code: Select all

inline function _transfer(b as WREG) as WREG
    begin_transfer()
    SSPBuffer = b           // send the byte
    wait_for_transfer()     // wait for byte to be sent/received
    // note: instead of using 'as SSPBUF' function return value,
    // this forces reading the SSPBUF even if caller ignores it
    // (otherwise BF will not get cleared)
    result = SSPBuffer      // get the byte (and clear BF)
end function

public function Transfer(b as WREG) as byte
    result = _transfer(WREG)
end function

// short-hand alias for a read transfer (automatically writes a dummy value)
public function Transfer() as WREG
    WREG = _transfer(DEFAULT_TRANSFER)
end function
Just so I know which code to look at, the problem is on the 47J13 side?
Which radio chip are you interfacing to?

garryp4
Posts: 125
Joined: Mon May 21, 2007 7:18 am
Location: Loveland, CO USA

Re: Can't get spi to read data

Post by garryp4 » Fri Jul 24, 2020 11:45 pm

The radio PN is MRF89XAM9A from Microchip. It was marketed as a simple radio module but has really not been. On the same circuit I have an SPI FRAM device and I can write and read from it with not problem. The only difference is the FRAM uses SPI1 and the radio SPI2. The project is two different circuits, one as a controller and the other are remotes. The controller has the 18F47J13 and the remotes an 18F14K22. You helped me with the 14K22 a while ago. I am able to set and read registers on the radio with the 14K22 but not with the 47J13 so do not know where the problem lies. I mostly wanted to verify that your SPI library satisfies what the Microchip help was asking.

This is my first circuit that could market to a specific sporting group so am a bit anxious to get it working. It seem very straight forward when designing the circuit.

As always, thanks so much for your help.

Garry

Jerry Messina
Swordfish Developer
Posts: 1469
Joined: Fri Jan 30, 2009 6:27 pm
Location: US

Re: Can't get spi to read data

Post by Jerry Messina » Sat Jul 25, 2020 2:41 pm

Looking through the datasheet for the MRF89XA, I think you want a different SPI setup.
SPI mode 0 is what's called out in the datasheet, and the ref library from mchip use this mode too.

Try this and see what happens. You'll have to adjust the SPI pin #options, IO pin assignments and PPS settings to match your hdw...
right now they're all assigned to PORTD pins

Code: Select all

// MRF89XA radio with 18F47J13 SPI2 using PPS
// SF v2.2.3.4
device = 18F47J13
clock = 8           // current intosc.bas v2.1 limit

include "intosc.bas"
include "setdigitalio.bas"

include "usart.bas"
include "convert.bas"

//-------------------------------------
// **HARDWARE DEPENDANT SECTION**
//-------------------------------------
// SPI2 (NOTE: if you change these you MUST change the PPS setup in OPEN_SPI2)  
include "pps.bas"
#option SPI2_SCK = PORTD.6         // SPI clock
#option SPI2_SDI = PORTD.5         // SPI data in
#option SPI2_SDO = PORTD.4         // SPI data out
include "SPI2.bas"

// IO pin assignments
public dim
    R_RESET     as PORTD.0,             // to MRF89XA RESET pin
    R_CSDAT     as PORTD.1,             // to MRF89XA CSDAT_N pin (active low)
    R_CSCON     as PORTD.2,             // to MRF89XA CSCON_N pin (active low)
    R_SCK       as SPI2_SCK.SPI2_SCK@,  // SPI2 SCK
    R_SDO       as SPI2_SDO.SPI2_SDO@,  // SPI2 SDO (to MRF89XA SDI)
    R_SDI       as SPI2_SDI.SPI2_SDI@   // SPI2 SDI (from MRF89XA SDO)

// MRF89XA register addresses
public const
    GCON_REG    = $00,      // GENERAL CONFIGURATION REGISTER (ADDRESS:0X00)(POR:0X28)
    DMOD_REG    = $01,      // DATA AND MODULATION CONFIGURATION REGISTER (ADDRESS:0X01)(POR:0X88)   
    FDEV_REG    = $02,      // FREQUENCY DEVIATION CONTROL REGISTER (ADDRESS:0X02)(POR:0X03)
    BRS_REG     = $03,      // BIT RATE SET REGISTER (ADDRESS:0x03) (POR:0x07)
    FLTH_REG    = $04,      // FLOOR THRESHOLD CONTROL REGISTER (ADDRESS:0x04) (POR:0x0C)
    FIFOC_REG   = $05,      // FIFO CONFIGURATION REGISTER (ADDRESS:0x05) (POR:0x0F)
    R1C_REG     = $06,      // R1 COUNTER SET REGISTER (ADDRESS:0x06) (POR:0x77)
    P1C_REG     = $07,      // P1 COUNTER SET REGISTER (ADDRESS:0x07) (POR:0x64)
    S1C_REG     = $08,      // S1 COUNTER SET REGISTER (ADDRESS:0x08) (POR:0x32)
    R2C_REG     = $09,      // R2 COUNTER SET REGISTER (ADDRESS:0x09) (POR:0x74)
    P2C_REG     = $0A,      // P2 COUNTER SET REGISTER (ADDRESS:0x0A) (POR:0x62)
    S2C_REG     = $0B,      // S2 COUNTER SET REGISTER (ADDRESS:0x0B) (POR:0x32)
    PAC_REG     = $0C       // POWER AMPLIFIER CONTROL REGISTER (ADDRESS:0x0C) (POR:0x38)
    // OTHER REGISTERS CAN BE ADDED FROM THE DATASHEET 
        
//
// MRF89XA SPI settings (from datasheet DS70000622E)
// SPI_MODE_0
//  SMP = 0     'Input data is sampled at the middle of data output time
//  CKE = 1     'Transmit occurs on transition from the active to Idle clock state
//  CKP = 0     'Idle state for clock is a low level
//
public sub OPEN_SPI2()
  // max MRF89XA SCK is 1MHz for DATA mode
  #if (_clock <= 16)
    const SPI_DIV = spiOscDiv16
  #else    
    const SPI_DIV = spiOscDiv64
  #endif
  
    // assign PPS for SPI2 (assumes 47J13 and PORTD)
    pps.unlock()
    pps.assign_input(PPS_SDI2, PPS_IN_RP22)     // SDI2 <- J13 PORTD.5
    pps.assign_output(PPS_SDO2, PPS_OUT_RP21)   // SDO2 -> J13 PORTD.4
    pps.assign_output(PPS_SCK2, PPS_OUT_RP23)   // SCK2 -> J13 PORTD.6
    pps.assign_input(PPS_SCK2IN, PPS_IN_RP23)   // SCK2 <- J13 PORTD.6
    
    // init spi
    SPI2.Open(SPI_MODE_0, SPI_DIV, spiSampleMiddle)
end sub

//
// An external hardware or manual reset of the MRF89XA can be performed
// by asserting the TEST8 pin (pin 13) to high for 100 µs and then releasing
// the pin. After releasing the pin, it takes more than 5 ms for the 
// transceiver to be ready for any operations. The reset pin is driven 
// with an open-drain output and therefore, is pulled high while the device 
// is in POR. The device does not accept commands during the Reset period
//
public sub RADIO_RESET()
    high(R_RESET)
    delayus(150)        // wait RESET high time (>100us)
    input(R_RESET)
    delayms(10)         // wait for transceiver ready (>5ms)
end sub

// init IO pins, SPI, and reset MRF89XA
public sub RADIO_INIT()
    // setup CS pins
    high(R_CSCON)
    high(R_CSDAT)
    input(R_RESET)
    
    // init SPI
    OPEN_SPI2()
    
    // reset MRF89XA
    RADIO_RESET()
end sub

// MRF89XA interface
public inline sub ASSERT_CSCON()
    R_CSCON = 0     // active low
    delayus(1)      // >500ns
end sub

public inline sub DEASSERT_CSCON()
    R_CSCON = 1
    delayus(1)      // >500ns
end sub
    
public inline sub ASSERT_CSDAT()
    R_CSDAT = 0     // active low
    delayus(1)      // >500ns
end sub

public inline sub DEASSERT_CSDAT()
    R_CSDAT = 1
    delayus(1)      // >500ns
end sub

// MRF89XA CONFIG register format
//  bit 7: START bit = 0
//  bit 6: R/W 0=write, 1=read 
//  bits 5-1: register address A4-A0
//  bit 0: STOP bit = 0

// write config register
public sub MRF89XA_WRITE_REG(reg as byte, val as byte)
    // format control byte for write (add STOP=0, R/W=0)
    dim control as byte = (reg << 1)    // adds STOP=0
    control.bits(6) = 0                 // R/W = 0
    
    ASSERT_CSCON()
    SPI2.transfer(control)
    SPI2.transfer(val)
    DEASSERT_CSCON()
end sub

// read config register
public function MRF89XA_READ_REG(reg as byte) as byte
    // format control byte for read (add STOP=0, R/W=1)
    dim control as byte = (reg << 1)    // adds STOP=0
    control.bits(6) = 1                 // R/W = 1
    
    ASSERT_CSCON()
    SPI2.transfer(control)
    result = SPI2.transfer()
    DEASSERT_CSCON()
end function

// write data FIFO
public sub MRF89XA_WRITE_FIFO(val as byte)
    ASSERT_CSDAT()
    SPI2.transfer(val)
    DEASSERT_CSDAT()
end sub

// read data FIFO
public function MRF89XA_READ_FIFO() as byte
    ASSERT_CSDAT()
    result = SPI2.transfer()
    DEASSERT_CSDAT()
end function



dim b as byte

main:
setalldigital()
USART.SetBaudrate(br19200)
USART.Write("MRF89XA SPI2 TEST",10,13)

RADIO_INIT()

b = MRF89XA_READ_REG(GCON_REG)
USART.Write("GCON = ",BinToStr(b,8),"  ",HexToStr(b),10,13)

MRF89XA_WRITE_REG(GCON_REG, $68)
b = MRF89XA_READ_REG(GCON_REG)
USART.Write("GCON = ",BinToStr(b,8),"  ",HexToStr(b),10,13)

MRF89XA_WRITE_REG(GCON_REG, $28)
b = MRF89XA_READ_REG(GCON_REG)
USART.Write("GCON = ",BinToStr(b,8),"  ",HexToStr(b),10,13)

MRF89XA_WRITE_REG(GCON_REG, $88)
b = MRF89XA_READ_REG(GCON_REG)
USART.Write("GCON = ",BinToStr(b,8),"  ",HexToStr(b),10,13)
Attachments
mrf89xa.zip
(2.16 KiB) Downloaded 239 times

garryp4
Posts: 125
Joined: Mon May 21, 2007 7:18 am
Location: Loveland, CO USA

Re: Can't get spi to read data

Post by garryp4 » Wed Jul 29, 2020 1:02 am

Jerry:

Thanks for the reply and code. I have not had a chance to do anything with it yet. Am one of the recently unemployed and picking up odd handyman jobs.

Here is what I had so far. I put a 'stop' after the first register write/read just to test in the INIT routine. The register values were recommended by the Microchip help guy.

Code: Select all

Module MRF89XA_M

Include "USART.bas"
Include "convert.bas"
Include "pps.bas"
Include "lcd.bas"
#option SPI2_SCK = PORTB.4         // SPI clock
#option SPI2_SDI = PORTB.5         // SPI data in
#option SPI2_SDO = PORTB.3         // SPI data out
Include "SPI2.bas"

Private Dim 
  irq0               As PORTB.2,
  irq1               As PORTA.1,
  r_reset            As PORTB.0,
  r_cscon            As PORTB.1,
  r_csdata           As PORTA.0,
  mem_sel            As PORTD.3,
  red                As PORTC.0,
  green              As PORTC.1
    
Private Dim
  b1                 As Byte,          ' Byte holder
  b2                 As Byte,          ' Byte holder
  b3                 As Byte,          ' Byte holder
  b4                 As Byte,          ' Byte holder
  loop1              As Word

Private Const
  wren As Byte       = $06,            ' Write enable
  rdsr As Byte       = $05,            ' Read satus register
  wrsr As Byte       = $01,            ' Write status register
  read_data As Byte  = $03,            ' Read instruction
  write_data As Byte = $02,            ' Write instruction
  
  gcon_addr_w        = $00,            ' Write address of GCONREG
  gcon_addr_r        = $40,            ' Read address of GCONREG 
  xmit_gcon          = $88,            ' Transmit mode
  rcv_gcon           = $68,            ' Recieve mode
  stby_gcon          = $28,            ' Standby mode
  dmod_addr_w        = $01,            ' Write address of DMODREG
  dmod_addr_r        = $41,            ' Read address of DMODREG
  fdev_addr_w        = $02,            ' Write address of FDEVREG
  fdev_addr_r        = $42,            ' Read address of FDEVREG  
  brs_addr_w         = $03,            ' Write address of BRSVREG
  brs_addr_r         = $43,            ' Read address of BRSVREG
  flth_addr_w        = $04,            ' Write address of FLTHVREG
  flth_addr_r        = $44,            ' Read address of FLTHVREG
  fifoc_addr_w       = $05,            ' Write address of FIFOCVREG
  fifoc_addr_r       = $45,            ' Read address of FIFOCVREG
  r1c_addr_w         = $06,            ' Write address of R1CVREG
  r1c_addr_r         = $46,            ' Read address of R1CVREG
  p1c_addr_w         = $07,            ' Write address of P1CVREG
  p1c_addr_r         = $47,            ' Read address of P1CVREG
  s1c_addr_w         = $08,            ' Write address of S1CVREG
  s1c_addr_r         = $48,            ' Read address of RS1CVREG  
  r2c_addr_w         = $09,            ' Write address of R2CVREG
  r2c_addr_r         = $49,            ' Read address of R2CVREG
  p2c_addr_w         = $0A,            ' Write address of P2CVREG
  p2c_addr_r         = $4A,            ' Read address of P2CVREG
  s2c_addr_w         = $0B,            ' Write address of S2CVREG
  s2c_addr_r         = $4B,            ' Read address of S2CVREG
  pac_addr_w         = $0C,            ' Write address of PACREG
  pac_addr_r         = $4C,            ' Read address of PACREG
  ftxrxi_addr_w      = $0D,            ' Write address of FTXRXI
  ftxrxi_addr_r      = $4D,            ' Read address of FTXRXI
  ftpri_addr_w       = $0E,            ' Write address of FTPRI
  ftpri_addr_r       = $4E,            ' Read address of FTPRI
  rsthi_addr_w       = $0F,            ' Write address of RSTHI
  rsthi_addr_r       = $4F,            ' Read address of RSTHI
  filc_addr_w        = $10,            ' Write address of FLIC
  filc_addr_r        = $50,            ' Read address of FLIC
  pfc_addr_w         = $11,            ' Write address of PFC
  pfc_addr_r         = $51,            ' Read address of PFC
  sync_addr_w        = $12,            ' Write address of SYNC
  sync_addr_r        = $52,            ' Read address of SYNC
  resv_addr_w        = $13,            ' Write address of RESV
  resv_addr_r        = $53,            ' Read address of RESV
  rsts_addr_w        = $14,            ' Write address of RSTS
  rsts_addr_r        = $54,            ' Read address of RSTS
  ookc_addr_w        = $15,            ' Write address of OOKC
  ookc_addr_r        = $55,            ' Read address of OOKC
  syncv31_addr_w     = $16,            ' Write address of SYNCV31
  syncv31_addr_r     = $56,            ' Read address of SYNCV31
  syncv23_addr_w     = $17,            ' Write address of SYNCV23
  syncv23_addr_r     = $57,            ' Read address of SYNCV23
  syncv15_addr_w     = $18,            ' Write address of SYNCV15
  syncv15_addr_r     = $58,            ' Read address of SYNCV15
  syncv07_addr_w     = $19,            ' Write address of SYNCV07
  syncv07_addr_r     = $59,            ' Read address of SYNCV07
  txcon_addr_w       = $1A,            ' Write address of TXCON
  txcon_addr_r       = $5A,            ' Read address of TXCON
  clkout_addr_w      = $1B,            ' Write address of CLKOUT
  clkout_addr_r      = $5B,            ' Read address of CLKOUT
  pload_addr_w       = $1C,            ' Write address of PLOAD
  pload_addr_r       = $5C,            ' Read address of PLOAD
  nadds_addr_w       = $1D,            ' Write address of NADDS
  nadds_addr_r       = $5D,            ' Read address of NADDS
  pktc_addr_w        = $1E,            ' Write address of PKTC
  pktc_addr_r        = $5E,            ' Read address of PKTC
  fcrc_addr_w        = $1F,            ' Write address of RCRC
  fcrc_addr_r        = $5F             ' Read address of FCRC
  
'****************************************************************************
Private Sub BLINK()
  High(green)
  DelayMS(100)
  Low(green)
End Sub
'****************************************************************************
Private Sub SET_RADIO_SPI()

  pps.unlock()  
  pps.assign_output(PPS_SDO2, PPS_OUT_RP6)   ' SDO2 <- RB3  
  pps.assign_output(PPS_SCK2, PPS_OUT_RP7)   ' SCK2 <- RB4
  pps.assign_input(PPS_SDI2, PPS_IN_RP8)     ' SDI2 -> RB5
  pps.assign_input(PPS_SCK2IN, PPS_IN_RP7)   ' SCK2 in <- RB4
  pps.lock()

End Sub
'****************************************************************************
Public Sub OPEN_SPI()

  SET_RADIO_SPI
'  SPI2.SetAsMaster(spiOscDiv16)
'  SPI2.SetClock(spiIdleLow,spiRisingEdge)           ' spiRisingEdge  spiFallingEdge
  SPI2.Open(SPI_MODE_0,spiOscDiv64,spiSampleEnd)    ' spiSampleEnd, spiSampleMiddle 
  DelayMS(1)
  
End Sub
'****************************************************************************
Public Sub RADIO_RESET()

  SET_RADIO_SPI

  High(r_reset)
  DelayUS(110)
  Low(r_reset)
  DelayMS(200)

  OPEN_SPI

End Sub  
'****************************************************************************
Private Sub RADIO_CONFIG_SPI()                      ' Set config Chip sel
'  OPEN_SPI
  High(r_csdata)
  Low(r_cscon)
  DelayUS(20)
End Sub
'****************************************************************************
Private Sub RADIO_DATA_SPI()                        ' Set data Chip sel
'  OPEN_SPI
  High(r_cscon)
  Low(r_csdata)
  DelayUS(20)
End Sub
'****************************************************************************
Private Sub DISABLE_RADIO_SPI()                     ' No Chip sel
  High(r_cscon)
  High(r_csdata)
  DelayUS(2)
'  SPI2.Close
End Sub  
'****************************************************************************
Public Sub RADIO_INIT()

  If USART.DataAvailable = true Then   ' Clear USART register
    b1 = USART.ReadByte
  EndIf

  RADIO_RESET
  High(red)
  DelayMS(2000)
  Low(red)
  
'  While TRUE
  b1 = 0  
  RADIO_CONFIG_SPI
  SPI2.Transfer(gcon_addr_w)                        ' GCON Register write  $00
  SPI2.Transfer($2A)                                ' Stby, 915MHZ, RPS1
  DISABLE_RADIO_SPI
'  DelayUS(20)
'  Wend
  RADIO_CONFIG_SPI
  SPI2.Transfer(gcon_addr_r)                        ' $40
  b1 = SPI2.Transfer()                              '
  DISABLE_RADIO_SPI
  DelayMS(1)
  USART.Write("GCON =    ",BinToStr(b1,8),"  ",HexToStr(b1),10,13)
  DelayMS(2)
  While TRUE  
  Wend

  RADIO_RESET    
  RADIO_CONFIG_SPI
  SPI2.Transfer(dmod_addr_w)                        ' DMOD Register write
  SPI2.Transfer($84)                                ' FSK, packet  
  DISABLE_RADIO_SPI
  RADIO_CONFIG_SPI
  SPI2.Transfer(dmod_addr_r)
  b1 = 0
  b1 = SPI2.Transfer()
  DISABLE_RADIO_SPI
  USART.Write("DMOD =    ",BinToStr(b1,8),"  ",HexToStr(b1),10,13)
  DelayMS(2)

  RADIO_CONFIG_SPI
  SPI2.Transfer(fdev_addr_w)                        ' FDEV Register write
  SPI2.Transfer($01)                                ' 
  DISABLE_RADIO_SPI
  RADIO_CONFIG_SPI
'  SPI2.Transfer(fdev_addr_r)
  b1 = SPI2.Transfer()
  DISABLE_RADIO_SPI  
  USART.Write("FDEV =    ",BinToStr(b1,8),"  ",HexToStr(b1),10,13)
  DelayMS(2)

  RADIO_CONFIG_SPI
  SPI2.Transfer(brs_addr_w)                         ' BRS Register write
  SPI2.Transfer($03)                                ' 50Kbps out
  DISABLE_RADIO_SPI
  RADIO_CONFIG_SPI
'  SPI2.Transfer(brs_addr_r)
  b1 = SPI2.Transfer()
  USART.Write("BRS =     ",BinToStr(b1,8),"  ",HexToStr(b1),10,13)
  DelayMS(2)

  RADIO_CONFIG_SPI
  SPI2.Transfer(flth_addr_w)                        ' FLTH Register write
  SPI2.Transfer($0C)                                ' 
  DISABLE_RADIO_SPI
  RADIO_CONFIG_SPI
  SPI2.Transfer(flth_addr_r)
  b1 = SPI2.Transfer()
  DISABLE_RADIO_SPI
  USART.Write("FLTH =    ",BinToStr(b1,8),"  ",HexToStr(b1),10,13)
  DelayMS(2)
  
  RADIO_CONFIG_SPI
  SPI2.Transfer(fifoc_addr_w)                       ' FIFOC Register write
  SPI2.Transfer($C1)                                '
  DISABLE_RADIO_SPI
  RADIO_CONFIG_SPI
  SPI2.Transfer(fifoc_addr_r)
  b1 = SPI2.Transfer()
  DISABLE_RADIO_SPI
  USART.Write("FIFOC =   ",BinToStr(b1,8),"  ",HexToStr(b1),10,13)
  DelayMS(2)

  RADIO_CONFIG_SPI
  SPI2.Transfer(r1c_addr_w)                       ' R1C Register write
  SPI2.Transfer($77)                                '
  DISABLE_RADIO_SPI
  RADIO_CONFIG_SPI
  SPI2.Transfer(r1c_addr_r)
  b1 = SPI2.Transfer()
  DISABLE_RADIO_SPI
  USART.Write("R1C =     ",BinToStr(b1,8),"  ",HexToStr(b1),10,13)
  DelayMS(2)

  RADIO_CONFIG_SPI
  SPI2.Transfer(p1c_addr_w)                       ' P1C Register write
  SPI2.Transfer($64)                                '
  DISABLE_RADIO_SPI
  RADIO_CONFIG_SPI
  SPI2.Transfer(p1c_addr_r)
  b1 = SPI2.Transfer()
  DISABLE_RADIO_SPI
  USART.Write("P1C =     ",BinToStr(b1,8),"  ",HexToStr(b1),10,13)
  DelayMS(2)

  RADIO_CONFIG_SPI
  SPI2.Transfer(s1c_addr_w)                       ' S1C Register write
  SPI2.Transfer($32)                                '
  DISABLE_RADIO_SPI
  RADIO_CONFIG_SPI
  SPI2.Transfer(s1c_addr_r)
  b1 = SPI2.Transfer()
  DISABLE_RADIO_SPI
  USART.Write("S1C =     ",BinToStr(b1,8),"  ",HexToStr(b1),10,13)
  DelayMS(2)

  RADIO_CONFIG_SPI
  SPI2.Transfer(r2c_addr_w)                       ' R2C Register write
  SPI2.Transfer($74)                                '
  DISABLE_RADIO_SPI
  RADIO_CONFIG_SPI
  SPI2.Transfer(r2c_addr_r)
  b1 = SPI2.Transfer()
  DISABLE_RADIO_SPI
  USART.Write("R1C =     ",BinToStr(b1,8),"  ",HexToStr(b1),10,13)
  DelayMS(2)

  RADIO_CONFIG_SPI
  SPI2.Transfer(p2c_addr_w)                       ' P2C Register write
  SPI2.Transfer($62)                                '
  DISABLE_RADIO_SPI
  RADIO_CONFIG_SPI
  SPI2.Transfer(p2c_addr_r)
  b1 = SPI2.Transfer()
  DISABLE_RADIO_SPI
  USART.Write("P2C =     ",BinToStr(b1,8),"  ",HexToStr(b1),10,13)
  DelayMS(2)

  RADIO_CONFIG_SPI
  SPI2.Transfer(s2c_addr_w)                       ' S2C Register write
  SPI2.Transfer($32)                                '
  DISABLE_RADIO_SPI
  RADIO_CONFIG_SPI
  SPI2.Transfer(s2c_addr_r)
  b1 = SPI2.Transfer()
  DISABLE_RADIO_SPI
  USART.Write("S2C =     ",BinToStr(b1,8),"  ",HexToStr(b1),10,13)
  DelayMS(2)

  RADIO_CONFIG_SPI
  SPI2.Transfer(pac_addr_w)                         ' PAC Register write
  SPI2.Transfer($38)                                '
  DISABLE_RADIO_SPI
  RADIO_CONFIG_SPI
  SPI2.Transfer(pac_addr_r)
  b1 = SPI2.Transfer()
  DISABLE_RADIO_SPI
  USART.Write("PAC =     ",BinToStr(b1,8),"  ",HexToStr(b1),10,13)
  DelayMS(2)

  RADIO_CONFIG_SPI
  SPI2.Transfer(ftxrxi_addr_w)                      ' FTXRX1 Register write
  SPI2.Transfer($C8)                                '
  DISABLE_RADIO_SPI
  RADIO_CONFIG_SPI
  SPI2.Transfer(ftxrxi_addr_r)
  b1 = SPI2.Transfer()
  DISABLE_RADIO_SPI
  USART.Write("FTXRXI =  ",BinToStr(b1,8),"  ",HexToStr(b1),10,13)
  DelayMS(2)

  RADIO_CONFIG_SPI
  SPI2.Transfer(ftpri_addr_w)                       ' FTPRI Register write
  SPI2.Transfer($18)                                '
  DISABLE_RADIO_SPI
  RADIO_CONFIG_SPI
  SPI2.Transfer(ftpri_addr_r)
  b1 = SPI2.Transfer()
  DISABLE_RADIO_SPI
  USART.Write("FTPRI =   ",BinToStr(b1,8),"  ",HexToStr(b1),10,13)
  DelayMS(2)

  RADIO_CONFIG_SPI
  SPI2.Transfer(rsthi_addr_w)                       ' RSTHI Register write
  SPI2.Transfer($00)                                '
  DISABLE_RADIO_SPI
  RADIO_CONFIG_SPI
  SPI2.Transfer(rsthi_addr_r)
  b1 = SPI2.Transfer()
  DISABLE_RADIO_SPI
  USART.Write("RSTHI =   ",BinToStr(b1,8),"  ",HexToStr(b1),10,13)
  DelayMS(2)

  RADIO_CONFIG_SPI
  SPI2.Transfer(filc_addr_w)                        ' FILC Register write
  SPI2.Transfer($FF)                                '
  DISABLE_RADIO_SPI
  RADIO_CONFIG_SPI
  SPI2.Transfer(filc_addr_r)
  b1 = SPI2.Transfer()
  DISABLE_RADIO_SPI
  USART.Write("FILC =    ",BinToStr(b1,8),"  ",HexToStr(b1),10,13)
  DelayMS(2)

  RADIO_CONFIG_SPI
  SPI2.Transfer(pfc_addr_w)                         ' PFC Register write
  SPI2.Transfer($38)                                '
  DISABLE_RADIO_SPI
  RADIO_CONFIG_SPI
  SPI2.Transfer(pfc_addr_r)
  b1 = SPI2.Transfer()
  DISABLE_RADIO_SPI
  USART.Write("PFC =     ",BinToStr(b1,8),"  ",HexToStr(b1),10,13)
  DelayMS(2)

  RADIO_CONFIG_SPI
  SPI2.Transfer(sync_addr_w)                        ' SYNC Register write
  SPI2.Transfer($38)                                '
  DISABLE_RADIO_SPI
  RADIO_CONFIG_SPI
  SPI2.Transfer(sync_addr_r)
  b1 = SPI2.Transfer()
  DISABLE_RADIO_SPI
  USART.Write("SYNC =    ",BinToStr(b1,8),"  ",HexToStr(b1),10,13)
  DelayMS(2)

  RADIO_CONFIG_SPI
  SPI2.Transfer(resv_addr_w)                        ' RESV Register write
  SPI2.Transfer($07)                                '
  DISABLE_RADIO_SPI
  RADIO_CONFIG_SPI
  SPI2.Transfer(resv_addr_r)
  b1 = SPI2.Transfer()
  DISABLE_RADIO_SPI
  USART.Write("RESV =    ",BinToStr(b1,8),"  ",HexToStr(b1),10,13)
  DelayMS(2)

  RADIO_CONFIG_SPI
  SPI2.Transfer(rsts_addr_w)                        ' RSTS Register write
  SPI2.Transfer($00)                                '
  DISABLE_RADIO_SPI
  RADIO_CONFIG_SPI
  SPI2.Transfer(rsts_addr_r)
  b1 = SPI2.Transfer()
  DISABLE_RADIO_SPI
  USART.Write("RSTS =    ",BinToStr(b1,8),"  ",HexToStr(b1),10,13)
  DelayMS(2)

  RADIO_CONFIG_SPI
  SPI2.Transfer(ookc_addr_w)                        ' OOKC Register write
  SPI2.Transfer($00)                                '
  DISABLE_RADIO_SPI  
  RADIO_CONFIG_SPI
  SPI2.Transfer(ookc_addr_r)
  b1 = SPI2.Transfer()
  DISABLE_RADIO_SPI
  USART.Write("OOKC =    ",BinToStr(b1,8),"  ",HexToStr(b1),10,13)
  DelayMS(2)

  RADIO_CONFIG_SPI
  SPI2.Transfer(syncv31_addr_w)                     ' SYNCV31 Register write
  SPI2.Transfer($69)                                '
  DISABLE_RADIO_SPI  
  RADIO_CONFIG_SPI
  SPI2.Transfer(syncv31_addr_r)
  b1 = SPI2.Transfer()
  DISABLE_RADIO_SPI
  USART.Write("SYNCV31 = ",BinToStr(b1,8),"  ",HexToStr(b1),10,13)
  DelayMS(2)

  RADIO_CONFIG_SPI
  SPI2.Transfer(syncv23_addr_w)                     ' SYNCV23 Register write
  SPI2.Transfer($81)                                '
  DISABLE_RADIO_SPI  
  RADIO_CONFIG_SPI
  SPI2.Transfer(syncv23_addr_r)
  b1 = SPI2.Transfer()
  DISABLE_RADIO_SPI
  USART.Write("SYNCV23 = ",BinToStr(b1,8),"  ",HexToStr(b1),10,13)
  DelayMS(2)

  RADIO_CONFIG_SPI
  SPI2.Transfer(syncv15_addr_w)                     ' SYNCV15 Register write
  SPI2.Transfer($7E)                                '
  DISABLE_RADIO_SPI
  RADIO_CONFIG_SPI
  SPI2.Transfer(syncv15_addr_r)
  b1 = SPI2.Transfer()
  DISABLE_RADIO_SPI
  USART.Write("SYNCV15 = ",BinToStr(b1,8),"  ",HexToStr(b1),10,13)
  DelayMS(2)

  RADIO_CONFIG_SPI
  SPI2.Transfer(syncv07_addr_w)                     ' SYNCV07 Register write
  SPI2.Transfer($96)                                '
  DISABLE_RADIO_SPI  
  RADIO_CONFIG_SPI
  SPI2.Transfer(syncv07_addr_r)
  b1 = SPI2.Transfer()
  DISABLE_RADIO_SPI
  USART.Write("SYNCV07 = ",BinToStr(b1,8),"  ",HexToStr(b1),10,13)
  DelayMS(2)

  RADIO_CONFIG_SPI
  SPI2.Transfer(txcon_addr_w)                       ' TXCON Register write
  SPI2.Transfer($F0)                                '
  DISABLE_RADIO_SPI   
  RADIO_CONFIG_SPI
  SPI2.Transfer(txcon_addr_r)
  b1 = SPI2.Transfer()
  DISABLE_RADIO_SPI
  USART.Write("TXCON =   ",BinToStr(b1,8),"  ",HexToStr(b1),10,13)
  DelayMS(2)

  RADIO_CONFIG_SPI
  SPI2.Transfer(clkout_addr_w)                      ' CLKO Register write
  SPI2.Transfer($00)                                '
  DISABLE_RADIO_SPI  
  RADIO_CONFIG_SPI
  SPI2.Transfer(clkout_addr_r)
  b1 = SPI2.Transfer()
  DISABLE_RADIO_SPI
  USART.Write("CLKOUT =  ",BinToStr(b1,8),"  ",HexToStr(b1),10,13)
  DelayMS(2)

  RADIO_CONFIG_SPI
  SPI2.Transfer(pload_addr_w)                       ' PLOAD Register write
  SPI2.Transfer($40)                                '
  DISABLE_RADIO_SPI   
  RADIO_CONFIG_SPI
  SPI2.Transfer(pload_addr_r)
  b1 = SPI2.Transfer()
  DISABLE_RADIO_SPI
  USART.Write("PLOAD =   ",BinToStr(b1,8),"  ",HexToStr(b1),10,13)
  DelayMS(2)

  RADIO_CONFIG_SPI
  SPI2.Transfer(nadds_addr_w)                       ' NADDS Register write
  SPI2.Transfer($00)                                '
  DISABLE_RADIO_SPI  
  RADIO_CONFIG_SPI
  SPI2.Transfer(nadds_addr_r)
  b1 = SPI2.Transfer()
  DISABLE_RADIO_SPI
  USART.Write("NADDS =   ",BinToStr(b1,8),"  ",HexToStr(b1),10,13)
  DelayMS(2)

  RADIO_CONFIG_SPI
  SPI2.Transfer(pktc_addr_w)                        ' PKTC Register write
  SPI2.Transfer($FA)                                '
  DISABLE_RADIO_SPI  
  RADIO_CONFIG_SPI
  SPI2.Transfer(pktc_addr_r)
  b1 = SPI2.Transfer()
  DISABLE_RADIO_SPI
  USART.Write("PKTC =    ",BinToStr(b1,8),"  ",HexToStr(b1),10,13)
  DelayMS(2)

  RADIO_CONFIG_SPI
  SPI2.Transfer(fcrc_addr_w)                        ' FCRC Register write
  SPI2.Transfer($00)                                '
  DISABLE_RADIO_SPI
  RADIO_CONFIG_SPI
  SPI2.Transfer(fcrc_addr_r)
  b1 = SPI2.Transfer()
  DISABLE_RADIO_SPI
  USART.Write("FCRC =    ",BinToStr(b1,8),"  ",HexToStr(b1),10,13)
  DelayMS(2)

End Sub
'****************************************************************************
Public Sub READ_REG()

  RADIO_RESET
  
  If USART.DataAvailable = true Then
    b1 = USART.ReadByte
  EndIf

  USART.Write(10,"ENTER REGISTER NUMBER  00 - 31",10,13)
  DelayMS(2)
  b1 = USART.ReadByte
  b1 = b1 - 48
  USART.Write(DecToStr(b1))
  DelayMS(2)
  b2 = USART.ReadByte
  b2 = b2 - 48
  USART.Write(DecToStr(b1),10,13)
  DelayMS(2)
  b3 = (b1 * 10) + b2
  b4 = b3 + $40                        ' Register read command
  USART.Write("  ",HexToStr(b3)," ",BinToStr(b3,8),"   ",HexToStr(b4)," ",BinToStr(b4,8) ,10,13)
  DelayMS(2)
  
        
  RADIO_CONFIG_SPI
  SPI2.Transfer($b4)           
  b1 = SPI2.Transfer()
  DISABLE_RADIO_SPI
  USART.Write("REG #",HexToStr(b3)," = ",BinToStr(b1,8),"  ",HexToStr(b1),10,10,13)
  DelayMS(2)

End Sub
'****************************************************************************
Public Sub READ_ALL_REG()

  RADIO_RESET

  RADIO_CONFIG_SPI
  SPI2.Transfer(gcon_addr_r)
  b1 = SPI2.Transfer()
  DISABLE_RADIO_SPI
  USART.Write("GCON = ",BinToStr(b1,8),"  ",HexToStr(b1),10,13)
  DelayMS(2)

  RADIO_CONFIG_SPI
  SPI2.Transfer(dmod_addr_r)
  b1 = SPI2.Transfer()
  DISABLE_RADIO_SPI
  USART.Write("DMOD = ",BinToStr(b1,8),"  ",HexToStr(b1),10,13)
  DelayMS(2)

  RADIO_CONFIG_SPI
  SPI2.Transfer(fdev_addr_r)
  b1 = SPI2.Transfer()
  DISABLE_RADIO_SPI  
  USART.Write("FDEV = ",BinToStr(b1,8),"  ",HexToStr(b1),10,13)
  DelayMS(2)

  RADIO_CONFIG_SPI
  SPI2.Transfer(brs_addr_r)
  b1 = SPI2.Transfer()
  DISABLE_RADIO_SPI
  USART.Write("BRS = ",BinToStr(b1,8),"  ",HexToStr(b1),10,13)
  DelayMS(2)

  RADIO_CONFIG_SPI
  SPI2.Transfer(flth_addr_r)
  b1 = SPI2.Transfer()
  DISABLE_RADIO_SPI
  USART.Write("FLTH = ",BinToStr(b1,8),"  ",HexToStr(b1),10,13)
  DelayMS(2)

  RADIO_CONFIG_SPI
  SPI2.Transfer(fifoc_addr_r)
  b1 = SPI2.Transfer()
  DISABLE_RADIO_SPI
  USART.Write("FIFOC = ",BinToStr(b1,8),"  ",HexToStr(b1),10,13)
  DelayMS(2)
  
  RADIO_CONFIG_SPI
  SPI2.Transfer(r1c_addr_r)
  b1 = SPI2.Transfer()
  DISABLE_RADIO_SPI
  USART.Write("R1C = ",BinToStr(b1,8),"  ",HexToStr(b1),10,13)
  DelayMS(2)

  RADIO_CONFIG_SPI
  SPI2.Transfer(p1c_addr_r)
  b1 = SPI2.Transfer()
  DISABLE_RADIO_SPI
  USART.Write("P1C = ",BinToStr(b1,8),"  ",HexToStr(b1),10,13)
  DelayMS(2)

  RADIO_CONFIG_SPI
  SPI2.Transfer(s1c_addr_r)
  b1 = SPI2.Transfer()
  DISABLE_RADIO_SPI
  USART.Write("S1C = ",BinToStr(b1,8),"  ",HexToStr(b1),10,13)
  DelayMS(2)

  RADIO_CONFIG_SPI
  SPI2.Transfer()
  b1 = SPI2.Transfer(r2c_addr_r)
  DISABLE_RADIO_SPI
  USART.Write("R2C = ",BinToStr(b1,8),"  ",HexToStr(b1),10,13)
  DelayMS(2)

  RADIO_CONFIG_SPI
  SPI2.Transfer(p2c_addr_r)
  b1 = SPI2.Transfer()
  DISABLE_RADIO_SPI
  USART.Write("P2C = ",BinToStr(b1,8),"  ",HexToStr(b1),10,13)
  DelayMS(2)

  RADIO_CONFIG_SPI
  SPI2.Transfer(s2c_addr_r)
  b1 = SPI2.Transfer()
  DISABLE_RADIO_SPI
  USART.Write("S2C = ",BinToStr(b1,8),"  ",HexToStr(b1),10,13)
  DelayMS(2)

  RADIO_CONFIG_SPI
  SPI2.Transfer(pac_addr_r)
  DISABLE_RADIO_SPI
  b1 = SPI2.Transfer()
  USART.Write("PAC = ",BinToStr(b1,8),"  ",HexToStr(b1),10,13)
  DelayMS(2)

  RADIO_CONFIG_SPI
  SPI2.Transfer(ftxrxi_addr_r)
  b1 = SPI2.Transfer()
  DISABLE_RADIO_SPI
  USART.Write("FTXRXI = ",BinToStr(b1,8),"  ",HexToStr(b1),10,13)
  DelayMS(2)

  RADIO_CONFIG_SPI
  SPI2.Transfer(ftpri_addr_r)
  b1 = SPI2.Transfer()
  DISABLE_RADIO_SPI
  USART.Write("FTPRI = ",BinToStr(b1,8),"  ",HexToStr(b1),10,13)
  DelayMS(2)

  RADIO_CONFIG_SPI
  SPI2.Transfer(rsthi_addr_r)
  b1 = SPI2.Transfer()
  DISABLE_RADIO_SPI
  USART.Write("RSTHI = ",BinToStr(b1,8),"  ",HexToStr(b1),10,13)
  DelayMS(2)

  RADIO_CONFIG_SPI
  SPI2.Transfer(filc_addr_r)
  b1 = SPI2.Transfer()
  DISABLE_RADIO_SPI
  USART.Write("FILC = ",BinToStr(b1,8),"  ",HexToStr(b1),10,13)
  DelayMS(2)

  RADIO_CONFIG_SPI
  SPI2.Transfer(pfc_addr_r)
  b1 = SPI2.Transfer()
  DISABLE_RADIO_SPI
  USART.Write("PFC = ",BinToStr(b1,8),"  ",HexToStr(b1),10,13)
  DelayMS(2)

  RADIO_CONFIG_SPI
  SPI2.Transfer(sync_addr_r)
  b1 = SPI2.Transfer()
  DISABLE_RADIO_SPI
  USART.Write("SYNC = ",BinToStr(b1,8),"  ",HexToStr(b1),10,13)
  DelayMS(2)

  RADIO_CONFIG_SPI
  SPI2.Transfer(resv_addr_r)
  b1 = SPI2.Transfer()
  DISABLE_RADIO_SPI
  USART.Write("RESV = ",BinToStr(b1,8),"  ",HexToStr(b1),10,13)
  DelayMS(2)

  RADIO_CONFIG_SPI
  SPI2.Transfer(rsts_addr_r)
  b1 = SPI2.Transfer()
  DISABLE_RADIO_SPI
  USART.Write("RSTS = ",BinToStr(b1,8),"  ",HexToStr(b1),10,13)
  DelayMS(2)

  RADIO_CONFIG_SPI
  SPI2.Transfer(ookc_addr_r)
  b1 = SPI2.Transfer()
  DISABLE_RADIO_SPI
  USART.Write("OOKC = ",BinToStr(b1,8),"  ",HexToStr(b1),10,13)
  DelayMS(2)

  RADIO_CONFIG_SPI
  SPI2.Transfer(syncv31_addr_r)
  b1 = SPI2.Transfer()
  DISABLE_RADIO_SPI
  USART.Write("SYNVC31 = ",BinToStr(b1,8),"  ",HexToStr(b1),10,13)
  DelayMS(2)

  RADIO_CONFIG_SPI
  SPI2.Transfer(syncv23_addr_r)
  b1 = SPI2.Transfer()
  DISABLE_RADIO_SPI
  USART.Write("SYNCV23 = ",BinToStr(b1,8),"  ",HexToStr(b1),10,13)
  DelayMS(2)

  RADIO_CONFIG_SPI
  SPI2.Transfer(syncv15_addr_r)
  b1 = SPI2.Transfer()
  DISABLE_RADIO_SPI
  USART.Write("SYNCV15 = ",BinToStr(b1,8),"  ",HexToStr(b1),10,13)
  DelayMS(2)

  RADIO_CONFIG_SPI
  SPI2.Transfer(syncv07_addr_r)
  b1 = SPI2.Transfer()
  DISABLE_RADIO_SPI
  USART.Write("SYNCV07 = ",BinToStr(b1,8),"  ",HexToStr(b1),10,13)
  DelayMS(2)

  RADIO_CONFIG_SPI
  SPI2.Transfer(txcon_addr_r)
  b1 = SPI2.Transfer()
  DISABLE_RADIO_SPI
  USART.Write("TXCON = ",BinToStr(b1,8),"  ",HexToStr(b1),10,13)
  DelayMS(2)

  RADIO_CONFIG_SPI
  SPI2.Transfer(clkout_addr_r)
  b1 = SPI2.Transfer()
  DISABLE_RADIO_SPI
  USART.Write("CLKOUT = ",BinToStr(b1,8),"  ",HexToStr(b1),10,13)
  DelayMS(2)

  RADIO_CONFIG_SPI
  SPI2.Transfer(pload_addr_r)
  b1 = SPI2.Transfer()
  DISABLE_RADIO_SPI
  USART.Write("PLOAD = ",BinToStr(b1,8),"  ",HexToStr(b1),10,13)
  DelayMS(2)

  RADIO_CONFIG_SPI
  SPI2.Transfer(nadds_addr_r)
  b1 = SPI2.Transfer()
  DISABLE_RADIO_SPI
  USART.Write("NADDS = ",BinToStr(b1,8),"  ",HexToStr(b1),10,13)
  DelayMS(2)

  RADIO_CONFIG_SPI
  SPI2.Transfer(pktc_addr_r)
  b1 = SPI2.Transfer()
  DISABLE_RADIO_SPI
  USART.Write("PKTC = ",BinToStr(b1,8),"  ",HexToStr(b1),10,13)
  DelayMS(2)

  RADIO_CONFIG_SPI
  SPI2.Transfer(fcrc_addr_r)
  b1 = SPI2.Transfer()
  DISABLE_RADIO_SPI
  USART.Write("FCRC = ",BinToStr(b1,8),"  ",HexToStr(b1),10,13)
  DelayMS(2)
    
End Sub
'****************************************************************************
'****************************************************************************
'****************************************************************************
'****************************************************************************
Public Sub RADIO_RCV()       ' this sub is in progress

 RADIO_RESET
 RADIO_DATA_SPI

End Sub
'****************************************************************************

'****************************************************************************
High(r_cscon)
High(r_csdata)
Input(irq0)
Input(irq1)
USART.SetBaudrate(br9600)              ' Baud rate
LCD.Initialize
It may be a week till get back to this.

Again,
Thanks!

Jerry Messina
Swordfish Developer
Posts: 1469
Joined: Fri Jan 30, 2009 6:27 pm
Location: US

Re: Can't get spi to read data

Post by Jerry Messina » Wed Jul 29, 2020 9:42 am

Ouch! Sorry to hear that Garry. I'm one of the unlucky ones who can work from home, so as of right now I'm still employed.

When you get back to it I have one suggestion. In the microchip example driver, they set the SPI control registers to:
// SPI_MODE_0
// SMP = 0 'Input data is sampled at the middle of data output time
// CKE = 1 'Transmit occurs on transition from the active to Idle clock state
// CKP = 0 'Idle state for clock is a low level

so in your code try changing the line in OPEN_SPI() to:

Code: Select all

    SPI2.Open(SPI_MODE_0, spiOscDiv64, spiSampleMiddle)
That should match up with what they used.

Anyway, keep us posted on how it goes. If you want to try my code and need help modifying it to match your hdw, just let me know.

garryp4
Posts: 125
Joined: Mon May 21, 2007 7:18 am
Location: Loveland, CO USA

Re: Can't get spi to read data

Post by garryp4 » Wed Aug 19, 2020 6:50 pm

Jerry:

Thanks for the mrf89xa.zip file. I have finally got some time between odd jobs to look at it. Sorry this had to take a back burner.

I changed the pins to match the hardware and tried to compile. The 18f47j13 doe not appear to be supported in the intosc.bas or setalldigital.bas, so commented out their include and added how I have been setting the clock since started with Swordfish. Tried to compile again and got the list of errors in the attached jpg. I could not figure out a way to get them in this message.

I also do not understand how Public Inline Sub differs from Public Sub.

Here is mostly your code with my few changes:

Code: Select all

// MRF89XA radio with 18F47J13 SPI2 using PPS
// SF v2.2.3.4
Device = 18F47J13
Clock = 8           // current intosc.bas v2.1 limit

'Include "intosc.bas"
'Include "setdigitalio.bas"

Include "usart.bas"
Include "convert.bas"

//-------------------------------------
// **HARDWARE DEPENDANT SECTION**
//-------------------------------------
// SPI2 (NOTE: if you change these you MUST change the PPS setup in OPEN_SPI2)  
Include "pps.bas"
'#option SPI2_SCK = PORTD.6         // SPI clock
'#option SPI2_SDI = PORTD.5         // SPI data in
'#option SPI2_SDO = PORTD.4         // SPI data out
#option SPI2_SCK = PORTB.4         // SPI clock
#option SPI2_SDI = PORTB.5         // SPI data in
#option SPI2_SDO = PORTB.3         // SPI data out
Include "SPI2.bas"

// IO pin assignments
Public Dim
'    R_RESET     As PORTD.0,             // to MRF89XA RESET pin
'    R_CSDAT     As PORTD.1,             // to MRF89XA CSDAT_N pin (active low)
'    R_CSCON     As PORTD.2,             // to MRF89XA CSCON_N pin (active low)
    R_RESET     As PORTB.0,             // to MRF89XA RESET pin
    R_CSDAT     As PORTA.0,             // to MRF89XA CSDAT_N pin (active low)
    R_CSCON     As PORTB.1,             // to MRF89XA CSCON_N pin (active low)
    R_SCK       As SPI2_SCK.SPI2_SCK@,  // SPI2 SCK
    R_SDO       As SPI2_SDO.SPI2_SDO@,  // SPI2 SDO (to MRF89XA SDI)
    R_SDI       As SPI2_SDI.SPI2_SDI@   // SPI2 SDI (from MRF89XA SDO)

// MRF89XA register addresses
Public Const
    GCON_REG    = $00,      // GENERAL CONFIGURATION REGISTER (ADDRESS:0X00)(POR:0X28)
    DMOD_REG    = $01,      // DATA AND MODULATION CONFIGURATION REGISTER (ADDRESS:0X01)(POR:0X88)   
    FDEV_REG    = $02,      // FREQUENCY DEVIATION CONTROL REGISTER (ADDRESS:0X02)(POR:0X03)
    BRS_REG     = $03,      // BIT RATE SET REGISTER (ADDRESS:0x03) (POR:0x07)
    FLTH_REG    = $04,      // FLOOR THRESHOLD CONTROL REGISTER (ADDRESS:0x04) (POR:0x0C)
    FIFOC_REG   = $05,      // FIFO CONFIGURATION REGISTER (ADDRESS:0x05) (POR:0x0F)
    R1C_REG     = $06,      // R1 COUNTER SET REGISTER (ADDRESS:0x06) (POR:0x77)
    P1C_REG     = $07,      // P1 COUNTER SET REGISTER (ADDRESS:0x07) (POR:0x64)
    S1C_REG     = $08,      // S1 COUNTER SET REGISTER (ADDRESS:0x08) (POR:0x32)
    R2C_REG     = $09,      // R2 COUNTER SET REGISTER (ADDRESS:0x09) (POR:0x74)
    P2C_REG     = $0A,      // P2 COUNTER SET REGISTER (ADDRESS:0x0A) (POR:0x62)
    S2C_REG     = $0B,      // S2 COUNTER SET REGISTER (ADDRESS:0x0B) (POR:0x32)
    PAC_REG     = $0C       // POWER AMPLIFIER CONTROL REGISTER (ADDRESS:0x0C) (POR:0x38)
    // OTHER REGISTERS CAN BE ADDED FROM THE DATASHEET 
        
//
// MRF89XA SPI settings (from datasheet DS70000622E)
// SPI_MODE_0
//  SMP = 0     'Input data is sampled at the middle of data output time
//  CKE = 1     'Transmit occurs on transition from the active to Idle clock state
//  CKP = 0     'Idle state for clock is a low level
//
Public Sub OPEN_SPI2()
  // max MRF89XA SCK is 1MHz for DATA mode
  #if (_clock <= 16)
    Const SPI_DIV = spiOscDiv16
  #else    
    Const SPI_DIV = spiOscDiv64
  #endif
  
    // assign PPS for SPI2 (assumes 47J13 and PORTD)
    pps.unlock()
    pps.assign_input(PPS_SDI2, PPS_IN_RP22)     // SDI2 <- J13 PORTD.5
    pps.assign_output(PPS_SDO2, PPS_OUT_RP21)   // SDO2 -> J13 PORTD.4
    pps.assign_output(PPS_SCK2, PPS_OUT_RP23)   // SCK2 -> J13 PORTD.6
    pps.assign_input(PPS_SCK2IN, PPS_IN_RP23)   // SCK2 <- J13 PORTD.6
    
    // init spi
    SPI2.Open(SPI_MODE_0, SPI_DIV, spiSampleMiddle)
End Sub

//
// An external hardware or manual reset of the MRF89XA can be performed
// by asserting the TEST8 pin (pin 13) to high for 100 µs and then releasing
// the pin. After releasing the pin, it takes more than 5 ms for the 
// transceiver to be ready for any operations. The reset pin is driven 
// with an open-drain output and therefore, is pulled high while the device 
// is in POR. The device does not accept commands during the Reset period
//
Public Sub RADIO_RESET()
    High(R_RESET)
    DelayUS(150)        // wait RESET high time (>100us)
    Input(R_RESET)
    DelayMS(10)         // wait for transceiver ready (>5ms)
End Sub

// init IO pins, SPI, and reset MRF89XA
Public Sub RADIO_INIT()
    // setup CS pins
    High(R_CSCON)
    High(R_CSDAT)
    Input(R_RESET)
    
    // init SPI
    OPEN_SPI2()
    
    // reset MRF89XA
    RADIO_RESET()
End Sub

// MRF89XA interface
Public Inline Sub ASSERT_CSCON()
    R_CSCON = 0     // active low
    DelayUS(1)      // >500ns
End Sub

Public Inline Sub DEASSERT_CSCON()
    R_CSCON = 1
    DelayUS(1)      // >500ns
End Sub
    
Public Inline Sub ASSERT_CSDAT()
    R_CSDAT = 0     // active low
    DelayUS(1)      // >500ns
End Sub

Public Inline Sub DEASSERT_CSDAT()
    R_CSDAT = 1
    DelayUS(1)      // >500ns
End Sub

// MRF89XA CONFIG register format
//  bit 7: START bit = 0
//  bit 6: R/W 0=write, 1=read 
//  bits 5-1: register address A4-A0
//  bit 0: STOP bit = 0

// write config register
Public Sub MRF89XA_WRITE_REG(reg As Byte, val As Byte)
    // format control byte for write (add STOP=0, R/W=0)
    Dim control As Byte = (reg << 1)    // adds STOP=0
    control.bits(6) = 0                 // R/W = 0
    
    ASSERT_CSCON()
    SPI2.Transfer(control)
    SPI2.Transfer(val)
    DEASSERT_CSCON()
End Sub

// read config register
Public Function MRF89XA_READ_REG(reg As Byte) As Byte
    // format control byte for read (add STOP=0, R/W=1)
    Dim control As Byte = (reg << 1)    // adds STOP=0
    control.bits(6) = 1                 // R/W = 1
    
    ASSERT_CSCON()
    SPI2.Transfer(control)
    result = SPI2.Transfer()
    DEASSERT_CSCON()
End Function

// write data FIFO
Public Sub MRF89XA_WRITE_FIFO(val As Byte)
    ASSERT_CSDAT()
    SPI2.Transfer(val)
    DEASSERT_CSDAT()
End Sub

// read data FIFO
Public Function MRF89XA_READ_FIFO() As Byte
    ASSERT_CSDAT()
    result = SPI2.Transfer()
    DEASSERT_CSDAT()
End Function



Dim b As Byte

OSCCON   = $7F                         ' Interanl clock to 8mhz
OSCCON2  = $54
REFOCON  = $00
OSCTUNE  = $00                           
ADCON0   = $00                         ' A/D disabled
ADCON1   = $00                         ' A/D disabled
ANCON0   = $FF
ANCON1   = $1F
PMCONH   = $00                         ' Disable parallel port
DSCONH   = $00
DSCONL   = $05
ODCON1   = $00
ODCON2   = $00
CCP4CON  = $00
CCP5CON  = $00
CCP6CON  = $00
CCP7CON  = $00
CCP8CON  = $00
CCP9CON  = $00
CCP10CON = $00
CTMUCONH = $00                         ' Disable the CTMU
INTCON   = $00                         ' Disable interupts

PMDIS3  = $FF                          ' Shut down peripherals
PMDIS2  = $FF
PMDIS1  = $E0                          ' F8
PMDIS0  = $F1                          ' Only ser1 and SPI1/2 enabled

These are how I have been setting the clock and disabling peripherals. I would 
definately take any input on any improvements here. Once I got things to work I 
just ran with it.

}

main:
SetAllDigital()
USART.SetBaudrate(br19200)
USART.Write("MRF89XA SPI2 TEST",10,13)

RADIO_INIT()

b = MRF89XA_READ_REG(GCON_REG)
USART.Write("GCON = ",BinToStr(b,8),"  ",HexToStr(b),10,13)

MRF89XA_WRITE_REG(GCON_REG, $68)
b = MRF89XA_READ_REG(GCON_REG)
USART.Write("GCON = ",BinToStr(b,8),"  ",HexToStr(b),10,13)

MRF89XA_WRITE_REG(GCON_REG, $28)
b = MRF89XA_READ_REG(GCON_REG)
USART.Write("GCON = ",BinToStr(b,8),"  ",HexToStr(b),10,13)

MRF89XA_WRITE_REG(GCON_REG, $88)
b = MRF89XA_READ_REG(GCON_REG)
USART.Write("GCON = ",BinToStr(b,8),"  ",HexToStr(b),10,13)

Any help is greatly appreciated.

Thanks,
Garry
Attachments
comp_err.jpg
comp_err.jpg (248.73 KiB) Viewed 6754 times

Jerry Messina
Swordfish Developer
Posts: 1469
Joined: Fri Jan 30, 2009 6:27 pm
Location: US

Re: Can't get spi to read data

Post by Jerry Messina » Wed Aug 19, 2020 10:16 pm

Garry,

According to my notes, intosc and setalldigital support for the 47J13 was added in SF version 2.2.3.3 and later.

There's a missing comment brace '{' around your "These are how I have been..." comment.
You'd have to comment out the SetAllDigital call since you commented out the include.
You also have to adjust the PPS calls since the SPI pins moved.

I've combined your code w/mine and tested it with the SwordfishSE lite version 2.2.3.4, so it compiles.
I also do not understand how Public Inline Sub differs from Public Sub
Declaring the sub 'inline' means that instead of using a call and return, the compiler will insert the 'inline sub' code directly
where it's "called", saving the overhead of the call/return. It ends up faster, but produces more code if the sub is used a lot
since the code is replicated inline every time you have a "call".
Attachments
mrf89xa_garryp.zip
(2.6 KiB) Downloaded 167 times

Post Reply