M2 series hardware SPI

GrahamGo

Senior Member
Here attached is a working code fragment. At the moment SPI master only.

I have a few questions.

1. The hardware SPI is as expected is very fast, however the chip select CS is relatively slow. Question is there anyway I can speed the CS (ENABLE) up? I have tried, but seem to be stuck to the speed of the interpreter. At 4m/z the /CS (ENABLE on the graph) active low time is about 1.04ms whilst the 8 bit data burst is only 120us (using the /64 clock rate). I realize adjusting the master clock from 4mh/z to 16mh/z or higher will make a big difference. But I was hoping to do better than this. Note for testing SDO is linked to SDI

Right now this hardware SPI is approx 10x quicker than the bit bang routine, but potentially could be another 10x quicker if the /CS speed can be improved.

2. Is there a way to get closer to the built in definitions. ie. like hspisetup spimode00, spimedium ; spi mode 0,0 ?
Compared to this clean code, my initialization method looks quite clunky, is there a better way?

3. Please, if there are any obvious coding improvements to be made please be critical.

4. I plan to try to modify this to work as an i2C slave - it seems doable.

Code:
; -------------------------------------------------------------------------
;|                                                                         
;|   Hardware SPI for M2 series                      Version 001  2/9/12
;|                                                                         
; -------------------------------------------------------------------------

#define test
#picaxe 14M2
#no_data ' reduce upload time
#terminal 4800

; -------------------------------------------------------------------------
;|                                                                        
;|   Project Description                                                
;|                                                                       
; -------------------------------------------------------------------------
{
; Work in progress.....Current requirement SPI driver for a graphics display
; SDI not required right now.
;
; Routines providing hardware SPI in Master mode,intended to replace the
; bit banging xxM2 routines. This method should be both quicker and more
; code efficient.
;
; Hopefully  this will work in the M2 multitasking mode and may be able
; to co-exist with I2C.
; 
; 14M2 pin connections 
; pin8 	B5	= SDO
; pin9	B4	= SDI
; pin10 B3	= SCL
; pin11 B2	= /CS
; 
; Note1 The CS select deselect is slow, the Buffer Full check does not seem
; to be required.
; 
; Notes For each byte sent @ 4mh/z the /CS active time is 1.04ms, 
; data time 120us using /64 clock rate.
; To Do:-
; Activate Alternative Pin mode.
; SPI read under interrupt
; Test alongside I2C
}

; --------------------------------------------------------------------------
;|                                                                     
;|   I/O and SPI Mode Definitions                                                   
;|                                                                    
; -------------------------------------------------------------------------
{

symbol APFCON0  = $5D
symbol APFCON1  = $5E
symbol SSP1BUF  = $91
symbol SSP1STAT = $94
symbol SSP1CON1 = $95
symbol CS      	= B.2

'Mode is a constant/variable to define the mode
symbol spimode00	= %00000000 '(mode 0,0 - TX idle-Active, input sampled middle of data time)
symbol spimode01	= %01000000 '(mode 0,1 - TX idle-Active, input sampled middle of data time
symbol spimode10	= %10000000 '(mode 1,0 - TX Active-idle, input sampled end of data time)
symbol spimode11	= %11000000 '(mode 1,1 - TX Active-idle, input sampled end of data time)

'Spispeed is a constant/variable to define the clock speed
symbol spifast		= %00100000 '(clock freq / 4 ) (= 1MHz with 4MHz resonator)
symbol spimedium 	= %00100001	'(clock freq / 16) (= 250kHz with 4MHz resonator)
symbol spislow 		= %00100010	'(clock freq / 64) (= 63 kHz with 4MHz resonator)

symbol SSP1STAT_BIT 	= bit0 ; Buffer full status bit
}
; --------------------------------------------------------------------------
;|                                                                     
;|   Variable Definitions                                                   
;|                                                                    
; -------------------------------------------------------------------------
{
Symbol V1					= b0					' scratch0
Symbol V2					= b1					' scratch1 
Symbol SpiMode 		= b2					'
Symbol SpiSpeed 	= b3					'
Symbol SPItxd 		= b4  				' Data to send to SPI bus
Symbol SPIrxd 		= b5  				' Received Data from SPI bus
}
; ---------------------------------------------------------------------------
;|     Testing section                                               
; --------------------------------------------------------------------------- 
#ifdef test 

SpiMode= SpiMode01 : SpiSpeed= SpiSlow 		' 
Gosub SPISETUP

