Shop OBEX P1 Docs P2 Docs Learn Events
Longmove in spin — Parallax Forums

Longmove in spin

AndyPropAndyProp Posts: 60
edited 2021-09-12 04:48 in Propeller 1

Anyone can explain me a little why the longmove(@cs, @cspin, 4) is used and how that relates to the pins that get assigned at the start.

I set my pins too 15, 12, 14, 13

isn't it easier to just write directly to a pin?

`
var

long cs ' chip select
long clk ' clock
long mosi ' master out slave it (to MCP3208)
long miso ' master in slave out (from MCP3208)

pub start(cspin, clkpin, mosipin, misopin)
stop

longmove(@cs, @cspin, 4) ' copy pins

outa[cs] := 1 ' output high (disable)
dira[cs] := 1

outa[clk] := 0 ' output low
dira[clk] := 1

`

Comments

  • @AndyProp said:
    Anyone can explain me a little why the longmove(@cs, @cspin, 4) is used and how that relates to the pins that get assigned at the start.

    I set my pins too 15, 12, 14, 13

    isn't it easier to just write directly to a pin?

    `
    var

    long cs ' chip select
    long clk ' clock
    long mosi ' master out slave it (to MCP3208)
    long miso ' master in slave out (from MCP3208)

    pub start(cspin, clkpin, mosipin, misopin)
    stop

    longmove(@cs, @cspin, 4) ' copy pins

    outa[cs] := 1 ' output high (disable)
    dira[cs] := 1

    outa[clk] := 0 ' output low
    dira[clk] := 1

    `

    The longmove is a more compact/efficient way to copy a set of long variables to another set of long variables.

    longmove is given three parameters, the first parameter is the address pointer of the destination long variable(s), the second parameter is the address/pointer of the source variable(s), the third parameter is the number of longs to be copied.

    Note: In order for this copy method/style to work, the source variables need to be consecutive in memory, likewise for the destination variables.

    Instead of doing the longmove, you could do this instead:

    cs := cspin
    clk := clkpin
    mosi := mosipin
    miso := misopin
    

    Your code snippet appears to be from one of the drivers for the MCP3208 ADC. I believe that driver includes the use of PASM code, which needs to have the four pin parameters passed to it. So the driver has the set of pin values copied to another set of longs to be passed to the PASM routine.

    Hopefully the above answers your question.

  • Hopefully the above answers your question.

    Yes, thanks a lot for replying, and yes you are correct about the MCP3208.

    I am editing it to read from 3 consecutive 3208s in one pass.

    All the best to you

  • JonnyMacJonnyMac Posts: 8,926
    edited 2021-09-12 19:39

    I believe that driver includes the use of PASM code...

    No, it's a simple Spin1 driver, but I do need to save the pins so they can be used by the readx() method (see attached).

    I am editing it to read from 3 consecutive 3208s in one pass.

    You could just use three copies of the driver with shared SCLK, MOSI, and MISO pins -- THE CS pins would be unique to each (six pins total for 3x chips). Then you could put a simple method like this in your application:

    pub read_all(p_buf) : idx | ch
    
    '' Read 24 total channels from 3x MCP3208 ADCs
    '' -- p_buf is pointer to array of 24 longs
    
      repeat ch from 0 to 7
        long[p_buf][idx++] := adc1.readx(ch, adc1#SE)
    
      repeat ch from 0 to 7
        long[p_buf][idx++] := adc2.readx(ch, adc2#SE)
    
      repeat ch from 0 to 7
        long[p_buf][idx++] := adc3.readx(ch, adc3#SE)
    

    This reads three consecutive devices in one pass without having to create an entirely new object. SPI bus pins, other than CS, are designed to be shared.

    You could also create a macro method that treats your three 8-channel ADCs as one 24-channel ADC.

    pub read_ch(ch) : value
    
    '' Read 1-of-24 (0..23) ADC channels
    
      case ch
        00..07 :
          return adc1.readx(ch, adc1#SE)
    
        08..16 :
          return adc2.readx(ch-8, adc2#SE)    
    
        17..23 :
          return adc3.readx(ch-16, adc3#SE)   
    
        other  :
          return 0
    

    The point here is that a we should use methods to cover specialty cases when a generic object handles the heavy load.

  • AndyPropAndyProp Posts: 60
    edited 2021-09-13 13:06

    Thanks for your reply Jon,

    Actually I like this idea, not sure exactly of the code requirement as I am not so familiar with spin. Basically just changing the chip select according to channel sets required. This would then be infinitely scalable.

    case ch
        00..07 :
          ChipSelect := chipselect1pin
    
        08..16 :
          ChipSelect := chipselect2pin
    
        17..23 :
          ChipSelect := chipselect3pin
    
        other  :
          return 0
    
      mux := 24+ch
    
      outa[ChipSelect] := 0     'set CS low
      dira[DatatoChipPin] := 1   'set pin to putput
    

    No clue why the case ch 0..7 etc.. ended up outside the code block, maybe Monday morning blues :-)

    then continue with the usual readx code.
    I also to make the code less complex set the mux with a simple addition 24 to the value of the channel

    mux := 24+ch

    Then the result can be stored in arrays by ch,

    Jon, if the array is based on longs will those values be available to other cogs.
    say for example


    As I stay thinking, the registers are 32 bits wide I understand and and we only use 12 bits. Almost worth using a register to store two channel value in bits 0-17 and 18-31, of course that would also get messy with 3 ADC's. With 4 it would be neat but I wonder with the additional code if anything would be saved.

  • AndyPropAndyProp Posts: 60
    edited 2021-09-13 13:03
    case ch
    00..07 : 
    ChipSelect := CS1Pin
    
    08..16 : 
    ChipSelect := CS2Pin
    
    17..23 : 
    ChipSelect := CS3Pin
    
    other  : 
    return 0
    
    mux := 24+ch
    
    outa[ChipSelect] := 0     'set CS low
    
  • avsa242avsa242 Posts: 430
    edited 2021-09-13 12:53

    Andy,

    For block code, use three backticks on a line by itself (before and after the code)

    code here

    (I added spaces in between the backticks just for illustrative purposes; there shouldn't be any spaces between them when you use them)

    Cheers

    EDIT: nevermind... it interprets it anyway. Imagine these are backticks:
    '''
    code
    '''

  • JonnyMacJonnyMac Posts: 8,926
    edited 2021-09-13 14:42

    I also to make the code less complex set the mux with a simple addition 24 to the value of the channel

    Really? This is the output I get doing the proper mux calculation (2nd column) vs your "less complex" version (3rd column).

     0  11000  11000
     1  11001  11001
     2  11010  11010
     3  11011  11011
     4  11100  11100
     5  11101  11101
     6  11110  11110
     7  11111  11111
     8  11000  00000
     9  11001  00001
    10  11010  00010
    11  11011  00011
    12  11100  00100
    13  11101  00101
    14  11110  00110
    15  11111  00111
    16  11000  01000
    17  11001  01001
    18  11010  01010
    19  11011  01011
    20  11100  01100
    21  11101  01101
    22  11110  01110
    23  11111  01111
    

    This would then be infinitely scalable.

    No, it isn't. If you next decide you want a 32-channel ADC object, you need a new object with another start() method parameter for the new CS pin. If you use the method I shared above, then it doesn't matter how many channels you have; you can scale that method as required and use pin constants from your application.

    I think it's a waste of time creating a 24-channel version, but if you're going to do it, I suggest the read() method do something like this. Of course, you would have passed the individual chip select pins through the start() method.

    pub read(ch) : level | cs, mux
    
    '' Read MCP3208 channel
    '' -- single-ended mode
    
      if ((ch => 0) and (ch <= 23))                                 ' valid channel?
        cs := lookupz(ch >> 3 : cs1, cs2, cs3)                      '  yes, get CS for device
      else
        return 0                                                    '  no, abort
    
      mux := %1_0_000 | (SE << 3) | (ch & %111)                     ' SE mux bits
    
      outa[cs] := 0                                                 ' activate adc         
    
      mux <<= constant(32-5)                                        ' prep for msb output
      repeat 5                                                      ' send mux bits
        outa[mosi] := mux <-= 1                                     ' output a bit
        outa[clk] := 1                                              ' clock the bit
        outa[clk] := 0
    
      repeat 13                                                     ' null + 12 data bits
        outa[clk] := 1                                              ' clock a bit
        outa[clk] := 0
        level := (level << 1) | ina[miso]                           ' input data bit
    
      outa[cs] := 1                                                 ' de-activate adc
    
      return (level & $FFF)
    

    Note that the proper calculation of mux truncates unused bits that your mux := 24 + ch does not. And, FWIW, even if you did this (which would work):

      mux := 24 + (ch & %111)
    

    ...it's not as obvious to newcomers using your code what's going on. Code should always be easy to understand. The compiler sees 24 and %11000 as the same thing, but using binary notation and named constants will help new people when the look up the mux bits

  • AndyPropAndyProp Posts: 60
    edited 2021-10-23 06:47

    @avsa242 said:
    Andy,

    For block code, use three backticks on a line by itself (before and after the code)

    I tried that, but actually it does not always appear to work, maybe a firefox thing I get smiley faces instead.

    It took

    @JonnyMac said:

    I also to make the code less complex set the mux with a simple addition 24 to the value of the channel

    Really? This is the output I get doing the proper mux calculation (2nd column) vs your "less complex" version (3rd column).

     0  11000  11000
     1  11001  11001
     2  11010  11010
     3  11011  11011
     4  11100  11100
     5  11101  11101
     6  11110  11110
     7  11111  11111
     8  11000  00000
     9  11001  00001
    10  11010  00010
    11  11011  00011
    12  11100  00100
    13  11101  00101
    14  11110  00110
    15  11111  00111
    16  11000  01000
    17  11001  01001
    18  11010  01010
    19  11011  01011
    20  11100  01100
    21  11101  01101
    22  11110  01110
    23  11111  01111
    

    Good luck with your project. Please remember to move my name from anything your derived object.

    Thank you for the input.

    In the end I will just have an object that loads the values from the ADC's into arrays, it is all I want to do with it.
    Like GetADCValues, how I share the array values to other cogs I have no idea yet but will find how to do that too. This is what I was doing for a few minutes:

    Fader := 0
         repeat cs from 1 to 3
              repeat channel from 0 to 7
               pst.Str(String("Fader="))
               pst.Dec(Fader)
               pst.Str(String(" CS="))
               pst.Dec(CS)
               pst.Str(String(" Channel="))
               pst.Dec(channel)
               pst.Str(String(" MUX="))
               mux :=channel +24
               pst.Bin(mux, 5)
               Fader := Fader + 1
               ' Read from ADC and store the result in the Fader array
               pst.Char(pst#NL)
    
              pst.Char(pst#NL)
    
    

    Fader=0 CS=1 Channel=0 MUX=11000
    Fader=1 CS=1 Channel=1 MUX=11001
    Fader=2 CS=1 Channel=2 MUX=11010
    Fader=3 CS=1 Channel=3 MUX=11011
    Fader=4 CS=1 Channel=4 MUX=11100
    Fader=5 CS=1 Channel=5 MUX=11101
    Fader=6 CS=1 Channel=6 MUX=11110
    Fader=7 CS=1 Channel=7 MUX=11111

    Fader=8 CS=2 Channel=0 MUX=11000
    Fader=9 CS=2 Channel=1 MUX=11001
    Fader=10 CS=2 Channel=2 MUX=11010
    Fader=11 CS=2 Channel=3 MUX=11011
    Fader=12 CS=2 Channel=4 MUX=11100
    Fader=13 CS=2 Channel=5 MUX=11101
    Fader=14 CS=2 Channel=6 MUX=11110
    Fader=15 CS=2 Channel=7 MUX=11111

    Fader=16 CS=3 Channel=0 MUX=11000
    Fader=17 CS=3 Channel=1 MUX=11001
    Fader=18 CS=3 Channel=2 MUX=11010
    Fader=19 CS=3 Channel=3 MUX=11011
    Fader=20 CS=3 Channel=4 MUX=11100
    Fader=21 CS=3 Channel=5 MUX=11101
    Fader=22 CS=3 Channel=6 MUX=11110
    Fader=23 CS=3 Channel=7 MUX=11111

Sign In or Register to comment.