spiin versus bit-banging

Aries

New Member
I am trying to read my water meter using a CC1101 transceiver. The module I have uses (allegedly) spiin/spiout to communicate with its master. However, although spiout works, spiin seems not to. Bit-banging does.
Code:
        for b0 = 0 to 7
            high SCK
            b1 = b1 * 2
            bit8 = pinMISO
            low SCK
        next b0
works, but
Code:
        spiin SCK,MISO,MSBPre_L,(b1)
whether MSBPre_L or MSBPost_L, does not.
Is this a limitation of spiin, or am I missing something?
 
Last edited:

hippy

Technical Support
Staff member
Which PICAXE type are you using ?

For a faster bit-banged solution you could try unrolling the loop -
Code:
high SCK : bit15 = pinMISO : low SCK ; MSB first
high SCK : bit14 = pinMISO : low SCK
high SCK : bit13 = pinMISO : low SCK
high SCK : bit12 = pinMISO : low SCK
high SCK : bit11 = pinMISO : low SCK
high SCK : bit10 = pinMISO : low SCK
high SCK : bit9  = pinMISO : low SCK
high SCK : bit8  = pinMISO : low SCK ; LSB last
This could be even faster -
Code:
high    SCK   : bit15 = pinMISO ; MSB first
pulsout SCK,1 : bit14 = pinMISO
pulsout SCK,1 : bit13 = pinMISO
pulsout SCK,1 : bit12 = pinMISO
pulsout SCK,1 : bit11 = pinMISO
pulsout SCK,1 : bit10 = pinMISO
pulsout SCK,1 : bit9  = pinMISO
pulsout SCK,1 : bit8  = pinMISO ; LSB last
low     SCK
 
Last edited:

Aries

New Member
I'm testing this with a 20X2, although I will probably need a 28X2 in the end (more memory). I have already unrolled the loop in places, as in your first example. I didn't think of reversing the clock to use pulsout.

There is an added complication as well - there are cases where you need to do spiin and spiout simultaneously (send a byte and receive a byte at the same time). It may be that this can only be done with bit-banging.
 

hippy

Technical Support
Staff member
For an X2 it might be better to use the HSPIOUT and HSPIIN commands.

There is no PICAXE mechanism for receiving while sending so that may have to be done by bit-banging though it may be possible to achieve what you want using HSPIOUT and PEEKSFR to recover what was received.

How well that works may depend on what device you are interfacing with.
 

Aries

New Member
Thanks for the thoughts - I will experiment a bit further. Meanwhile, a bit of good news: using your idea of "reversing" the clock, I think I have made spiin work by using
Code:
        high SCK
        spiin SCK,MISO,MSBPre_H,(b1)
and only dropping SCK when the CC1101 is not enabled (CSN high).
 

hippy

Technical Support
Staff member
PULSOUT pulses in the opposite direction of how the signal is set so, when set high, it is equivalent to a low then a high, but one command rather than two -
Code:
    _____   _____   __ _ _ _ ___   _____ 
___|     |_|     |_|            |_|     |___

   High  Pulse   Pulse          Pulse   Low
 

Aries

New Member
As a postscript - it seems that using MSBPre_H inverts the meaning of SDA, so the result has to be NOTted ...
Code:
        high SCK
        spiin SCK,MISO,MSBPre_H,(b1)
        b1 = NOT b1
This is (at the moment) giving the desired results in terms of correctness and speed.

As always, thanks for the pointers and insights.
 

hippy

Technical Support
Staff member
As a postscript - it seems that using MSBPre_H inverts the meaning of SDA, so the result has to be NOTted ...
As I understand it using _H should merely invert the SCK, making it active high idle.

I am not sure exactly what is happening with the hardware you are using but, if you are getting results which need inverting, I would say there is some sort of issue in whatever you are doing. Without knowing what that is, it is impossible to say the solution will be correct in all circumstances.

Perhaps detail what device you are communicating with. I would also try it with the HSPI commands to see if those work.
 

Aries

New Member
Here is the datasheet for the CC1101: http://www.ti.com/lit/ds/symlink/cc1101.pdf (although I confess mine came from China).
I've put together a short test routine to read using bit-banging plus Pre and Post, High and Low, Normal and NOTted. Bit-banging works, the others mostly do not, although there are three which do (all with NOTting). I don't know why, but pragmatism means I will go with one of those that works. I need to use SPI for speed - bit-banging is not fast enough even at 64MHz.
Code:
#picaxe 20x2

symbol PATABLE_ADDR                            =    $3E                                        ' PA Table Adress
symbol READ_BURST                                =    $C0
symbol WRITE_BURST                            =    $40

symbol CSN                                            = B.1                                        ' Chip Select Not (keep high)
symbol SCK                                            = B.2                                        ' SPI clock
symbol pinSCK                                        = pinB.2
symbol MOSI                                            = B.3                                        ' Master Out, Slave In (to CC1101)
symbol pinMOSI                                    = pinB.3
symbol MISO                                            = B.4                                  ' Master In, Slave Out (from CC1101)
symbol pinMISO                                    = pinB.4

' initialise CC1101
    pause 10000
'    setfreq m64
   
CCInit:
    output SCK
    output MOSI
    output CSN
    pause 1000

    low CSN
    pause 10
    high CSN
    pause 100

    b0 = PATABLE_ADDR | WRITE_BURST                    ' write to PATABLE
    low CSN
    do until pinMISO = 0 : loop    ' wait for chip to stabilise
    spiout SCK,MOSI,MSBFirst_L,(b0)
    spiout SCK,MOSI,MSBFirst_L,(0x60,0x00,0xF0,0x00,0x0F,0x00,0x00,0x00)       
    high CSN

    for w20 = 0 to 16
        gosub CC_ReadBurst
    next w20

    end
   
