0

I tried to obtain 32Hz pulse train(between 30Hz and 35Hz is also fine) with around 20us pulse width with Arduino Uno but couldn't succeed. The problem is:

If I do the following:

void setup() {

    TCCR1B = TCCR1B & B11111000 | B00000101;  

    pinMode(pwmPin9, OUTPUT); // sets the pin as output

}


void loop() {

analogWrite(pwmPin9, 1)


}

I can set the freq. roughly at 30Hz but analogWrite(pwmPin9, 1) will give minimum 100us cannot go down to 20us.

digitalWrite method with delay on the other hand doesn't work to obtain frequencies below about 60Hz.

How can I obtain a pulse train output with between 30Hz to 35Hz and with around 20us pulse width?

  • 1
    Instead of digitalWrite() write the values directly to the PORT register. This takes a lot less time than the big digitalWrite() function. Also you can do this with a timer compare interrupt, so that you will be able to get exactly the frequency you want. – chrisl Jun 26 '18 at 11:09
  • could you write an answer an an example? – user16307 Jun 26 '18 at 11:21
2

For this kind of problems, the first thing to do is convert the two relevant times (the signal period and the duration of the HIGH state) into a number of CPU cycles. With a 16 MHz clock that gives:

  • HIGH time: 20 µs = 320 CPU cycles
  • period: 31.25 ms = 500000 cycles (±7%)

Then divide those numbers by the available prescaler values, to get the corresponding number of prescaled clock cycles:

prescaler       1      8    64     256      ...
HIGH time     320     40     5       1.25   ...
period     500000  62500  7812.5  1953.125  ...

Ideally, you want a prescaler that gives you integer numbers, in order to avoid rounding errors. And since you are using a 16-bit timer, you want these numbers to be no larger than 216. Or twice that if you use a phase-correct PWM mode, but I prefer to avoid that complication if possible.

From the table above, it appears that a prescaler of 8 is ideal. 64 would be practically as good, with a frequency error of only 64 ppm, which is less than the frequency tolerance of the ceramic resonator clocking your Uno.

Then you use either OCR1A or OCR1B to set the high time, depending on whether you are outputting to pin OC1A (digital 9) or OC1B (digital 10). An you use either OCR1A or ICR1 to set the period. Since you are outputting on pin 9, the OCR1A register will be used to set the high time, thus you have to use ICR1 to set the period. Then you search the table of modes in the datasheet and find the one relevant to this mode of operation is mode 14: fast PWM with the TOP value defined by ICR1. This gives the following configuration:

void configure_timer_1()
{
    DDRB  |= _BV(PB1);    // set pin 9 = PB1 = OC1A as output
    TCCR1A = 0;           // reset timer
    TCCR1B = 0;           // ditto
    OCR1A  = 40 - 1;      // HIGH for 40 * 8 CPU cycles
    ICR1   = 62500 - 1;   // period = 62500 * 8 CPU cycles
    TCCR1A = _BV(COM1A1)  // non inverting PWM on pin OC1A
           | _BV(WGM11);  // mode 14: fast PWM, TOP = ICR1
    TCCR1B = _BV(WGM12)   // ditto
           | _BV(WGM13)   // ditto
           | _BV(CS11);   // clock at F_CPU/8
}

Note that OCR1A and OCR1B have to be set to desired number of cycles minus one.

0

analogWrite is 8-bit.

The reciprocal of 30Hz (the time per full period) is 0.0313.

0.0313 divided by 256 (28) is 0.000122, or 122uS.

At 8 bits that's the smallest time that the 30Hz waveform can be cut up into.

To get smaller slices you need a higher resolution PWM signal. This question and answer shows how it can be done, but it's limited to which pins can be used, since you need the PWM channels attached to the 16-bit timer.

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.