Complex PWM-like timer problem

I want to generate a complex timer code on an ATmega328P for controlling a vibration pump. The timer code (similar to PWM but more complex) has the following properties: a repeating on-pulse with a length from 0.5 ms to 10 ms ("pulse width"), with a pause between successive on-pulses which can vary in the range 10 ms to 190 ms ("pause"). The aim is to be able to set the pulse width and pause through variables in the program, and the Arduino will then generate the required waveform. Normal timing functions of the arduino should not be interferred with.

Does somebody know if it is possible to do this using Timer1 or Timer2? Ideally I would like to use Timer2 on Arduino pin 3 (OC2B), but if necessary I could use Timer1 on pin 10 (OC1B).

I am speculating it might be possible with Timer2 in non-PWM mode and maybe toggle OC2A/B on compare match. Is there any way of doing a compare match on both OC2A and OC2B for the same waveform output, setting pulse width from OC2A and pause from OC2B?

Essentially all I need to do is have a counter to count from BOTTOM up to TOP1 for the on-pulse width, which starts a second counter to count from BOTTOM up to TOP2 for pause, which then restarts the first timer.

It is not so critical whether the timing for the pause is counted from the beginning of the on-pulse or the end of the on-pulse (or even from a fixed point 10ms after the start of the on-pulse). More critical is that I can get the on-pulse fixed in the manner outlined (not as a proportion of the total period, as in PWM), and that I can set the pause time from about 10ms to about 190ms.

I have Ken Sherriff's excellent article (Secrets of Arduino PWM) but that does not cover non-PWM waveforms. I also have the 328 datasheet of course, but that is pretty tough going. Can someone kindly set me on the right track?

Many thanks.

"a length from 0.5 ms to 10 ms ("pulse width"), with a pause between successive on-pulses which can vary in the range 10 ms to 190 ms ("pause")."

That's pretty slow for a 16MHz part.
You could use blink without delay like code, every 500uS check if its time to make some change in output.

So something like this:

unsigned long currentMicros = 0;
unsigned long previousMicros = 0;
unsigned long elapsedMicros = 0;
unsigned long duration = 100;
void setup(){
whatever you had here
}
void loop(){
currentMicros = micros();  // capture the "time"
elapsedMicros = currentMicros - previousMicros;  // how much passed?
if ( elapsedMicros >=duration){
previousMicros = currentMicros; // reset for next time around
// flesh this part out, the basic idea is laid out like this:
// set up counters to keep track how many on/off of 0.1mS (100uS) went by 
// if pulse is not started, bring the output high & set a flag
// if the On flag is set, see if enough cycles went by to turn the output off & clear the flag, and set a flat to indicate off time is running
// if the Off flag is set, see if enough cycles went by to clear it & start the On time & its flag.
}
// not time to make any changes, read your other inputs, etc. to see if the on/off durations need updating.
 }

The previous two replies outline software solutions to the problem, which are straightforward if you are not trying to do much else in software. If you want a hardware solution, I think you can use timer/counter 1. My understanding of the data sheet is that you can use register ICR1 to define the TOP value of the counter (i.e. the on + off time), and either OCR1A or OCR1B to define the on-time for the associated PWM pin.

"if you are not trying to do much else in software"
There is so little going on tho to set the pulses tho and check the elapsed duration, I think there would plenty of time to do other stuff, leaving the timers free for PWMing other stuff or something.

Many thanks to all of you for all your replies.

KE7GKP:
Why do you think you need to use the timers at all? Have you tried simply coding this as an ordinary timed on/off sequence using numbers from the micros() call?

Well first of all because I am not a real programmer. You could call me a "fake" programmer. I have some hardware and software from another application (developed by others), and I am trying to adapt it to this application. The hardware has PWM outputs on pins 3 and 9, and potentially also on pin 10. The software is already set up for PWM on pins 3 and 9. A substantial amount of the hardware and software requirements can be left unchanged. As I struggle with these things, the less dramatic the changes I have to make to the software the more the chances of success and the less frustration along the way! And the less code required, the less there is to further confuse me on my long journey!

dc42:
... if you are not trying to do much else in software.

Other parts of the software are doing quite a lot of things, including extensive use of timer 0. Whilst CrossRoads may well be right that the software solution would not significantly load the processor - and indeed I was starting to think I might be forced to resort to such an approach - if I can get the timers to do most of the work it would probably overload me a bit less.

dc42:
If you want a hardware solution, I think you can use timer/counter 1. My understanding of the data sheet is that you can use register ICR1 to define the TOP value of the counter (i.e. the on + off time), and either OCR1A or OCR1B to define the on-time for the associated PWM pin.

That is roughly what I was hoping. However looking at the datasheet I wasn't clear about this. Looking again at the datasheet now I see that there is a Clear Timer on Compare match Waveform Generation mode 12 with ICR1 as TOP. It says on page 136 that the update of OCR1x is "immediate". What does "immediate" mean? What does "update" of OCR1x mean anyway in this context? Is it assuming we want to generate an output on pin OC1x as soon as the timer reaches ICR1, i.e. at TOP? Or does it mean "The OCR1A Compare Register will then be updated with the value in the Buffer Register at the next timer clock cycle the TCNT1 matches TOP. The update is done at the same timer clock cycle as the TCNT1 is cleared and the TOV1 Flag is set" (this quote found under PWM modes)?

