Shop OBEX P1 Docs P2 Docs Learn Events
Where to begin? — Parallax Forums

Where to begin?

pgbpsupgbpsu Posts: 460
edited 2014-05-02 18:46 in Propeller 1
I'm trying to convert a project from Spin/PASM into C and I can't figure out where to begin. Mike Green's SRAM driver (the one written for the Parallax memory card) is one of the objects in my project so I thought that seems like a good place to start. His code uses PASM to do the transfers back and forth to SRAM but has a spin interface for it. If I'm going to have any chance at converting this whole project to C, this is something I'll need to understand.

Where does one even begin?

I started with spin2cpp, but that fails with a syntax error on Mike's Demo code. spin2cpp might be a really nice way to deal with some of these issue, but I want to understand what's going on, not have some black box do it all for me.

I also found this example:
http://forums.parallax.com/showthread.php/138033-Converting-Spin-PASM-Object

Which is helpful, but it doesn't explain what's going on. It also doesn't address a specific need. Mike declares and populates some PASM variables before launching the cog. That doesn't show up in this example so I don't know how this should be handled.

I've worked around the edges of PropGCC a bit and I can get basic stuff to work, but now I need to figure out how to get an entire project ported over- one that includes SRAM, my own ADC PASM code, counters, etc.

Have I missed the obvious tutorials/examples?

Thanks,
Peter

