Shop OBEX P1 Docs P2 Docs Learn Events
How can I start a cog with more than 1 function? — Parallax Forums

How can I start a cog with more than 1 function?

Hal AlbachHal Albach Posts: 747
edited 2014-12-20 10:17 in Learn with BlocklyProp
I would like to start a cog where the program that is loaded into it with, for example;
cog_run(&wheels, 15);
where the wheels function can call other functions in the cog.
//                                    Motor COG Function
void wheels()
{       
    // set forward direction
//    low(14);                     // dir left, low = fwd, high = rev
//    low(16);                     // dir right 
  test();
    
  while(1)                       // Forever loop
  {
    // turn on pwm to drivers
    pwm_set(13, 0, pwmR);        // pwm_set(Pin, channel, dutyCycle)
    pwm_set(15, 1, pwmL);        //
//    pause(100);
    
    int i;
    int temp = 0;
    left = right = 0;
    for(i = 0; i < 10; i++)
    {
      temp = count(22, span);    // count pulses from LEFT encoder & accumulate in
      left += temp;              //  global variable "left"
      temp = count(23, span);    // count pulses from RIGHT encoder & accumulate in
      right += temp;             //  global variable "right"
    }
    leftb = left / 10;                  // average the result
    rightb = right / 10;                 //
    
    // Adjust pwm for each wheel to match with the set point and display parms
    if(rightb > sp) pwmR -= cp;       // if right is faster then slow it down
    if(rightb < sp) pwmR += cp;       // if right slower than speed it up
    if(leftb < sp) pwmL += cp;        // if left is slower then speed it up
    if(leftb > sp) pwmL -= cp ;       // if left is faster slow it down
  }     // ends while(1)
  
  void test()
  {
    low(14);                     // dir left, low = fwd, high = rev
    low(16);                     // dir right
  }     // ends void test()
}       // ends wheels

Here I"m using the function "test" just to figure out how to have additional functions in a cog.
Using the Hammer Icon to compile the program returns the following;
C:\Users\Admin\AppData\Local\Temp\ccg3d39N.o: In function `_wheels':
(.text+0x9): undefined reference to `_test'
collect2: ld returned 1 exit status
Done. Build Failed!

Check source for bad function call or global variable name `_test'
Both test and wheels are prototyped above Main.
I know I'm missing something or doing it incorrectly (obviously!) but the answer eludes me. I've searched the forum for several days and cannot locate how (or IF) this can be done.
I would be grateful for any assistance.

Comments

  • Dave HeinDave Hein Posts: 6,347
    edited 2014-12-16 12:18
    Move the last closing brace "}" to just before the line "void test()". You cannot define a function within another function.
  • Heater.Heater. Posts: 21,230
    edited 2014-12-17 21:46
    Sure you can:
    int main (int argc, char* argv[0])
    {
        double foo (double a, double b)
        {
            double square (double z)
            {
                return z * z;
            }
    
            return square (a) + square (b);
        }
    
        return foo (3, 4);
    }
    
    Compiles just fine. Not sure if it runs, I have no Props to hand.

    Not recommended practice though as nested functions is a GCC extension. Not standard C at all.

    Strange but true.
  • kwinnkwinn Posts: 8,697
    edited 2014-12-18 10:59
    It can be done in spin by passing the function and it's parameters via hub memory locations, so I don't see why it couldn't be done in C. Also done by the various retro micro emulators if you think of the instruction as a function and the address, register, or immediate data as the function parameters.
  • edited 2014-12-19 13:57
    Let's start with the example code from http://learn.parallax.com/multicore-approaches/simple-multicore. Here is a modified version that makes the cog running the blink function call a function named blink_counter with each repetition of its while loop. The main function monitors what the cog running blink and blink_counter has been doing to the reps variable and displays it in the SimpleIDE Terminal. So, use Run with Terminal, and see what' it's doing...

    attachment.php?attachmentid=112410&d=1419026543
    /*
      Cog Run Example.c
      
      Run function that blinks a light with another cog continuously.
    
      http://learn.parallax.com/propeller-c-multicore-approaches
    */
    
    #include "simpletools.h"                      // Library include
    
    void blink();                                 // Forward declaration
    void blink_counter();                         // <-- add
    
    volatile int reps = 0;                        // <-- add
    
    int main()                                    // Main function
    {
      cog_run(&blink, 120);                       // <-- change! // Run blink ...
    
      while(1)                                    // <-- add
      {                                           // <-- add
        print("reps = %d\n", reps);               // <-- add
        pause(500);                               // <-- add
      }                                           // <-- add
    }                                             // <-- add
    
    void blink()                                  // Blink function for other cog
    {
      while(1)                                    // Endless loop for other cog
      {
        high(26);                                 // P26 LED on
        pause(100);                               // ...for 0.1 seconds
        low(26);                                  // P26 LED off
        pause(100);                               // ...for 0.1 seconds
        blink_counter();                          // <-- add
      }
    }
    
    void blink_counter()                          // <-- add
    {                                             // <-- add
      reps++;                                     // <-- add
    }                                             // <-- add
    


    One important thing to keep in mind is that the function running in another cog needs extra stack space for pushing and popping the function call and return addresses along with any local variables the function (blink_counter in this case) might declare. With that in mind, I increased the cog_run function's stacksize argument from 10 to 120. That's way more than necessary here, but it's best to be liberal with stack space while prototyping because bugs from undersized stacks can be real head-scratchers.

    When you are ready to make a coding pass to reduce the program size, there's a Cog Stack Usage Test.side project in Documents\SimpleIDE\Learn\Examples\Multicore. If your code makes the function(s) running in another cog perform to their maximum extent, it will tell you how much stack space was actually used. You can take that number and reduce the cog_run function's stackSize argument accordiingly.
    540 x 236 - 86K
  • Hal AlbachHal Albach Posts: 747
    edited 2014-12-19 20:20
    Thank you, Dave, Heater, Kwinn, and Andy, your suggestions and examples worked. I had been under the impression that cog_run(&wheels, 15) would physically load a cog with a program (function??) named wheels() and any additional functions called by wheels() into the cog ram for execution, but I am beginning to believe that is not the case. It seems all the functions are loaded into ram somewhere and that pointers play a very large part in how a cog actually executes that program.
    I was able to place that void test() function nearly anywhere in the source, at the end, or before Main(), or right after Main(), and it operated the same.
  • jazzedjazzed Posts: 11,803
    edited 2014-12-19 22:33
    Hal Albach wrote: »
    I had been under the impression that cog_run(&wheels, 15) would physically load a cog with a program (function??) named wheels() and any additional functions called by wheels() into the cog ram for execution, but I am beginning to believe that is not the case.


    No, it is not the case. What gets loaded into the cog is an "interpreter" (as Chip calls it) just like in spin, and that interpreter runs "wheels()" and anything else wheels calls.

    If you want C code to load in a COG to run, you would load it in a way similar to how PASM gets loaded. Actually the C code is compiled to PASM and that PASM is run in the COG.
  • Hal AlbachHal Albach Posts: 747
    edited 2014-12-20 10:17
    Thanks, Jazzed! That cleared up a lot of confusion on my part. As Dave stated in post #2, "a function cannot be defined within another function", therefore all functions are global and available to be called by any other function, even if it happens to be running in another cog. With the help of all the great minds on this forum I think I may yet get the hang of this "C" thing!
Sign In or Register to comment.