0

In modern timekeeping, there is a 10 MHz reference frequency (or any power of 10). The digital clock takes 32768 Hz frequency only (or some other power of 2 possible).

The problem is how to convert power of 10 to power of 2 nicely. It is done by a series of 1.25 (every 5 inputs output 4) and 2.5 dividers (every 5 inputs output 2).

I am interested in how this can be done by Arduino. I have a high input frequency, say 1 MHz, therefore i have to use registers directly. Then I have output frequency 1/2 (counter of 2) of the input and 1/4 of the input frequency and I would like to have output 4/5 of the input (divide by 1.25 counter). So, do I need 3 output pins, 1 for counting twos, one for fours, and one for fives? Or can this be done somehow just by 1 input pin and 1 output pin with 4/5 input frequency?

I tried to modify the code but I can not tell how to skip every 5th cycle. Let's say in the chaining of these counters I can get from 1 000 000 Hz frequency: 1000000 Hz - 800000 Hz - 640000 Hz - 512000 Hz - 409600 Hz - 327680 Hz - 2262144 Hz so input = power of ten, then output = power of two My question is how high could be the maximal input frequency to this code? and can you please help me to modify that code?

/*
 * From one square wave generate 3 waves 1/2, 1/4 and 4/5 of the original frequency.
 *
 * Original sketch tried to be modified from here:
 *
 * https://arduino.stackexchange.com/questions/63202
 *                               _   _   _   _   _   _   _   _   _   _
 *  input:  digital 13 = PB7    / \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/  1 000 000 Hz
 *                              R1F0R1F0R1F0R1F0R1F0R1F0R1F0R1F0R1F0R1F0R
 *                               ___     ___     ___     ___     ___
 *  output: digital 10 = PB4    /   \___/   \___/   \___/   \___/   \___/    500 000 Hz
 *                              R 1 F 0 R 1 F 0 R 1 F 0 R 1 F 0 R 1 F 0 R  
 *                               _______         _______         _______
 *  output: digital 11 = PB5    /       \_______/       \_______/       \    250 000 Hz
 *                              R   1   F   0   R   1   F   0   R   1   F
 *                               _   _   _   _       _   _   _   _   
 *  output: digital 12 = PB6    / \_/ \_/ \_/ \_____/ \_/ \_/ \_/ \_____/    800 000 Hz
 *                              R1F0R1F0R1F0R1F00000R1F0R1F0R1F0R1F00000R
 */

int main(void)
{
    DDRB = _BV(PB4) | _BV(PB5) | _BV(PB6) | ~_BV(PB7);  // PB4 PB5 PB6 as outputs and PB7 as input
    for (;;) {
        loop_until_bit_is_clear(PINB, PB7);             // wait for rising input
        loop_until_bit_is_set(PINB, PB7);
        PORTB |= _BV(PB4);                              // rise PB4

        loop_until_bit_is_clear(PINB, PB4);             // wait for rising input
        loop_until_bit_is_set(PINB, PB4);
        PORTB |= _BV(PB5);                              // rise PB5

        loop_until_bit_is_clear(PINB, PB5);             // wait for rising input
        loop_until_bit_is_set(PINB, PB5);
        PORTB |= _BV(PB6);                              // rise PB6

        loop_until_bit_is_clear(PINB, PB7);             // wait for rising input
        loop_until_bit_is_set(PINB, PB7);
        PORTB &= ~_BV(PB4);                             // fall PB4

        loop_until_bit_is_clear(PINB, PB7);             // wait for rising input
        loop_until_bit_is_set(PINB, PB7);
        PORTB |= _BV(PB4);                              // rise PB4

        loop_until_bit_is_clear(PINB, PB4);             // wait for rising input
        loop_until_bit_is_set(PINB, PB4);
        PORTB |= ~_BV(PB5);                             // fall PB5

        loop_until_bit_is_clear(PINB, PB7);             // wait for rising input
        loop_until_bit_is_set(PINB, PB7);
        PORTB &= ~_BV(PB4);                             // fall PB4

        loop_until_bit_is_clear(PINB, PB7);             // wait for rising input
        loop_until_bit_is_set(PINB, PB7);
        PORTB |= _BV(PB4);                              // rise PB4

        loop_until_bit_is_clear(PINB, PB4);             // wait for rising input
        loop_until_bit_is_set(PINB, PB4);
        PORTB |= _BV(PB5);                              // rise PB5

        loop_until_bit_is_clear(PINB, PB5);             // wait for rising input
        loop_until_bit_is_set(PINB, PB5);
        PORTB |= ~_BV(PB6);                             // fall PB6

        loop_until_bit_is_clear(PINB, PB7);             // wait for rising input
        loop_until_bit_is_set(PINB, PB7);
        PORTB &= ~_BV(PB4);                             // fall PB4

        loop_until_bit_is_clear(PINB, PB7);             // wait for rising input
        loop_until_bit_is_set(PINB, PB7);
        PORTB |= _BV(PB4);                              // rise PB4

        loop_until_bit_is_clear(PINB, PB4);             // wait for rising input
        loop_until_bit_is_set(PINB, PB4);
        PORTB |= ~_BV(PB5);                             // fall PB5

        loop_until_bit_is_clear(PINB, PB7);             // wait for rising input
        loop_until_bit_is_set(PINB, PB7);
        PORTB &= ~_BV(PB4);                             // fall PB4
    }
}

