Shop OBEX P1 Docs P2 Docs Learn Events
Spin Programming Questions — Parallax Forums

Spin Programming Questions

NASA Robotics TeamNASA Robotics Team Posts: 24
edited 2008-07-19 00:59 in Propeller 1
Ok so I come from a C background in programming and I'm finally getting familiar with the Spin language, but there are a couple of things that I have not been able to figure out. Hopefully someone out there has run into these problems and can steer me in the right direction. I'll put the C code that I'm trying to emulate and hopefully you can see what I mean in the statements. I do have the Spin programming book, but I have not been able to find help in it or on the web. Here goes nothing!

1) Are there user defined structures in Spin? I was trying to create a data type called robot that would contain the X and Y position, heading, speed, etc. In C I would have used a structure, but I haven't found an equivalent for Spin.
typedef struct {
    int x;
    int y;
    int heading;
  } robot_t;



2) When declaring variables (VAR section), I would like to use constants (CON section) to declare array sizes. The compiler won't let me use anything but literal numbers.
#define MAX_SIZE 10
  int array[noparse][[/noparse]MAX_SIZE];



3) 2D arrays... are they supported?
int array[noparse][[/noparse]10][noparse][[/noparse]12];



4) This last one is giving me the run around. I'm trying to communication to a SICK LADAR using serial. I have used the FullDuplexSerial, ExtendedFullDuplexSerial, SimpleSerial, and SimpleDebug. We haven't run into any problems transmitting data, but we're running into some problems using the receive. We can rx less than 16 bytes fine, even over multiple successive messages, however the message that contains the distance data from the LADAR is 100+ bytes long. We have tried using all of the spin objects from the exchange and have not been able to get the data after 16 or so bytes. I even tried the ExtendedFullDuplexSerial and someone else's modified one that allowed for 256 bytes, neither worked. We know the data we are supposed to be seeing and they are not matching. Has anyone run into problems rxing many bytes of data?

Thanks for all of the help,
NASA_Robotics (Josh)

