Welcome to the Parallax Discussion Forums, sign-up to participate.

# Can SPI in Simple Libraries be speeded up?

13»

• Posts: 14,055
edited 2014-09-28 - 04:07:48
twm47099 wrote: »
Dave,
Thanks I see that now. How do you keep track of instruction bits?

Tom
I'm not sure I understand the question but the S field of an instructionis bits 8:0 and the D field is bits 17:9. The MOVS instruction will store the low order 9 bits of the source into bits 8:0 of the destination and MOVD will store the low order 9 bits of the source into bits 17:9 of the destination. Adding \$200 to an instruction increments the D field since \$200 is a constant with only bit 9 set which is the low order bit of the D field. After modifying an instruction, you must execute at least one other instruction before you can execute the modified instruction.
• Posts: 858
edited 2014-09-28 - 09:00:36
I'm thinking that the easiest way to make this work more generally is to modify the pasm to store ClockDelay and ClockState.

At the beginning of the pasm it tests for nonzero command.
So right after the "if_z jmp #loop" I would add the following.
```      mov  t7,  t1    '' move 'command' to new variable t7
mov t8,   t1    '' make a copy
and t7, #\$40000  wz     '' C program defines command 4 <<16 for start up
If_nz  jmp #startup
```
Put startup at end of current code (but before 'done').
```startup  mov t7,  t8       '' make t7 whole
and t7 , #1           " first bit of command is Clockstate
mov ClockState, t7
mov t7, t8
shr t7, #2            " C packed ClockDelay in command by shifting  ClockDelay << 2
and t7, #\$FF
mov ClockDelay, t7
wrlong zero, par    ''set command back to zero
jmp #loop
```
define after arg4 the following
```t7      long    0
t8      long    0
```
In the C program, in the start function, after starting the cog, but before the return I would add the following
```device->command = (4 << 16) | ( clkdly << 2) | ( clkst);
```
and all of the needed declarations.

Sorry for the poor spacing, but I'm doing this on a tablet.

Thanks
Tom
• Posts: 14,055
edited 2014-09-28 - 09:03:43
This isn't the same PASM code but you could do something like this:
```dat
org     0

ws2812                  jmp     #ws2812_cont

resettix                long    0                               ' frame reset timing
bit0hi                  long    0                               ' bit0 high timing
bit0lo                  long    0                               ' bit0 low timing
bit1hi                  long    0                               ' bit1 high timing
bit1lo                  long    0                               ' bit1 low timing

ws2812_cont             mov     t1, par                         ' get the pin number
rdlong  t2, t1
andn    outa, txmask                    ' set to output low
rdlong  hubpntr, t1                     ' get the buffer address
rdlong  ledcount, t1                    ' get the led count
mov     t2, #0                          ' init is done
wrlong  t2, t1
```
You then access the fields of the PASM code using a structure like this:
```    // driver header structure
typedef struct {
uint32_t    jmp_inst;
uint32_t    resettix;
uint32_t    bit0hi;
uint32_t    bit0lo;
uint32_t    bit1hi;
uint32_t    bit1lo;
} ws2812_hdr;
```
• Posts: 858
edited 2014-09-28 - 13:04:29
Dave,
I had already modified the spin file's pasm and once I fixed a couple of things, (like trying to use \$40000 as an immediate), I tried it and it did work. I am thinking about trying to write a version, and would use your suggestion.

It is very fast getting 14+ blocks of Pixy data per frame, which is much faster than the spin or simpleIDE library versions.

My next step in this exercise is to put all of the spi constants (MSPRE, etc.) into the program. (The pixy only used MSPRE which = 2 in the original spin program), and the separate the parts into a library.