Nick Gamon personally :D I saw you so many times here. what an honour. I do need a source clock for multiplanetary clock, without fractions. The least common multiple for periods of second for: Earth, Mars, Titan.

If I will keep the ratio between planets exactly, then even if the Earth clock using Arduino resonator will drift by a few tens of seconds per day, Mars clock and Titan clock will drift the same amount of time.

This does not matter to me because if I stop the clock and wait then All 3 planets will be aligned again. But if I would not keep the ratio between planets exactly, for Example Earth would drift 30 seconds per day, Mars would drift 31 seconds per day, and Titan would drift 29 seconds per day- it depends on how close approximation I will take. So, by stopping the clock for 30 seconds to set it correctly, Earth would go back to 0 seconds, but Titan would be 1 second behind and Mars would be 1 second before. I would not correct all 3 just by stopping the clock. I would have to stop the clocks 3 times individually (once for each planet, if not, the error for the 2 remaining planets would accumulate over time, even if correcting Earth) And trust me, it's not easy to calculate Mars time (ok it is because NASA application already exists) but Titan Time I have only in my Excel, and that one I have to recalculate when setting the clock.

Periods for Earth: 1 s, Mars 1.027 491 251 7 s, Titan 0.998 068 437 5 s,

Obviously, Arduino math is not exact to the 10 decimal places, maximally 32 bytes which is 0.000 000 000 2 and something ... that is 2 to -32nd power.

The options for least common multiple:
Earth:Mars = 291/299, Earth:Titan = 3624/3617
Earth:Mars:Titan = integer:integer:1 054 584 (that is 291 x 3624)

so 1st option, is to generate 1 054 584 Hz (I call it golden Megahertz) which is slightly more than 15 Arduino clock cycles = to be exact 15 and 16 cycles would have to alternate with some leap cycle rule (analogous to our leap year rule) to get this frequency.

Find another, decimal frequency slightly off.

Option 1:
Earth:Mars = 812993/800000 and Titan could be 1/4 cycle off, so 800 kHz

Option 2:
Earth:Mars:Titan = integer:integer:3200000 so 3.2 MHz (5 clock cycles) and Mars, Titan, Earth are better aligned,

Option 3: 9600000 Hz frequency (5 clock cycles on 48 MHz Arduino Zero exactly, or 10 cycles on overclocked 96 MHz Arduino Due), and perfect planet alignment.

These options need to have 24 bit counter, up to 16777216 not just 16 bytes. If I don't keep the ratio exactly, the clock would drift at different speeds and to correct the clock to stop all 3 clocks at once would not be enough.