Comments

  • stevenmess2004stevenmess2004 Posts: 1,102
    edited 2008-06-26 02:00
    1) no user defined structures. The closest you will get is using objects, passing the address and using an enumeration something like this
    'in the object that we are using for a structure
    PUB getAddress
      return @var1
    
    CON
      #0 var1,var2,var3...
    VAR
      long var1,var2,var3
    
    


    obviously you will have to use different names. Also, you would have to put them in the order long, word, byte. You would then use it something like this
    OBJ
      struct : "myStructObject"
    VAR
      word structBase
    
    PUB doSomething
      long[noparse][[/noparse]structBase][noparse][[/noparse]struct#var1]:=someValue
    



    2) Should work but you will need to put the array in a VAR section like this
    CON 
      MAX_SIZE=10
    
    VAR
      long array[noparse][[/noparse]MAX_SIZE]
    
    



    3) Nope. But its not that hard to do it just using a single array.

    4) Could be that the buffer isn't big enough. What speed is the data stream and how long does it take you to use the data? I think the buffer is only 16 bytes long for fullDuplexSerial
  • TimmooreTimmoore Posts: 1,031
    edited 2008-06-26 02:03
    2. works
    CON
    SIZE = 10
    VAR
    word array[noparse][[/noparse]SIZE]
    3. No but creat an array 10x12 and index correctly e.g. array[noparse][[/noparse]x*10+y]
    4. Some things to look at:
    The rx routines will overwrite the receive buffer if you dont read from the rx buffer fast enough, so make sure you are reading quickly when testing i.e. reading and writing to PC is a problem if the pc speed is slower than the ladar speed.
    Does the ladar support cts/rts flow control, there is a version of the serial driver that supports flow control (at least cts is tested), this might help if the ladar is sending a lot very quickly and overwriting the receive buffer
    I have found the serial routines dont have the same tolerence for baud rates that most hardware uarts have. i.e. I have a serial camera that works to a PC at 115200 but with prop the baud rate is 116500 (at 115200 is close but not every char is correct but some are) so try a few baud rates around the one the ladar uses
  • Mike GreenMike Green Posts: 23,101
    edited 2008-06-26 02:16
    The FullDuplexSerial routine that's part of DongleBasic (from the Object Exchange) can be compiled for any buffer size that's a power of 2 and the receive and transmit buffers can be different sizes. FullDuplexSerial can handle Bauds of at least 230K. If the buffer is large enough and you read from it in a timely way, you should have no problems.
  • NASA Robotics TeamNASA Robotics Team Posts: 24
    edited 2008-06-26 16:16
    Thanks for the help.

    1) I'm going to try using the objects as a structure and see how that turns out.
    2) I don't why that didn't work when I tried it. Went over and over the code and couldn't figure out why I couldn't use the CON defines as array sizes, but it's working now. Hmm.... go figure [noparse]:)[/noparse]
    3) I was hoping that 2D arrays were supported ... just makes reading the code a bit easier to read.
    4) We're still at odds about the serial data. Going to try the suggestions and see if we can make any headway.

    Thanks guys!!!
  • RaymanRayman Posts: 14,233
    edited 2008-06-26 16:29
    Or, you could try the new Imagecraft C compiler!
  • jazzedjazzed Posts: 11,803
    edited 2008-06-26 17:59
    Too bad spin doesn't have a way to declare an object variable public and accessible via an objname.var mechanism. But that wouldn't be normal usage of an oop language data considering encapsulation ... but that doesn't seem as important in the small uC world relative to conserving memory size.

    Rayman's suggestion is worth a look [noparse]:)[/noparse]

    Using C may help solve the packet problem if the corruption is a function of speed in getting data off the hopper ... you could also write the interface in Propeller asm. It is quite easy to make a .spin assembly driver work with C, and a FullDuplexSerial example already exists.

    Look this post for·FullDuplexSerial·C code: http://forums.parallax.com/showthread.php?p=727942


    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
  • NASA Robotics TeamNASA Robotics Team Posts: 24
    edited 2008-06-30 18:53
    We looked into changing the baud rate and found that 9150 worked for getting the data. I have no clue why the baud rate would be so far off of 9600. We checked the clkfreq and other init variables and they all appear correct. Still scratching my head about that one.

    While we're at it. Here's another one:
    I'm trying to compute the heading of the robot based off of (X,Y) position data. Since we're simulating navigation on the moon, a compass won't help and we can't use GPS. To compute the angle I need to use an inverse trig function (ie: arccos, arcsin, etc). Is there anything in Spin that can compute this? I'm looking through the Math Lib but nothing stuck out at me. Is there other ways of computing an angle using distances that the Prop could handle?

    [noparse][[/noparse]edit]
    Is is possible to have a common header file between all of the objects? I have a bunch of CON defines that I would like to only change in one place.

    Post Edited (NASA Robotics Team) : 6/30/2008 7:14:21 PM GMT
  • TimmooreTimmoore Posts: 1,031
    edited 2008-06-30 20:08
    About the baud rate. You will find a lot of devices do not send at exactly the baud rate. They normally have a crystal that is divided to get the baud rate, often an exact division can't be done so you get a few % off. Yours is 5% which is normally acceptable. FullDuplexSerial because of how it works is not very tolerant of baud rate error. The way it works is it looks for the edge of the start bit and then times to the middle of each bit by taking the baud rate and calculating when the middle should be. Any errors between the actual baud rate and the set baud rate accumulate over the 8 bits. If too far off you may read early or late for the last bit and get the wrong value. The 2nd source of timing error is that the driver is handling both tx and rx, if it is handling tx when the start bit arrives, it may be late seeing the start of the start bit, so its timing to the middle of the bits may actually be later in the bit making the 1st timing problem worse.
  • stevenmess2004stevenmess2004 Posts: 1,102
    edited 2008-07-01 11:50
    Float32 has trig functions in it.
  • agodwinagodwin Posts: 72
    edited 2008-07-01 16:10
    I've got a similar problem with global definitions : I have objects controlling a number of actuators, and a comms object modifying their parameters. I would expect to implement that using a global data object containing all the variables and have the various comms and actuator objects update it.

    This maps nicely to the propellor hardware architecture, but doesn't seem to suit the object encapsulation at all well, as the central object will be re-instantiated by any callers rather than sharing the data. The solution in the FAQ thread (put the data in DAT) doesn't really help as it makes more sense to hold a large data item in the

    Am I structuring it the wrong way ? I'm a C programmer really .. and yes, I might be better off using iccv7 but I'd like to get some understanding of the native environment before I try something else.

    -adrian
  • TimmooreTimmoore Posts: 1,031
    edited 2008-07-01 16:17
    define an object - i.e. a spin file, define the "globals" in the file, add a bunch of spin pub methods to get and set the variables. You can define the constants in for the variables in the same file sine you can do OBJNAME#CONSTANT

    CON
    val =1
    var
    long avariable
    pub get
    return avariable
    pub set(p)
    avariable := p
  • agodwinagodwin Posts: 72
    edited 2008-07-02 09:05
    Yes, that sounds like what I want.

    But I'd need to include that file at the top level (receiving updates from the comms object) and also in some of the device driver objects. Wouldn't that result in several instances of the data object in VAR space ? And if I put it in DAT space, I'll be limited to the RAM on a cog, I think. Is that right ?

    I could include it just once at the top level and have the code there shuttling data from comms object to data and out to devices .. which sounds fine, except that there are a lot of operations to perform and I think the top level object will become large and difficult to maintain - I'd prefer to split it into an update object for each of the devices. I guess that means I need to embed the data into each device.
  • TimmooreTimmoore Posts: 1,031
    edited 2008-07-02 16:19
    I would have many objects, collect the variables that are for the same subject within 1 object. I tend to dedicate 1 cog to background services such has this data copying rather than have modified versions of each driver that know where to push the data to. Many times I have a driver that talks to the device and gets data, all apps use this in the same way but the processing of this data changes. So I tend to put this processing in a separate object from the driver and have a background cog that gets data from the driver and pushes it into the processing object. Often that processing object doesnt need a COG it does everything on the background cog.
  • NASA Robotics TeamNASA Robotics Team Posts: 24
    edited 2008-07-02 16:36
    Thanks Steve!! My programming was held up because I couldn't compute the heading of the robot. Using the ACos requires the Float32Full library, which in turn uses 2 cogs. Is is possible to erase functionality I don't need and have a modified Float32 running on one cog? Or does ACos mathematically require 2 cogs for computing solutions?

    Tim, we came to the same type of solution. Having one cog be more or less the scheduler/data transporter and the other cogs doing continuous processing on the information. So far that seems to working out good for us. Thanks!

    Post Edited (NASA Robotics Team) : 7/2/2008 4:42:15 PM GMT
  • Paul BakerPaul Baker Posts: 6,351
    edited 2008-07-02 16:54
    @Agodwin, placing data in a DAT doesn't mean it's part of the cog memory. It only becomes part of the cog memory when a cognew/coginit is executed, data declared in DAT always resides in hub memory, so you can share it between objects.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Paul Baker
    Propeller Applications Engineer

    Parallax, Inc.
  • agodwinagodwin Posts: 72
    edited 2008-07-03 09:19
    Thanks,Paul - I hadn't appreciated that.
  • stevenmess2004stevenmess2004 Posts: 1,102
    edited 2008-07-05 11:42
    NASA Robotics Team said...
    Thanks Steve!! My programming was held up because I couldn't compute the heading of the robot. Using the ACos requires the Float32Full library, which in turn uses 2 cogs. Is is possible to erase functionality I don't need and have a modified Float32 running on one cog? Or does ACos mathematically require 2 cogs for computing solutions?

    Tim, we came to the same type of solution. Having one cog be more or less the scheduler/data transporter and the other cogs doing continuous processing on the information. So far that seems to working out good for us. Thanks!

    You should be able to cut float32 down so that you only need one cog. You can even copy just the code you need if you are using pasm. There is info in the pdf about the different float libraries (it should be in your library folder)
  • NASA Robotics TeamNASA Robotics Team Posts: 24
    edited 2008-07-10 18:31
    I was able to grab the functions that I needed and put them into one cog. Woohoo! Now there is the problem of not enough cogs for us to run all of the systems that we need. We're combining objects together and running them on less cogs. I would like to know if you can have Assembly programming call out and execute a Spin function then return to the Assembly code? I tried using some of the jmp and call functions, but I was unable to get the program to compile.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    http://www.youtube.com/user/NASAROBOTICS
  • jazzedjazzed Posts: 11,803
    edited 2008-07-10 18:45
    NASA Robotics Team said...
    ...·I would like to know if you can have Assembly programming call out and execute a Spin function then return to the Assembly code? ...
    Here is an example of calling spin from asm. You don't need the LMM section; just consider the print routines and interface to the "VgaTextProxy.spin" code.


    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
  • NASA Robotics TeamNASA Robotics Team Posts: 24
    edited 2008-07-10 19:46
    PUB main
      vga.start(16)
      vgaptr := vga.getCmdPtr
      cognew(@lmm, @MainEntry)
    
    CON     ' LMM user strings
    DAT
    hellostr      byte      "Hello World",0
    onechar       byte      $55
    
    CON     ' LMM User Code
    DAT
    MainEntry     mov       dira, #1
                  jmp       #lmmCall
                  long      @vgaInit                'initialize vga proxy pointers
    
                  jmp       #lmmGetHubPtr
                  long      p1
                  long      @onechar                'onechar ptr
    
                  mov       p2, #4
                  jmp       #lmmCall
                  long      @putx                   'print address of onechar ptr
    
                  rdbyte    p1, p1                  'read char for putc 
                  jmp       #lmmCall
                  long      @putc                   'put onechar
    
    


    This is the beginning section of code from the LmmPrint.spin. I see that you send cognew the AsmAddress of 'lmm' to begin execution, but I don't understand how you can pass a Parameter of '@MainEntry'. You get the address of MainEntry, but it's assembly code. I'm confused as to how the assembly code is calling the Spin code through the '@onechar', '@putx', '@putc', etc functions.

    I guess before you answer that one. VGATextProxy is watching 'vpcmd' and executing commands based off of that value. I see that this works using two cogs and a shared variable, but I need to be able to do this on one cog. Here's a bit of pseudo-code for what I'm trying to do:
    PUB start
      repeat
        GetDataUsingSpin
        GetDataUsingAssembly
    
    PRI GetDataUsingSpin
      <a bunch of spin code>
    
    DAT
    GetDataUsingAssembly
      <assembly code>
    
    



    How can you get the assembly to return back to the Spin code in 'start'? Or vise versa?

    Thanks,
    Josh

    <Edit> : I'm looking through some of the libraries and I think one of the SPI objects I'm using might have what I am looking for. Hmm....

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    http://www.youtube.com/user/NASAROBOTICS

    Post Edited (NASA Robotics Team) : 7/10/2008 7:56:22 PM GMT
  • jazzedjazzed Posts: 11,803
    edited 2008-07-10 20:01
    Well you could include the loop waiting for commands from asm in the same file that started the asm code without starting another cog. Of course, the example actually uses 3 cogs because of the VGA start, but could be cut to 2.

    The example I gave complicates matters a little. Like I said ignore the LMM stuff. I should have just provided a straight asm example; sorry about that. You should replace the "jmp #LmmCall" with call #subroutine and "jmp #LmmReturn" with subroutine_ret.

    You can change the start routine to directly start the asm cog with the MainEntry as "cognew(@MainEntry, @SomeParameterTable)" ... SomeParameterTable could be a variable or array with control/status info. The "par" variable is used to get SomeParameterTable data to cog memory.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
  • Mike GreenMike Green Posts: 23,101
    edited 2008-07-10 20:09
    The LMM kernel is an interpreter. It doesn't really execute native Propeller instructions (sort of), so you can't use the same rules you'd use for "real" assembly. Read the original thread on LMM for a discussion of how it works (http://forums.parallax.com/showthread.php?p=615022).

    You can't return to Spin from assembly code. COGNEW or COGINIT forks a new processor (execution thread), then returns as soon as the new processor is set up. After the COGNEW, you can put code to communicate with the new processor through shared variables and you can wait until the new processor has done something, but this is not a return. You've essentially got several independent computers in the same room and they're connected to a network and can communicate back and forth (through shared variables or I/O pins). The 2nd processor can't "return". It can only do stuff or turn itself off. The Spin interpreter happens to be a program that's stored in ROM. An assembly program can load and execute a copy of the Spin interpreter either on top of itself or it can fork a new processor to do it. The instruction that does this has an operand that points to some tables in shared memory that define the Spin program to be executed. This is not a return. You're starting a new processor on a new program.
  • jazzedjazzed Posts: 11,803
    edited 2008-07-19 00:59
    Curious. Did you ever get your asm to interact with spin for printing debug info ?

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Sign In or Register to comment.