The revised C program and pasm are listed below, and the spin file is attached.
```/* spipasm 3 -  a fast spin interface library project
Updated to use pasm to set ClockDelay and ClockState

Use pasm modified from SPI_Asm.spin propeller tool library object.
T Montemarano (with a lot of help)

NOTES:
Uses spin file xSPIAsm.spin,

This version does run correctly.

The SPI device called in main() is a Pixy CMUCAM5.  It does not required a start command
since it always transmits data at 1usec.
It should be possible to change main to use other spi devices
like the DS1620 used in the spi spin demo.

This version uses the same calls as the SPI_Asm.spin library object.

device->command is either 0 or packed with 2 or 3 numbers to be used by pasm.
command = 0 -- pasm in idle loop
in spipasm_start, the number 4 is shifted left 16 bits to indicate 'startup'
the ClockDelay value is shifted left 2 bits
the ClockState value is in bit 0.  All 3 values are or'd.
in spipasm_SHIFTIN the number 2 is shifted left 16 bits, this is or'd with the address of the Dpin
in spipasm_SHIFTOUT, the number 1 is shifted left 16 bits, and or'd with the address of the Dpin

*/

#include "simpletools.h"                      // Library includes

typedef struct spipasm_struct               // Structure contains varaibles
{                                             // for cogs to share + cog & stack
volatile int command;
int Dpin;
int Cpin;
volatile int Mode;
volatile int Bits;
volatile int Value;
volatile int Flag;
int cog;                                    // Remembers cog for spiasm_stop
} spia_t;                                    // Data type for struct

int pixydata[400];
int clkst = 0;              // 0 = start clock low, 1 = start clock high
int clkdly = 15;           // ClockDelay, actual clock delay = 300ns + (clkdly-1) * 50ns
//  clkdly = 15 gives clock delay = 1 usec

// function prototypes

spia_t *spipasm_start(int clkdly, int clkst);

void spipasm_stop(spia_t *device);

void spipasm_SHIFTOUT(spia_t *device,
int DQ, int CLK, int MD, int BIT, int VLU);

int spipasm_SHIFTIN(spia_t *device,int DQ, int CLK, int MD, int BIT);

void spipasm_setcommand(spia_t *device, int cmd, int *argptr);

/*
Test harness for the spipasm library.
*/

// #include "spipasm.h"

spia_t *spia;

int main()                                    // Main function
{
int px;
// Put demo code here

spia = spipasm_start(clkdly, clkst);    // Launch, get handles
int i = 1;
while(i<398)    //  fill pixydata array with spi bytes
{
// Pixy SPI interface continuously transmits data (no starting or stopping it)
// Pixy data out rate is 1usec
// Pixy SPI data out -> Prop P0, Pixy Clock -> Prop P2
//  spipasm_shiftin(Dpin, Cpin, Mode, Bits)
pixydata[i] = spipasm_SHIFTIN(spia, 0, 2, 0, 8);  // Mode 0 = MSBPRE
i++;
}
i = 1;
while(i<398)    //  write pixydata array
{
//    printi(" i = %d   color = %x\n", i, pixydata[i]);
printi("%d\n", pixydata[i]);
i++;
}
spipasm_stop(spia);
}

// These are the actual functions

//  ****************************************
spia_t *spipasm_start(int clkd, int clks)
{
spia_t *device;                                    // Declare spipasm pointer
device = (void *) malloc(sizeof(spia_t));         // Allocate memory for it

if(device->cog == 0)
{
device->command = 0;                         // pasm stays in waiting loop till non zero
extern int binary_xSPIAsm_dat_start[];
device->cog = 1 + cognew((void*)binary_xSPIAsm_dat_start, (void*)device);
}
device->command = (4 << 16) | (clkd << 2) | clks ;    // pasm reads command, sets ClockDelay, ClockState
while(device->command);                                       // wait till pasm resets command to zero
return device;                                                        // Return pointer to structure
}

//  ************************************
void spipasm_stop(spia_t *device)
{
if(device->cog > 0)                                  // If cog running
{
cogstop(device->cog - 1);                          // Shut down cog
device->cog = 0;                                   // Clear cog varaible
free(device);                                      // Release allocated memory
}
}

//  ************************************
void spipasm_SHIFTOUT(spia_t *device, int DQ, int CLK, int MD, int BIT, int VLU)
{
device->Dpin = DQ;            // load variable values for pasm
device->Cpin = CLK;
device->Mode = MD;
device->Bits =  BIT;
device->Value = VLU;
spipasm_setcommand(spia,1, &device->Dpin);
}

//  ************************************
int spipasm_SHIFTIN(spia_t *device,
int DQ, int CLK, int MD, int BIT)
{
device->Dpin = DQ;
device->Cpin = CLK;
device->Mode = MD;
device->Bits = BIT;
device->Flag = 1;
spipasm_setcommand(spia, 2, &device->Dpin);
while(device->Flag);                                       //     pasm sets Flag =0 when finished reading all bits in Value
return device->Value;
}

//  ************************************
void spipasm_setcommand(spia_t *device, int cmd, int *argptr)
{
device->command = (int) argptr | (cmd << 16);
while(device->command);
}

```
```DAT           org
'
'' SPI Engine - main loop
'
loop          rdlong  t1,par          wz                ''wait for command
if_z  jmp     #loop
mov       t7,     t1                      '' command to t7
mov       t8,     t7                      '' copy command
and       t7,     stconst         wz      '' test for startup
if_nz jmp       #startup

movd    :arg,#arg0                        ''get 5 arguments ; arg0 to arg4
mov     t2,t1                             ''    │
mov     t3,#5                             ''───┘
:arg          rdlong  arg0,t2
djnz    t3,#:arg
''variables back to Spin language.
wrlong  zero,par                          ''zero command to signify command received
movs    :table,t1
rol     t1,#2
shl     t1,#3
:table        mov     t2,0
shr     t2,t1
and     t2,#\$FF
jumps         byte    0                                 ''0
byte    SHIFTOUT_                         ''1
byte    SHIFTIN_                          ''2
byte    NotUsed_                          ''3
NotUsed_      jmp     #loop
'################################################################################################################
'tested OK
SHIFTOUT_                                               ''SHIFTOUT Entry
mov     t4,             arg3      wz      ''     Load number of data bits
if_z      jmp     #Done                             ''     '0' number of Bits = Done
mov     t1,             #1        wz      ''     Configure DataPin
shl     t1,             arg0
muxz    outa,           t1                ''          PreSet DataPin LOW
muxnz   dira,           t1                ''          Set DataPin to an OUTPUT
mov     t2,             #1        wz      ''     Configure ClockPin
shl     t2,             arg1              ''          Set Mask
test    ClockState,     #1        wc      ''          Determine Starting State
if_nc     muxz    outa,           t2                ''          PreSet ClockPin LOW
if_c      muxnz   outa,           t2                ''          PreSet ClockPin HIGH
muxnz   dira,           t2                ''          Set ClockPin to an OUTPUT
sub     _LSBFIRST,      arg2    wz,nr     ''     Detect LSBFIRST mode for SHIFTOUT
if_z      jmp     #LSBFIRST_
sub     _MSBFIRST,      arg2    wz,nr     ''     Detect MSBFIRST mode for SHIFTOUT
if_z      jmp     #MSBFIRST_
jmp     #loop                             ''     Go wait for next command
'------------------------------------------------------------------------------------------------------------------------------

SHIFTIN_                                                ''SHIFTIN Entry
mov     t4,             arg3      wz      ''     Load number of data bits
if_z      jmp     #Done                             ''     '0' number of Bits = Done
mov     t1,             #1        wz      ''     Configure DataPin
shl     t1,             arg0
muxz    dira,           t1                ''          Set DataPin to an INPUT
mov     t2,             #1        wz      ''     Configure ClockPin
shl     t2,             arg1              ''          Set Mask
test    ClockState,     #1        wc      ''          Determine Starting State
if_nc     muxz    outa,           t2                ''          PreSet ClockPin LOW
if_c      muxnz   outa,           t2                ''          PreSet ClockPin HIGH
muxnz   dira,           t2                ''          Set ClockPin to an OUTPUT
sub     _MSBPRE,        arg2    wz,nr     ''     Detect MSBPRE mode for SHIFTIN
if_z      jmp     #MSBPRE_
sub     _LSBPRE,        arg2    wz,nr     ''     Detect LSBPRE mode for SHIFTIN
if_z      jmp     #LSBPRE_
sub     _MSBPOST,       arg2    wz,nr     ''     Detect MSBPOST mode for SHIFTIN
if_z      jmp     #MSBPOST_
sub     _LSBPOST,       arg2    wz,nr     ''     Detect LSBPOST mode for SHIFTIN
if_z      jmp     #LSBPOST_
jmp     #loop                             ''     Go wait for next command

'------------------------------------------------------------------------------------------------------------------------------
MSBPRE_Sin    test    t1,             ina     wc        ''          Read Data Bit into 'C' flag
rcl     t3,             #1                ''          rotate "C" flag into return value
call    #PreClock                         ''          Send clock pulse
djnz    t4,             #MSBPRE_Sin       ''          Decrement t4 ; jump if not Zero
'------------------------------------------------------------------------------------------------------------------------------
'tested OK
LSBPRE_Sin    test    t1,             ina       wc      ''          Read Data Bit into 'C' flag
rcr     t3,             #1                ''          rotate "C" flag into return value
call    #PreClock                         ''          Send clock pulse
djnz    t4,             #LSBPRE_Sin       ''     Decrement t4 ; jump if not Zero
mov     t4,             #32               ''     For LSB shift data right 32 - #Bits when done
sub     t4,             arg3
shr     t3,             t4
'------------------------------------------------------------------------------------------------------------------------------
MSBPOST_Sin   call    #PostClock                        ''          Send clock pulse
test    t1,             ina     wc        ''          Read Data Bit into 'C' flag
rcl     t3,             #1                ''          rotate "C" flag into return value
djnz    t4,             #MSBPOST_Sin      ''          Decrement t4 ; jump if not Zero
'------------------------------------------------------------------------------------------------------------------------------
LSBPOST_Sin   call    #PostClock                        ''          Send clock pulse
test    t1,             ina       wc      ''          Read Data Bit into 'C' flag
rcr     t3,             #1                ''          rotate "C" flag into return value
djnz    t4,             #LSBPOST_Sin      ''          Decrement t4 ; jump if not Zero
mov     t4,             #32               ''     For LSB shift data right 32 - #Bits when done
sub     t4,             arg3
shr     t3,             t4
'------------------------------------------------------------------------------------------------------------------------------
'tested OK
LSBFIRST_                                               ''     Send Data LSBFIRST
mov     t3,             arg4              ''          Load t3 with DataValue
LSB_Sout      test    t3,             #1      wc        ''          Test LSB of DataValue
muxc    outa,           t1                ''          Set DataBit HIGH or LOW
shr     t3,             #1                ''          Prepare for next DataBit
call    #PostClock                        ''          Send clock pulse
djnz    t4,             #LSB_Sout         ''          Decrement t4 ; jump if not Zero
mov     t3,             #0      wz        ''          Force DataBit LOW
muxnz   outa,           t1
jmp     #loop                             ''     Go wait for next command
'------------------------------------------------------------------------------------------------------------------------------
'tested OK
MSBFIRST_                                               ''     Send Data MSBFIRST
mov     t3,             arg4              ''          Load t3 with DataValue
mov     t5,             #%1               ''          Create MSB mask     ;     load t5 with "1"
shl     t5,             arg3              ''          Shift "1" N number of bits to the left.
shr     t5,             #1                ''          Shifting the number of bits left actually puts
''          us one more place to the left than we want. To
''          compensate we'll shift one position right.
MSB_Sout      test    t3,             t5      wc        ''          Test MSB of DataValue
muxc    outa,           t1                ''          Set DataBit HIGH or LOW
shr     t5,             #1                ''          Prepare for next DataBit
call    #PostClock                        ''          Send clock pulse
djnz    t4,             #MSB_Sout         ''          Decrement t4 ; jump if not Zero
mov     t3,             #0      wz        ''          Force DataBit LOW
muxnz   outa,           t1

jmp     #loop                             ''     Go wait for next command
'------------------------------------------------------------------------------------------------------------------------------
'tested OK
Update_SHIFTIN
mov     t1,             address           ''     Write data back to Arg4
add     t1,             #16               ''          Arg0 = #0 ; Arg1 = #4 ; Arg2 = #8 ; Arg3 = #12 ; Arg4 = #16
wrlong  t3,             t1
add     t1,             #4                ''          Point t1 to Flag ... Arg4 + #4
wrlong  zero,           t1                ''          Clear Flag ... indicates SHIFTIN data is ready
jmp     #loop                             ''     Go wait for next command
'------------------------------------------------------------------------------------------------------------------------------
'tested OK
PreClock
mov     t2,             #0      nr        ''     Clock Pin
test    t2,             ina     wz        ''          Read ClockPin
muxz    outa,           t2                ''          Set ClockPin to opposite  of read value
call    #ClkDly
muxnz   outa,           t2                ''          Restore ClockPin to original read value
call    #ClkDly
PreClock_ret  ret                                       ''          return
'------------------------------------------------------------------------------------------------------------------------------
'tested OK
PostClock
mov     t2,             #0      nr        ''     Clock Pin
test    t2,             ina     wz        ''          Read ClockPin
call    #ClkDly
muxz    outa,           t2                ''          Set ClockPin to opposite  of read value
call    #ClkDly
muxnz   outa,           t2                ''          Restore ClockPin to original read value
PostClock_ret ret                                       ''          return
'------------------------------------------------------------------------------------------------------------------------------
'tested OK
ClkDly
mov       t6,     ClockDelay
ClkPause      djnz      t6,     #ClkPause
ClkDly_ret    ret
'------------------------------------------------------------------------------------------------------------------------------
'tested OK
startup       mov       t7,     t8              '' t7 = command
and       t7,     #1              '' clockstate in bit 0
mov       ClockState,  t7
mov       t7,     t8
shr       t7,     #2              '' clockdelay was stored lsb bit 2
and       t7,     #\$FF
mov       ClockDelay,  t7
wrlong    zero,   par             '' zero command
jmp       #loop

' --------------------------------------------------
Done                                                    ''     Shut COG down
mov     t2,             #0                ''          Preset temp variable to Zero
add     t1,             #4                ''          Add offset for the second perimeter ; The 'Flag' variable
wrlong  t2,             t1                ''          Reset the 'Flag' variable to Zero
COGSTOP t1                                ''          Stop this Cog!
'------------------------------------------------------------------------------------------------------------------------------
{
########################### Assembly variables ###########################
}
zero                    long    0                       ''constants
d0                      long    \$200

_MSBPRE                 long    \$0                      ''          Applies to SHIFTIN
_LSBPRE                 long    \$1                      ''          Applies to SHIFTIN
_MSBPOST                long    \$2                      ''          Applies to SHIFTIN
_LSBPOST                long    \$3                      ''          Applies to SHIFTIN
_LSBFIRST               long    \$4                      ''          Applies to SHIFTOUT
_MSBFIRST               long    \$5                      ''          Applies to SHIFTOUT

ClockDelay              long    0
ClockState              long    0

''temp variables
t1                      long    0                       ''     Used for DataPin mask     and     COG shutdown
t2                      long    0                       ''     Used for CLockPin mask    and     COG shutdown
t3                      long    0                       ''     Used to hold DataValue SHIFTIN/SHIFTOUT
t4                      long    0                       ''     Used to hold # of Bits
t5                      long    0                       ''     Used for temporary data mask
t6                      long    0                       ''     Used for Clock Delay
address                 long    0                       ''     Used to hold return address of first Argument passed

t7                      long    0
t8                      long    0
stconst                 long    \$40000

arg0                    long    0                       ''arguments passed to/from high-level Spin
arg1                    long    0
arg2                    long    0
arg3                    long    0
arg4                    long    0

```