If TOP is defined by ICR1, how can OCR1x be triggered in between BOTTOM and TOP? Can this be done with FOC1x for example?

If not, what about using the Timer1 Capture interrupt vector to swap the TOP value in ICR1 between ON pulses and PAUSEs every time the timer reaches TOP? I can store the ON and PAUSE values in constants (respectively set by changing two potentiometers), and just copy alternately the one or the other into ICR1 in the interrupt handler (if ICR1 > ONvalue then ICR1 == ONvalue else ICR1 == PAUSEvalue). I suspect this might be the simplest and most elegant solution. However, I know absolutely NOTHING about how to use and program interrupts.

A related question: I cannot understand from the datasheet on the symmetry or asymmetry of channels A and B of Timer1 with respect to the use of the OCR1x or ICR1 registers as TOP. Sometimes the datasheet uses OCR1x, sometimes OCR1A, and sometimes "OCR1A or OCR1B". When OCR1A alone is used, is OCR1B explicitly excluded, or is it just a lax use of terms?

Bhante

KE7GKP:

  1. There is a LOT LESS code, and MUCH SIMPLER code required for the simple/dumb method of just using the timed on/off method.
  2. You can NOT use the built-in PWM solution because you need a much different period than the one PWM uses (~490Hz)
  3. Diddling with the registers is NOT something one would expect a "fake" programmer to even consider as a viable solution. And certainly not if one wants to avoid confusing oneself. {scratching head in disbelief}

You can make the built in PWM functions produce anything up to at least a MHz or two if all you want is a square wave or a PWM'd square wave.

KE7GKP:
resorting to opening the box where it says "No user-servicable parts inside." :slight_smile:

Oh wow! That sounds interesting! What does it look like inside? Let's have a look!

Well, I reckon something along these lines might work:

// ISR interrupt service routine
#include < avr/interrupt.h >
// needs to be declared as volatile for ISR:
volatile int ONvalue = 155;  // about 10 ms - maximum stroke power (range 7 to 155)
volatile int PAUSEvalue = 3124; // about 200 ms - minimum flow rate (range 155 to 3124)
// -------------------------------------------------------------------
// install the interrupt routine
ISR(TIM1_CAPT_vect)
{
    ICR1value = ICR1;
    if (ICR1value > ONvalue) {
         ICR1 = ONvalue;
    }
    else {
         ICR1 = PAUSEvalue;  
    }
}
// -------------------------------------------------------------------

void setup() {
...
}


void loop() {
...
PumpOn(ONvalue, PAUSEvalue);
...
}
// -------------------------------------------------------------------
void PumpOn(ONvalue, PAUSEvalue) {
// setup Timer1 for WG mode 12 with ICR1 as TOP, Clear Timer on Compare match
noInterrupts();
// non inverting, fast PWM, TOP is in ICR1, toggle OC1B on compare match, prescale 1024
TCCR1A = _BV(COM1B0);  // Port A disabled while pump is running 
TCCR1B = _BV(WGM13) | _BV(WGM12) | _BV(CS12) | _BV(CS10);
ICR1 = ONvalue; // This is how it's done in our current library; maybe the high 
                              // and low bites are handled automatically somewhere?
TIMSK1 = _BV(ICIE1); // set the Input Capture interrupt enable flag
interrupts();
}
// -------------------------------------------------------------------
void PumpOff() {
// reset Timer1 for PWM mode for normal operation on channel A
...
}
// -------------------------------------------------------------------

Hi Bhante,

I was wondering if you ever managed to achieve a satisfactory solution?
I am looking to carry out the same modification to my Ulka espresso pump...

Any guidance given your experience would be much appreciated!

Thanks,
David

Hello David,

I'm very glad to see someone else is interested in this problem. Unfortunately I had too many other commitments and had to put it on the back burner. In any case I am not competent as a programmer and was hoping for some guidance from the experts. I still think the basic approach above ought to work, if it is put together by a competent programmer.

I also found a patent using the same on-off protocol for an Ulka pump for a drink dispensing machine (mixing several different liquids on demand), using different parameters according to the viscosity of the fluid and the desired flow rate for a particular fluid, along the lines I described. The patent description was for one of the DC versions of the Ulka rather than the AC mains EP5 generally used in coffee machines, but the operating principle of the EP5 is essentially identical (half-wave rectified!). I think it would have to be used in DC mode to work properly - i.e. you would have to convert the mains to DC at 230V (or 110V for a US Ulka), and then pulse that DC at around 50 Hz - not a trivial task for high voltages but certainly possible. ST Microelectronics for example market solid state devices that can switch 230V at 50Hz.

If you get the chance to take the idea any further please keep me informed.

Regards
Bhante