Each one would have to be corrected individually (even though Earth would have the biggest error from Arduino resonator, some residual errors from not exact ratios will accumulate over time, and once in a while, additional 2 clocks would have to be stopped and set individually)


Also, i would like to have something like calendar for each planet, what would the fastest and shortest way to configure it? Earth: 365.2425 days, Mars 668.592 days and Titan 673.54388 days. For Earth we have 365 plus every 4th leap but every 100th not leap but every 400th leap. Days in month are 31,28/29,31,30,31,30,31,31,30,31,30,31. For Mars we would have: 56,56,55,56,56,55,56,56,55,56,56,55/56, and every odd and every 10th leap (thats .6) but not every 100 (thats .59 )but every 500 leap (thats .592). Leap month would be 12-th month. For Titan we would have 56,56,56,56,56,57,56,56,56,56,56,56/57, and every odd and every 20th leap (thats .55) but not every 160 th leap (thats .54388). Also, that lenghth mentioned is not 1 Titan day, but 1/16th Titan day, yes Titan day is almost 16 days long. So, i would like to have something like "days in a day" = 8 days week of 24 hours of 60 minutes of 60 seconds of 0.98 above mentioned earth seconds, and AM and PM week. The 8th day would be called "Earthday and would be between Sunday and Monday". For Mars we would have 7 day weeks as on Earth, and in a similar way, Monday 1st, Tuesday 2nd, .... But for Titan we would have 16 times 1st: AM Monday 1st, AM Tuesday 1st, ... AM Sunday 1st, AM Earthday 1st, PM Monday 1st, PM Tuesday 1st, ... PM Sunday 1st, PM Earthday 1st, and then 16 times 2nd, ... again from AM Monday till PM Earthday. So we can divide terribly long 16 day day into weeks and days. do we have some reasonable function like RTC where i in/out put (year, month, day, week, weekday, hour, minute, second) when i in/out put "unix time" for Earth, Mars and Titan based on different length of seconds? We would have Unix Earth, Unix Mars and unix Titan. Obviously for Earth and Mars, the weekday and date would advance simultaneously and we dont need "week" for Earth and Mars (that would be dislabled) and for Titan there are 8 weekdays and then 2 weeks and then 1 titan day (2 weeks long) so yes 16 days day and 673.54 of them thats almost 30 years long year. the formulas among Earth Mars and Titan Unix time have to be ready yet, but how to break that unsigned long unix time for each planet into the components, and one more (week) component for Titan, an titan day is awfully long, do weekday (and week) here is time, not day, but day within a day, and week within a day.


Nick Gamon, i dont want to be disrespectful, this is my very first question after several years of reading these forums.

There is indeed a capability of Arduino Uno r4 generating 1/256 sec interrupt directly, as there is dedicated library for that, using its bult in RTC clock, which are already part of that Arduino.

Unfortunately, the nice round multiple for Earth, Mars, Titan would be 3.2 million Hz. As Arduino 4 is 32 bit processor 48 MHz, it does math up to 32 bits within one or two clock cycles. and 3.2 million Hz would be exactly 15 clock cycles, however that not much cycles for interrupt. But 256 Hz is plenty of time for 48 MHz 32 bit processor right? im not sure how exact are that RTC clock, need to find out as soon as ill get it. That Arduino uno R4 appeared just few days ago in the shop and its totally new and different. (32 bit, 48 MHz)

1/256 th of second is 12500 / 3200000 second. so Thank god 3.2 million is divisible by 256 without any fraction.

I am enclosing the example for Arduino r4 documentation:

https://docs.arduino.cc/tutorials/uno-r4-wifi/rtc#unix

#include "RTC.h"

const int LED_ON_INTERRUPT  = 22;

void setup(){
  RTC.begin();
  if (!RTC.setPeriodicCallback(periodic_cbk, Period::ONCE_EVERY_2_SEC)) {
    Serial.println("ERROR: periodic callback not set");
  }
}

