2

Requirement:

  • I have input 4KHz pulse input
  • Divide it's frequency in half
  • Arbitrarily adjust phase
  • Arbitrarily adjust duty
  • Out put the modified pulse

My solution (CPU):

uint8_t cnt = 0;
uint16_t t1 = 235;
uint16_t t2 = 5;
String cmd;
void setup() {
 pinMode(3, OUTPUT);
 pinMode(2, INPUT);
 
 Serial.begin(9600);
 delay(500);
 attachInterrupt(digitalPinToInterrupt(2), rising_edge, RISING);
 Serial.println("PWM init");
}
void loop() {
 if (Serial.available() > 0) {
 cmd = Serial.readString();
 if (cmd.substring(0,8) == "setdelay"){
 t1 = cmd.substring(9).toInt();
 Serial.print("setting delay to ");
 Serial.print(t1);
 Serial.println(" us");
 }
 else if (cmd.substring(0,7) == "sethigh"){
 t2 = cmd.substring(8).toInt();
 Serial.print("setting high time to ");
 Serial.print(t2);
 Serial.println(" us");
 }
 Serial.flush();
 }
}
void rising_edge() {
 cnt = cnt + 1;
 if (cnt >= 2) { //divide by 2
 delayMicroseconds(t1);
 digitalWrite(3,HIGH);
 delayMicroseconds(t2);
 digitalWrite(3,LOW);
 cnt = 0;
 }
}

However this makes heavy use of CPU, and I need to use CPU for something else.Is it possible to implement all this with TC ?

Thanks in Advance !

asked Jan 29, 2021 at 12:52
1
  • You could use the incoming clock pulses as an external (asynchronous) clock source for timer2. See the ASSR register in the datasheet, for starters. You can't change the duty cycle with only this method. Though you use use an additional timer in "one-shot mode", but that would still require interrupts. Commented Jan 29, 2021 at 14:09

1 Answer 1

1

this makes heavy use of CPU, and I need to use CPU for something else

The issue with this code is that is heavily uses the CPU for busy waiting rather than for doing useful work. You should try to get rid of those waiting loops:

For the pulse generation, you can keep roughly the same logic as your existing code: output a single pulse every other time a rising edge is sensed on the input. This pulse can be conveniently generated by Timer 1 on pin 9 by using a PWM mode and stopping the timer after a single cycle. I would use inverting PWM mode, with the timer starting at zero on the input pulse:

  • at t1 microseconds, a compare match sets the output HIGH

  • at t1+t2 microseconds, the timer overflow sets the output LOW, and the overflow interrupt stops and resets the timer

Here is a tentative, untested implementation:

// Set the pulse timings. To be called from loop() on user's request.
void set_timings(uint16_t t1, uint16_t t2)
{
 uint16_t ocr1a = t1 * 16 - 1; // initial time in LOW state
 uint16_t icr1 = (t1 + t2) * 16 - 1; // timer period
 noInterrupts(); // protect critical section
 OCR1A = ocr1a;
 ICR1 = icr1;
 interrupts();
}
// Configure Timer 1. To be called from setup().
void setup_timer() {
 DDRB |= _BV(PB1); // set pin digital 9 = PB1 = OC1A as output
 TCCR1A = _BV(COM1A0) // PWM on OC1A, inverting mode
 | _BV(COM1A1) // ditto
 | _BV(WGM11); // mode 14: fast PWM, top = ICR1
 TCCR1B = _BV(WGM12) // ditto
 | _BV(WGM13) // ditto
 | 0; // stopped
 TCNT1 = 0; // reset
 TIFR1 = _BV(TOV1); // clear overflow flag
 TIMSK1 = _BV(TOIE1); // enable overflow interrupt
 set_timings(235, 5); // initial timings
}
// Interrupt triggered by a rising edge of the input.
void on_rising_edge() {
 static uint8_t count;
 if (++count % 2) return; // divide by 2
 TCCR1B |= _BV(CS10); // start timer, clock @ F_CPU
}
// Stop and reset the timer when it overflows.
ISR(TIMER1_OVF_vect) {
 TCCR1B = 0; // stop
 TCNT1 = 0; // reset
}
answered Jan 29, 2021 at 14:05

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.