Reading 5 digital input pins (5 bit) to change clock frequency

Hello -
I've been researching this quite a bit, and this is the closest I've gotten to a solution. I'm attempting to use 5 digital inputs to generate 32 values for a variable. The source of the inputs is a toy which I circuit bent that has five lights that flash in rhythm to the songs it contains. When circuit bent, it creates looping patterns, and the lights (actual tiny incandescent lights, not LEDs) flash in rhythm with the patterns. I have circuitry that outputs a 5v pulse from each of the 5 lights when they illuminate. With this code, I can get the serial monitor to display a 5 bit binary number corresponding to the state of each input. I'm trying to use that binary number to change the value of variable "interval" (currentMillis - previousMillis >= interval) to change clock speed. That is where it breaks down. I am not getting anywhere near the clock frequencies I get if I substitute the desired value for the variable 'interval". In other words, if I use "interval", set the binary value to 00100 which I want to make "interval" = 200, I get a completely different frequency than I do if I type in 200 in interval's place. Here's the code - thoughts?

#define LENGTH 5
const int pins[LENGTH] = {6, 5, 4, 3, 2};

int pinState;
int statusWord;

const int clockPin =  9;
int clockState = LOW;
unsigned long previousMillis = 0;
long interval;

int statusLast;

void

frequencySet()

{
  if (statusWord = 00001)
    interval = 1;
  if (statusWord = 00010)
    interval = 100;
  if (statusWord = 00100)
    interval = 200;
  if (statusWord = 01000)
    interval = 400;
  if (statusWord = 10000)
    interval = 800;
  unsigned long currentMillis = micros();



  if (currentMillis - previousMillis >= interval) {
    // save the last time you blinked the LED
    previousMillis = currentMillis;

    // if the LED is off turn it on and vice-versa:
    if (clockState == LOW) {
      clockState = HIGH;
    } else {
      clockState = LOW;
    }

    // set the LED with the ledState of the variable:
    digitalWrite(clockPin, clockState);

  }
}

void setup() {


  Serial.begin(9600);

  for (int i = 0; i < LENGTH; i++) {
    pinMode(pins[i], INPUT);
    pinMode(8, OUTPUT);
    pinMode(clockPin, OUTPUT);
  }
}


void loop() {
  frequencySet();
  statusWord = 0;

  for (int i = 0; i < LENGTH; i++) {
    pinState = digitalRead(pins[i]);
    if (pinState == HIGH) {
      statusWord |= 1 << i;
    }
  }

  if (statusWord != statusLast) {
    Serial.print("Status: ");
    Serial.println(statusWord, BIN);
    statusLast = statusWord;

  }
}

I'm totally not understanding what you mean by setting the "clock". The clock frequency of your board is a constant. You can adjust it when flashing your program to your board (with some boards). Are you saying that you are trying to adjust the frequency the CPU operates at dynamically while the program is operating?

You also don't seem to take into account that reading the digital pins is going to take some period of time. Also, you are using micros() instead of millis(). You are not going to be able to do all of what you are trying to do within 1 microsecond. Even an interval of 100 microsecond isn't going to be realistic. Generating a PWM signal with a frequency is accomplish by the board using a specific hardware peripheral that is able to flip the pin much faster than your program is going to do. Why don't you use the signal word to adjust a PWM frequency signal that is being generated by the board rather than manually flipping the digital pin.

1 Like

"=" is an assignment, "==" is used to see if two values are equal.
Numbers starting with zero are converted to octal by the compiler, 10000 is 10,000 in decimal.

2 Likes

right on, documented here..

integer constants..

have fun.. ~q

1 Like

If you use 00100 as a number and don't indicate the radix (binary, octal, decimal, hexadecimal) the compiler looks a the leading '0' and chooses 'octal'. That makes the value 64. If you type 200 the compiler sees no leading '0' so it chooses 'decimal' and you get a value of 200. If you want binary you have to put on a prefix of '0b' (or, only for Arduino, 'B')

It's not clear why you expected '00100' to represent 200.

1 Like