Comments

  • jazzedjazzed Posts: 11,803
    edited 2014-04-30 16:31
    pgbpsu wrote: »
    Where does one even begin?

    How much C or other programming experience do you have?
    pgbpsu wrote: »
    Have I missed the obvious tutorials/examples?

    Have you gone through the Parallax Learn tutorials?

    Learn Parallax PropellerC: http://learn.parallax.com/propeller-c
  • RossHRossH Posts: 5,504
    edited 2014-04-30 23:54
    Hi Peter,

    If you want to use GCC particularly, then jazzed has pointed you in the right direction.

    But if just want to be able to use the Propeller Memory Card from C, then Catalina has support for that card "out of the box" - which means one less driver you will need to worry about.

    What are the other PASM objects you need to convert? Maybe Catalina already supports some of those as well.

    Ross.
  • pgbpsupgbpsu Posts: 460
    edited 2014-05-01 06:20
    Hi Jazzed, thanks for taking the time to respond. I know you've been one of the main drivers of PropGCC. I appreciate your efforts on that and trying to help folks get started with it.
    jazzed wrote: »
    How much C or other programming experience do you have?
    I'm quite comfortable with SPIN/PASM and I've done a bit of non-embedded stuff in C.

    jazzed wrote: »
    Have you gone through the Parallax Learn tutorials?
    Yes. They are fine for learning the basics, but I think/hope I'm past that. I'm looking for instructions/examples on how to build or reuse PASM code into a C project. So any links/threads you could point me to which discuss building a PASM routine for use with C would really help.

    p
  • mindrobotsmindrobots Posts: 6,506
    edited 2014-05-01 06:47
    Maybe this will help?

    A thread about converting a Spin/PASM object to a C/PASM usable "library".

    It's a good example for taking a standard OBEX object and replacing the SPIN wrapper with a C wrapper and using the same underlying PASM code.
  • DavidZemonDavidZemon Posts: 2,973
    edited 2014-05-01 06:49
    Perhaps you can find help in the following examples. My knowledge only includes PropGCC - though I'm sure Catalina can do similar/equivalent/identical things.

    C/C++ introduces the ability for inline assembly, which is a nice change from Spin. The PropGCC source has lots of examples of inline assembly (search for __asm__), PropWare has a new example in UART (I used PropGCC and libpropeller as examples to get me going) and libpropeller has some of the best examples of inline assembly (written by SRLM).

    However, I know you were probably asking about an entire module - not something inlinable. That's where I started too - with SPI. Check out PropWare/spi.h, PropWare/spi.cpp, and PropWare/spi_as.S in the PropWare repository. If C++ isn't your thing, here's a link to v1.2 which is the last C release before I switched to C++.
  • pgbpsupgbpsu Posts: 460
    edited 2014-05-01 06:58
    Hi Ross,

    I don't much care what version of C for the prop I use. I've been instructed by the boss to convert this project to C. I can't post the project, but I've used a few objects from the OBEX and written a few of my own. The project has several devices in it-
    24-bit ADC MAX11040K - I have a PASM driver for this which I wrote which I believe will need to remain in PASM
    PGA MAX9939 - this is simple SPI and I can recode this in C
    a NewHaven OLED display that uses SPI - also something I can code in C
    a couple of I2C devices including the PCF8574 I2C expander
    Microchip 23LC1024 SRAM chip
    a couple of serial devices - one for debugging, and one for a GPS unit
    an SPI bus where the prop is the slave. This part is not in the current project so rather than converting existing code, I'll need to write this functionality in C/PASM from scratch.

    I've been using Mike Green's code for the SRAM chip and that's what I thought I'd start translating/folding into the new project. Data come from the ADC into the prop then onward to the linux system. If the linux system isn't ready I buffer them on the SRAM so I don't lose any data. In reality, every sample comes into the prop from the ADC, then goes to the SRAM and sits there until the linux system requests them.

    I doubt there are objects available for all these specific needs so I suspect I'll need to figure this out rather than use canned stuff. And I look forward to the challenge and learning something new. I'm just looking for an example, or "best practices" for incorporating PASM with C.

    Thanks,
    Peter
  • pgbpsupgbpsu Posts: 460
    edited 2014-05-01 07:08
    Hi mindrobots-

    I think that thread is the one I was was following earlier- see OP. It's a really great start. Maybe my original question should have been more pointed. In Mike Green's (and much of my PASM) DAT section variables are loaded prior to be launched into the PASM cog. Can that be done with C? The examples I've found so far, like the one you linked to, do all the set-up via mailboxes. I know how I would/could do these things when using SPIN and PASM. I suspect SPIN/PASM interact differently than C/PASM but haven't figured out in what ways.

    I've seen a number of your other examples and they've been very helpful. Especially this one:
    http://forums.parallax.com/showthread.php/135924-Updated-pasm_toggle-example

    Thanks for taking the time to post these things.
    p
  • DavidZemonDavidZemon Posts: 2,973
    edited 2014-05-01 07:08
    Peter,

    I'd love to help you out with the SPI slave side of things if you're willing to open-source that module. I haven't run across any implementation of the propeller as an SPI slave - I believe because of speed related issues.

    David
  • pgbpsupgbpsu Posts: 460
    edited 2014-05-01 07:09
    SwimDude-

    Thanks for the links. I did find PropWare yesterday in my searching but couldn't locate the specific modules that would help. I'll have a look right now! Thanks,
    p
  • DavidZemonDavidZemon Posts: 2,973
    edited 2014-05-01 07:11
    pgbpsu wrote: »
    In Mike Green's (and much of my PASM) DAT section variables are loaded prior to be launched into the PASM cog. Can that be done with C?

    Yep. I use C pre-processor macros at the top of the file for ease of reading and then reference those at the bottom, just like in PASM. See line 552 to 569 here.


    EDIT
    Make sure to build your assembly source with the "-xassembler-with-cpp" flag for preprocessor stuff to work correctly.
  • pgbpsupgbpsu Posts: 460
    edited 2014-05-01 07:21
    David-

    I've got no problem with the Slave side of things being open source and I'd love the help. Since that is a new part to this project I didn't figure on getting there until AFTER I'd converted the existing code so it could be a while until I'm ready or able to contribute there. For my particular project I figured on dedicating an entire cog to this. If I were to write it in SPIN/PASM I'd launch a cog in PASM to handle the read/writes and pass those data up to HUB for the top level program to deal with. I haven't figure out yet if that paradigm still works or how it's implemented using C.

    To start with and to keep things simple I was going to limit the options for CPOL and CPHA. I'm not sure yet which would be easiest, probably 0,0.

    p
  • DavidZemonDavidZemon Posts: 2,973
    edited 2014-05-01 07:30
    pgbpsu wrote: »
    David-

    I've got no problem with the Slave side of things being open source and I'd love the help. Since that is a new part to this project I didn't figure on getting there until AFTER I'd converted the existing code so it could be a while until I'm ready or able to contribute there. For my particular project I figured on dedicating an entire cog to this. If I were to write it in SPIN/PASM I'd launch a cog in PASM to handle the read/writes and pass those data up to HUB for the top level program to deal with. I haven't figure out yet if that paradigm still works or how it's implemented using C.

    To start with and to keep things simple I was going to limit the options for CPOL and CPHA. I'm not sure yet which would be easiest, probably 0,0.

    p

    I don't mind doing the extra work for it to allow any configuration. My original reason for starting PropWare was because I couldn't find any configurable SPI driver in C - I'm not about to create an unconfigurable one now :P. But speed is important - do you have a frequency you're aiming for? Are you okay with blocking calls or do you need a buffer so that when you call spi.read() it simply reads from the already existing buffer? If you need non-blocking calls and a buffer, my guess is you're looking at a dedicated cog for each of master and slave comm.
  • pgbpsupgbpsu Posts: 460
    edited 2014-05-01 07:32
    Yep. I use C pre-processor macros at the top of the file for ease of reading and then reference those at the bottom, just like in PASM. See line 552 to 569 here.
    It's clear I need to spend time looking carefully at PropWare. It looks like there's a lot to be learned here.

    However, doing this with a pre-processor fixes the assignments before to compilation right?

    Here's what an example from Mike's code:
    PUB Start(Clk, DIO, DO, CSflsh, CSsram, WP, HOLD) | WPMask, HoldMask
       Stop                                                 ' Stop any running I/O driver
       ClkMask     := (Clk <> -1) & |< Clk
       DIOMask     := (DIO <> -1) & |< DIO                  ' Initialize the I/O masks
       DOMask      := (DO  <> -1) & |< DO
       if Cog := cognew(@EntryPoint, @Params)                ' Return 0 if no cogs
          result := Cog                                      ' Default is unknown size flash
      return cog
    
    DAT
    ' PASM code here  
    ClkMask                 long    0                       ' Clock to flash
    DIOMask               long 0 
    DOMask                long 0
    
    

    In this case these PASM assignments aren't made until this section of code is called- at run time, right? I guess functionally there's no difference but conceptually those seem very different to me.

    p
  • DavidZemonDavidZemon Posts: 2,973
    edited 2014-05-01 07:42
    The pin assignments - you're correct. Pin assignments are not set in assembly until the high-level driver sets them during runtime. You could hardcode them in assembly if you'd like, but of course it would be silly to do such a thing in an OBEX object or API.
  • pgbpsupgbpsu Posts: 460
    edited 2014-05-01 07:45
    I'm looking for something in the neighborhood of 2.5Mbits/sec. In this project the ADC data are top priority. I can buffer several seconds of data on the SRAM chip, but it all needs to get moved over to the linux box through this SPI bus before the buffer fills. This 2.5Mbit/s should allow me to drain the buffer as long as linux can service this regularly. 98% of the data will flow from slave to master. The slave will have data ready to send to the master. When the master is ready and requests it the slave needs to be ready; for that reason I see a cog dedicated to this process. When commands are clocked into the slave they will get processed by the main/top level program but data flowing in that direction will be much less frequent.

    I've got a very specific application in mind and it's possible I'm not thinking of SPI master/slave as broadly as I should/could.

    I don't think I've got enough resources to have a cog for each master and slave.
    p
  • pgbpsupgbpsu Posts: 460
    edited 2014-05-01 07:50
    The pin assignments - you're correct. Pin assignments are not set in assembly until the high-level driver sets them during runtime.
    You're referring to the SPIN/PASM here right? How would you characterize what you're doing with the pre-processor? I'm just trying to understand the sequencing.

    I agree, hard coding would be bad for everyone.

    p
  • DavidZemonDavidZemon Posts: 2,973
    edited 2014-05-01 07:53
    pgbpsu wrote: »
    I'm looking for something in the neighborhood of 2.5Mbits/sec

    I don't believe a Propeller running at 80 MHz can do 2.5 MHz slave SPI communication. Master isn't a problem - I know the SD driver in PropGCC runs at 4 MHz (PropWare doesn't yet, but will as soon as I take the time to learn the counter modules better).
  • DavidZemonDavidZemon Posts: 2,973
    edited 2014-05-01 07:57
    pgbpsu wrote: »
    You're referring to the SPIN/PASM here right? How would you characterize what you're doing with the pre-processor? I'm just trying to understand the sequencing.

    I agree, hard coding would be bad for everyone.

    p

    My C/C++ driver also sets pin assignments during runtime. The pre-processor runs before compilation and does a lot of copy/paste for me. For example, BYTE_2 is a macro for 0x00ff0000 - that's the Quickstart's LEDs. So I have
    #define BYTE_2 0x00ff00000
    ....
    ....
    ....
    testLEDs                long    BYTE_2
    

    The pre-processor runs before anything else and turns that into
    ...
    ....
    ....
    ....
    testLEDs                long    0x00ff0000
    

    Therefore, testLEDs is initialized before run-time with the correct value.
  • pgbpsupgbpsu Posts: 460
    edited 2014-05-01 08:01
    Do you think 2.0Mbit/sec is possible? My board runs at 100Mhz which should provide some help. I've clearly got a lot to learn about the C side of things but maybe before doing that I'll code up what I can in SPIN/PASM so see what I can get out of the slave side. It may turn out to be some subset or very specialized driver just for this purpose.
  • DavidZemonDavidZemon Posts: 2,973
    edited 2014-05-01 08:27
    I'm not sure about 100 and 2.0. Maybe... When I get home I can start looking at some slave options. But my guess is you need something special for your purpose - and almost certainly you will have to hard code the frequency. And ignore the clock signal from the master
  • DavidZemonDavidZemon Posts: 2,973
    edited 2014-05-01 10:38
    Brainstorming here...

    A receive loop could look like this:
    mov data, #0
    mov loopIdx, bitCount
    waitpne csMask, csMask       // Wait for CS to go low
    
    // StartLoop
    shl data, #1          // Clear the way for the next bit of data
    test mosiMask, ina wz           // Check if MOSI is high or low
    muxnz data, #1          // Set next bit of the data
    djnz loopIdx, #StartLoop
    

    I can't remember the timing exactly on a waitxxx instruction. Your worst case scenario is going to be the first bit, when you have to wait on the chip select line. But assuming the worst for waitpne, that's only only <6 instructions. 100MHz would give you around 4.16 MHz with this. Granted - this is only a single data word. If you need to hold that speed for a variable number of data words, your max clock rate will quickly decrease.
  • pgbpsupgbpsu Posts: 460
    edited 2014-05-01 11:31
    I think that's about right. If I had no other choice, I could skip the CS line. This will be the only device on this bus, in which case all waitpeqs would be on the clock line which might save some time but to allow re-sysncing I'd really like to keep CS. I'm working on a SPIN/PASM test setup right now. Hopefully I can post those speed results soon.

    I imagine only spitting out 8-bit words in 512 byte chunks so that should be minimal overhead.
  • DavidZemonDavidZemon Posts: 2,973
    edited 2014-05-01 11:38
    pgbpsu wrote: »
    I think that's about right. If I had no other choice, I could skip the CS line. This will be the only device on this bus, in which case all waitpeqs would be on the clock line which might save some time but to allow re-sysncing I'd really like to keep CS. I'm working on a SPIN/PASM test setup right now. Hopefully I can post those speed results soon.

    I imagine only spitting out 8-bit words in 512 byte chunks so that should be minimal overhead.
    the bit-width of each word doesn't matter. The total byte-width of your chunks does matter. If you know you're always looking at 512 bytes, then make sure you reserve a block of 512 bytes in your assembly cog so that you can read an entire chunk before emptying it into HUB RAM.

    Do you have a logic analyzer? If you do, it's worth scoping your master and seeing how much of a pause (if any) exists between data words. The datasheet for your master might also tell you. If there's an extra pause, that's good - that will allow you to a few extra instructions to shuffle the data variable away to your cog RAM buffer and reset it for the next data word.
  • jazzedjazzed Posts: 11,803
    edited 2014-05-01 12:57
    I want to be helpful here. There is much more to this post so read on.

    But first, I have to say right now that a 2.0Mb/s SPI Slave is impossible with one COG ;-) The best you can do in conventional means is about 400Kb/s.

    Now, on to the helpful part ....

    New features for a PropellerGCC releases (currently release_1_0) are a bit strapped by the requirements of the Education program because Parallax is the primary customer.

    We cooperate with Parallax to bring their product to market. A benefit of the decision to use PropellerGCC is that it is a modern implementation of C that any new BSCS graduate will be able to use without issue. Evidence of this is the in the library efforts of SwimDude99 and SRLM.

    The open-source libraries SwimDude99 and SRLM provide for use with PropellerGCC are great values. Parallax provides the Learn libraries and I support that effort. I've also happily helped these other library efforts.

    The default branch of PropellerGCC is more advanced supports things like multi-cog XMMC and that PMC board, but it is still in alpha_1_9 state. Over the summer we will be conducting a formal test program with the default branch. We are also a bit hamstrung by Propeller2 completion.

    There are many things to learn here and many ways to do it. You can learn from all the C/C++ experts here including: me, Dave Hein, David Betz, ersmith, Heater, RossH, SRLM, SwimDude99, and others (not intentionally trying to exclude anyone ... sorry if I'm hurting anyone's feelings by not remembering to mention them).

    In PropellerGCC we were asked to provide freedom for the user to load and use COGs as users wish. This means there is not pre-defined structured interface. It also means that an interface to a COG is your responsibility.

    Generally speaking however, there are several projects that can be used to compare C code and Spin code. One example which you may find familiar is in using VGA. Back in 2009 I devised a method of extracting PASM code from a Spin file and creating an interface in C that was essentially the same as that for Spin by using a mailbox concept (the method built on my experience). The method is still in use today with PropellerGCC and Catalina although the mailbox structure is different.

    So, let's compare SPIN VGA_Text and C VGA_Text ? It won't be done all in one post ....

    I'll start a new topic for the discussion for Propeller-GCC there will be some differences for Catalina and RossH can cover those. Hopefully we can talk only about code there.

    Here are some topics on the comparison:

    1. Files and their roles.
    2. The API (Application Program Interface)
    3. Connecting C API code and Driver code.
    4. PASM Drivers and alternatives.
  • jazzedjazzed Posts: 11,803
    edited 2014-05-02 10:38
    @pgbpsu

    I've started a thread for you here. I'll add more later today.

    @SwimDude0614

    Seems I got your user name wrong in multiple places. Sorry about that.
  • DavidZemonDavidZemon Posts: 2,973
    edited 2014-05-02 11:18
    That thread looks awesome. I may go buy a board with VGA on it to start playing some :)
    jazzed wrote: »
    @SwimDude0614

    Seems I got your user name wrong in multiple places. Sorry about that.

    No problem. I'll probably get over it.
  • pgbpsupgbpsu Posts: 460
    edited 2014-05-02 18:33
    Hi Jazzed-

    I appreciate you willingness to provide examples. I hope others find it useful. I'm very much a 'learn by example' person so working through these and other examples will surely help educate me. SwimDude has pointed me to some sophisticated but very useful examples. I think by studying them I might get to the heart of my problem.

    I think my problems are two fold, I know the basics in C, but some of the more complicated stuff is still beyond me. So if C code isn't well commented it can take a long time for me to get through if ever. The second is I know how the things I want to do function in SPIN/PASM and I carry those notions over to C and want them to function the same way only with different syntax. And that's just not the case. Add to that the different uses/ways of setting up a project in C (I'm referring to memory models here) and things get further muddled.

    I'll watch the other thread carefully and hopefully learn what I need. Again, thanks for doing this.

    Peter
  • pgbpsupgbpsu Posts: 460
    edited 2014-05-02 18:46
    Here's a first crack at an SPI slave function written in PASM. I wrote a test harness and SPI master to help debug it. I've only done limited testing, but it seems to work. It's not very flexible, it's not very robust, but it does clock at about 2.3Mhz on my board which has a 6.25Mhz crystal. I'm open and interested in comments/critiques/improvements.
    '**************************************************************
    '* quick test program to see what kind of spi slave speeds can
    '* be reached for geopebble project.
    '*
    '*
    '**************************************************************
    CON
      _CLKMODE = XTAL1 + PLL16X
      _XINFREQ = 6_250_000
      '_XINFREQ = 5_000_000
    
                                                                               
    CON
      LOW               =  0
      HIGH              =  1
      INPUT             =  0
      OUTPUT            =  1
    
    CON ' constants for spi buffers
      BYTES_IN_BUFFER   = 512
      LONGS_IN_BUFFER   = BYTES_IN_BUFFER >> 2
      EMPTY             =   0
      NOT_EMPTY         =   1
      FULL              =   1
      NOT_FULL          =   0
    
      MASTER_CS           = 0
      MASTER_CLK          = 1
      MASTER_MISO         = 2
      MASTER_MOSI         = 3
      
      SLAVE_CS            = 0
      SLAVE_CLK           = 1
      SLAVE_SOMI          = 2
      SLAVE_SIMO          = 3
    
      TESTPT              = 27
           
    CON   ' UART constants
      DEBUG             =     0
      DEBUG_BAUD        = 38400
    
      SPACE             = 32
      DOT   = 46
      COLON = 58
      TAB   = 09 
             
    OBJ                                   
      UARTS    : "FullDuplexSerial4portPlus_0v3"       '1 COG for 3 serial ports
      NUM      : "Numbers"                             'Include Numbers object for writing numbers to debuginal
    
    VAR
      long outBufferEmpty, outBuffer[LONGS_IN_BUFFER]         
      long inBufferFull, inBuffer[LONGS_IN_BUFFER]                 
    
    PUB MAIN | idx, masterCogId, slaveCogId, serialCogId 
      SETUP_SERIAL
      UARTS.PUTC(DEBUG, CL)
      PAUSE_MS(1000)
      UARTS.STR(DEBUG,string(13, "Passing data through SPI master/slave routines."))
      slaveCogId := cognew(@SPI_SLAVE,@inBufferFull)        ' Start the SPI slave cog first
    
      outBufferEmpty := NOT_EMPTY
      PAUSE_MS(1000)
    
      masterCogId := cognew(@SPI_MASTER,@outBufferEmpty)        ' Start the SPI master cog
      UARTS.STR(DEBUG,string(13, "Populating output buffer."))
      repeat idx from 0 to LONGS_IN_BUFFER - 1
        outBuffer[idx] := LONGS_IN_BUFFER - idx
        if (idx//8==0)
          uarts.putc(DEBUG, CR)
        uarts.putc(DEBUG, TAB)
        uarts.dec(DEBUG, outBuffer[idx])
    
      UARTS.STR(DEBUG,string(13, "Transmitting data."))
      longfill(@inBuffer, 0, 128)                          ' init buffer where data will be stored
      outBufferEmpty := EMPTY
    
      repeat until inBufferFull == FULL
    
      UARTS.STR(DEBUG,string(13, "Received data."))
      repeat idx from 0 to LONGS_IN_BUFFER - 1
        if (idx//8==0)
          uarts.putc(DEBUG, CR)
        uarts.putc(DEBUG, TAB)
        uarts.dec(DEBUG, inBuffer[idx])
    
      
    
      repeat
        waitcnt(0)
        
    PUB SETUP_SERIAL
    '' method that sets up the serial ports
      NUM.INIT
      UARTS.INIT
      UARTS.ADDPORT(DEBUG,31,30,-1,-1,0,0,DEBUG_BAUD) 'Add DEBUG port
      UARTS.START
      return UARTS.getCogID                                            'Start the ports
    
    PUB PAUSE_MS(mS)
      waitcnt(clkfreq/1000 * mS + cnt)
    
    DAT
    SPI_MASTER        org            0
              mov     memptyPtr,     par                    ' pointer to outBufferEmpty
              mov     masterBasePtr, par                    ' pointer to buffer base
              add     masterBasePtr, #4
    
              call    #SETUP_MASTER_PINS                           ' lower CS line
    
    ' take care of a bit of book-keeping; (re)initializing things
    ' set pointer to beginning of outBuffer, reset 
              mov     mindex,         #LONGS_IN_BUFFER        ' keep track of how many blocks are full; init here
              mov     masterPtr,      masterBasePtr           ' get copy of the start of the buffer
    
    
    :waitForSpace     ' wait here until there told to continue
              rdlong  mtemp,        memptyPtr          wz   ' Z=1 if does emptyPtr -> 0
       if_nz  jmp     #:waitForSpace                        ' finished<>0 ->Z=0;   move on
    
    
              call    #LOWER_CS                             ' lower CS line
              wrlong  notEmpty,      memptyPtr 
    
    :masterLoop
              ' get next long to shift out
              rdlong  moutData,      masterPtr               ' write A0 sample to buffer
              add     masterPtr,     #4                      ' increment bufferPtr
              'mov     moutData,      mindex
              call    #WRITE_LONG
    
              djnz    mindex,        #:masterLoop
    
              call    #RAISE_CS                             ' raise CS line
    
    ' we've written all 128 longs; jump back up and wait
              jmp     #:waitForSpace
    
    :stop     waitpeq msclkMask,msclkMask
              jmp     #:stop          
    
    {******************************** WRITE_WORD *******************************************************
    This command will write the LOWEST 32 BITS of data MSB to the DATA line.  This routine does not touch CS at all.   SCLK is low on exit
    ***************************************************************************************************}
    WRITE_LONG
              mov     minData,      #0
              mov     mBits,        #32        wz   ' setup number of bits in write; set Z=0 
                                                    ' Z=0; Z flag is set (1) if Value equals zero
    :wlongLoop
              rol     moutData,     #1         wc    ' shift data left 1; C=bit shifted out
              muxc    outa,         mmosiMask        ' Set OUTA[mosiMask]=C
              muxnz   outa,         msclkMask        ' Rause clock Pin
              test    mmisoMask,    ina        wc   ' C=INA[misoMask]
              rcl     minData,      #1              ' shift C left into data
              nop                                    ' delay so slave can keep up
              xor     outa,         msclkMask        ' lower the clock line
              nop                                    ' delay so slave can keep up
              djnz    mBits,        #:wlongLoop      ' Decrement loop count.
              muxz    outa,         mmosiMask        ' make sure MOSI is low on exit
    
    WRITE_LONG_ret    ret
    
    
    {******************************** SETUP_PINS *******************************************************
    Short function to setup the SPI and test pins.  Only used once and it's really
    for cleaning up the code more than anything.
    ***************************************************************************************************}
    SETUP_MASTER_PINS
    ' Setup CS pin: Preset high; set to output
              mov     mtemp,         #1          wz         ' Z=0; put $01 into temp; Z=1 if #1=0
              muxnz   outa,          mcsMask                 ' Preset OUTA to high        "1"
              muxnz   dira,          mcsMask                 ' Set    DIRA to HIGH=OUTPUT "1"
    
    ' Setup SCLK pin: Preset high; set to output
              mov     mtemp,         #1          wz         ' Z=0; put $01 into temp; Z=1 if #1=0
              muxz    outa,          msclkMask               ' Preset OUTA to low         "0"
              muxnz   dira,          msclkMask               ' Set    DIRA to HIGH=OUTPUT "1"
    
    ' Setup MOSI pin: Preset low; set to output
              mov     mtemp,         #1          wz         ' Z=0; put $01 into temp; Z=1 if #1=0
              muxz    outa,          mmosiMask               ' Preset OUTA to low         "0"
              muxnz   dira,          mmosiMask               ' Set    DIRA to HIGH=OUTPUT "1"
    
    ' Setup TESTPT pin: Preset low; set to output
    '          mov     mtemp,         #1          wz         ' Z=0; put $01 into temp; Z=1 if #1=0
    '          muxz    outa,          mtestMask               ' Preset OUTA to low         "0"
    '          muxnz   dira,          mtestMask               ' Set    DIRA to HIGH=OUTPUT "1"
    
    SETUP_MASTER_PINS_ret    ret
    
    {******************************** LOWER_CS *********************************************************
    This command simply lowers the CS line.  Nothing else.
    ***************************************************************************************************}
    LOWER_CS
              cmp     mzero,        mzero        wz      ' set Z=1
              muxnz   outa,         mcsMask              ' set OUTA[csMask]=0
    LOWER_CS_ret      ret
    
    {******************************** RAISE_CS *********************************************************
    This command simply raises the the SCLK and CS line.  Nothing else.
    ***************************************************************************************************}
    RAISE_CS
              cmp     mzero,        mzero        wz  ' set Z=1
              muxz    outa,         msclkMask        ' set OUTA[clkMask]=1
              muxz    outa,         mcsMask          ' set OUTA[csMask]=1
    RAISE_CS_ret      ret
    
                                         
    {**************************************************************************************************
    ' Pin Masks
    ***************************************************************************************************}
    
    mcsMask      long            |< MASTER_CS               '
    msclkMask    long            |< MASTER_CLK
    mmisoMask    long            |< MASTER_MISO
    mmosiMask    long            |< MASTER_MOSI
    mzero        long            0
    notEmpty     long            NOT_EMPTY
    
    mtemp             res             1
    mBits             res             1
    mindex            res             1
    memptyPtr         res             1
    masterPtr         res             1
    masterBasePtr     res             1
    moutData          res             1
    minData           res             1
                     FIT 496    
    
    DAT
    SPI_SLAVE      org         0
              mov     sFullPtr,      par                    ' pointer to inBufferFull
              mov     slaveBasePtr,  par                    ' pointer to inBuffer
              add     slaveBasePtr,  #4
                                                            ' data we've collected.
    
              call    #SETUP_SLAVE_PINS                     ' setup pins
    
              mov     stemp,         #NOT_FULL
              wrlong  stemp,         sFullPtr
              
    :fallingCS  ' get ready for a new buffer full.  
              mov     sindex,        #LONGS_IN_BUFFER        ' keep track of how many blocks are full; init here
              mov     slavePtr,      slaveBasePtr           ' get copy of the start of the buffer
              call    #WAIT_FOR_CS                          ' wait here until CS falls
    
    :slaveLoop
              ' get ready to shift data in
              mov     soutData,      #0
              call    #READ_LONG
    
              'mov     sinData,       #4
              wrlong  sinData,       slavePtr              ' write A0 sample to buffer
              add     slavePtr,      #4                     ' increment bufferPtr
    
              djnz    sindex, #:slaveLoop
    
              mov     stemp,         #FULL
              wrlong  stemp,         sFullPtr
    
              jmp     #:fallingCS                           ' jump up and wait for next buffer
    
    
    :stop     waitpeq msclkMask,msclkMask
              jmp     #:stop          
    
    {******************************** READ_LONG *******************************************************
    Read 32 bits from master.  
    ***************************************************************************************************}
    READ_LONG
              mov     sBits,        #32        wz   ' setup number of bits in write; set Z=0 
                                                    ' Z=0; Z flag is set (1) if Value equals zero
              mov     sinData,      0               ' 
    
    :wslaveLoop
              waitpne ssclkMask,    ssclkMask        ' wait for CLK to go low
              rol     soutData,     #1         wc    ' get highest bit of outData
              muxc    outa,         somiMask         ' put bit onto MISO
              waitpeq ssclkMask,    ssclkMask        ' wait for CLK to go high
              test    simoMask,     ina        wc    ' get bit from MOSI
              muxc    outa,         stestMask        ' copy bit onto test pin
              rcl     sinData,      #1               ' shift C left into data
              djnz    sBits,        #:wslaveLoop     ' Decrement loop count.
    
    
    READ_LONG_ret    ret
    
    
    {******************************** SETUP_PINS *******************************************************
    Short function to setup the SPI and test pins.  Only used once and it's really
    for cleaning up the code more than anything.
    ***************************************************************************************************}
    SETUP_SLAVE_PINS
    
    ' Setup MISO pin: Preset low; set to output
              mov     stemp,         #1           wz         ' Z=0; put $01 into temp; Z=1 if #1=0
              muxz    outa,          somiMask                ' Preset OUTA to low         "0"
              muxnz   dira,          somiMask               ' Set    DIRA to HIGH=OUTPUT "1"
    
    
    ' Setup TESTPT pin: Preset low; set to output
              mov     mtemp,         #1          wz         ' Z=0; put $01 into temp; Z=1 if #1=0
              muxz    outa,          stestMask               ' Preset OUTA to low         "0"
              muxnz   dira,          stestMask               ' Set    DIRA to HIGH=OUTPUT "1"
    
    SETUP_SLAVE_PINS_ret    ret
    
    
    {******************************** WAIT_FOR_CS*****************************************************
    Wait here until CS falls
    ***************************************************************************************************}
    WAIT_FOR_CS
              waitpeq   scsMask, scsMask         ' wait for CS to go HIGH
              waitpne   scsMask, scsMask         ' wait for CS to go LOW
    
    WAIT_FOR_CS_ret  ret
    
    {**************************************************************************************************
    ' Pin Masks
    ***************************************************************************************************}
    
    scsMask      long            |< SLAVE_CS               '
    ssclkMask    long            |< SLAVE_CLK
    somiMask     long            |< SLAVE_SOMI
    simoMask     long            |< SLAVE_SIMO
    stestMask    long            |< TESTPT
    szero        long            0
    
    sFullPtr         res             1
    slaveBasePtr     res             1
    slavePtr         res             1
    stemp            res             1
    sBits            res             1
    sindex           res             1
    soutData         res             1
    sinData          res             1
                     FIT 496    
                     
    
Sign In or Register to comment.