Frequency multiplication by x1.2

PaulRB your code compiled successfully after defining input and output pins.

PaulRB

Here is a code I try to play with (some strings maybe unnecessary, just tried a lot of ideas):

// Define the input and output pins
const int inputPin = 9;
const int outputPin = 10;

// Define the variables to store the input and output periods
unsigned long inputPeriod = 0;
unsigned long outputPeriod = 0;

// Define the variables to store the input and output states
int inputState = 0;
int outputState = 0;
int output = 0;
int waitout;

// Define the variables to store the previous and current times
unsigned long prevTime = 0;
unsigned long currTime = 0;

float value = 64000;

void setup() {
  // Set the input pin as input and the output pin as output
  pinMode(inputPin, INPUT);
  pinMode(outputPin, OUTPUT);
  noInterrupts();
  TCCR1A = 0;
  TCCR1B = 0;
  TCNT1 = value;
  TCCR1B |= (1 << CS10)|(1 << CS12);
  TIMSK1 |= (1 << TOIE1);
  interrupts();
}
ISR(TIMER1_OVF_vect)
{
  TCNT1 = value;
  // Read the input state
  inputState = digitalRead(inputPin);

  // Get the current time
  currTime = micros();
  
  // If the input state is high
  if (inputState == HIGH) {
    // If the previous state was low
    if (outputState == LOW) {
      // Calculate the input period
      inputPeriod = currTime - prevTime;

      // Divide the input period by 1.2 and round it to the nearest integer
      outputPeriod = round(inputPeriod / 1.2);

      // Set the output state to high
      outputState = HIGH;

      // Update the previous time
      prevTime = currTime;
    }
  }
  // If the input state is low
  else {
    // If the previous state was high
    if (outputState == HIGH) {
      // Calculate the input period
      inputPeriod = currTime - prevTime;

      // Divide the input period by 1.2 and round it to the nearest integer
      outputPeriod = round(inputPeriod / 1.2);

      // Set the output state to low
      outputState = LOW;

      // Update the previous time
      prevTime = currTime;
    }
  }
}
void loop() {
  waitout = round(outputPeriod / 2000);
  digitalWrite(outputPin, digitalRead(outputPin) ^ 1);
  delay(waitout);
}

The last idea was to probe input signal every 100ms and output it according signal period measured.
Here I stuck with mysterious effect of delay(waitout);
Can't find right coefficient for it. Some time it seems ok, but after tuning input frequency it (output frequency) suddenly changes unpredictably.

From the Nyquist theorem you MUST probe an input signal at least twice on period. If your signal has a 50 Hz frequency, therefore the period will be 1sec / 50 Hz = 20 ms and you need to read the signal at least every 10 ms.
The second, but not last - in your code you need to test level change on the input pin rather than just a level itself.

A more productive idea might be to use an interrupt based on changes in the level of the incoming signal, rather than based on a timer. In the interrupt measure the time elapsed since the last change - this will give you the period of the input pulses

You can see the example code to measure the frequency of the input signal in the thread

Hi @finderbinder,

this sketch uses

  • TimerOne library to create test pulses (T1) with a certain timing that can be changed by a slide potentiometer
  • TimerThree library to create a signal (T3) with 1.2 times more pulses than TimerOne within the same time period.
  • Two interrupt routines to count the changes of both signals
  • And a millis() function with 100 ms timing that adjusts the signal of T3 with respect to T1

Not sure if if applies to your application but maybe you can derive something from it.

It is testable on Wokwi: PulseCount MEGA - Wokwi ESP32, STM32, Arduino Simulator

/*
  Forum: https://forum.arduino.cc/t/frequency-multiplication-by-x1-2/1208037
  Wokwi: https://wokwi.com/projects/386371065171313665
*/

#include <TimerOne.h>
#include <TimerThree.h>

constexpr byte inT1  {2};
constexpr byte outT1 {4};

constexpr byte inT3  {3};
constexpr byte outT3 {12};
constexpr byte inputPin {A0};

volatile unsigned long countT1 = 0;
volatile unsigned long countT3 = 0;

void setup() {
  Serial.begin(115200);
  pinMode(outT1, OUTPUT);
  pinMode(outT3, OUTPUT);
  pinMode(inT1, INPUT_PULLUP);
  pinMode(inT3, INPUT_PULLUP);
  Timer1.initialize(10000);
  Timer1.attachInterrupt(createPulseT1);
  Timer3.initialize(10000);
  Timer3.attachInterrupt(createPulseT3);
  attachInterrupt(digitalPinToInterrupt(inT1), countPulsesT1, CHANGE);
  attachInterrupt(digitalPinToInterrupt(inT3), countPulsesT3, CHANGE);
}