Let me explain exactly what I'm trying to do, with the understanding that it might not be doable. I have 5 input pins receiving 5 different pulses from the circuit bent toy. At the output pin I want to have a square wave, the frequency of which is controlled by the 5 bit binary number. There would be 32 distinct frequencies. I want to record that as an audio signal which can be incorporated into a video editing timeline. (I'd be sure the highest frequency can be fairly faithfully reproduced at whatever sampling rate I use to record). I then want to receive that signal from the video back into an input pin, detect the frequencies and and have each discrete frequency generate a unique 5 bit number, turning on any of 32 possible combinations of 5 output pins, recreating the sequence used to create the audio file. The idea is sort of an encoder/decoder operation. It didn't occur to me that I can modulate the frequency of a PWM signal - I did try using this setup to change the PW of a PWM signal and use simple RC DAC to create 32 discrete voltage levels, but as I anticipated, that is too many discrete levels to squeeze into 5V, and the D to A conversion was either too slow or too ripply - the usual tradeoffs for such a simple circuit. I'll find out how to modulate the PWM signal. (Unless of course you feel generous and care to enlighten me). Thank you for responding! I work in electronics, and often I incorporate Atmega microcontrollers in my devices. I go for long periods without writing code, and then I get back into it when it's called for. I do try to figure things out before I post here, so if I do, you know I've done all I can.

Oh of course - I didn't include the radix! This is the first time I've ever tried to do anything with binary. I was aware of the conventions for binary, decimal, hex, etc., but just overlooked that as I'm green. I did not expect 00100 to represent 200. What I intended was that if the binary number is 00100, the variable "interval" would be set at 200. The 200 was just an arbitrary number anyway. I just chose 5 different values to try to get a range of frequencies. For testing, I just assigned values created by single pins being high (00001, 00010, 00100, 01000, 10000). Ultimately, I'd create all 32 discrete possible values. Thank you for your response!

Yes, you aren't the only one to call this to my attention :slight_smile: I've never worked in binary before Arduino wise, but since I started this project, I did learn about the designations for binary, decimal and hex, but failed to remember when writing the code. I very much appreciate your response!

Even if you fix the "=" vs "==", and the binary constants, you're still only checking for a fracation of the possible values. 5 bits (which it looks like you're gathering appropriately) gives you a value between 0 and 31. Why not just have frequencySet multiply that by an appropriate constant?

I plan on using all 32 possible values. This code is incomplete - I was just trying to get it to work properly before setting up the remaining possible values. The 5 input signals will enter in varying patterns and combinations from the circuit bent toy, depending on the settings on its control panel - they're somewhat unpredictable, and I cannot say for certain if they create all 32 possible combinations, but it will come close. Thanks for pointing out the "=" vs "==". I am active in Arduino land inconsistently - I work with electronics in my projects, and use Arduino when called for, which is often. I embed Atmega 328 microcontrollers in my musical instruments. Due to various reasons, I had not been working on any projects for many months and I'm a bit (pun not intended) rusty, so I miss details like that. Thank you for your very helpful response!

What do you expect this statement to do?

You are close, but to speed things up, I made a few changes to make it work.

Reading the input values can be done in a function. Then you can use that function to check if you get 0...31. Then you can use that to calculate the interval.

By doing that, I get this sketch:

// Forum: https://forum.arduino.cc/t/reading-5-digital-input-pins-5-bit-to-change-clock-frequency/1109064
// Original sketch by: JB8256
// This Wokwi project: https://wokwi.com/projects/360706483828795393
// Changes by: Koepel, 31 March 2023
//
// Changes:
//   Changed 'pins' to 'inPins'
//   Added a unknownPin for pin 8
//   Changed the for-loop in setup()
//   Added a function that reads the inputs and returns an integer 0...31
//   Moved global variables to local variables where appropriate
//   Pin 2 has now the switch for the lowest bit
//   Lowered the frequency for a blinking led

#define LENGTH 5
const int inPins[LENGTH] = {2, 3, 4, 5, 6};
const int clockPin = 9;
const int unknownPin = 8;

int clockState = LOW;
unsigned long previousMillis = 0;
unsigned long interval;

int lastInputValue;

void frequencySet()
{
  unsigned long currentMillis = millis();

  if (currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;

    // if the LED is off turn it on and vice-versa:
    if (clockState == LOW) {
      clockState = HIGH;
    } else {
      clockState = LOW;
    }

    // set the LED with the ledState of the variable:
    digitalWrite(clockPin, clockState);
  }
}

