Shop OBEX P1 Docs P2 Docs Learn Events
Global Variables shared between 2 cogs. — Parallax Forums

Global Variables shared between 2 cogs.

I am missing something here and I am not sure what it is. It is probably obvious, but not too me.

I am trying to read the raw gyro values from a lsm9ds1 module.
This code works and it all runs in a single cog.
#include "simpletools.h"
#include "lsm9ds1.h"

void Read_Gyro(void);

volatile int gX;
volatile int gY;
volatile int gZ;

int main() // Main function
{
imu_init(1, 2, 3, 4);

//cog_run(Read_Gyro, 256);

while(1)
{
Read_Gyro();

term_cmd(CLS);
print("gX = %d\r", gX);
print("gY = %d\r", gY);
print("gZ = %d\r", gZ);
pause(10);
}
}
Read_Gyro(){
imu_readGyro(&gX, &gY, &gZ);
}


When I try fire up a second cog to read the module, I display all 0's for gX, gY and gZ. I don't understand what is going on. According to the learn site in order to share global variables they need to be declared as volatile.

This code displays 0's.

#include "simpletools.h" // Include simple tools
#include "lsm9ds1.h"

void Read_Gyro(void);

volatile int gX;
volatile int gY;
volatile int gZ;

int main() // Main function
{
imu_init(1, 2, 3, 4);

cog_run(Read_Gyro, 256);

while(1)
{
term_cmd(CLS);
print("gX = %d\r", gX);
print("gY = %d\r", gY);
print("gZ = %d\r", gZ);
pause(10);
}
}
void Read_Gyro(){
while(1){
imu_readGyro(&gX, &gY, &gZ);
}
}

I would imagine I am missing something simple but I can't figure it out.

On a side note, I am not sure why when reading the IMU this is the proper call to the function.
imu_readGyro(&gX, &gY, &gZ);

I originally tried this and it does not work properly. This returns 0's.
imu_readGyro(gX, gY, gZ);

Thanks for the help!
Shawn A.
«1