void loop() {
}

void periodic_cbk() {
  static bool clb_st = false;
  if(clb_st) {
    digitalWrite(LED_ON_INTERRUPT,HIGH);
  }
  else {
    digitalWrite(LED_ON_INTERRUPT,LOW);
  }
  clb_st = !clb_st;
 
  Serial.println("PERIODIC INTERRUPT");
}

The period can be specified using the following enumerations:

ONCE_EVERY_2_SEC
ONCE_EVERY_1_SEC
N2_TIMES_EVERY_SEC
N4_TIMES_EVERY_SEC
N8_TIMES_EVERY_SEC
N16_TIMES_EVERY_SEC
N32_TIMES_EVERY_SEC
N64_TIMES_EVERY_SEC
N128_TIMES_EVERY_SEC
N256_TIMES_EVERY_SEC

Now, I am also strugglling with how to incorporate that calendar from Unix time, to break it into individual components for Earth, Mars and Titan. Unix time began on 01/01/1970 00:00:00, unix Mars would began on 01/01/192 00:00:00 and Titan Unix would began on 01/01/13 AM Monday 00:00:00, but i would like to have the initial years adjustable because. It will eventually run out of range after 136 earth years, 72 Mars years and some 4.6 Titan years so we could re-use it by shifting initial year, and also, i would like to have it valid at least till 2323, so, only with resolution 4 second, because 300 years has less than 2 to the 32th 4-second intervals. But that calendar is giving me a headache. I saw RTC library but i cant just change numbers to get Mars, and Titan calendar.

The reason i would like to have it, is because in some point in the future, the human colonisation of Mars will begin, and in more distant future, the human colonisation of Titan (Saturns moon) will begin (there is already a nuclear powered helicopter which will operate in Titan high density atmosphere and low gravity and very cold environment, in 2034 (the date is already approved by NASA, it will lift off in 2027, but as u can suppose the flight takes 7 years to cover one billion miles, or 1.6 billion kilometers to Saturn), and plan for nuclear powered submarine which will swim in its liquid methane seas, not sure when, but the concept already exists, its close to liquid nitrogen temperature, liquid methane has temperature of minus 180 degrees Celsius or like minus 300 degrees of Farenheit, so heating by and insulation have to be perfect) and for enery planet we do need different calendar


If i would have to be capable to construct PLL (phase-locked-loop) then i would have to be able to go from 32768 Hz exactly to 3.2 MHz (as exactly as the 32768 Hz crystal is exact), by 5 times multiplying it by 2.5 that is (multiply by 5 and divide by 2). Lol I could not do 5 times multiply by five because i would get 102.4 MHz (and then divide it 5 times by 2) and not even Arduino due is that fast enough to catch that interrupt. But pure TTL logic is capable to to do it (assuming it can multiply by fractions, that is 2.5 to the 5th power)


Hi guys, the lcd keypad shield 1602 A is compatible with which library and which arduino? how the pins have to be set? I just cant make it working

13
  • 1. What Arduino are you using? 2. You may, using a hardware timer, convert 1 MHz to 64 Hz (i.e. divide by 15625). Would that be an acceptable frequency to you? Jul 7 at 11:40
  • It's unclear what you are trying to do. Are you trying to make a clock that ticks every second, given a 1 MHz clock rate? Or are you trying to measure some unknown input signal and report on its frequency?
    – Nick Gammon
    Jul 7 at 11:55
  • If I had to go this way, I'd probably go with counting fractions and occasionally extend period by one (when it gets over decimal point). Eg. 16MHz / 488.28125 = 32768 -> fract += 28125; if (fract > 100000) { fract -= 100000ul; period = 489; } else { period = 488; }
    – KIIV
    Jul 7 at 18:36
  • @Peter Thank you for the compliment. Your "answer" is really supporting material for your question so I have respectfully appended it to your question as additional information.
    – Nick Gammon
    Jul 7 at 21:49
  • Thank you Nick ... Well im here years... and never talked to you personally. do u know where on Github i can see RTC library for Arduino Uno R4 which is capable of generating 1/256 s interrupts? I would like to see if i can get from there somehow 1/32768 s interrupts, if it is possible so i would like to see that particular RTC library for Arduino Uno R4 which is bulit-in there so no need to install. But before i get it i would like to see that library
    – Peter
    Jul 9 at 19:14

