PWM converter, need help

Hey all!

I've done some basic AVR programming before, but I'm not super savvy... that's why I need some help from the community with UNO.

I have a little project where I need to capture PWM signal ranging from 0Hz to 240Hz and convert it to 0Hz to 150Hz. Separately I'm able to capture PWM using TCCR1 and ICR1 and to create 1Hz to 150Hz PWM using TCCR1 but I don't know how to combine these two routines. It seems that I cannot capture input using any other timers and I'm having issues producing low frequency PWM using anything besides timer counter 1 as well:(

Is there a trick that I could use? I will be very grateful for any help!

In preparation for the trick: Post your code in question.

2 Likes

Interesting puzzle as for both you need the same timer. Should be easy with the Atmega328PB, as it has 2 extra 16 bit timers, but you have to do it with one.

Thinking out loud:
Is that input frequency changing slowly, like the tachometer of an engine or a odometer?
In that case the input what you capture will most likely fit within one cycle that you generate, as you are generating a lower frequency than what your input is. And since you know what frequency you generate, I suspect you could still calculate the pulsewidth of the incoming pulse.

I I were in your situation I would define a pin change interrupt connected to the rising edge of your incoming pulse signal.
In the pinchange ISR you would have to take a snapshot of the timer value and store that for the next time you ener the ISR.
When in the ISR again calculate the amount of timer ticks that have passed since the last time you were in the ISR.
Two possibilities
a) previous snapshot < current timer value (timer did not restart) period = "timer value" - "previous snapshot"
b) previous snapshot > current timer value (timer restarted inbetween) period = "timer value" + TOP - "previous snapshot"

where TOP is the timer top that you change to generate your desired output frequency.

1 Like

I will post my sketch tonight, thanks for the suggestion!

Hey! Actually it is for a speedometer, instead of buying Kph to Mph converter I first want to try to make own. So yes, good guess :slight_smile:

That sounds interesting! I'm not that proficient in programming so it will take me some time to figure that out, but thanks for the idea! I will try to implement it!

What is the max voltage behind the input PWM signal?

You can use a 328P based board without issue if the max voltage is under 5V. Just run it into an analog pin.

  • analog read the input pin
  • divide that value by 1.609344 (you will need to be careful with the result as you are dividing an int type number by a float)
  • analog write the result to a PWM pin as your output

Should be responsive enough for a gauge and fairly simple to program.

1 Like

Believe it or not, but I just got this idea as well! I thought that I could use ADC since it is 0-5v signal.
Thanks for the suggestion!!

Post efforts in this thread for further help and to aid future users, please.

1 Like

While you are posting your code using code tags also post an annotated schematic, this will help us understand what you are asking.

I moved your topic to an appropriate forum category @mech314.

In the future, please take some time to pick the forum category that best suits the subject of your topic. There is an "About the _____ category" topic at the top of each category that explains its purpose.

This is an important part of responsible forum usage, as explained in the "How to get the best out of this forum" guide. The guide contains a lot of other useful information. Please read it.

Thanks in advance for your cooperation.

1 Like

Here are the code snippets that've used to read or produce PWM.
Read:

int Hz;
void setupTimer() {   //  timer1
  TCCR1A = 0;      // 
  TCCR1B = 132;    // (10000100) Falling edge trigger, Timer = CPU Clock/256, noise cancellation on
  TCCR1C = 0;      // 
  TIMSK1 = 33;     // (00100001) Input capture and overflow interupts enabled
  TCNT1 = 0;       // start from 0
}

ISR(TIMER1_CAPT_vect) {    // Interupt when pulse is detecte
  revTick = ICR1;      // save duration
  TCNT1 = 0;       // restart timer
}
ISR(TIMER1_OVF_vect) {    // counter overflow/timeout
  revTick = 0;  // Ticks per second = 0
}
void setup() { 
  pinMode(9, OUTPUT);                               // Set digital pin 9 (D9) to an output
  TCCR1A = _BV(COM1A1) | _BV(WGM11);                // Enable the PWM output OC1A on digital pins 9
  TCCR1B = _BV(WGM13) | _BV(WGM12) | _BV(CS12);     // Set fast PWM and prescaler of 256 on timer 1
  ICR1 = 62499;                                     // Set the PWM frequency to 1Hz: 16MHz/(256 * 1Hz) - 1 = 62499
  OCR1A = 31249;                                     // Set the duty-cycle to 50%: 62499 / 2 = 31249
}
void loop() {}

