0

I am trying to divide a incoming high frequency signal 600kHz by 4 and output wave at arduino MEGA2560 digital pin.

  • The input to arduino comes from incremental Encoder which is connected to motor that runs at 9000 rpm and the generates 1024 pulses per rotation.
  • I am trying to downsample the frequency I am getting from arduino to 250kHz or less.
  • I tried following approaches :

    a. I counted the rising edge of the incoming wave and a interrupt is triggered as number of rising edge reaches certain value and ISR toggle an output pin. b. I tried to get hardware counter work at T0 pin but I was not able to hit ISR when the count has reached specific value.

The problem with approach 1 is: As my frequency of incoming wave increases the output wave starts missing some pulses and generates one half cycle for 6/8/10 pulses of incoming wave. It should actually generate one half cycle for 4 pulses.

Here is approach 2 code:

#define outPin1 10
volatile short encoder = 4;

void setup ()
{
   pinMode (outPin1, OUTPUT);
   Serial.begin(9600);
   cli();
   // reset Timer 0
   TCCR0A = 0;
   TCCR0B = 0;

 }  // end of setup

 ISR(TIMER0_COMPA_vect)
 {
     digitalWrite(outPin1, !digitalRead(outPin1));

 }

 void loop ()
 {
    TCNT0 = 0;      //counter to zero

    // set compare match register to desired timer count:
   OCR0A = encoder;
    // turn on CTC mode:

   bitSet(TCCR0A, WGM01);
   // Timer 0 - counts events on pin D4

  bitSet(TIMSK0, OCIE0A);    // interrupt on Timer 0 CTC
   // start Timer 0
  // External clock source on T0 pin (D4). Clock on rising edge.

   bitSet(TCCR0B, CS02);
   bitSet(TCCR0B, CS01);
   bitSet(TCCR0B, CS00);
}

Please someone help me out with this issue

  • Why would you waste an Arduino on this when you can use a single 10¢ chip? ti.com/product/SN74HC163 – Majenko Apr 5 at 11:57
  • I think the code you put the loop should be in setup. Otherwise you loop code is constantly resetting the counter, back to zero (TCNT0 = 0;) – Gerben Apr 5 at 14:01
  • Couldn't you just attach an interrupt to the input pin, have if count up every time it's called, and set the output to low when the count is equal to 4. 600kHz is probably slow enough to have enough time for the ISR to run it's code. – Gerben Apr 5 at 14:03
3

This is probably too fast to have have software involved in the loop. You should try to use a hardware-only solution. Note the Arduino timers should be usable as hardware frequency dividers:

  • configure a timer to be clocked off the input signal, so that it behaves like a counter.
  • configure it for delivering a PWM output with a period of 4 timer clock cycles and a duty cycle of 50%.

You may try this, but beware I have not tested it:

void setup()
{
    // Configure Timer 5 as a frequency divider:
    //   input: digital pin 47 = PL2 = T5
    //   ouput: digital pin 46 = PL3 = OC5A
    DDRL  |= _BV(PL3);    // set pin PL3 as output
    TCCR5A = 0;           // undo the Arduino core's configuration
    TCCR5B = 0;
    ICR5   = 4 - 1;       // period = 4 timer clock cycles
    OCR5A  = 2 - 1;       // output HIGH for 2 timer clock cycles
    TCCR5A = _BV(COM5A1)  // non-inverting PWM on pin OC5A
           | _BV(WGM51);  // mode 14: fast PWM, TOP = ICR5
    TCCR5B = _BV(WGM52)
           | _BV(WGM53)
           | _BV(CS50)    // clock on rising edges of T5
           | _BV(CS51)
           | _BV(CS52);
}

void loop(){}
  • It worked for one output of encoder but i also have another output from encoder for which i have no input pin to give on mega2560 since on T5 can be used and T0 has not Input capture functionality. I have one idea whether i can use on one input of encoder and generate two wave at output pin with phase shift of 90 degress. Depending on a flag it must be possible for me to change which wave is leading or lagging – Vishal777 Apr 5 at 13:54
  • @Vishal777: Re “T0 has not Input capture functionality”: you don't need input capture. You can use T0 if you don't mind loosing the Arduino timing functions (millis() and co.) which depend on Timer 0. Or you could use T1 if you can solder a header onto the unpopulated footprint JP5. Re generating two waves at 90° from a single timer: I do not think this is possible. – Edgar Bonet Apr 5 at 19:32
  • I am trying to use timer 0 in CTC mode and triggering an ISR which toggles the pin when TCNT0 reaches 4. But without the valid input I am getting a wave at digital output pin 10. I think it is taking internal clock as reference instead of external clock. I have made all CS bits high to select clock from T0 PIN This is strange behavior – Vishal777 Apr 6 at 2:25
  • @Vishal777: Let me insist: “This is probably too fast to have have software involved in the loop.” An ISR is a piece of software, so forget about it. – Edgar Bonet Apr 6 at 7:23
  • I could successfully divide by 4 using 2 timers T0 and T5. As per my existing setup I get input wave of less than 200kHz. I have not tested for 600kHz input wave – Vishal777 Apr 6 at 8:20
0

I am writing another answer because what I propose here is radically different from the previous answer. In that answer I wrote that this is too fast for being done in software, and here I am going to contradict myself. :-/

At 600 kHz, the input stays low for roughly 13 CPU cycles, then high for another 13 cycles. You cannot do much in 13 cycles. Notably, the prologue of an ISR can quite often take more than this. Also, whenever the Timer 0 overflow ISR from the Arduino core fires, it can delay your own ISR for way more than 13 cycles. That's why it is usually illusory to expect any job to be reliably done in this time frame. Especially if interrupts are involved.

That being said, if the CPU has absolutely nothing else to do. And specifically if it is never interrupted, Then it can keep up with that pace. Here is a program that should do what you expect. I initially thought about writing it in assembly, but it can also be written in C, as the compiler translates it in a quite straightforward way. If in doubt, however, check the generated assembly:

/*
 * From one square wave generate two waves in quadrature at 1/4 of the
 * original frequency.
 *
 * https://arduino.stackexchange.com/questions/63202
 *                               _   _   _   _   _   _
 *  input:  digital 13 = PB7    / \_/ \_/ \_/ \_/ \_/ \_/
 *                               _______         _______
 *  output: digital 10 = PB4    /       \_______/       \
 *                                   _______         ____
 *  output: digital 11 = PB5    ____/       \_______/
 */

int main(void)
{
    DDRB = _BV(PB4) | _BV(PB5);  // PB4 and PB5 as outputs
    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, PB7);  // wait for rising input
        loop_until_bit_is_set(PINB, PB7);
        PORTB |= _BV(PB5);                   // rise 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(PB5);                  // fall PB5
    }
}

Note that, since this is not using the Arduino core, I wrote main() rather than setup() and loop(). This overrides the main() provided by the core, thus you don't have the initializations that come with it and, in particular, you don't have the Timer 0 interrupt.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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