xSPIAsm - Archive [Date 2014.09.28 Time 15.46].zip
• Posts: 858
edited 2014-10-02 - 22:24:19
I've completed the code and compile (Make Project Library) part of making an SPI library that uses modified PASM from the Spin Library object SPI_Asm.spin. I still have to do the documentation and clean up (add the MIT license info).

I originally got the library working to receive data from the CMUCAM5 pixy.
The pixy does not require any SHIFTOUTs from the prop, so it is just a matter of shifting in the data as fast as possible. (and the library functions are very fast).

But I also wanted to make the library more general. I just bought the DS1620 digital thermometer that was used in the spin demo, and that is the "test harness" in the library folder. That does require sending commands to the SPI device to configure it and to get the data. I did find that I had to add some pauses between sending commands and setting the "RESET" pin to low that were not in the spin program.

Until I get the documentation done, to run the demo (libspiasm.c) use the same connections for the DS1620 as are in the SPI_Spin_demo.spin file that comes with the Prop Tool. The pin numbers are also in the c file.

The zipped library is attached.

Tom
• Posts: 858
edited 2014-12-11 - 17:43:49
I haven't had a chance to add new devices to the SPI library, but I noticed that the library attached to the previous post contained a buggy version of xSPIAsm.spin, and that it did not include my program for the MMA7455 3 axis accelerometer. So I have attached the updated library below.