2 Answers 2

4

Your question is a perfect example of an XY problem: you are asking something that has little to do with your actual problem, but is rather about your misguided idea of a solution. Counting Mars seconds or Titan seconds is an easy problem. Dividing the frequency of a 1 MHz signal by successive powers of 1.25 is a hard problem. There is little value in turning an easy problem into a hard one.

I suggest a solution inspired by Bresenham's algorithm for drawing slanted lines:

  • configure a timer to deliver a periodic interrupt to your program, with a period shorter than your shortest relevant second
  • compute the ratio of your favorite planet's second to the interrupt period
  • approximate this ratio by a rational number p/q

In the interrupt service routine:

  • increment a counter by q
  • when the counter reaches p, decrement it by p and note that one second has elapsed.

The lazy way of getting a periodic interrupt is to simply enable the TIMER0_COMPA interrupt. Timer 0 is already configured by the Arduino core to count full cycle every 1,024 µs. You may want to set OCR0A to something near 128 in order to prevent the overflow interrupt (used by Arduino for timing) and your interrupt from getting too close.

For the Earth, the ratio is

1 s / 1,024 µs = 1,000,000 / 1,024 = 976.5625 = 15625 / 16

For Mars and Titan, they can be approximated by 22,075/22 and 33,139/34 respectively, to within a fraction of a ppm.

Here is the interrupt service routine:

// Count of elapsed seconds on each planet.
volatile uint32_t earth_secs, mars_secs, titan_secs;

// Executed every 1,024 us on a timer interrupt.
ISR(TIMER0_COMPA_vect) {
    static uint16_t earth_ticks, mars_ticks, titan_ticks;
    earth_ticks += 16;
    if (earth_ticks >= 15625) {
        earth_ticks -= 15625;
        earth_secs++;
    }
    mars_ticks += 22;
    if (mars_ticks >= 22075) {
        mars_ticks -= 22075;
        mars_secs++;
    }
    titan_ticks += 34;
    if (titan_ticks >= 33139) {
        titan_ticks -= 33139;
        titan_secs++;
    }
}

Note that you can build better approximations of Mars and Titan seconds by using rational numbers with larger p and q, but then you would have to do 32-bit arithmetics in the ISR. You could also configure another timer to deliver an interrupt with a longer period: you would then be able to achieve better accuracy with 16-bit arithmetics, at the cost of increased jitter.


Edit: Here is a variation on this idea that uses millis() instead of interrupts. Of course, Nick Gammon is right: you do not need interrupts for a clock that updates its display a few times per second. It was just a bit easier to write an interrupt-based code, as the time between successive calls of the function is a known constant.

Given that you are going to use a 32-bit processor, I wrote this using 32-bit arithmetic. The ratios should be a little bit more accurate.

uint32_t earth_seconds, mars_seconds, titan_seconds;

void update_planet_times() {
    // Compute number of elapsed milliseconds since last update.
    static uint32_t last_update;
    uint32_t now = millis();
    uint32_t elapsed_millis = now - last_update;
    last_update = now;

    // Update Earth time. 1 Earth second = 1000 / 1 ms.
    static uint32_t earth_ticks;
    earth_ticks += elapsed_millis * 1;
    uint32_t elapsed_earth_seconds = elapsed_earth_ticks / 1000;
    earth_ticks -= elapsed_earth_seconds * 1000;
    earth_seconds += elapsed_earth_seconds;

    // Update Mars time. 1 Mars second = 763426 / 743 ms.
    static uint32_t mars_ticks;
    mars_ticks += elapsed_millis * 743;
    uint32_t elapsed_mars_seconds = elapsed_mars_ticks / 763426;
    mars_ticks -= elapsed_mars_seconds * 763426;
    mars_seconds += elapsed_mars_seconds;

    // Update Titan time. 1 Titan second = 3193819 / 3200 ms.
    static uint32_t titan_ticks;
    titan_ticks += elapsed_millis * 3200;
    uint32_t elapsed_titan_seconds = elapsed_titan_ticks / 3193819;
    titan_ticks -= elapsed_titan_seconds * 3193819;
    titan_seconds += elapsed_titan_seconds;
}