int oldInp = -1;
unsigned long t1Interval;
unsigned long oldDuration = -1;

unsigned long lastTime = 0;
const unsigned long countInterval = 100; // [ms]
constexpr unsigned long timeConst = countInterval * 1000UL;

void loop() {
  int inp = analogRead(inputPin);
  if (inp != oldInp){
    oldInp = inp;
    t1Interval = map(inp,0,1023,50, 5000);
    Timer1.initialize(t1Interval);
  }
  if (millis()-lastTime >= countInterval){
    lastTime = millis();
    noInterrupts();
    unsigned long cT1 = countT1;
    countT1 = 0;
    unsigned long cT3 = countT3;
    countT3 = 0;
    interrupts();
    Serial.print(cT1);
    Serial.print("\t");
    Serial.println(cT3);
    Timer3.initialize(timeConst/(cT1*1.2));

  }
}

void createPulseT1() {
  static byte pulseValue = HIGH;
  pulseValue = !pulseValue;
  digitalWrite(outT1, pulseValue);
}

void createPulseT3() {
  static byte pulseValue = HIGH;
  pulseValue = !pulseValue;
  digitalWrite(outT3, pulseValue);
}

void countPulsesT1(){
   countT1++;
}

void countPulsesT3(){
   countT3++;
}

Good luck!

And you want to keep those pins a secret. Ok.

This bears almost no resemblance to the code I suggested. I thought you wanted help with that. Please let me know if you change your mind and want to give my suggestion a chance.

That was essentially my suggestion. But @finderbinder has decided it doesn't work, but seems unwilling to share the code that was tried, so we don't know what the problem was.

PaulRB
I'm open to offers, the main goal is to make a working code for given task.

Here is your code with 2 strings added at the top and "..." commented.
It compiles ok and uploads to Uno.

#define inPin 9 // define the input pin
#define outPin 10 // define the output pin

volatile int counter;
//...
void incrementCounter() {
  counter++;
}
//..
void setup() {
  //...
  attachInterrupt(digitalPinToInterrupt(inPin), incrementCounter, RISING);
  //...
}
void loop() {
  //...
  static unsigned long lastMeasurement;
  static unsigned int lastCount;
  //...
  if (millis() - lastMeasurement >= 100) {
    noInterrupts();
    unsigned int newCount = counter;
    interrupts();
    unsigned int frequency = 10 * (newCount - lastCount);
    tone(outPin, 6 * frequency / 5);
    lastMeasurement += 100;
    lastCount = newCount;
  }
  //...
}

Here is 200Hz signal at input pin and you see what I get at the output:
(and as I said, I get constant ~4kHz at output by this example. And it has no relation to input frequency)


Change this to pin 2 or 3.

The attachInterrupt() function needs a pin with external interrupt capability. Uno has only those 2.

The ... was only intended to indicate places where some of your other code might be, if needed.

PaulRB
Your recommendation to change inPin to pin 2 worked! Additionally needed to pullup input pin to VCC to work correctly in device.
The only drawback is that your code outputs 4kHz if there is no frequency (or very low frequency < 20Hz?) at inPin.
Thank you and others.

However, this hack does not helped to force device not to shut off because of AC 50Hz instead of AC 60Hz at AC socket. Probably it controls this differently, not by hall sensor :slightly_frowning_face: I think I should give up...
I tried to solve problem with AC120V 60Hz kitchen mixer in AC230V 50Hz socket (voltage changed with autotransformer) but frequency is a pitfall...

That's why I asked for a schematic.

You could have used INPUT_PULLUP mode for the pin.

Yes, tone() function cannot produce frequency under 31Hz. It seems if requested to produce a frequency of less than 31Hz it defaults to 4KHz. But this should not have been a problem because

Glad you got the frequency converter circuit working, even if it did not fix your original problem. We are quite used to fixing problems on this forum that turn out to be not the real problem.

In payment, it would be interesting to know how high the input frequency can go before this code stops working correctly.

PaulRB
It works almost correctly (frequency jumps/fluctuates a little over the whole range) till 10kHz. Input frequency is steady over the whole range (as seen by scope).

Interesting, thanks.

10,000 interrupts per second, one every 100us, or 1600 clock cycles.

I would expect it to go faster than that, but I guess not 10 times faster, that would only be 160 clock cycles between interrupts.

PaulRB
To be more precise 10kHz at input, so 12kHz at output. If input higher then output locks HI level or LOW level.

My immediate question on reading this thread for the first time is - what are you trying to do? It feels very xyproblem to me - hall sensors on a motor are for commutation principally.

1 Like

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.