Sequence programming in C/C++ Part 1:Counter

Introduction

A sequence is a series of predefined actions with clear conditions for transitioning between each other and since it is predetermined it also has a beginning and an end. Sometimes a sequence is in a loop that repeats itself and in those cases it can be hard to see where it starts and ends but when programming a sequence it is important to have both start and stop conditions defined. You might also have a bifurcated sequence where the sequence have two or more outcomes depending on some factor.

In my area of work, industrial control and automation, sequence programming is probably one of the most common uses for PLC:s, with data acquisition being the second most common, but that is a completely different article. PLC:s are programmed in one of four fashions, there are really five standardized languages but as far as I know the fifth one, Instruction List, has not been used since the nineties. When using the 2 most common ones, Ladder and Function Block, building a control sequence is fairly easy since both are graphical programming languages so you can actually see the sequence, almost like a flow chart, on your screen. The third graphical programming language for PLC:s is even named Sequential Function Chart but unfortunately none of the major industrial automation companies have spent enough time to implement it so it’s easy to use and gives a clear overview of the sequence.

Here you see one example of sequence programming for a PLC. This sequence is built in ladder code and uses a move command to increase a counter to activate the next sequence step.

Sequence programming in C/C++

So how do you build a sequence in a text based programming-language you might ask. Well, it is here that your knowledge of control structure comes into play. Your “do..while” and “for-loops” and “if-statements” just to name a few. You can even set up an entire sequence where every program-step is determined by timers, but you should avoid this in most cases. If it’s only a matter of flashing led:s in a cool pattern it’s fine but for process-control it’s not accurate or reliable enough, there you want proper logic control where the sequence steps are determined by limit-switches or analog values.

Using a timer and Switch…Case

I’m going to show a couple of ways to program sequences in C for the Arduino platform. There are as many ways as there are programmers but these are simple and I have used all with good result. The first is a digital dice where the sequence is a series of numbers that the dice cycle through to simulate a throw before stopping at a random number. Here I have first programmed a simple clock pulse that gives me a rising edge at 1Hz (1 times per second). After that I use the clock pulse to cycle through the sequence steps, so every time I get a pulse the sequence counter is increased by one. Even though I have used a timer in this example the sequence counter could just as easily be incremented by a limit-switch or a by a boolean expression (an expression that evaluates to either 1 or 0)

Generating a clock pulse


  //Clock pulse function, the cP will change value approximately every 500ms

  if ((millis() - millisOld) >= 500) {
    cP = !cP;
    millisOld = millis();
  }

I am using the millis() function to generate a clockpulse, this can be a useful function to remember because you can use it for any type of cyclical events. What is important to note however is that millis() is an unsigned long (link to data type) so you have to make sure that the millisOld variable is the same data type. An unsigned long can have a maximum value of 4,294,967,295 now imagine if millisOld is only an integer, a 16 bit value, with a range of -32,768 to 32,767. So when the millis() is over 32,767 the millisOld will always be 32,767 and the if statement in the figure above will always equate to true.

Throwing the dice


  //Throwing the dice

  diceThrow = digitalRead(10);

  if (diceThrow != diceThrowOld) {
    if (diceThrow == HIGH) {
      caseCounter = 1;
    }
  }

  //Incrementing the caseCounter to progress the sequence

  if (caseCounter > 1) {
    if (cP == HIGH && !cPold) {
      caseCounter++;
    }
  }

Any dice-game is started with a throw although in this case the “Throw” is simply the push of a button. To detect a level change on a pin we must have a value to compare the current value to, that is what the diceThrowOld variable is for, and if the comparison is true then the button has been pushed. We set the sequence counter to one if the first statement is true, I will explain why I call the variable caseCounter in a little bit if you haven’t figured it out yet. When the sequence is initialized we then use the rising edge of the clock pulse to increment the caseCounter by 1 and since the clockpulse changes value every 500ms it has a rising edge once a second.

Switching cases


  switch (caseCounter) {  //The variable that determines whicj case should be active
    case 1:  //What value the determining variable should have, make sure you don't use semicolon as a line breaker here. It should be a regular colon
      digitalWrite (dice_1, HIGH);
      digitalWrite (dice_2, LOW);
      digitalWrite (dice_3, LOW);
      digitalWrite (dice_4, LOW);
      digitalWrite (dice_5, LOW);
      digitalWrite (dice_6, LOW);
      caseCounter = 2;                  //To be able to increment the caseCounter every second it has to be more than one so we set it to 2 in the first case.
      break;              //Leave the case and recheck the condition to determine which case shoud be active
    case 2:
      digitalWrite (dice_1, HIGH);
      digitalWrite (dice_2, HIGH);
      digitalWrite (dice_3, LOW);
      digitalWrite (dice_4, HIGH);
      digitalWrite (dice_5, LOW);
      digitalWrite (dice_6, HIGH);
      break;
    case 3:
      digitalWrite (dice_1, HIGH);
      digitalWrite (dice_2, LOW);
      digitalWrite (dice_3, LOW);
      digitalWrite (dice_4, LOW);
      digitalWrite (dice_5, HIGH);
      digitalWrite (dice_6, HIGH);
      break;

The image above depicts the structure of a switch…case function, as the name implies the function allows us to switch between a series of cases based on the value of the counter we are using. Here I am using the cases to turn on different leds to indicate what number the dice has, the first 6 cases are the set sequence and 7th case:

      break;
      case 7:
      digitalWrite (dice_1, random(0, 2)); //The random functions max limit is exclusive so we are generating a value of either 0 or 1
      digitalWrite (dice_2, random(0, 2));
      digitalWrite (dice_3, random(0, 2));
      digitalWrite (dice_4, random(0, 2));
      digitalWrite (dice_5, random(0, 2));
      digitalWrite (dice_6, random(0, 2));
      caseCounter = 0;      //Reset the sequence to enter our idle state where the programis waiting for the next dicethrow
      break;
  }

Here we use the random function to generate a different number for each dice throw. The random funcion has a max and min value as you can see, they are set to 0 and 2 because for some reason the max value is exclusive meaning it can generate any value up tp the max value you set but not the actual number you write in the code. So here it will randomly write 1 or 0 to our outputs for the led:s. Doing it in this fashion has one drawback but I will let you figure out which one, please let me know in the comments if you do.

We also reset the caseCounter to 0 since the sequence is finished and we want ot return to our idle state where we are waiting to start the next sequence.

Finally

This is only one way to program a sequence and I think it is a simple one to start with, next up I will show you how to use chained if-statements. After that we will see but I think I will show at least one more way, possibly two, before I am done with the subject.

If you have any thoughts or improvements let me know in the comments and I’ll make sure to get back to you.

If you want to download the source code you can do that here:

digitalDice.7z

Till next time.

Posted in All, How To's.

Leave a Reply

Your email address will not be published. Required fields are marked *