The Functions lesson Memory Functions Can Share showed you how to make functions share information with global variables instead of with parameters. Code running in different cogs can use this technique to exchange information as well. There’s just one extra detail: when you declare global variables for cores to share, you have to precede each one with the keyword volatile. So instead of int globalVar, the declaration would be volatile int globalVar.
This example program declares a global volatile variable named t, that allows the main function to control the blink function’s LED on/off rate. The main function sets the value of t, and then the blink function uses t to set its pause times between high/low instructions.
Circuit
We'll keep using the Activity Board's (original or WX version) built-in LEDs on P26 and P27. See the Blink a Light tutorial for a schematic if you are using a different board.
Test Code
- Use SimpleIDE’s Open Project button to open Cog Info Exchange from SimpleIDE\Learn\Examples\Multicore.
- Click SimpleIDE’s Load RAM & Run button.
- Verify that the LED blinks at one rate for 2 seconds, and then at twice that rate for another two seconds, and then stops.
/* Cog Info Exchange.c Example of two cogs exchanging information with a volatile global variable. The main function in cog 0 changes the value; and it affects the blink function's rate running in cog 1. http://learn.parallax.com/propeller-c-multicore-approaches */ #include "simpletools.h" // Library include void blink(); // Forward declaration int *cog; // For storing process ID volatile int dt; // Declare dt for both cogs int main() // Main function { dt = 100; // Set value of dt to 100 cog = cog_run(blink, 128); // Run blink in other cog pause(2000); // Let run for 2 s dt = 50; // Update value of dt pause(2000); // New rate for 2 s cog_end(cog); // Stop the cog } void blink() // Function for other cog { while(1) // Endless loop { high(26); // LED on pause(dt); // ...for dt ms low(26); // LED off pause(dt); // ...for dt ms } }
How it Works
The program begins with three declarations. The first gives us access to the simpletools library functions, including cog_run and cog_end. The second is the forward declaration void blink(); for the blink function defined below the main routine. The third declares a global volatile int variable dt. Since it is declared outside of any functions it is global, and therefore available to all of the functions in the program. Since it is volatile, it can be used by functions running in different cores with confidence.
#include "simpletools.h" // Library include void blink(); // Forward declaration volatile int dt; // Declare dt for both cogs
First thing inside the main routine, dt is initialized to 100. Next, a call to cog_run launches the blink function in another cog. The blink function uses dt in the parameter of its pause functions to set the blink rate of the P26 LED. The call pause(2000) in the main routine means nothing else happens in that core for 2 seconds, while another core blinks the P26 LED on/off every 100 milliseconds.
int main() // Main function { dt = 100; // Set value of dt to 100 int *cog = cog_run(blink, 128); // Run blink in other cog pause(2000); // Let run for 2 s
The next bit of code lets the main routine change what's happening in the other cog. First, the variable dt is assigned a new value of 50. Since dt is a volatile global variable, the blink function running in that other core is affected immediately — its pause(100) calls become pause(50) calls and the P26 LED starts blinking twice as fast. After another 2-second pause, the main routine shuts down the core running the blink function with cog_end(cog).
t = 50; // Update value of t pause(2000); // New rate for 2 s cog_end(cog); // Stop the cog
Below the main routine is the code for the blink function that was launched into the other core. It is simply an infinite while(1) loop that sets P26 high and low alternately every dt milliseconds, to blink the LED.
void blink() // Function for other cog { while(1) // Endless loop { high(26); // LED on pause(dt); // ...for dt ms low(26); // LED off pause(dt); // ...for dt ms } }
Did You Know?
volatile - this keyword tells the C compiler not to perform any optimizations on statements with that variable. Optimizations are actions the compiler can take to make code execute faster and/or take less memory by removing "unused" code. For example, the compiler might see a function that just prints a variable without performing other operations on it. Since the function doesn't change the variable's value, the compiler might remove code to re-check the value before printing. But, if another function in another core also updates that variable, the wrong value may be used if it is not re-checked before printing. The volatile keyword prevents such unintended consequences.
Try This
The last example let the main routine's core control the behavior of a function in another core. A core can also monitor another core’s behavior with a variable. Here’s an example that monitors how many times the blink function turned the LED on/off.
- Click Save Project As, and save a copy of your program under a new name.
- Add a volatile int variable named reps and initialize it to zero.
- Add two print statements in the main routine to check and display the value of reps.
- Add a statement to the blink function’s while loop that increments reps each time the loop repeats.
With these changes, the main function monitors and displays the value of reps from one core, while the blink function updates the value of reps from another core.
- Use Run with Terminal to verify that the main function is able to check how many times the blink function has incremented reps.
Your Turn
Let's blink P27 in a third core, but at a rate of dt*2. We'll display its accumulated blink repetitions as well.
- Starting with Cogs Exchange Info (Try This), add a reps2 volatile global variable.
- Add a blink2 function that blinks the P27 LED on/off using dt*2 for the pause parameters, and adds 1 to reps2 each time through the loop.
- Update the two print statements in the main routine to display the values of both reps and reps2.
- Be sure to launch both cores at the beginning of the main routine, and stop them at the end.
If all goes well, your SimpleIDE Terminal output should look something like the image below.