Shop OBEX P1 Docs P2 Docs Learn Events
C & GAS SPI library — Parallax Forums

C & GAS SPI library

DavidZemonDavidZemon Posts: 2,973
edited 2013-04-07 21:13 in Propeller 1
After some initial Googling, I was unable to find a SPI library written in C/GAS. So, I've begun writing my own. I used obex #433 (SPI Spin - Serial Peripheral Interface) for my inspiration and it is largely compatible with it (constants like MSB_FIRST are the same and, for the most part, parameters remain the same).

My goal with this library is to have a good compromise between fast and safe. I think I've done a good job with that by allowing the programmer to choose between fast and safe and it's made debugging the code much easier.

You can find the SPI code (spi.c, spi.h, and spi_as.S) in my git repository here. The driver program is an ongoing attempt to make an SD library and is not yet stable.

I am unsure of the robustness at this point, but I have been able to (sometimes) get the first R1 response from the SD card which makes me think the SPI library might be working correctly. My next step is to switch to inline assembly stored in FCACHE.

Thoughts and comments are welcome! I'd love to get some criticism on the code so I can make it even better!

David

Comments

  • jazzedjazzed Posts: 11,803
    edited 2013-03-25 07:49
    Looking good so far. Sorry I on't have enough time to dig deep enough to find any suggestions.
  • SRLMSRLM Posts: 5,045
    edited 2013-03-25 19:53
    I aim to have SPIShiftOut() completed by the end of tomorrow or maybe Tuesday. Unfortunately... the "easiest" way for me to test this thing is with an SD card (I don't have many devices lying around), and I have to write that library too so it could take me a while to verify the robustness of the code.

    You may want to buy a simple SPI device to test it out with, and go from there. If you really do want to use the SD card, perhaps the easiest option would be to convert FSRW, and replace safe_spi with your version of the SPI. Then, you don't have to worry about the FAT stuff.
  • kuronekokuroneko Posts: 3,623
    edited 2013-03-25 20:09
    There are spikes in the data when a 1 remains a 1 though, and I'm not sure if that will cause trouble on the receiving side.
    Regardless, don't generate spikes then. Replace
    and temp, #BIT_0                        
            shl temp, mosiPinNum                    
                                
            andn outa, mosi                         
            or outa, temp                           
    
    with something like this (or at least prepare the new outa value in a temporary and have one final update):
    test temp, #BIT_0 wc                    
            muxc outa, mosi                         
    
  • DavidZemonDavidZemon Posts: 2,973
    edited 2013-03-25 20:15
    Thanks! I didn't know about the mux commands. That will work much better!
  • DavidZemonDavidZemon Posts: 2,973
    edited 2013-03-25 22:26
    First attempt is ready to go. Oscilloscope won't do much to test the SPIShiftIn() function though so any help with that sure is appreciated.

    Also, the change suggested by kuroneko was implemented.

    If I can get the first R1 response from an SD card, I'll call v1.0 complete.
    S
    S
    c
    c
    5K
    h
    h
    2K
  • kuronekokuroneko Posts: 3,623
    edited 2013-03-25 23:02
    For reading you use 3x POST_CLOCK and 1x PRE_CLOCK. Copy+Paste? Bit collection is done more efficiently with rcl/rcr insns. Also, WRITE_DATA needs another look ...
  • DavidZemonDavidZemon Posts: 2,973
    edited 2013-03-25 23:30
    Thanks for those.

    Ah! I was looking for two instructions like rcr/rcl but apparently not hard enough. That'll do much nicer

    I'm going through SPIShiftIn() now trying to get it to at least run without errors. I definitely should have done that before posting tonight lol
  • kuronekokuroneko Posts: 3,623
    edited 2013-03-25 23:50
    Ah! I was looking for two instructions like rcr/rcl but apparently not hard enough. That'll do much nicer
    Forget the manual and grab the datasheet (rev 1.4, section 6.4).
  • DavidZemonDavidZemon Posts: 2,973
    edited 2013-03-26 00:00
    And I keep on learning. That's much better organized for someone who doesn't know what instruction (s)he is looking for.

    Also, I fixed the djnz instructions in READ. They all pointed back to msb_pre :/
  • SRLMSRLM Posts: 5,045
    edited 2013-03-26 01:29
    I think you should also get rid of the separate cog: PropGCC allows you to have inline PASM, so many of the old Spin-PASM programming models are old and outdated. In particular, with this library your main thread waits for the SPI cog to do it's work. That work might as well be done in the main thread: you can get the same speed, and you don't need to dedicate a cog.
  • DavidZemonDavidZemon Posts: 2,973
    edited 2013-03-26 01:34
    Calling it a night for now. Git repo here: https://github.com/SwimDude0614/PropGCC_SPI

    The status of the MISO pin seems to have no affect on the value being read (always reads 0, even when shorted to Vcc). Not sure what that's about yet but I'll continue poking around tomorrow.
  • DavidZemonDavidZemon Posts: 2,973
    edited 2013-03-26 01:38
    @SRLM: I started a new cog because my first attempt involved C only, and I saw large delays and inconsistencies. I assumed this was due to the LMM kernel not poor compilation. Certainly, running this in a single cog would make life a lot easier! If I go down that path though, how would I use assembly to access C variables?
  • SRLMSRLM Posts: 5,045
    edited 2013-03-26 01:42
    @SRLM: I started a new cog because my first attempt involved C only, and I saw large delays and inconsistencies. I assumed this was due to the LMM kernel not poor compilation. Certainly, running this in a single cog would make life a lot easier! If I go down that path though, how would I use assembly to access C variables?

    I'm glad you asked: I wrote a short tutorial on that here: https://sites.google.com/site/propellergcc/documentation/tutorials/inline-asm-basics

    To get the best timing, you should put the SPI loop in FCACHE.
  • kuronekokuroneko Posts: 3,623
    edited 2013-03-26 01:42
    Re: input, you should use test miso, ina wc not test ina, test wc. The latter works on the shadow register (default 0).
  • DavidZemonDavidZemon Posts: 2,973
    edited 2013-03-26 13:22
    @kuroneko: Making that change gave me the R1 response from the SD card, so I think that means I can call v1.0 complete. I don't know why though... brief googling on "shadow register" has given me a vague idea of what a shadow register is but not how it's used in the prop or even why it would make a difference.

    @SRLM: That's a very well written tutorial; thanks. I'm going to continue a bit more work on the SD side of things and then go back and make the changes you suggested with inline GAS and FCACHE.
  • DavidZemonDavidZemon Posts: 2,973
    edited 2013-03-26 23:55
    Well, spent most of today fighting with the code trying to get a valid response from CMD8. If anyone has ideas, I'm sure open to them! Code is now available in a git repo:
    https://github.com/SwimDude0614/PropGCC_SPI

    For a long while, I was getting 0x7F as the first-byte response to CMD8. Not sure when exactly it it changed, but now I'm just getting a timeout during the first-byte read.

    During the debugging I tried one specific suggestion: send CMD0 multiple times. This resulted in successful first read (same as always) but unsuccessful (timed out) second read every time.

    @SRLM: fsrw was actually where I started with this whole thing. I wanted to do exactly what you suggested and just replace safe_spi. Unfortunately, for the life of me I can not figure out where fsrw does it's initialization. I've looked at both mount_explicit() and init() and neither seems to do anything with SPI. Without knowing where the SPI calls are, I can't replace them. Or even use it as a code reference (until I get past the initialization).
  • DavidZemonDavidZemon Posts: 2,973
    edited 2013-03-28 13:15
    Wasn't expecting safe_spi to do the SD initialization... finally found it and working through it now.
  • DavidZemonDavidZemon Posts: 2,973
    edited 2013-03-31 01:39
    Didn't find anything in there that helped any.

    On the bright side, I hooked it up to a DAC and was able to (fix a bug and) confirm that SPIShiftOut() works correctly! I was able to create and measure a saw-tooth wave from the DAC so that's at least a small accomplishment there.

    Still odd things with the SD card though. I have an ADC that would, seemingly, be easier to test SPIShiftIn() with but I haven't been able to get it working and I don't know if it's because I burnt the chip a year ago or if my library is incorrect :(

    Git repository updated.
  • DavidZemonDavidZemon Posts: 2,973
    edited 2013-04-02 22:22
    SPI library works! :D
    Hooked it up to an ARM board running in slave mode and I'm able to send/receive at 1.5 MHz with debugging turned on and 1.9 MHz without debugging. The test program was simply
    char string[20] = "Hello world!\n";
    while (1)
      // send next character
      // receive same character back
      // print character to screen
    

    As soon as I increased speed to 1.91 MHz, it had a hissy-fit. This is probably something I can fix later but for now, I'm off to continue down the road of the SD card as my senior project relies on it.

    Thanks again for all the help debugging this thing! I would like opinions though - is this something worth submitting to the obex once I give it another once-over and finish up documentation?

    David
  • jazzedjazzed Posts: 11,803
    edited 2013-04-02 23:25
    SPI library works! :D

    Congrats!

    Maybe we can put what you have into the propeller-gcc demos.
  • DavidZemonDavidZemon Posts: 2,973
    edited 2013-04-02 23:31
    Thanks!

    I certainly don't mind, but the project should be re-written before that happens. The spi* files are okay but the driver needs to be cleaned up.
  • DavidZemonDavidZemon Posts: 2,973
    edited 2013-04-06 18:46
    @jazzed: I've re-built the git repository from scratch and included what I hope to be a decent driver program for both SPI and SD (separately this time). It's not quite perfect yet... there's some weird stuff going on because I'm using shared files without compiling the libraries as libraries. But, I've tried to put notes in the README files that explains it.

    I think, and I hope, it is all much better labeled and explained that previously.

    https://github.com/SwimDude0614/PropWare
  • jazzedjazzed Posts: 11,803
    edited 2013-04-06 19:12
    @jazzed: I've re-built the git repository from scratch and included what I hope to be a decent driver program for both SPI and SD (separately this time). It's not quite perfect yet... there's some weird stuff going on because I'm using shared files without compiling the libraries as libraries. But, I've tried to put notes in the README files that explains it.

    I think, and I hope, it is all much better labeled and explained that previously.

    https://github.com/SwimDude0614/PropWare

    Interesting work so far. I like your documentation style. You only need a few changes for Doxygen to make html pages.

    I noticed you mentioned private functions, but did not make them private by using the static keyword.

    Looks good, but I did not make a detailed study of all functions.

    Generally, I think the work is a very nice coding example.
  • DavidZemonDavidZemon Posts: 2,973
    edited 2013-04-07 21:13
    Re: Doxygen
    I'm glad you like it - making readable code is something I've been working very hard on this past year, and not something we are taught in school. I picked up on the format by reading APIs like TI's StellarisWare (henceforth, "PropWare"). Didn't realize it was part of an official standard that could auto-generate html. I'll have to look up the rules later and tweak them to make it work.

    Re: Private Functions
    I also didn't realize there was an official way to make private functions without classes. I was just told at the beginning of this semester about the concept of "private" functions in C by placing the prototypes in the source file instead of header.
Sign In or Register to comment.