Comments

  • Can you try the imu_init statement in the Read_Gyro function before the while?
    Tom
  • I have not C for the prop. But some ideas:

    Your are calling mu_init() on one cog and imu_readGyro() on another cog. Are you sure that this second cog knows which pins to use and are the dira bits set correctly by his cog?

    Your second point about having to call imu_readGyro() with addresses is simple. C uses call by value. So when you write imu_readGyro(gX, gY, gZ) this function gets only the values of these variables, but you want the function to to write into these vars. Therefor imu_readGyro(9 needs the addresses.
  • There are a few problems with your code.

    IMU_INIT() is called from the main thread. This causes those pins to be allocated to the main cog. Another cog can not use those pins.

    You are also reading the gyro as fast as possible and not waiting for the sensor to have updated information. zero is normal for this sensor.
    #include "lsm9ds1.h"
    #include "simpletools.h"
    
    void Read_Gyro(void *);
    
    #define SCL_PIN       1
    #define SDIO_PIN      2
    #define CS_AG_PIN     3
    #define CS_M_PIN      4
    
    volatile int Gx, Gy, Gz;
    
    int main()
    {
      
      cog_run(&Read_Gyro, 256);
     
      while(1)
      {
        printi("x=%d, y=%d, z=%d\n", Gx, Gy, Gz);
        pause(250);
        
      }  
    }
    
    void Read_Gyro(void *par)
    {
      int gx, gy, gz;
      int whoAmI;
      
      whoAmI = imu_init(SCL_PIN, SDIO_PIN, CS_AG_PIN, CS_M_PIN);
      
      imu_setGyroScale(245);
      
      while (1)
      {
        imu_readGyro(&gx, &gy, &gz);
        Gx = gx;
        Gy = gy;
        Gz = gz;
      }
    }
    

    Mike
  • twm47099 wrote: »
    Can you try the imu_init statement in the Read_Gyro function before the while?
    Tom
    Yeah, that's the main reason the program was displaying 0's. I moved the imu_init statement and it works.
    Thanks Tom


    rbehm wrote: »
    Your second point about having to call imu_readGyro() with addresses is simple. C uses call by value. So when you write imu_readGyro(gX, gY, gZ) this function gets only the values of these variables, but you want the function to to write into these vars. Therefor imu_readGyro(9 needs the addresses.

    The learn site says what you just said, but it didn't sink in properly until now. Sometimes I just have too hear things a certain way before my brain will pickup on it.
    Thanks rbehm

    @Mike
    Thanks for the example. I have a couple questions.

    1) Why is the " * " required in the declaration of the function Read_Gyro? I think it is a pointer.
    void Read_Gyro(void *);

    2) Why " *par " in the actual function Read_Gyro? Is par a variable that stores the address of where the function Read_Gyro actually starts in memory?
    void Read_Gyro(void *par)

    3)Why the " & " in the start new cog statement? It works without the " & ".
    cog_run(&Read_Gyro, 256);

    4) Is it necessary to use local variables to read the gyro data into and then load the data into global variables? What are the advantages to doing it this way?
    imu_readGyro(&gx, &gy, &gz);
    Gx = gx;
    Gy = gy;
    Gz = gz;
    Thanks for the help.


    Shawn A.





  • The definition of the cog function passes a pointer to the parameters but that function is not implemented so it really is a dummy item. You can't pass parameters to a cog function.

    Since the cog_run is calling your function you need to pass the address of the function. In the background it copies it into cog memory and then runs it.

    In the case of local variables in this case it is not necessary but in some cases you want the variables to be updated at the same time as they are running asynchronous to the main function.

    Mike

  • When you transfer data from one cog to another you can get in trouble if the data should be synchronized, such as x & y from a joystick.

    I wrote a program that collected joystick data from an android bluetooth app in one cog and used it to control an activitybot in an other cog. When I was developing the code I ran tests just printing the x & y data each loop. But I saw that the y value was not from the same data pair as the x value. It was from the previous pair.

    So in the bluetooth cog I collected x & y ascii digits, converted them to numbers in local variables (xval & yval) (9 bits each), then packed them together into one 32 bit integer global (joydata). The control cog unpacked the global maintaining the synchronization.

    The code in the bluetooth cog was:
    ...
    xval = (c1[2] - 48) * 100 + (c1[3] - 48) * 10 + (c1[4] - 48);   // make number from ascii digits
    yval = (c1[5] - 48) * 100 + (c1[6] - 48) * 10 + (c1[7] - 48);
    joydata=(xval << 10) | yval;
    ...
    

    (note in the bluetooth app the joystick values were from -100 to +100, but it added 200 to that value when sending so there was no need to have to deal with sign bits when sending or receiving. But since I wanted to use -100 to +100 I subtracted 200 from each value.)

    The code in the control cog was:
    ...
    xy = joydata;                      // transfer global with value from getbtjoy() to local
    y2 = (xy & 511) - 200;       // unpack y joystick value
    x2 = (xy >> 10) - 200;      // unpack x 
    ...
    

    The full program is developed in this thread:
    https://forums.parallax.com/discussion/156970/bluetooth-app-to-control-activitybot

    Hope this helps.
    Tom
  • I guess what your suppose to do is get a lock and use it to sync the other thread.

    I wrote an SBUS driver and used this to make sure that it had all the channels value converted before it would allow reading of the values. SBUS has 8 or 16 channel values
    int _Lock;
    
    
    _Lock = locknew();
    
    while (lockset(_Lock));  // wait for other thread
    // do updates now
    lockclr(_Lock);
    
    
    // In read thread
    while (lockset(_Lock)); // wait for other thread
    // read data now
    lockclr(_Lock);
    return value
    
    
    Mike
  • twm47099 wrote: »
    I wrote a program that collected joystick data from an android bluetooth app in one cog and used it to control an activitybot in an other cog. When I was developing the code I ran tests just printing the x & y data each loop. But I saw that the y value was not from the same data pair as the x value. It was from the previous pair.

    The full program is developed in this thread:
    https://forums.parallax.com/discussion/156970/bluetooth-app-to-control-activitybot

    Hope this helps.
    Tom

    That's a neat project Tom!



  • ShawnaShawna Posts: 508
    edited 2019-03-16 00:00
    I have read about locks before, but have never figured out how to use them, or if they really need to be used. If you look at the diagram of the prop, page 1.
    https://parallax.com/sites/default/files/downloads/P8X32A-Propeller-Datasheet-v1.4.0_0.pdf

    It looks like only 1 cog can access the hub at a time. If global variables are stored in the hub this would make me think that locks are not necessary. However, on page 8 the document says...................
    If a block of memory is to be used by two or more cogs at once and that block consists of more than one long (four bytes), the cogs will each have to perform multiple reads and writes to retrieve or update that memory block. This leads to the likely possibility of read/write contention on that memory block where one cog may be writing while another is reading, resulting in misreads and/or miswrites.
    To me this sounds like locks are important. Maybe this is why I have had problems in the past with programs with multiple cogs sharing variables.

    Anyways, I started playing with Locks in what I thought would be the most basic place to start and the results are strange.
    //Library Includes
    #include "simpletools.h" // Include simple tools

    int _Lock;

    int main(){

    _Lock = locknew();

    while(1)
    {
    lockset(_Lock);
    print("_Lock = %d\r", _Lock);

    pause(1000);

    lockclr(_Lock);
    print("_Lock = %d\r", _Lock);

    pause(1000);
    }
    }

    I expected to see the value of _Lock to change from 0 to 1 when displayed, or maybe even a -1. The only thing that is displayed is a 1. Not sure what i am missing here.

    In Mikes example above, I do not see where the lock is actually set. I see where it is cleared. From what I have read, when locknew() is called the initial state is random, or maybe that is when the lock is returned. I will have to try and find that again.


    Thanks
    Shawn A.
  • Cluso99Cluso99 Posts: 18,069
    I have never used locks because you can rely on the hub rotation. In most instances this is fine.

    If you are updating a group of bytes/words/longs in hub with one cog just use a hub position as a flag to indicate its done. Don't update that flag until its all copied in, and likewise when another cog takes it out, don't update the flag til its done.
  • As I understand them, locks are not physical locks. They're like flags that are set to indicate something. A flag can mean anything you want it to mean but they're usually used to indicate that the program is updating a series of variables in hub ram where each member of the set has to be updated before the data set is considered valid.
  • exactly
  • the only difference is that the lock instructions are atomic, thus when in a loop to set a lock there will be no situation where 2 or more cogs think the lock is free and set it, believing it has worked. That can happen in HUB ram, but not with locks.

    Enjoy!

    Mike
  • Back to your lock issue: print("_Lock = %d\r", _Lock);

    That will only show the lock number not its state. There are 8 locks available you have lock 1.

    I like locks as you can set and for get them. efficient instructions that don't take up any space.

    Mike
  • As @iseries mentioned, the "_Lock" variable is 1 because you happened to allocate lock number 1. If you call "locknew" again you'll get lock number 2.

    The state of the variable is returned by the lockset/lockclr functions. This is actually important; calling "lockset(n)" is a request to set lock number "n", but it is not guaranteed to do so (another COG may have already done that). So you need to test in a loop. lockset() returns the old state, so loop until we get 0 (meaning no other COG has it locked):
      n = locknew(); /* allocate a new lock */
      ...
      /* aquire the lock */
      while (lockset(n) != 0) {
          /* we have to wait, someone else owns lock n */
      }
      /* critical section here */
    
      /* release the lock */
      lockclr(n);
    

    Finally, a minor nit: the name "_Lock" that you chose for your lock variable is reserved in C for the system (all names starting with an underscore then a capital letter are reserved). You could break the C library by using a name like that.

    Eric
  • I have trouble remember what language does what with what.

    C# wants capital letters first then lower case. Java wants lower case first followed by Uppercase. Python wants underscores between names.

    I give up.

    Mike
  • kwinnkwinn Posts: 8,697
    iseries wrote: »
    I have trouble remember what language does what with what.

    C# wants capital letters first then lower case. Java wants lower case first followed by Uppercase. Python wants underscores between names.

    I give up.

    Mike

    +1

    It's all the minor nits like this that make using multiple languages such a PITA, not the actual language differences. That's why I like to stick to a few languages whenever possible.
  • Mark_TMark_T Posts: 1,981
    edited 2019-03-16 15:24
    iseries wrote: »
    I have trouble remember what language does what with what.

    C# wants capital letters first then lower case. Java wants lower case first followed by Uppercase. Python wants underscores between names.

    I give up.

    Mike

    Mixed case style guidelines are nothing to do with the language, they are a human invention to
    try to improve readability and uniformity in a large code base that's worked on by many people.

    Unless you have a need for this, ignore it and just use well chosen names and lots of whitespace,
    those are always the most important style guidelines.

    Some of the case conventions are there to try to make visually obvious the difference between
    constant, variable, class, keyword, etc etc. Unfortunately there are a variety of conflicting conventions
    and some languages aren't case sensitive which causes more confusion!

    [ and anyway these days any good programming editor can colour-code the different kinds of names
    for you, making the case conventions entirely superfluous ]
  • iseries wrote: »
    I have trouble remember what language does what with what.

    C# wants capital letters first then lower case. Java wants lower case first followed by Uppercase. Python wants underscores between names.

    I give up.

    Mike

    I was bound to work for a while as a 'fix it man'. So when some stuff was hitting the fan, I was send in to - hmm - take over the project and tame a horde of cats/programmers.

    Code conventions are just that. 'conventions'. It is a agreement between the programmers working on a project together so that the code base is someway conforming to a common style.

    This has nothing to do (or say mostly nothing) with the language used, it is more important that it makes reading/refactoring/reviewing the code more easy. Usually there is (or should be) a document describing how the code should look like and where, when and how one is supposed to use CamelCase Underscores or pre/post addons to names.

    This varies from company to company, sometimes even from project to project. C# does not want capital letters first, you can do whatever you want, but most Microsoft examples are written in a common MS code style and people adapt to it to have consistency between self written code and that copy and paste stuff you find in every program.

    I also was teaching COBOL in the late 1980's. I visited a friend of mine, just dropping by at his workplace. He did offered me the job and I basically declined two weeks earlier. So he tricked me into it. While walking over the campus we went into a building and he opened the door to a classroom. And stated to the class 'Here is Michael Sommer he is your new teacher' then he padded me on the back and left the room.

    I never ever wanted to stay in front of a group and teach. Gosh I am a Code Monkey, I don't do PEOPLE ok? But one has to do what one has to do, so I took the class over, the last teacher had a car accident and those guys and girls needed to pass their class in fall.

    Programming COBOL for at least a decade at that time knowledge was not the point, teaching was. What a experience. For weeks I was barely one day ahead with producing course material for the next days.

    But I learned a lot about coding styles. Every programmer has a handwriting. Even with strict coding rules, formatting rules and language restrictions. COBOL is very good at the last one.

    At every test they had to pass I was reviewing their code. And two members of the group where helping out others to pass their tests. I could see their handwriting all over the place. This one had problems with the menu, all other code from him but the menu code definitely written by Paul, that listing clearly shows Laura's handwriting in the SCREEN SECTION and so on.

    I had 35 students for half a year and in at least 75% of the code I reviewed - there was Paul and Laura involved somewhere.

    Coding style is very important, the best example here is @JonnyMac, he is just brilliant and all his Objects in the OBEX are super clean and well written. I wish I could do that, but I am still fighting to get there.

    How you name things is 'coding convention' for the project/company, but how to dissect a problem into routines and subroutines, how to solve things elegant and/or efficient is the Art of programming.

    I do not just put @JonnyMac out here, @kuroneko's code is a joy to read, @"Mike Green", @"Phil Pilgrim (PhiPi)", @lonesock and others.

    I am sadly not really good at that. Always unhappy with my own code, convoluted, compressed, efficient but somehow ugly(?) to read. But I try to learn from other coders...

    Enjoy!

    Mike
  • Just to clarify: not using underscore to start a variable name in C isn't a matter of coding convention, it's a restriction of the lanuage standard. There are some specific exceptions in the standard, but in general unless you know what you're doing don't start a variable name with an underscore.

    The problem is that underscored variable names are reserved for the system and compiler, and so they get used a lot in system header files. Programmers look at the header files and think that's good style, and so start using underscores themselves. But the underscore in the header file isn't a matter of style, it's to avoid conflicts with user code. If users start putting underscores at the start of variables it defeats the purpose of having the reserved names.
  • There is no problem using C or C++ _local_ variables with a leading underscore, so long as it is not followed
    by an uppercase letter, as that may clash with a macro/ifdef

    In fact leading underscores in C++ constructor arglists is common practice in order to be able to name
    both instance variables and arguments similarly.

    But these are all still conventions. See the obfuscated C contest for examples of what to avoid at all costs!
  • Dave HeinDave Hein Posts: 6,347
    edited 2019-03-17 01:04
    I grep'ed the include and include/sys directories for "_", and it appears that most things that used a single leading underscore are #define symbols. These generally use all caps. Most other things that have a leading underscore actually use a leading double-underscore. There are a few symbols with a single underscore, but the names are unique enough that they shouldn't conflict with user symbols.

    Now C++ may have different conventions. I wouldn't know about that.
  • There is a lot of discussion going on here guys, thanks for the input.

    Since we are talking about locks...... would it be possible to read the LSM9DOF module with 2 cogs if locks were used?

    The code below is an outline of how I think it might work. It obviously won't compile. In theory I think it might work, but I have know idea.

    What do you guys think?
    #include "simpletools.h"
    #include "lsm9ds1.h"

    #define SCL_PIN 1
    #define SDIO_PIN 2
    #define CS_AG_PIN 3
    #define CS_M_PIN 4

    n = locknew(); /* allocate a new lock */

    int main()
    {
    cog_run(&Read_Gyro, 128);
    cog_run(&Read_Acc, 128);

    while(1)
    {
    //Do something with here.
    }
    }

    Read_Gyro(){

    imu_init(SCL_PIN, SDIO_PIN, CS_AG_PIN, CS_M_PIN);

    while(1){
    while (lockset(n) != 0) {
    //Wait till lock is clear
    }

    imu_readGyro(&gx, &gy, &gz);
    lockclr(n);
    }
    }

    Read_Acc(){

    imu_init(SCL_PIN, SDIO_PIN, CS_AG_PIN, CS_M_PIN);

    while(1){
    while (lockset(n) != 0) {
    //Wait till lock is clear
    }
    imu_readAccel(&ax, &ay, &az);
    lockclr(n);
    }
    }
  • That will not work as once the pin is accessed by that cog it cannot be accessed by another cog.

    Mike
  • Yeah, that makes sense.
  • With a gyro you want to read it at designed intervals so that you can calculate degrees per second of turn.

    So if you read the sensor at every 10 milliseconds and then multiple by 100 you could get degrees per second change.

    Mike


  • Yeah, I am trying to read the gyro and acc at 100Hz. I have that part working now, but there is no time left in the loop to do any math or filtering.

    I have the calculations working to determine degrees of rotation for the gyro. However I am using floating point math to do it. I would like to switch to integers only, but at this point in time its not a big deal. I can read the gyro and do the math in a separate cog, but there is no time left to read the acc.

    So I am going to try and use a lock and do the math in a cog dedicated just for math.

    I am not quite there yet. Getting close.


  • kwinnkwinn Posts: 8,697
    One way to share data from sensors is to have one cog read the sensor and place the data in one or more global variables that other cogs can access. For sensors that produce multiple related values I include a flag that indicates when the readings are valid.

  • @Shawna
    try this I wrote this tutorial for a C library. Let me know if it helps.

    Propeller c Library tutorial

    Follow the link.
    http://forums.parallax.com/discussion/download/123134/Propeller C library tutorial.docx
    http://forums.parallax.com/discussion/download/123135/libgps10.zip
  • @Shawna

    this should help you go between cogs. Took me a while but it works. First take the raw program then follow the steps. to create a library of your own if you like.
Sign In or Register to comment.