void setup() {
  Serial.begin(9600);

  for (int i = 0; i < LENGTH; i++) {
    pinMode(inPins[i], INPUT);
  }
  pinMode(unknownPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
}

void loop() {
  int inputValue = readInputs();
 
  // Convert 0...31 to a interval of 1000...100
  // That is a offset of 100.
  // The input range of 31 should match the output range of 900
  interval = 100UL + (900UL - ((unsigned long) inputValue * 900UL / 31UL));

  // run the millis-timer
  frequencySet();

  if (inputValue != lastInputValue) {
    Serial.print("Input Value: ");
    Serial.print(inputValue);
    lastInputValue = inputValue;

    Serial.print(", interval: ");
    Serial.print(interval);
    Serial.println();
  }
}

// Read the 5 inputs and create an integer 0...31
int readInputs()
{
  int result = 0;

  for (int i = 0; i < LENGTH; i++) {
    int pinState = digitalRead(inPins[i]);
    if (pinState == HIGH) {
      result |= 1 << i;
    }
  }
  return(result);
}

Try the sketch in Wokwi simulation:

1 Like

Honestly, I was just trying to see if it improved the results over millis(), which it seemed to do, but the real problems lay elsewhere I assume, like using "=" instead of "==" in my 'if' statements, or the fact that I omitted 0b to the left of my 5 bit binaries...
If you're referring to the whole statement, it's part of the clock setup (based on the classic blink without delay example sketch.)

Wow, Koepel, this is above and beyond! What I'm trying to do with this project is a lot of new stuff for me as far as Arduino goes. My involvement with Arduino comes and goes with necessity. Sometimes I go months without a project that calls for it, then something comes up where I get fairly deep into it. I greatly appreciate you taking time to modify my sketch; I know much of what I do can be done more efficiently my more highly skilled people. I do try to learn as much as I can on my own before posting here. I will go over your work and do my best to understand all of it. It's the wee hours here at the moment and I'm going back to bed, hopefully to sleep this time. I will test this out in the "lab" first thing. THANK YOU for your time!

Hello JB8256

Consider and fine tune.

#define ProjectName "Reading 5 digital input pins (5 bit) to change clock frequency"
/* BLOCK COMMENT
  https://forum.arduino.cc/t/reading-5-digital-input-pins-5-bit-to-change-clock-frequency/1109064
  https://europe1.discourse-cdn.com/arduino/original/4X/7/e/0/7e0ee1e51f1df32e30893550c85f0dd33244fb0e.jpeg
*/
#define usl unsigned long // I am too lazy to type
// make variables
constexpr int Pins[] {A0, A1, A2, A3, A4};
constexpr int ClockPin {9};
usl previousMillis;
usl intervalMillis[] {0, 1, 100, 200, 400, 800};
// tools
void heartBeat(int LedPin, usl currentMillis)
{
  static bool setUp = false;
  if (!setUp) pinMode (LedPin, OUTPUT), setUp = !setUp;
  digitalWrite(LedPin, (currentMillis / 500) % 2);
}

int readButtons()
{
  int returnValue = 0;
  for (int n = 0; n <= 4; n++)
  {
    if (digitalRead(Pins[n]) == LOW)
    {
      returnValue = n+1;
      continue;
    }
  }
  return returnValue;
}

void setup()
{
  Serial.begin(115200);
  Serial.println(ProjectName);
  Serial.println(__FILE__);
  Serial.println(__DATE__);
  Serial.println(__TIME__);
  for (auto &Pin : Pins) pinMode(Pin, INPUT_PULLUP);
  pinMode(ClockPin, OUTPUT);
}
void loop()
{
  usl currentMillis = millis();
  heartBeat(LED_BUILTIN, currentMillis);

  if (currentMillis - previousMillis >= intervalMillis[readButtons()-1] && readButtons())
  {
    previousMillis = currentMillis;
    digitalWrite(ClockPin, digitalRead(ClockPin) ? LOW : HIGH);
  }
}
//------------------------------------------------------------

Have a nice day and enjoy coding in C++.

Thank you Paul! I will check this out momentarily when I go back into the shop. I'll study it and try to understand fully what you did here. I REALLY appreciate the time and feedback. Arduino has been invaluable to me in my projects and designs. I work in many areas other than coding, and I am average at best at it, and inexperienced compared to most folks on this forum. But, I really enjoy it, and I really do try to understand what's going on under the hood. thank you again - I look forward go diving in!

I got the thing working. I activated the circuit bent device and sent its 5 trigger outs into the Atmega's 5 input pins. It generates 32 discrete frequencies using "tone", reading the trigger inputs as a 5 bit number. I recorded the output signal onto a PCM recorder while videotaping the 5 lights flashing on the toy. I brought the video and audio into Premiere and synced them up, the exported to QuickTime. I played back the QuickTime, plugged the audio into the Atmega input (pin 8), which translates each frequency into a 5 bit number, each bit of which switches an output pin on. I then connected each output pin to 5 circuit bent devices with gate inputs, and voila, the video is sending triggers out to each one, and I can see by monitoring the video that they are triggering in sync with it. I'm sure my code is clunky and overweight - I'll work on that, but this works perfectly so I'm pleased. Thank you again for all your help - even if I didn't implement it exactly, I learned a great deal and I'm grateful for all your input. Thank you everyone.

#include <FreqMeasure.h>


#define LENGTH 5
const int pins[LENGTH] = {6, 5, 4, 3, 2};
const int outPins[LENGTH] = {18, 17, 16, 15, 14};

int pinState;
int statusWord;

const int clockPin =  9;
int clockState = LOW;
unsigned long previousMillis = 0;
long interval;

int statusLast;
int frequencySetting;

byte bitGenerate;

void

frequencySet()

{
  if (statusWord == 0)
    frequencySetting = 1;
  if (statusWord == 1)
    frequencySetting = 4500;
  if (statusWord == 2)
    frequencySetting = 5000;
  if (statusWord == 3)
    frequencySetting = 5500;
  if (statusWord == 4)
    frequencySetting = 6000;
  if (statusWord == 5)
    frequencySetting = 6500;
  if (statusWord == 6)
    frequencySetting = 7000;
  if (statusWord == 7)
    frequencySetting = 7500;
  if (statusWord == 8)
    frequencySetting = 8000;
  if (statusWord == 9)
    frequencySetting = 8500;
  if (statusWord == 10)
    frequencySetting = 9000;
  if (statusWord == 11)
    frequencySetting = 9500;
  if (statusWord == 12)
    frequencySetting = 10000;
  if (statusWord == 13)
    frequencySetting = 10500;
  if (statusWord == 14)
    frequencySetting = 11000;
  if (statusWord == 15)
    frequencySetting = 11500;
  if (statusWord == 16)
    frequencySetting = 12000;
  if (statusWord == 17)
    frequencySetting = 12500;
  if (statusWord == 18)
    frequencySetting = 13000;
  if (statusWord == 149)
    frequencySetting = 13500;
  if (statusWord == 20)
    frequencySetting = 14000;
  if (statusWord == 21)
    frequencySetting = 14500;
  if (statusWord == 22)
    frequencySetting = 15000;
  if (statusWord == 23)
    frequencySetting = 15500;
  if (statusWord == 24)
    frequencySetting = 16000;
  if (statusWord == 25)
    frequencySetting = 16500;
  if (statusWord == 26)
    frequencySetting = 17000;
  if (statusWord == 27)
    frequencySetting = 17500;
  if (statusWord == 28)
    frequencySetting = 18000;
  if (statusWord == 29)
    frequencySetting = 18500;
  if (statusWord == 30)
    frequencySetting = 19000;
  if (statusWord == 31)
    frequencySetting = 19500;

  tone(9, frequencySetting);
}




void setup() {


  {
    Serial.begin(57600);
    FreqMeasure.begin();
  }




  for (int i = 0; i < LENGTH; i++) {
    pinMode(pins[i], INPUT);
    for (int i = 0; i < LENGTH; i++)
      pinMode(outPins[i], OUTPUT);

    pinMode(8, INPUT);
    pinMode(clockPin, OUTPUT);
  }
}
double sum = 0;
int count = 0;


void loop() {
  frequencySet();
  statusWord = 0;

  for (int i = 0; i < LENGTH; i++) {
    pinState = digitalRead(pins[i]);
    if (pinState == HIGH) {
      statusWord |= 1 << i;
    }
  }

  if (statusWord != statusLast) {

    statusLast = statusWord;

  }
  if (FreqMeasure.available()) {
    // average several reading together
    sum = sum + FreqMeasure.read();
    count = count + 1;
    if (count > 30) {
      float frequency = FreqMeasure.countToFrequency(sum / count);
      Serial.println(frequency);
      sum = 0;
      count = 0;

      if (frequency < 4000)
        bitGenerate = 0b00000000;
      if ((frequency > 4250) && (frequency < 4750))
        bitGenerate = 0b00000001; //dec 1
      if ((frequency > 4750) && (frequency < 5250))
        bitGenerate = 0b00000010; //dec 2
      if ((frequency > 5250) && (frequency < 5750))
        bitGenerate = 0b00000011; //dec3
      if ((frequency > 5750) && (frequency < 6250))
        bitGenerate = 0b00000100; //dec 4
      if ((frequency > 6250) && (frequency < 6750))
        bitGenerate = 0b00000101; //dec 5
      if ((frequency > 6750) && (frequency < 7250))
        bitGenerate = 0b00000110; //dec 6
      if ((frequency > 7250) && (frequency < 7750))
        bitGenerate = 0b00000111; //dec7
      if ((frequency > 7750) && (frequency < 8250))
        bitGenerate = 0b00001000; //dec 8
      if ((frequency > 8250) && (frequency < 8750))
        bitGenerate = 0b00001001; //dec9
      if ((frequency > 8750) && (frequency < 9250))
        bitGenerate = 0b00001010; //dec 10
      if ((frequency > 9250) && (frequency < 9750))
        bitGenerate = 0b00001011; //dec11
      if ((frequency > 9750) && (frequency < 10250))
        bitGenerate = 0b00001100; //dec 12
      if ((frequency > 10250) && (frequency < 10750))
        bitGenerate = 0b00001101; //dec13
      if ((frequency > 10750) && (frequency < 11250))
        bitGenerate = 0b00001110; //dec 14
      if ((frequency > 11250) && (frequency < 11750))
        bitGenerate = 0b00001111; //dec15
      if ((frequency > 11750) && (frequency < 12250))
        bitGenerate = 0b00010000; //dec 16
      if ((frequency > 12250) && (frequency < 12750))
        bitGenerate = 0b00010001; //dec17
      if ((frequency > 12750) && (frequency < 13250))
        bitGenerate = 0b00010010; //dec 18
      if ((frequency > 13250) && (frequency < 13750))
        bitGenerate = 0b00010011; //dec19
      if ((frequency > 13750) && (frequency < 14250))
        bitGenerate = 0b00010100; //dec 20
      if ((frequency > 14250) && (frequency < 14750))
        bitGenerate = 0b00010101; //dec21
      if ((frequency > 14750) && (frequency < 15250))
        bitGenerate = 0b00010110; //dec 22
      if ((frequency > 15250) && (frequency < 15750))
        bitGenerate = 0b00010111; //dec23
      if ((frequency > 15750) && (frequency < 16250))
        bitGenerate = 0b00011000; //dec 24
      if ((frequency > 16250) && (frequency < 16750))
        bitGenerate = 0b00011001; //dec25
      if ((frequency > 16750) && (frequency < 17250))
        bitGenerate = 0b00011010; //dec 26
      if ((frequency > 17250) && (frequency < 17750))
        bitGenerate = 0b00011011; //dec27
      if ((frequency > 17750) && (frequency < 18250))
        bitGenerate = 0b00011100; //dec 28
      if ((frequency > 18250) && (frequency < 18750))
        bitGenerate = 0b00011101; //dec29
      if ((frequency > 18750) && (frequency < 19250))
        bitGenerate = 0b00011110; //dec 30
      if (frequency > 19250)
        bitGenerate = 0b00011111; //dec 31
      digitalWrite(14, bitRead (bitGenerate, 0));
      digitalWrite(15, bitRead (bitGenerate, 1));
      digitalWrite(16, bitRead (bitGenerate, 2));
      digitalWrite(17, bitRead (bitGenerate, 3));
      digitalWrite(18, bitRead (bitGenerate, 4));
    }
  }
}`

Hello all...as I said, this code works very well for what I want to accomplish. I did some testing by connecting pin 9 to pin 8. The source device has indicator LEDs, so I can see when the inputs go high. By plugging pin 9 into 8, and then attached LEDs to the output pins, I can see if the "decoded" signal is reproducing the incoming signals accurately, and if there is any noticeable latency. Uploading the sketch to the UNO, it werks perfectly. However, I bought some Atmega 328s a while ago, and after uploading the sketch to a breadboard and using them in standalone mode, there was significant latency. When I swapped in 328s I'd purchased previously, there was no discernable latency. I'm wondering if anyone's heard of this - could they be defective? Is there such thing as "bootleg" Atmegs 328s? I tried several of them, and the latency persisted. I tried burning the bootloader (on the breadboard, Arduino as ISP). Any thoughts, suggestions?

How did you measured it?

I didn't measure it. I could see it. With the older Atmega328s (the good ones), while watching the incoming trigger LEDs, which are encoded and simultaneously decoded to turn on the corresponding output LEDs. I could not tell by looking that there was latency (of course there is always latency, but for my purposes, if I can't see the latency by watching the input and output LEDs, it's more than good enough for my purposes). When I uploaded my sketch to the newer, suspect Atmega328s, I could see the latency - maybe 1/2 second, which is way too much.