Main:
	For V2= $85 to $127	
		SpiTxd = V2 : Gosub WriteToSPI
	 	SERTXD ("SP1Txd = ", #SpiTxd," SP1Rxd = ", #SpiRxd,CR,LF)
	 	Pause 1000
	Next V2
	Goto main
	
#endif	
; ---------------------------------------------------------------------------
;|     SPI Initialisation                                               
; --------------------------------------------------------------------------- 	
 
SpiSetup: ' b2 = spimode, b3 = spispeed

'B2 CS output, B3 SCK0 output, B4 SDI input, B5 SDO output     
							'76543210        
 let dirsB =	%11101111 	' set port pin directions
  
 		PeekSFR SSP1CON1, V1 : V1 = V1 AND %11011111
 		PokeSFR SSP1CON1, V1 	'turn off module, in case it was on
 		PeekSFR SSP1STAT, V1 : V1 = V1 AND %00111111 : V1 = V1 OR SpiMode
		PokeSFR SSP1STAT, V1 ' Set the SPImode
		PeekSFR SSP1CON1, V1 : V1 = V1 AND %11011100 : V1 = V1 OR SpiSpeed
    PokeSFR SSP1CON1, V1	'enable MSSP, idle is low,set clock rate
   
'    pokesfr APFCON1,0	'alternate pin config
 return
 
; ---------------------------------------------------------------------------
;|     SPI Transmit Routines                                                
; --------------------------------------------------------------------------- 

WriteToSPI:
	Low CS	
  pokesfr SSP1BUF,SpiTxd
'  Do while SSP1STAT_BIT=1 loop ' * see note 1
	High CS
  peeksfr SSP1BUF,SpiRxd
  return 
  
  end

M2 hardware SPI.JPG
 
Of this bit;

Low CS
pokesfr SSP1BUF,SpiTxd
' Do while SSP1STAT_BIT=1 loop ' * see note 1
High CS

How much time is down to the Low CS, High CS without the pokesfr ?

And even if it could be sped up a bit, will the SPI write time form a significant part of the overall program speed ?
 
Of this bit;

Low CS
pokesfr SSP1BUF,SpiTxd
' Do while SSP1STAT_BIT=1 loop ' * see note 1
High CS

How much time is down to the Low CS, High CS without the pokesfr ?

And even if it could be sped up a bit, will the SPI write time form a significant part of the overall program speed ?

Good question, I only have had this working for an hour. Without, the pokesfr the time from Low CS to High CS is 0.42 ms, with pokesfr inserted its back to 1.047ms. The clock is (15 spaces) 8us each = 120us.

You are right in that perhaps a group of data encapsulated in one CS, might be quicker. I just wish (right now) that I could insert a little assembler code!

I am trying to drive a color graphics LCD and I simply want more speed than the bit bang way. I would also prefer to use the M2 series. From my pathetic number of posts you see the Picaxe is fairly new to me.
 
The colour Graphics LCD may well support burst writes.

I have used burst mode on an RF module to fill a FIFO buffer, you just leave CS low and each successive write went to the next location in the buffer. Same for a read.

However, when using the SPI interface in general, the CS low\high does not occupy a large overall part of the program. Even if you could eliminate the CS low\high delay completly you may well find that overall program speed does not improve very much at all.

Good work.
 
Hi,

An old thread and the OP/@GrahamGo is unlikely to be interested now, but this thread has recently been referenced from a current thread and some definitive answers now might be worthwhile :

The logic analyser traces in post #1 confirm my own measurements: The delay from the Enable signal going Low to the start of SPI data transfer is about 0.7ms, i.e. the execution/decoding time of the POKESFR . The delay from the start of the SPI data to the rising edge of the Enable is about 0.4ms, i.e. the execution/delay time of the enable HIGH instruction. Of course the LOW instruction is also 0.4ms so the Enable pulse is effectively doubling the execution time of this core part of the program. Therefore, as discussed above, the Enable Pulse should be omitted when/if possible, but some SPI protocols use the Enable Pulse as a Strobe or synchronising signal. Similarly, the SPISLOW constant should be more than fast enough to keep up with the interpreter, even without testing the Status flag.

Otherwise, the only possibility is to increase the clock frequency as much as practical, i.e. to 32 MHz for the M2 chips. Thus the "best" which might be achieved is about 16 times faster than shown in #1, IF the Enable Pulse can be omitted at the individual byte level. The 08M2 might be marginally faster because it has simpler internal code, but @srnet in #4 above is probably correct that "overall program speed may not improve very much at all". For example, the Test code above uses a subroutine CALL to a single byte transfer, but a GOSUB with its RETURN takes about five times longer than the POKESFR instruction alone, or to send a Pulse (via LOW ... : HIGH ... or via PULSOUT... ).

Therefore, for "high(er) speed", the subroutine structure should be omitted (perhaps using a #MACRO or a #DEFINE instead), or at least process a sequence of multiple data values in a single pass. Below is my proposal (Untested, but syntax-checked), which uses the "trick" of having multiple (subroutine) entry points (or a "fall through" into it) to give various software "options" for its function (e.g. with or without a Chip Enable pulse). However, one of the reasons that I so dislike the "SPI protocol" is that it has far too many options, demanding specific hardware characteristics (rather than configuration in the software). The data transfer can both WRITE and READ at the same time, but it may be necessary to connect the (separate SDI and SDO) pins accordingly (and I don't believe a combined transfer is supported by the X2 PICaxes). The code below does not employ the Alternative Pin Function Control (APFCON) SFR because there are no alternatives for the overlapping I2C and SPI pin functions (e.g. SCL and SCK). However, the 18M2 and 20M2 base PIC chips do have duplicate (M)SSP (I2C+SPI) modules, so it may be worth using the second SSP Module for the SPI, should both interfaces be required.

The routine below uses the byte pointer (@bptr) to transmit single or multiple bytes stored in RAM, either for a normal register/variable (e.g. {LET} BPTR = 27 ; to select the contents of b27), or in the "extended" memory (accessed via PEEK , POKE or @BPTR). If the bptr is (Auto-) INCremented, then (almost unlimited) multiple bytes can be transmitted without leaving the subroutine. Here, the last variable (b27) is used to mark the end of the data, but the source could be from a list of named variables, or by DECrementing through the extended RAM bytes. The subroutine can be constructed to use any @BPTR addresses, and to READSFR and/or WRITESFR as required. Of course the data must be loaded into the RAM, but there are a few "fast" methods such as COPYTABLE and HI2CIN.

For the fastest transfers, the "Auto-Incrementing" feature of @BPTR (i.e. @BPTRINC ) can (probably) be used, but there is a potential "Known Bug" in the PICaxe compiler/interpreter, that the pointer may be Incremented TWICE when used within a POKESFR or PEEKSFR instruction. A forum discussion is HERE (and then posts #19 - 20), but even hippy could not give a definitive answer, so it needs to be tested on the "Real" PICaxe, because the Simulator does not support hardware aspects such as the SFRs. I have assumed that the "Bug" does exist, which should cause the WRITE and READ bytes to become interleaved within the RAM (obviously requiring twice the nominal number of bytes). Therefore, this section of code needs to be adjusted (with reference to the specific PICaxe hardware) to accommodate, READing and/or WRITEing the SFR bytes, as required.

Code:
#picaxe 14m2              ; And most others (with different Leg/pin connections)
symbol SSP1BUF = $91      ; SFR Addresses
symbol SSP1STAT = $94
symbol SSP1CON  = $95
symbol SPISTART = 18      ; Address of data in RAM (Could be a variable)
symbol SPIEND   = 27      ; Last address  (Could be a variable)
symbol SDI    = b.4       ; Leg 8 SPI Input
symbol SDO    = b.3       ; Leg 9 SPI Output
symbol SCK    = b.2       ; Leg 10 SPI Clock
symbol SNEN   = b.1       ; Leg 11 SPI Enable, Active Low
   bptr = SPISTART
   poke SPISTART,1,2,3,4,5,6,7,8,9,0   ; Test data into registers b18 to b27
   call initspi
;   call sendspi          ; As required
   for bptr = SPISTART to SPIEND
      sertxd(" ",#@bptr)
   next bptr
stop
initspi:
   pokeSFR SSP1STAT,%01000000      ; SPImode01
   pokeSFR SSP1CON,%00100010       ; SPIslow (Still faster than any single PICaxe instruction)
   dirsB = %00001110               ; Adjust Output pins (1) as required
; return                            ; If required  
startspi:
;     Add any other "optional" functions here, as desired
   low SNEN                        ; Assuming (Active Low) is required for byte synchronisation
sendspi:
   pokesfr SSP1BUF,@bptr           ; Required to Clock the transaction. May be NUL/dummy data for a READ
;         Add BPTR INC here if Double-Increment Bug is not present (and TX data is to be retained) ... 
   peeksfr SSP1BUF,@bptrinc        ; ...  Or Delete/Replace with INC BPTR to NOT READ SFR
   if bptr <= SPIEND then sendspi  ; Or repeat, or use a Macro  (use ">" if DECrementing) 
   high SNEN                       ; Assuming (Active Low) device needs to be Disabled (Otherwise can delete)
return

Personally, I don't believe that implementing I2C Slave functionality in an M2 would be easy or worthwhile, nor probably Slave Select (SS) for the SPI.

Cheers, Alan.
 
Back
Top