Note that using this technique for the Earth may seem superfluous: one may count seconds by simply computing millis()/1000. This, however, would fail when millis() rolls over in 49.7 days. The code above is immune to this rollover, and should count Earth seconds reliably for the next 136.1 years.

7
  • Yes, Thank you, and i am planning to get Arduino Uno R4 which is capable to generate - fastest - 1/256 s interrupts using its in-built RTC - Clock, so this would also probably help. just numbers P,Q would be different right?
    – Peter
    Jul 9 at 18:37
  • 1
    @Peter: Right. As stated in my answer, you have to compute the ratio of a planet's second to the interrupt period, then approximate this by a rational number. This Wikipedia section on Best rational approximations gives you a technique for computing the rational number p/q. Jul 9 at 19:22
  • @Peter Regarding the 1/256 s comment, please see my expanded answer.
    – Nick Gammon
    Jul 9 at 22:07
  • @Nick, I saw the answer, however, if Uno R4 has RTC clock, this clock will be way more exact than millis which probably takes the clock from resonator (or whatever else is attached to Uno R4 microprocessor), not sure, thats my guess.
    – Peter
    Jul 11 at 6:45
  • @Edgar, As Uno R4 has the built in RTC clock, i think i should use advantage to take the 1/256 s interrupt from way more precise RTC clock. I dont think millis are attached to that RTC clock, just they are attached to the 48 MHz clock, using less precise resonator or whatever is attached to that chip as clock.
    – Peter
    Jul 11 at 6:48
0

What you are suggesting is far too complex. Use the internal timers, say timer 0, 1 or 2 on the Atmega328P (Arduino Uno) with a prescaler of 64 (which gives you 4 µs per timer tick) and then count up to 250, giving you a period of 1 ms.

The timer can be configured to toggle an output pin, giving you this frequency as an output. If you are toggling you may want to halve what you count to (one count for the leading edge and the second count for the trailing edge). Or use PWM mode with a duty cycle of 50%.


From a comment to a different answer:

I am planning to get Arduino Uno R4 which is capable to generate - fastest - 1/256 s interrupts using its in-built RTC - Clock

Not at all. The Uno timers can be configured to interrupt at practically any rate (the limit being how fast the interrupt routine can execute).

The software from the IDE configures Timer 0 to interrupt once every 1.024 ms, which is a lot more often than 1/256 s. However you can reconfigure that, or indeed the other timers, to interrupt at virtually any interval, within reason.

See my page about timers for more information.

I could easily give you code that would generate an interrupt every 1/256s or indeed 1/32768s but I don't see how those rather unusual numbers, from the point of view of the planets, would help you.

In fact, probably better from your point of view is to simply keep track of elapsed time and do your calculations from that. For example, millis() tells you elapsed time since startup in milliseconds and will wrap around every 49.710 days. Or micros() which tells you the elapsed time in microseconds, and will wrap around every 71.58 minutes.

Now, you would need to handle wrap-around in any case, so that is no big deal.

It seems to me that your calculations for the planets' positions would simply be to take the current time (eg. using millis()) and then apply some arithmetic for each planet to work out where it must be.

Your desire to get interrupts, and your focus on 32768 as an interrupt interval seems to me to be be an XY problem. You know what they say, "if the only tool you have is a hammer, everything looks like a nail". Well, you have more tools than a hammer here.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.