Can SPI in Simple Libraries be speeded up?

13»

Comments

  • David BetzDavid Betz Posts: 13,471
    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.
  • twm47099twm47099 Posts: 813
    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.
    Comment please.

    Thanks
    Tom
  • David BetzDavid Betz Posts: 13,471
    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
                            mov     txmask, #1                      ' create mask for tx
                            shl     txmask, t2
                            andn    outa, txmask                    ' set to output low
                            or      dira, txmask
                            add     t1, #4
                            rdlong  hubpntr, t1                     ' get the buffer address
                            add     t1, #4
                            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;
    
  • twm47099twm47099 Posts: 813
    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);
      
     /*
       libblinkpasm.c
       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
                  add     :arg,d0
                  add     t2,#4
                  djnz    t3,#:arg
                  mov     address,t1                        ''preserve address location for passing
                                                            ''variables back to Spin language.
                  wrlong  zero,par                          ''zero command to signify command received
                  ror     t1,#16+2                          ''lookup command address
                  add     t1,#jumps
                  movs    :table,t1
                  rol     t1,#2
                  shl     t1,#3
    :table        mov     t2,0
                  shr     t2,t1
                  and     t2,#$FF
                  jmp     t2                                ''jump to command
    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_                                                 ''     Receive Data MSBPRE
    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
                  jmp     #Update_SHIFTIN                   ''     Pass received data to SHIFTIN receive variable
    '------------------------------------------------------------------------------------------------------------------------------              
    'tested OK
    LSBPRE_                                                 ''     Receive Data LSBPRE
                  add     t4,             #1                
    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
                  jmp     #Update_SHIFTIN                   ''     Pass received data to SHIFTIN receive variable
    '------------------------------------------------------------------------------------------------------------------------------
    MSBPOST_                                                ''     Receive Data MSBPOST
    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
                  jmp     #Update_SHIFTIN                   ''     Pass received data to SHIFTIN receive variable
    '------------------------------------------------------------------------------------------------------------------------------
    LSBPOST_                                                ''     Receive Data LSBPOST
                  add     t4,             #1
    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
                  jmp     #Update_SHIFTIN                   ''     Pass received data to SHIFTIN receive variable
    '------------------------------------------------------------------------------------------------------------------------------
    '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
                  mov     t1,             par               ''          Read the address of the first perimeter
                  add     t1,             #4                ''          Add offset for the second perimeter ; The 'Flag' variable
                  wrlong  t2,             t1                ''          Reset the 'Flag' variable to Zero
                  CogID   t1                                ''          Read CogID
                  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
  • twm47099twm47099 Posts: 813
    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
  • twm47099twm47099 Posts: 813
    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
  • twm47099twm47099 Posts: 813
    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.
       T Montemarano  (MIT License)
       
       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;
      int adcraw;           // raw value from ADC
    
      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
        {
    // read channel i
          cmd3208 = (startbit | mode_se | ch[i]) & 0b11111;       // build 3208 command 
          low(CS);              // activate 3208 ADC
          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
    
  • twm47099twm47099 Posts: 813
    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.
       T Montemarano  (MIT License)
       
       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 adcraw; 
      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
        {
         int adcraw = get3208se(i);     // read channel i
    // 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 
      low(CS);              // activate 3208 ADC
      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;
    }
    
  • twm47099twm47099 Posts: 813
    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
  • 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
Sign In or Register to comment.