It contains:
- the SPI library code and header -- spiasm.c and spiasm.h (#INCLUDE spiasm.h in the main program)
- the ASM code in -- xSPIAsm.spin (this file has to be added to each SimpleIDE Project that calls the SPI functions. This version has the MSBFIRST_ code correction.)
- the program for the DS1600 digital thermometer chip -- libspiasm.c (it was the "test harness for the library, and I need to change the name to be more descriptive).
- the program for reading the MMA7455 3 axis accelerometer -- mma7455-spi.c
- the documentation for the library
- supporting files.

I hope to find time to write the code (convert from SPIN sources) for the SCP1000 pressure sensor module. That one will be interesting since it uses one pin for Rx and one for Tx, and seems a bit more complex (different). But I haven't had a lot of quiet time lately.

Tom

libspiasm.zip
• Posts: 858
edited 2015-01-06 - 17:36:04
I've added code for using the MCP3208 ADC in Single-Ended Mode with the spiasm library below.

Tom

```/* 3208-spi.c       3208 ADC interface using spiasm library

Uses pasm in xspiasm.spin, modified from SPI_Asm.spin propeller tool library object.

The SPI device called in main() is a 3208 ADC.
Used wiring from Programming & Customizing the Multicore Propeller Microcontroller
page 158, but using 3208 ADC.  (16 pin vs 14 pin device). Note different device pin definitions.

*/

#include "simpletools.h"                      // Library includes
#include "spiasm.h"

/* ******************************************************
Uses 3208 ADC as SPI device

3208 pin              Prop pin

CS                <-    Prop P2  low starts transfer, high terminates
DATA in/out   <->   Prop P1  Prop out (MFIRST), Prop in (MPOST)
Clock             <-    Prop P0  rise then fall  State = 0
*/

#define cstate 0            // 0 = start clock low, 1 = start clock high
#define clockd_ns 1000   // Actual delay in nanoseconds (1000ns = 1 usec), not less than 300ns
// ClockDelay, actual clock delay = 300ns + (cdelay-1) * 50ns
// e.g. cdelay of 15 gives actual clock delay = 1 usec
// cdelay = ((clockd_ns) - 250ns)) / 50ns
// e.g. for 2 usec (i.e. 2000 ns): cdelay = 35
//  ** Propeller pins
#define CS 2
#define IOPIN 1
#define CLKPIN 0

#define VREF 5       // for 5 volt Vdd and Vref  For lower Vdd voltage may have to set clock_ns slower
#define MAXCHAN 3    // max channel number (ch 0 & 1 = pots, 2 = 3.3v buss, 3 = Vss)

spia_t *spia;

int main()                                    // Main function
{
int i;
// ** declare 3208 Command values here - mode, channel
int startbit = 0b10000;
int mode_se = 0b01000;
int ch[8];
ch[0] = 0b00000;
ch[1] = 0b00001;
ch[2] = 0b00010;
ch[3] = 0b00011;
ch[4] = 0b00100;
ch[5] = 0b00101;
ch[6] = 0b00110;
ch[7] = 0b00111;
//  ** end of command values
int cmd3208;

high(CS);                                        // disable 3208 data transfer

spia = spi_start(clockd_ns, cstate);    // Launch, get handles, set ClockDelay & ClockState
while(1)  // bb
{
for(i = 0; i <= MAXCHAN; i++)   // aa
{
cmd3208 = (startbit | mode_se | ch[i]) & 0b11111;       // build 3208 command
pause(1);
spi_out(spia, IOPIN, CLKPIN, MFIRST, 5, cmd3208);    // start conversion hi, mode, channel(3bits)
spi_out(spia, IOPIN, CLKPIN, MFIRST, 1, 0);         //  send don't care bit
adcraw = spi_in(spia, IOPIN, CLKPIN, MPOST, 13);        // read null & 12 bit value
pause(5);
high(CS);                // release 3208

// put data handling & printout here

int voltsin = (adcraw * VREF * 1000) / 4095;
printi("Channel %d: raw = %d,  %d.%d volts\n", i, adcraw, (voltsin/1000), voltsin - (voltsin/1000) *1000);

cmd3208 = 0;
pause(10);
} // end aa
printi("\n");
pause(800);
}  // end bb
} // end main
```
• Posts: 858
edited 2015-01-07 - 19:11:12
I've made a function version of the MPC3208 code where main() is a demo calling the function "get3208se(channel_number)" Use Run with Terminal.
```/* 3208-spi-function.c       MPC3208 ADC interface using spiasm library
Function version with main() as demo
Uses pasm in xspiasm.spin, modified from SPI_Asm.spin propeller tool library object.

The SPI device called in main() is a MPC3208 ADC.
Used wiring from Programming & Customizing the Multicore Propeller Microcontroller
page 158, but using MPC3208 ADC instead of MPC3204.  (16 pin vs 14 pin device).
Note there are different device pin definitions.

*/

#include "simpletools.h"                      // Library includes
#include "spiasm.h"

/* ******************************************************
Uses MPC3208 ADC as SPI device

MPC3208 pin                  Prop pin

CS                <-    Prop P2  low starts transfer, high terminates
DATA in/out       <->   Prop P1  Prop out (MFIRST), Prop in (MPOST)
Clock             <-    Prop P0  rise then fall  State = 0
*/

#define cstate 0            // 0 = start clock low, 1 = start clock high
#define clockd_ns 1000   // Actual delay in nanoseconds (1000ns = 1 usec), not less than 300ns
// ClockDelay, actual clock delay = 300ns + (cdelay-1) * 50ns
// e.g. cdelay of 15 gives actual clock delay = 1 usec
// cdelay = ((clockd_ns) - 250ns)) / 50ns
// e.g. for 2 usec (i.e. 2000 ns): cdelay = 35
//  ** Propeller pins
#define CS 2
#define IOPIN 1
#define CLKPIN 0

#define VREF 5       // for 5 volt Vdd and Vref  For lower Vdd voltage may have to set clock_ns slower
#define MAXCHAN 3    // max channel number 0 to 7 (ch 0 & 1 = pots, 2 = 3.3v buss, 3 = Vss)

int get3208se(int chnum);  // function prototype for single ended 3208 read

spia_t *spia;

int main()                              // Main function
{
int i;
int voltsin;
high(CS);                               // disable 3208 data transfer
spia = spi_start(clockd_ns, cstate);    // Launch, get handles, set ClockDelay & ClockState
while(1)  // bb
{
for(i = 0; i <= MAXCHAN; i++)   // aa
{
// data handling & printout
voltsin = (adcraw * VREF * 1000) / 4095;
printi("Channel %d: raw = %d,  %d.%d volts\n", i, adcraw, (voltsin/1000), voltsin - (voltsin/1000) *1000);
pause(10);
}           // end aa
printi("\n");
pause(800);
}            // end bb
}              // end main

int get3208se(int chnum)
{
// ** declare 3208 Command values here - start bit, mode
int startbit = 0b10000;
int mode_se = 0b01000;
// channel number = 0 to 7 in chnum
//  ** end of command values
int cmd3208 = 0;
int rawval = 0;
high(CS);
pause(5);

cmd3208 = (startbit | mode_se | chnum) & 0b11111;       // build 5 bit 3208 command
pause(1);
spi_out(spia, IOPIN, CLKPIN, MFIRST, 5, cmd3208);    // start conversion hi, mode, channel(3bits)
spi_out(spia, IOPIN, CLKPIN, MFIRST, 1, 0);         //  send don't care bit
rawval = spi_in(spia, IOPIN, CLKPIN, MPOST, 13);        // read null & 12 bit value
pause(5);
high(CS);                // release 3208
return rawval;
}
```
• Posts: 858
edited 2015-02-07 - 19:17:06
I’ve made significant changes to the LIBSPIASM library.
Note that programs that used the libraries in the LIBSPIASM.ZIP file of post 67 will NOT work with the new version.

Originally, I trasferred parameters like pin numbers to the functions using Globals. (Note in the post above that the main 3208 program gets the data into adcraw by:
``` int adcraw = get3208se(i);     // read channel i
```

The only parameter directly passed in the function call is the ADC channel number.

But then I realized that one of the purposes of an SPI bus is to attach a number of devices and select each one using its chip select protocol. And sometimes there will be a number of the same type of devices (for example DS1620 digital thermometers) connected to the bus. Passing parameters using globals won’t work.

To enable the use of multiple devices, it was necessary to pass all the needed parameters to the library function in the call to the function.

For example the code above becomes:
```int adcraw = get_3208se(spia_1, IOPIN3208, CLKPIN3208, CS3208, i); // read channel i
```

The parameters needed and the returned values are listed in the library documentation.

Note that a number of the same or different types of devices can be used with only one call to the "start" function. The advantage of doing that is that only one extra cog is used. (Each call to "start" starts a new cog.) One limitation is that all the devices used with that "start" (spia_t variable) need to have the same timing characteristics (clock delay timing and clock state).

In addition I changed the names of the functions to be in accordance with the guidelines in the Simple Libraries Tutorial.

I have attached 2 zips.
demo-spi lib functions.zip
libspiasm v2.zip

libspiasm v2.zip contains the library and documentation. It should be unzipped into a Learn Simple libraries folder
(for example: SimpleIDE\Learn\Simple Libraries\My Libraries\libspiasm)

There is a documentation file and the libraries:
libspiasm – the test harness for the library, and used to build libraries for new devices
libSPI-DS1620.c -- for DS1620 digital thermometer
libSPI-MCP3208.c -- for MCP 3208, 8 channel 12 bit ADC used in SE mode
libSPI-MMA7455.c -- for MMA7455 accelerometer – just basic for reading the 3 axes
libSPI-SCP1000.c -- for SCP1000 absolute pressure sensor

The basic library functions (spi_start, spi_stop, spi_out, and spi_in) were not changed and can be called directly rather than using the device functions for other devices or for using the advanced features of the devices.

The other zip is a group of 6 simple demo programs that use the libraries.
They include:
the MCP3208,
the DS1620,
the MCP3208 and DS1620 using the same cog (only 1 call to “start”) and different I/O pins
the MCP3208 and DS1620 using common I/O pins with separate Chip Select pins
the MMA7455 accelerometer
the SCP1000 absolute pressure sensor.

When you load the demos into SimpleIDE, if it doesn't build, you may need to use the project command to add the library.

Since this is a learning experience for me, I'd appreciate any comments.

Tom
• Posts: 858
I am not sure if the SCP1000 code works. I get one reading from the sensor, but the as I change the height of the sensor, the pressure reading stays the same.

Tom