CC_ReadBurst:
    sertxd(13,10,"ReadBurst ")
    if w20 < 10 then: sertxd(" "): endif
    sertxd(#w20,": ")
   
' next section writes description of reading mode
    if w20 = 0 then
        sertxd(" Bit bang             :")
    else
        b0 = w20 - 1 // 4
        lookup b0,("L","H","L","H"),b1        ' clock L or H
        lookup b0,("r","r","o","o"),b2        ' pre or post
        lookup b0,("e","e","s","s"),b3        ' pre or post
        lookup b0,(" "," ","t","t"),b4        ' pre or post
        sertxd(" MSBP",b2,b3,b4,"_",b1)
        if w20 > 4 and w20 <= 12 then
            sertxd(" INV ")
        else
            sertxd("     ")
        endif
        if w20 > 8 then
            sertxd("SCK HI :")
        else
            sertxd("       :")
        endif
    endif
           

    b0 = PATABLE_ADDR | READ_BURST
    low CSN
    do until pinMISO = 0 : loop    ' wait for chip to stabilise
    spiout SCK,MOSI,MSBFirst_L,(b0)
   
    b0 = 8                                                                ' 8 bytes
    select case w20
    case 0                                    ' bit bang
        do until b0 = 0
            high    SCK   : bit15 = pinMISO     ' MSB first
            pulsout SCK,1 : bit14 = pinMISO
            pulsout SCK,1 : bit13 = pinMISO
            pulsout SCK,1 : bit12 = pinMISO
            pulsout SCK,1 : bit11 = pinMISO
            pulsout SCK,1 : bit10 = pinMISO
            pulsout SCK,1 : bit9  = pinMISO
            pulsout SCK,1 : bit8  = pinMISO     ' LSB last
            low     SCK
            gosub PrintB1ToHex
            dec b0
        loop
        high CSN
    case 1,5,9,13                                ' MSBPre_L
        if w20 > 8 then                        ' reverse clock
            high SCK
        endif
        do until b0 = 0
            spiin SCK,MISO,MSBPre_L,(b1)
            if w20 > 4 and w20 <= 12 then    ' invert result
                b1 = NOT b1
            endif
            gosub PrintB1ToHex
            dec b0
        loop
        high CSN
        if w20 > 8 then
            low SCK
        endif
    case 2,6,10,14                            ' MSBPre_H
        if w20 > 8 then                        ' reverse clock
            high SCK
        endif
        do until b0 = 0
            spiin SCK,MISO,MSBPre_H,(b1)
            if w20 > 4 and w20 <= 12 then    ' invert result
                b1 = NOT b1
            endif
            gosub PrintB1ToHex
            dec b0
        loop
        high CSN
        if w20 > 8 then
            low SCK
        endif
    case 3,7,11,15                            ' MSBPost_L
        if w20 > 8 then                        ' reverse clock
            high SCK
        endif
        do until b0 = 0
            spiin SCK,MISO,MSBPost_L,(b1)
            if w20 > 4 and w20 <= 12 then
                b1 = NOT b1    ' invert result
            endif
            gosub PrintB1ToHex
            dec b0
        loop
        high CSN
        if w20 > 8 then
            low SCK
        endif
    case 4,8,12,16                            ' MSBPost_H
        if w20 > 8 then                        ' reverse clock
            high SCK
        endif
        do until b0 = 0
            spiin SCK,MISO,MSBPost_H,(b1)
            if w20 > 4 and w20 <= 12 then    ' invert result
                b1 = NOT b1
            endif
            gosub PrintB1ToHex
            dec b0
        loop
        high CSN
        if w20 > 8 then
            low SCK
        endif
    end select
   
    return   

PrintB1ToHex:
    gosub B1ToHex
    sertxd(" ",b2,b3)
    return

' convert value in b1 into hex characters in b2,b3 (in that order)
B1ToHex:
    b2 = b1 // 16                                ' report in hex
    b3 = $FF
    do
        if b2 < 10 then
            b2 = b2 + "0"
        else
            b2 = b2 + "A" - 10
        endif
        if b3 <> $FF then exit
        b3 = b2
        b2 = b1 / 16
    loop
    return
Results:
Code:
ReadBurst  0:  Bit bang             : 60 00 F0 00 0F 00 00 00
ReadBurst  1:  MSBPre _L            : 9F FF 0F FF F0 FF FF FF
ReadBurst  2:  MSBPre _H            : 9F FF 0F FF F0 FF FF FF
ReadBurst  3:  MSBPost_L            : 3F FE 1F FF E1 FF FF FF
ReadBurst  4:  MSBPost_H            : 3F FE 1F FF E1 FF FF FF
ReadBurst  5:  MSBPre _L INV        : 60 00 F0 00 0F 00 00 00
ReadBurst  6:  MSBPre _H INV        : 60 00 F0 00 0F 00 00 00
ReadBurst  7:  MSBPost_L INV        : C0 01 E0 00 1E 00 00 00
ReadBurst  8:  MSBPost_H INV        : C0 01 E0 00 1E 00 00 00
ReadBurst  9:  MSBPre _L INV SCK HI : C0 01 E0 00 1E 00 00 00
ReadBurst 10:  MSBPre _H INV SCK HI : 60 00 F0 00 0F 00 00 00
ReadBurst 11:  MSBPost_L INV SCK HI : 80 03 C0 00 3C 00 00 01
ReadBurst 12:  MSBPost_H INV SCK HI : C0 01 E0 00 1E 00 00 00
ReadBurst 13:  MSBPre _L     SCK HI : 3F FE 1F FF E1 FF FF FF
ReadBurst 14:  MSBPre _H     SCK HI : 9F FF 0F FF F0 FF FF FF
ReadBurst 15:  MSBPost_L     SCK HI : 7F FC 3F FF C3 FF FF FE
ReadBurst 16:  MSBPost_H     SCK HI : 3F FE 1F FF E1 FF FF FF
 

hippy

Technical Support
Staff member
Bit-banging works, the others mostly do not, although there are three which do (all with NOTting). I don't know why, but pragmatism means I will go with one of those that works.
I am at a loss as to why it behaves as it does and works when the data is inverted other than there is some bug somewhere.

My concern is that, while your fix seems to work for the data you are seeing, without knowing what that bug or issue may be, there's no guarantee that fix will be valid for all data.

I need to use SPI for speed - bit-banging is not fast enough even at 64MHz.
This is why I suggested using HSPI. It really would be worth seeing what results you get with those.
 

Aries

New Member
My previous experience of HSPI was not successful, hence my hesitation in using it. You suggested using HSPIOUT and PEEKSFR to get the two-way SPI needed for the first 8 bits. Having never had to resort to PEEKSFR, could you give me some guidance (eg, what do I PEEK)? The alternative would I think, mean switching HSPI on and off so that the bit-banging approach could be used to get the initial data (if my assumption is right that HSPI disables the pins for all other purposes).
 
Last edited:

hippy

Technical Support
Staff member
As HSPI clocks out a byte it will also clock in a byte and it should be possible to read that via the SSPBUF register ...
Code:
Symbol SSPBUF = $C9 ; $FC9
Do
  HSpiOut (b0)
  PeekSfr SSPBUF, b1
Loop
 

Aries

New Member
Thanks for the info, Hippy. I must be missing something very obvious, but at the moment, I can't even get hspiout to write, let alone read anything.
The code below does an SPIOUT and the eight possible HSPIOUTs. The output section of the code is the same as before, but I'm only using the bit-banging. Only SPIOUT is working. What have I done wrong?

Code:
#picaxe 20x2

symbol PATABLE_ADDR                            =    $3E                                        ' PA Table Adress
symbol READ_BURST                                =    $C0
symbol WRITE_BURST                            =    $40

symbol CSN                                            = B.1                                        ' Chip Select Not (keep high)
symbol SCK                                            = B.7                                    ' SPI clock
symbol pinSCK                                        = pinB.7
symbol MOSI                                            = C.1                                        ' Master Out, Slave In (to CC1101)
symbol pinMOSI                                    = pinC.1
symbol MISO                                            = B.5                                  ' Master In, Slave Out (from CC1101)
symbol pinMISO                                    = pinB.5

' initialise CC1101
    pause 10000
'    setfreq m64
    
CCInit:
    output SCK
    output MOSI
    output CSN
    pause 1000

    low CSN
    pause 10
    high CSN
    pause 100

    for b44 = 0 to 8
    low CSN
    do until pinMISO = 0 : loop    ' wait for chip to stabilise
    b45 = b44 + 1
    if b44 = 0 then
        b0 = PATABLE_ADDR | WRITE_BURST                    ' write to PATABLE
        spiout SCK,MOSI,MSBFirst_L,(b0)
        spiout SCK,MOSI,MSBFirst_L,(0x60,b45,0xF0,0x00,0x0F,0x00,0x00,0x00)        
    else
        lookup b44,(0,spimode00,spimode01,spimode10,spimode11,spimode00e,spimode01e,spimode10e,spimode11e),w5
        hspisetup w5,spislow
        pause 100
        b0 = PATABLE_ADDR | WRITE_BURST                    ' write to PATABLE
        hspiout (b0)
        hspiout (0x60,b45,0xF0,0x00,0x0F,0x00,0x00,0x00)        
        pause 100
        hspisetup off
    endif
    high CSN
    for w20 = 0 to 0
        gosub CC_ReadBurst
    next w20
    next b44
    end
Output:
Code:
ReadBurst  0:  Bit bang             : 60 01 F0 00 0F 00 00 00
ReadBurst  0:  Bit bang             : 60 01 F0 00 0F 00 00 00
ReadBurst  0:  Bit bang             : 60 01 F0 00 0F 00 00 00
ReadBurst  0:  Bit bang             : 60 01 F0 00 0F 00 00 00
ReadBurst  0:  Bit bang             : 60 01 F0 00 0F 00 00 00
ReadBurst  0:  Bit bang             : 60 01 F0 00 0F 00 00 00
ReadBurst  0:  Bit bang             : 60 01 F0 00 0F 00 00 00
ReadBurst  0:  Bit bang             : 60 01 F0 00 0F 00 00 00
ReadBurst  0:  Bit bang             : 60 01 F0 00 0F 00 00 00
 

hippy

Technical Support
Staff member
I am not sure what is going on. It could be an issue of mixing bit-banging with HSPI. That only bit-banging works as expected makes me wonder if there isn't some kind of hardware issue, on the PICAXE side or with the module.

I am not familiar with the module, what should be sent and what one can expect to get back.

Do you have another module or any other SPI devices you can test ?
 

hippy

Technical Support
Staff member
Having read through the datasheet it seems that the CC1101 latches MOSI data on the rising edge of SCK so, when sending, MOSI should be set before raising SCK. When reading, it doesn't appear it should matter if done just before or after setting SCK.

That should mean SPIIN/SPOUT and HSPI commands should all work and behave as expected, so still not sure why they don't.

The only thing I did spot is that the levels of MOSI and SCK when CSN falls low does affect the chip's operating mode. Perhaps it's going into some odd mode and then not responding. If that's being caused by one of the HSPI mode it could be disrupting what would have otherwise been correct operations later.

Can you try the attached code, see what that does. This is my interpretation of a bit-banged version. Obviously untested, but hopefully will work. Then we can see about making it work with HSPI and/or SPIIN/SPIOUT.
Code:
#Picaxe 20X2
#Terminal 9600
#No_Data

Symbol CSN     = B.1
Symbol SCK     = B.7
Symbol MOSI    = C.1 : Symbol pinMOSI = pinC.1 
Symbol MISO    = B.5 : Symbol pinMISO = pinB.5 

Symbol RD      = %10000000 ; 1--- ----
Symbol WR      = %00000000 ; 0--- ----

Symbol BURST   = %01000000 ; -1-- ----
Symbol SINGLE  = %00000000 ; -0-- ----

Symbol PATABLE = $3E       ; --11 1110
 
#Define Init    Gosub Do_Init
#Define Start   Gosub Do_Start
#Define Finish  Gosub Do_Finish
#Define TxRx(n) b0 = n : Gosub Do_TxRx
#Define Tx(n)   b0 = n : Gosub Do_TxRx
#Define Rx      Gosub Do_Rx

PowerOnReset:
  Init
  Pause 2000
  SerTxd( "Started" )

MainLoop:
  Do
    SerTxd( CR, LF, "PATABLE Write" )
    Start
    Tx( WR | BURST | PATABLE ) : Gosub ShowWrStatus
    Tx( $01 )
    Tx( $23 )
    Tx( $45 )
    Tx( $67 )
    Tx( $89 )
    Tx( $AB )
    Tx( $CD )
    Tx( $EF )
    Finish
    SerTxd( CR, LF, "PATABLE Read" )
    Start
    Tx( RD | BURST | PATABLE ) : Gosub ShowRdStatus
    Rx                         : Gosub ShowRx
    Rx                         : Gosub ShowRx
    Rx                         : Gosub ShowRx
    Rx                         : Gosub ShowRx
    Rx                         : Gosub ShowRx
    Rx                         : Gosub ShowRx
    Rx                         : Gosub ShowRx
    Rx                         : Gosub ShowRx
    Finish
    Pause 1000
  Loop

Do_Init:
  High  CSN
  Low   SCK
  Low   MOSI
  Input MISO
  Return

Do_Start:
  Low SCK
  Low MOSI
  Low CSN
  If pinMISO <> 0 Then
    SerTxd( " BUSY" )
    Do : Loop Until pinMISO = 0
  End If
  Return

Do_Finish:
  High CSN
  Return

Do_Rx:
  b0 = 0

Do_TxRx:
  pinMOSI = bit7 : bit7 = pinMISO : PulsOut SCK, 1 
  pinMOSI = bit6 : bit6 = pinMISO : PulsOut SCK, 1 
  pinMOSI = bit5 : bit5 = pinMISO : PulsOut SCK, 1 
  pinMOSI = bit4 : bit4 = pinMISO : PulsOut SCK, 1 
  pinMOSI = bit3 : bit3 = pinMISO : PulsOut SCK, 1 
  pinMOSI = bit2 : bit2 = pinMISO : PulsOut SCK, 1 
  pinMOSI = bit1 : bit1 = pinMISO : PulsOut SCK, 1 
  pinMOSI = bit0 : bit0 = pinMISO : PulsOut SCK, 1 
  Return

ShowWrStatus:
  SerTxd( TAB )
  Gosub ShowRx_NoSpace
  b1 = b0 & $80 / $80 : SerTxd( "=", #b1 )
  b1 = b0 & $70 / $10 : SerTxd( ",", #b1 )
  b1 = b0 & $0F       : SerTxd( ",", #b1 )
  Return

ShowRdStatus:
  Gosub ShowWrStatus
  SerTxd( " :" )
  Return

ShowRx:
  SerTxd( " " )
ShowRx_NoSpace:
  b1 = b0 / $10 + "0" : If b1 > "9" Then : b1 = b1+7 : End If
  b2 = b0 & $0F + "0" : If b2 > "9" Then : b2 = b2+7 : End If
  SerTxd( b1, b2 ) 
  Return
Corrected to "If pinMISO <> 0 Then" which was "=0" for testing.
 
Last edited:

Aries

New Member
Hippy,
Thanks for spending time on this - much appreciated. In answer to your first point - I have tried two 20x2, with the same results. I have also tried SPI and HSPI with an NRF24L01 and both work as expected. So it does look as if there is something with the CC1101. I've tried your code and the result is
Code:
Started
PATABLE Write 0F=0,0,15
PATABLE Read  00=0,0,0 : 01 23 45 67 89 AB CD EF
PATABLE Write 0F=0,0,15
PATABLE Read  00=0,0,0 : 01 23 45 67 89 AB CD EF
etc
 

hippy

Technical Support
Staff member
Glad the bit-banging works. It would now be worth trying HSPI. That just requires the "Do_" routines to be replaced ...
Code:
Do_Init:
  High  CSN
  Low   SCK
  Low   MOSI
  Input MISO
  HSpiSetup SPIMODE01, SPISLOW ; <-- May have to tweak this
  Return

Do_Start:
  Low CSN
  If pinMISO <> 0 Then
    SerTxd( " BUSY" )
    Do : Loop Until pinMISO = 0
  End If
  Return

Do_Finish:
  High CSN
  Return

Do_Rx:
  b0 = 0

Do_TxRx:
  HSpiOut( b0 )
  PeekSfr $C9, b0
  Return
I think it's SPIMODE01 but might be worth trying SPIMODE10 if that doesn't work.

The thing I noticed is that HSPIOUT doesn't necessarily zero MOSI. The option I have looks from the datasheet diagram to leave it tri-stated or maybe 'undefined output'. It might be worth trying a 10K from MOSI to 0V.

I'm wondering if that's a similar problem with SPIIN/SPIOUT ? Because those probably seek to emulate what HSPI would do.

So course of action is ...

Try SPIMODE01
Try SPIMODE10
Connect 10K between MOSI and 0V
Try both again
Change all eight Tx($xy) to be Tx($x0); that is set last digit to zero
Try both again.

The last tests won't be a cure, but will identify if it's the MOSI setting which is causing a problem. If it is I'm not sure what we can do about that. There may be some trick with turning HSPI off and on again which may work. Otherwise it seems the CC1101 just isn't compatible with Microchip's HSPI implementation. I'll take a look to see if I can find any Microchip example code for using it.

If it is a similar CSN going low issue with SPIIN/SPIOUT that could perhaps be fixed by simply setting SCK and MOSI low before setting CSN low, but there's no way to use SPIIN/SPOUT tor read and write at the same time. That would be a more complicated code change and would need to mix bit-banging and SPIIN/SPIOUT, so it would be nice to see where we are with HSPI before going down that route.
 

inglewoodpete

Senior Member
@Aries, can you check what firmware your 20X2 is running? In the early days of 20X2s, there were a few issues with various peripheral modules. AFAIK these were resolved several years ago but I just want to rule that one out. (The current firmware for 20X2 is C.3)
 

hippy

Technical Support
Staff member
Otherwise it seems the CC1101 just isn't compatible with Microchip's HSPI implementation. I'll take a look to see if I can find any Microchip example code for using it.
Not a lot out there, but I did find some useful information along the way.

First thing is the CC1101 is a 1V8-3V6 device and is reported to be rather sensitive to supply fluctuations, isn't tolerant to 5V - Weird things can happen at 5V. Are you running at 5V ?

The power-on start-up sequence seems rather complicated. See section 19.1, Page 51 in my datasheet. The full sequence seems to be -

Set CSN high, SCK high, MOSI low
Wait 5ms from power-on
Set CSN low
Set CSN high
Set CSN low
Set CSN high for at least 40us
Set CSN low
Wait for MISO to be low, then issue SRES strobe ...
Set CSN high
Set SCK ?
Set MOSI ?
Set CSN low
Wait for MISO to be low
SpiOut $30
Set CSN high
Then carry on as normal

Why it seems we can ignore this when using bit-banging I have no idea, or maybe it's because we're not starting from a powered-off state ?

The other thing, from having better understood the various states of the CC1101, is that I might have been wrong about how SCK and MOSI should be when CSN is brought low.

Table 24 on page 34, suggests SCK high and MOSI low to get an IDLE state.

This would reflect the first set-up for powering-up. It could also explain why a preceding SCK high seems to have an effect on SPIIN/SPIOUT.

But our bit-banging code doesn't set SCK high, so it's also a mystery as to why that works. I think there may be some time related issue involved.

I can understand why there seem to be so many comments out there about this being a "tricky chip" to work with.

Do you have a scope or a logic analyser ? Watching the SPI bus might help explain what is going on.
 

Aries

New Member
Hippy,
Thanks for doing more research. I've been busy on other things so not much time to test. However, I can report that your code with HSPI does work for SPIMODE00 - I've not checked other things. In my earlier post, I cut down the original code (which behaved oddly for SPI) and omitted most of the startup code as it didn't seem to make any difference. However, sometime soon (may not be for a few days) I should have time to do a more thorough test, based on your research. The only scope I have is an OSC001 which I have not used so far - it was for a project some way in the future.
 

hippy

Technical Support
Staff member
However, I can report that your code with HSPI does work for SPIMODE00
Is that genuinely SPIMODE00 or a typo ?

If it is SPIMODE00 that is odd. Mode 00 raises its SCK before setting MOSI, expecting the slave device to sample on the falling edge of SCK ...
Code:
         ___     ___     ___
SCK  ___|   |___|   |___|   |___
           _______ _______ _____
MOSI -----|_______|_______|_____
When the CC1101 reads the data on the rising SCK it would get random data,

It could be that the CC1101 is not seeing / accepting the new data but is reporting the old which the PICAXE is reading. Changing one of the "Tx($xx)" to "Tx(b7) : b7=b7+1" would check it is accepting data every pass through the loop.

But then again; it wouldn't be responding with that data if it hadn't received a command to send that data. Unless it is simply repeatedly sending what it was last told to. Are you power-cycling the CC1101 between running tests ? If not I would try that and see if things keep working or fall over.
 

Aries

New Member
Hippy,

I used your code with a power-down/up and it worked for SPIMODE00 and not for SPIMODE01. I've gone back to my earlier sample code as well, and cycled through all the modes, using hspiin and bitbang to read the data back. Oddly, SPIMODE00 and SPIMODE11 both work, but the others don't. Essentially, it goes through an SPIOUT and then the eight HSPI modes. All are read back with bitbang; the HSPIs are also read (first) with HSPIIN. This is my code:
Code:
#picaxe 20x2

symbol PATABLE_ADDR                            =    $3E                                        ' PA Table Adress
symbol READ_BURST                                =    $C0
symbol WRITE_BURST                            =    $40

symbol CSN                                            = B.1                                        ' Chip Select Not (keep high)
symbol SCK                                            = B.7                                    ' SPI clock
symbol pinSCK                                        = pinB.7
symbol MOSI                                            = C.1                                        ' Master Out, Slave In (to CC1101)
symbol pinMOSI                                    = pinC.1
symbol MISO                                            = B.5                                  ' Master In, Slave Out (from CC1101)
symbol pinMISO                                    = pinB.5

' initialise CC1101
    pause 10000
'    setfreq m64
    
CCInit:
    output SCK
    output MOSI
    output CSN
    pause 1000

    low CSN
    pause 10
    high CSN
    pause 100

    for b44 = 0 to 8
    b45 = b44 + 1
    if b44 = 0 then
    low CSN
do until pinMISO = 0 : loop    ' wait for chip to stabilise
        b0 = PATABLE_ADDR | WRITE_BURST                    ' write to PATABLE
        spiout SCK,MOSI,MSBFirst_L,(b0)
        spiout SCK,MOSI,MSBFirst_L,(0x60,b45,0xF0,0x00,0x0F,0x00,0x00,0x00)        
    else
        lookup b44,(0,spimode00,spimode01,spimode10,spimode11,spimode00e,spimode01e,spimode10e,spimode11e),w5
        hspisetup w5,spislow
        pause 100
    low CSN
    do until pinMISO = 0 : loop    ' wait for chip to stabilise
        b0 = PATABLE_ADDR | WRITE_BURST                    ' write to PATABLE
        hspiout (b0)
        hspiout (0x60,b45,0xF0,0x00,0x0F,0x00,0x00,0x00)        
        pause 100
        high CSN
        low CSN
        b0 = b44-1
        sertxd(13,10,13,10,"Seq#",#b45,"                     SPIMODE",#bit1,#bit0)
        if b0 > 3 then
            sertxd("e:")
        else
            sertxd(" :")
        endif
        
        b0 = PATABLE_ADDR | READ_BURST
        hspiout (b0)
        for b0 = 0 to 7
            hspiin (b1)
            gosub PrintB1ToHex
        next b0
        hspisetup off
    endif
    high CSN
    for w20 = 0 to 0
        gosub CC_ReadBurst
    next w20
    next b44
    end
    
CC_ReadBurst:
    sertxd(13,10,"ReadBurst ")
    if w20 < 10 then: sertxd(" "): endif
    sertxd(#w20,": ")
    
' next section writes description of reading mode
    if w20 = 0 then
        sertxd(" Bit bang             :")
    endif
            

    b0 = PATABLE_ADDR | READ_BURST
    low CSN
    do until pinMISO = 0 : loop    ' wait for chip to stabilise
    spiout SCK,MOSI,MSBFirst_L,(b0)

    b0 = 8
    do until b0 = 0
            high    SCK   : bit15 = pinMISO     ' MSB first
            pulsout SCK,1 : bit14 = pinMISO
            pulsout SCK,1 : bit13 = pinMISO
            pulsout SCK,1 : bit12 = pinMISO
            pulsout SCK,1 : bit11 = pinMISO
            pulsout SCK,1 : bit10 = pinMISO
            pulsout SCK,1 : bit9  = pinMISO
            pulsout SCK,1 : bit8  = pinMISO     ' LSB last
            low     SCK
            gosub PrintB1ToHex
            dec b0
    loop
    high CSN
    return    

PrintB1ToHex:
    gosub B1ToHex
    sertxd(" ",b2,b3)
    return

' convert value in b1 into hex characters in b2,b3 (in that order)
B1ToHex:
    b2 = b1 // 16                                ' report in hex
    b3 = $FF
    do
        if b2 < 10 then
            b2 = b2 + "0"
        else
            b2 = b2 + "A" - 10
        endif
        if b3 <> $FF then exit 
        b3 = b2
        b2 = b1 / 16
    loop
    return
and the results:
Code:
ReadBurst  0:  Bit bang             : 60 01 F0 00 0F 00 00 00

Seq#2                     SPIMODE00 : 60 02 F0 00 0F 00 00 00
ReadBurst  0:  Bit bang             : 60 02 F0 00 0F 00 00 00

Seq#3                     SPIMODE01 : 8A 53 C1 0A F2 D7 94 CB
ReadBurst  0:  Bit bang             : 60 02 F0 00 0F 00 00 00

Seq#4                     SPIMODE10 : F0 06 78 00 0F 80 00 00
ReadBurst  0:  Bit bang             : E0 0C F0 00 1F 00 00 00

Seq#5                     SPIMODE11 : 60 05 F0 00 0F 00 00 00
ReadBurst  0:  Bit bang             : 60 05 F0 00 0F 00 00 00

Seq#6                     SPIMODE00e: 60 06 F0 00 0F 00 00 00
ReadBurst  0:  Bit bang             : 60 06 F0 00 0F 00 00 00

Seq#7                     SPIMODE01e: 5A 5E BA E8 E8 C4 E1 93
ReadBurst  0:  Bit bang             : 60 06 F0 00 0F 00 00 00

Seq#8                     SPIMODE10e: E0 18 F0 00 1F 00 00 00
ReadBurst  0:  Bit bang             : E0 18 F0 00 1F 00 00 00

Seq#9                     SPIMODE11e: 60 09 F0 00 0F 00 00 00
ReadBurst  0:  Bit bang             : 60 09 F0 00 0F 00 00 00
The key thing seems to be the position of LOW CSN, which has to come after hspisetup - othewise, presumably, the Picaxe sends some random signals to the CC1101.
As far as power supply is concerned - I have had too many experiences of burning out 3V units with a 5V supply, so the 20X2 and CC1101 are being powered by two AAA batteries.
It's unlikely I'll be able to do anything further now until sometime over the weekend, but thanks for all your help so far.
 

hippy

Technical Support
Staff member
I realised there is a way to tell whether the CC1101 is latching on the rising edge of SCK, as the datasheet appears to indicate, or the falling edge. Use the bit-banged code in Post #15 with the Do_TxRx subroutine replaced with the following -
Code:
Do_TxRx:
  pinMOSI = 0 : bit7 = pinMISO : High SCK : pinMOSI = bit7 : Low SCK
  pinMOSI = 0 : bit6 = pinMISO : High SCK : pinMOSI = bit6 : Low SCK
  pinMOSI = 1 : bit5 = pinMISO : High SCK : pinMOSI = bit5 : Low SCK
  pinMOSI = 1 : bit4 = pinMISO : High SCK : pinMOSI = bit4 : Low SCK
  pinMOSI = 0 : bit3 = pinMISO : High SCK : pinMOSI = bit3 : Low SCK
  pinMOSI = 1 : bit2 = pinMISO : High SCK : pinMOSI = bit2 : Low SCK
  pinMOSI = 0 : bit1 = pinMISO : High SCK : pinMOSI = bit1 : Low SCK 
  pinMOSI = 1 : bit0 = pinMISO : High SCK : pinMOSI = bit0 : Low SCK 
  Return
If that returns read results of 35 35 35 35 35 35 35 35 it is latching on rising edge, if 01 23 45 67 89 AB CD EF it is falling edge.

That will take the big unknown out of the equation, would explain SPIMODE00 working and may give us a path to making things work.
 

Aries

New Member
If that returns read results of 35 35 35 35 35 35 35 35 it is latching on rising edge, if 01 23 45 67 89 AB CD EF it is falling edge.
How do you feel about 2F?
Code:
Started
PATABLE Write 0F=0,0,15
PATABLE Read  2F=0,2,15 : 2F 2F 2F 2F 2F 2F 2F 2F
The reason, I think, is that it is indeed picking up 35 - the problem is that register 35 is read-only, so you just get back the current value. Using the "old" TxRx to define the register, and then the "new" TxRx to read or write, produces 35 35 35 35 35 35 35 35.
Code:
Started
PATABLE Write 0F=0,0,15
PATABLE Read  00=0,0,0 : 35 35 35 35 35 35 35 35
 

hippy

Technical Support
Staff member
Good point about that code leading to the register accessed being $35; I had forgotten to take that into account and your analysis is likely correct.

I think we can already conclude the chip latches on SCK going high but to confirm it you can try this -
Code:
Do_TxRx:
  pinMOSI = 0 : bit7 = pinMISO : High SCK : pinMOSI = bit7 : Low SCK
  pinMOSI = 0 : bit6 = pinMISO : High SCK : pinMOSI = bit6 : Low SCK
  pinMOSI = 1 : bit5 = pinMISO : High SCK : pinMOSI = bit5 : Low SCK
  pinMOSI = 1 : bit4 = pinMISO : High SCK : pinMOSI = bit4 : Low SCK
  pinMOSI = 1 : bit3 = pinMISO : High SCK : pinMOSI = bit3 : Low SCK
  pinMOSI = 1 : bit2 = pinMISO : High SCK : pinMOSI = bit2 : Low SCK
  pinMOSI = 1 : bit1 = pinMISO : High SCK : pinMOSI = bit1 : Low SCK 
  pinMOSI = 0 : bit0 = pinMISO : High SCK : pinMOSI = bit0 : Low SCK 
  Return
That should access the PATABLE register, so should return -
Code:
PATABLE Write 0F=0,0,15
PATABLE Read  00=0,0,0 : 3E 3E 3E 3E 3E 3E 3E 3E
I just spotted there's a bug in the above; it's actually outputting what was read before SCK is lowered. This may be a better test -
Code:
Do_TxRx:
  pinMOSI = bit7 : bit7 = pinMISO : High SCK : pinMOSI = 0 : Low SCK
  pinMOSI = bit6 : bit6 = pinMISO : High SCK : pinMOSI = 0 : Low SCK
  pinMOSI = bit5 : bit5 = pinMISO : High SCK : pinMOSI = 1 : Low SCK
  pinMOSI = bit4 : bit4 = pinMISO : High SCK : pinMOSI = 1 : Low SCK
  pinMOSI = bit3 : bit3 = pinMISO : High SCK : pinMOSI = 1 : Low SCK
  pinMOSI = bit2 : bit2 = pinMISO : High SCK : pinMOSI = 1 : Low SCK
  pinMOSI = bit1 : bit1 = pinMISO : High SCK : pinMOSI = 1 : Low SCK 
  pinMOSI = bit0 : bit0 = pinMISO : High SCK : pinMOSI = 0 : Low SCK 
  Return
should give -
Code:
PATABLE Write 0F=0,0,15
PATABLE Read  00=0,0,0 : 01 23 45 67 89 AB CD EF
 
Last edited:

Aries

New Member
The results are:
Code:
Original version (pinMOSI set first)
Started
PATABLE Write 0F=0,0,15
PATABLE Read  0F=0,0,15 : 0F 0F 0F 0F 0F 0F 0F 0F

Second version (pinMOSI set after)
Started
PATABLE Write 0F=0,0,15
PATABLE Read  00=0,0,0 : 01 23 45 67 89 AB CD EF
 

hippy

Technical Support
Staff member
The results are:
That looks right, given the bug I belatedly spotted, seems to confirm the chip latches on rising edge.

Using SPIMODE01 SPIMODE10 would seem to be the best option for HSPI.

That starts with SCK high which seems required when we bring CSN low, and keeps bit 0 of the last byte sent on the bus, which allows us to force MOSI low for when CSN is lowered by sending a $00 byte.

That $00 byte will be ignored because CSN is high, but it will leave MOSI set low -
Code:
      _____________________________                            __
CSN     :                          |__________________________|
        :                          :                          :
      ____    _   _         _   _____    _   _         _   ______
SCK     : |__| |_| |_ ... _| |_|   : |__| |_| |_ ... _| |_|   :
        :                          :                          :
      ______                       :    ___ ___ _ _ _ ___ _______
MOSI  ______|___|___|_ _ _|___|________|___|___|_ _ _|___|_______
        :                          :                          :
Bit     :    7   6         1   0   :    7   6         1   0   :
        : `----------------------' : `----------------------' :
        :      Send $00 byte       :      Send data bytes     :
So, starting with the code in Post #5, and replacing the Do_ routines with ...
Code:
Do_Init:
  High  CSN
  High  SCK
  Low   MOSI
  Input MISO
  HSpiSetup SPIMODE10, SPISLOW
  Return

Do_Start:
  HSpiOut( $00 )
  Low CSN
  If pinMISO <> 0 Then
    SerTxd( " BUSY" )
    Do : Loop Until pinMISO = 0
  End If
  Return

Do_Finish:
  High CSN
  Return

Do_Rx:
  b0 = 0

Do_TxRx:
  HSpiOut( b0 )
  PeekSfr $C9, b0
  Return
May just work.
 
Last edited:

Aries

New Member
Hippy,

Thanks for your further thoughts. I tried SPIMODE10 and got some odd results - the READs are consistent (albeit wrong), the WRITEs are 07 or 00, but the pattern is not consistent and is different for each time the Picaxe is powered up. SPIMODE00 and SPIMODE11 seem to give the right results (as I reported previously). SPIMODE01, once it has got going, seems to cycle with a period of 8 but with the occasional glitch which changes the pattern and random 08 values for the WRITEs.

Code:
SPIMODE10:
Started
PATABLE Write 07=0,0,7
PATABLE Read  00=0,0,0 : 01 B3 E7 F7 CD FF EF FF
PATABLE Write 00=0,0,0
PATABLE Read  00=0,0,0 : 01 B3 E7 F7 CD FF EF FF
PATABLE Write 00=0,0,0
PATABLE Read  00=0,0,0 : 01 B3 E7 F7 CD FF EF FF
PATABLE Write 07=0,0,7
PATABLE Read  00=0,0,0 : 01 B3 E7 F7 CD FF EF FF
PATABLE Write 00=0,0,0
PATABLE Read  00=0,0,0 : 01 B3 E7 F7 CD FF EF FF
PATABLE Write 00=0,0,0
PATABLE Read  00=0,0,0 : 01 B3 E7 F7 CD FF EF FF
PATABLE Write 00=0,0,0
PATABLE Read  00=0,0,0 : 01 B3 E7 F7 CD FF EF FF
PATABLE Write 07=0,0,7
PATABLE Read  00=0,0,0 : 01 B3 E7 F7 CD FF EF FF

SPIMODE00:
Started
PATABLE Write 0F=0,0,15
PATABLE Read  00=0,0,0 : 01 23 45 67 89 AB CD EF
PATABLE Write 0F=0,0,15
PATABLE Read  00=0,0,0 : 01 23 45 67 89 AB CD EF
PATABLE Write 0F=0,0,15
PATABLE Read  00=0,0,0 : 01 23 45 67 89 AB CD EF
PATABLE Write 0F=0,0,15
PATABLE Read  00=0,0,0 : 01 23 45 67 89 AB CD EF

SPIMODE01:
Started
PATABLE Write 0F=0,0,15
PATABLE Read  00=0,0,0 : 00 E9 4C 39 45 14 3A A7
PATABLE Write 0F=0,0,15
PATABLE Read  0F=0,0,15 : 93 03 A7 A0 41 B0 11 A4
PATABLE Write 0F=0,0,15
PATABLE Read  0F=0,0,15 : 4F 35 A5 70 C9 30 5A 9B
PATABLE Write 0F=0,0,15
PATABLE Read  0F=0,0,15 : 8A 52 E1 8A F0 D7 94 CB
PATABLE Write 0F=0,0,15
PATABLE Read  0F=0,0,15 : 25 2F 5E 70 74 62 70 E1
PATABLE Write 0F=0,0,15
PATABLE Read  0F=0,0,15 : C7 60 61 0C 54 F5 CC D7
PATABLE Write 0F=0,0,15
PATABLE Read  0F=0,0,15 : 40 96 E8 3C 43 FE E3 A9
PATABLE Write 08=0,0,8
PATABLE Read  0F=0,0,15 : D2 23 4C FE 30 FC 9A 77
PATABLE Write 00=0,0,0
PATABLE Read  0F=0,0,15 : 84 E9 4C 39 45 14 3A A7
PATABLE Write 08=0,0,8
PATABLE Read  0F=0,0,15 : 93 03 A7 A0 41 B0 11 A4

SPIMODE11:
Started
PATABLE Write 0F=0,0,15
PATABLE Read  0F=0,0,15 : 01 23 45 67 89 AB CD EF
PATABLE Write 0F=0,0,15
PATABLE Read  0F=0,0,15 : 01 23 45 67 89 AB CD EF
PATABLE Write 0F=0,0,15
PATABLE Read  0F=0,0,15 : 01 23 45 67 89 AB CD EF
PATABLE Write 0F=0,0,15
PATABLE Read  0F=0,0,15 : 01 23 45 67 89 AB CD EF
PATABLE Write 0F=0,0,15
PATABLE Read  0F=0,0,15 : 01 23 45 67 89 AB CD EF
 

hippy

Technical Support
Staff member
It does seem odd that SPIMODE00 and SPIMODE11 seem to deliver the best results as they put out very different signals.

It is notable that status with SPIMODE00 is different on the read "00=0,0,0, while SPIMODE11 is a consistent "0F=0,0,15". I would say the later is more likely correct.

It's confusing though because neither of those is what I would have expected to work.

But I did have a realisation; HSPIOUT sends in the background, so the PICAXE continues to its next command while it's still sending. That could mean that CSN is being lowered before the preceding HSPIOUT($00) has completed, and we could be peeking the received byte SFR before all data has been clocked in.

I would recommend changing the code to below and giving it another try with all four modes -
Rich (BB code):
Do_Start:
  HSpiOut( $00 )
  Pause 8
  Low CSN
  If pinMISO <> 0 Then
    SerTxd( " BUSY" )
    Do : Loop Until pinMISO = 0
  End If
  Return
Rich (BB code):
Do_TxRx:
  HSpiOut( b0 )
  Pause 8
  PeekSfr $C9, b0
  Return
Those are quite long delays but will save altering things if we bump operating speed up with SETFREQ. Ideally we should peek the status register but I'd have to read the datasheet to determine which register and bit to check.
 

Aries

New Member
Fundamentally, it looks like "no change". I've printed out the values for all four modes, and then the value of the mode selected. The results are in order SPIMODE00, SPIMODE01, SPIMODE10, SPIMODE11.
Code:
SPIMODE00=64 SPIMODE01=0 SPIMODE10=80 SPIMODE11=16
Started 64
PATABLE Write 0F=0,0,15
PATABLE Read  00=0,0,0 : 01 23 45 67 89 AB CD EF
PATABLE Write 0F=0,0,15
PATABLE Read  00=0,0,0 : 01 23 45 67 89 AB CD EF
PATABLE Write 0F=0,0,15

Started 0
PATABLE Write 0F=0,0,15
PATABLE Read  00=0,0,0 : 00 E9 4C 39 45 16 2A E7
PATABLE Write 0F=0,0,15
PATABLE Read  0F=0,0,15 : 93 03 A7 A8 41 B0 11 A4
PATABLE Write 0F=0,0,15
PATABLE Read  0F=0,0,15 : 4F 37 A1 70 C9 30 5A 9B
PATABLE Write 0F=0,0,15
PATABLE Read  0F=0,0,15 : 8A 53 C1 9A F3 D7 94 CB
PATABLE Write 0F=0,0,15
PATABLE Read  0F=0,0,15 : 2D 2F 5D 74 74 62 70 C3
PATABLE Write 0F=0,0,15
PATABLE Read  0F=0,0,15 : C7 60 21 0C 54 F5 CC D7
PATABLE Write 0F=0,0,15
PATABLE Read  0F=0,0,15 : C0 96 E8 1C 43 FE E3 E9
PATABLE Write 08=0,0,8
PATABLE Read  0F=0,0,15 : C2 2F 4C FE 38 FC 9A 77
PATABLE Write 00=0,0,0
PATABLE Read  0F=0,0,15 : 84 E9 4C 39 45 16 2A E7
PATABLE Write 08=0,0,8
PATABLE Read  0F=0,0,15 : 93 03 A7 A8 41 B0 11 A4

Started 80
PATABLE Write 07=0,0,7
PATABLE Read  07=0,0,7 : 81 B3 E7 F7 CD FF EF FF
PATABLE Write 07=0,0,7
PATABLE Read  07=0,0,7 : 81 B3 E7 F7 CD FF EF FF
PATABLE Write 07=0,0,7

Started 16
PATABLE Write 0F=0,0,15
PATABLE Read  0F=0,0,15 : 01 23 45 67 89 AB CD EF
PATABLE Write 0F=0,0,15
PATABLE Read  0F=0,0,15 : 01 23 45 67 89 AB CD EF
PATABLE Write 0F=0,0,15
PATABLE Read  0F=0,0,15 : 01 23 45 67 89 AB CD EF
 

hippy

Technical Support
Staff member
It's all a bit odd but I'd suggest sticking with SPIMODE11 and see how it goes.

I would also change the Tx($EF) to "Tx(b20) : b20=b20+1" just to check the numbers do increment every loop.
 
Top