Hi, @mech314
Welcome to the forum.

Do you wish to convert the changing frequency? 50% duty cycle.
OR
Do you wish to convert the changing frequency and changing duty cycle?

Thanks... :grinning: :+1: :coffee: :australia:

Hey, thanks for warm welcoming!

I only need to change frequency and retain 50% duty cycle.

how are you mapping those two frequency ranges?

[0 to 240 period] * 240/150 = [0 to 150 period]

Can you give this a try?

input capture on pin PB0 (Arduino D8)
Output of stretched PWM on pin PB2 (Arduino D10)

void setup() {
  TCCR1A = 1 << COM1B1 | 1 << WGM11 | 1 << WGM10;
  TCCR1B = 1 << ICNC1 | 1 << ICES1 | 1 << WGM13 | 1 << WGM12 | 1 << CS12;
  TIMSK1 = 1 << ICIE1 | 1 << TOIE1;
  DDRB = 1 << DDB2;
  OCR1A = 0xffff; // start at max value for timer
  OCR1B = OCR1A / 2; // 50% dutycycle
  ICR1 = 0;
}

ISR(TIMER1_CAPT_vect) {
  static uint16_t previous_icr;
  uint16_t timer_ticks;
  if (ICR1 <= previous_icr) timer_ticks = (OCR1A - previous_icr) + ICR1;// calculate period if timer did overflow
  else timer_ticks = ICR1 - previous_icr;// timer did not overflow
  OCR1A = constrain((timer_ticks * 240UL) / 150, 0, 0xffff); // stretch the period, but not more than 16 bits can hold
  OCR1B = OCR1A / 2; // 50% dutycycle
  previous_icr = ICR1;
}

void loop() {
}

[edit] I did set on the overflow interrupt, but didn't use it in the end.

visualization, how I configured Timer1

If you don't want to mess around with floats, you can use 64 miles = 103 km (very close, correct to within 7 feet in the 64 miles) to convert. So, just multiply the kilometers by 64, and then divide by 103 to get miles. Important: you don't want your analog read multiplied by 64 to exceed the capacity of an int (an int will only go up to 32767), so multiply by 64U to get an unsigned int (which can go up to 65535) instead of an int. Or multiply by 64UL to get an unsigned long (which can go up to 4 billion).

1 Like

Hey Hmeijidam,

Your code works! Thank you very much!
Interestingly, initially when I was determining the frequency for each speed on the speedometer I used Atmega168 to output 50% PWM at different HZ and this is where I found 0 - 240 Hz, however - today I quickly used NE555 timer, which I don't trust as much and I found much lower frequency resulting in the same speed reading.

I found that at lower speeds the conversion is perfect, 60 Kph = 40 Mph, however 160 Kph = 108 Mph. It is not entirely clear who is at fault - converter or speedometer error when I set 160 Kph - this needed to be tested using GPS (probably not going to do that, way to fast to do on normal roads).

The website with timer visualization is amazing, thanks for showing it! I will definitely use in future projects!

I'm reading other replies in the thread, I will adjust accordingly later when I check what is the actual frequency coming from the speed sensor. I will post later when I install new speedometer on my car, that probably won't happen in the nearest couple of weeks but I will post the results!

Guys, you are the best! I am very grateful!!

P.s. Now I need to read it carefully and figure out how it works

Hi, @mech314

160kph would be 106.6 Mph, the difference will be the speedo itself.
Don't forget how accurate is your speedo, with tyres wearing, changing diameter.

Tom... :grinning: :+1: :coffee: :australia:

@mech314

Also, 60 km/h should be 37.3 mph (not 40 mph).

1 Like