20MHZ PWM on 4 pins needed

I've been using the following code for years, to get 20Mhz PWM on D9 & D10 (Atmega328P-AU), to drive 2 small motors. My motor driver is no longer available so I've had to switch to a different one, which requires 2 PWM pins, one for each direction, rather than just 1. So now, I need 2 PWM pins each, 4 total. I need the 20mhz as the project is used very close to pro-audio equipment. Is it possible to do this same thing with digital PWM pins 5 & 6, as well as 9 & 10. The Atmega328P-AU isn't doing much else, other than monitoring 2 analog joystick inputs, and a couple switches & LEDS. This changing registry things on the arduino is way out of my league, and I greatly appreciated coding_badly for helping me with this over 10 years ago. If someone could help with doing this for 4 PWM outputs, it would be greatly appreciated.

//From this thread:
//http://forum.arduino.cc/index.php?topic=135847.15

void analogWriteSAHA( uint16_t value )
{
  if ( (value >= 0) && (value < 800) )
  {
    OCR1A = value;
  }
}


void analogWriteSAHB( uint16_t value )
{
  if ( (value >= 0) && (value < 800) )
  {
    OCR1B = value;
  }
}

void analogWriteSAH_Init( void )
{
  // Stop the timer while we muck with it

  TCCR1B = (0 << ICNC1) | (0 << ICES1) | (0 << WGM13) | (0 << WGM12) | (0 << CS12) | (0 << CS11) | (0 << CS10);

  // Set the timer to mode 14...
  //
  // Mode  WGM13  WGM12  WGM11  WGM10  Timer/Counter Mode of Operation  TOP   Update of OCR1x at TOV1  Flag Set on
  //              CTC1   PWM11  PWM10
  // ----  -----  -----  -----  -----  -------------------------------  ----  -----------------------  -----------
  // 14    1      1      1      0      Fast PWM                         ICR1  BOTTOM                   TOP

  // Set output on Channel A and B to...
  //
  // COM1z1  COM1z0  Description
  // ------  ------  -----------------------------------------------------------
  // 1       0       Clear OC1A/OC1B on Compare Match (Set output to low level).

  TCCR1A =
    (1 << COM1A1) | (0 << COM1A0) |   // COM1A1, COM1A0 = 1, 0
    (1 << COM1B1) | (0 << COM1B0) |
    (1 << WGM11) | (0 << WGM10);      // WGM11, WGM10 = 1, 0

  // Set TOP to...
  //
  // fclk_I/O = 16000000
  // N        = 1
  // TOP      = 799
  //
  // fOCnxPWM = fclk_I/O / (N * (1 + TOP))
  // fOCnxPWM = 16000000 / (1 * (1 + 799))
  // fOCnxPWM = 16000000 / 800
  // fOCnxPWM = 20000

  ICR1 = 799;

  // Ensure the first slope is complete

  TCNT1 = 0;

  // Ensure Channel A and B start at zero / off

  OCR1A = 0;
  OCR1B = 0;

  // We don't need no stinkin interrupts

  TIMSK1 = (0 << ICIE1) | (0 << OCIE1B) | (0 << OCIE1A) | (0 << TOIE1);

  // Ensure the Channel A and B pins are configured for output
  DDRB |= (1 << DDB1);
  DDRB |= (1 << DDB2);

  // Start the timer...
  //
  // CS12  CS11  CS10  Description
  // ----  ----  ----  ------------------------
  // 0     0     1     clkI/O/1 (No prescaling)

  TCCR1B =
    (0 << ICNC1) | (0 << ICES1) |
    (1 << WGM13) | (1 << WGM12) |              // WGM13, WGM12 = 1, 1
    (0 << CS12) | (0 << CS11) | (1 << CS10);
}

Is it a secret which one it is? If not, please post a link to its datasheet.

I think you mean 20kHz. I don't know any motor or motor driver that can work on 20Mhz.
Post a link to the driver.
You only need one PWM pin per motor. Direction is just a logic level.
Leo..

1 Like

That made me go "what???" too. Then I read the code, and you are correct; it's 20KHz.

  // fclk_I/O = 16000000
  // N        = 1
  // TOP      = 799
  //
  // fOCnxPWM = fclk_I/O / (N * (1 + TOP))
  // fOCnxPWM = 16000000 / (1 * (1 + 799))
  // fOCnxPWM = 16000000 / 800
  // fOCnxPWM = 20000

Motor driver: DRV8256PPWPR
data sheet: DRV8256PPWPR

yes, 20Khz :slight_smile:
20Mhz would definitely by out of audible hearing range!

Another thing is more important - it is absolutely impossible to generate 20 MHz PWM on an atmega328, the core frequency of which is only 16 MHz :slight_smile:

The DRV8256PPWPR needs 2 PWM pins.
I was hoping to use the DRV8256PPWPR , as I've already used it, and made some custom PCBS with it, but only fro 1 motor. So I already have circuitry for it, but I don't have to use it.
I'm looking now for some alternate motor drivers in case it's not possible to get 4 PWM pins @ 20Khz on the Atmega328P

Thanks for the datasheet. Several pins need to be hardwired in various ways.

That's the key question. The PWM uses timers/internal resources and the might no have the same range. What's available, more deep hardware knowing guys must tell.

You can't set arduino PWM to exact 20 KHz, but you could setup both Timer1 and Timer2, and it will give you 4 pins with 32 KHz PWM output

1 Like

Also if you PWM the EN pin?
Leo..

Because the timer is switched to 16 bit mode the resolution is also improved.

Why do I read 20 Mhz when your sketch indicates that you are generating 20 kHz PWM signal using 16 MHz system clock frequency?

Hi @SouthernAtHeart

If you require a PWM signals out of audible range then it's possible to generate 31.25kHz PWM on pins 3, 9, 10 and 11. Just add the following lines to your sketch then use the analogWrite() function as normal:

TCCR1B &= ~_BV(CS11);	// Increase the timer1 PWM frequency to 31.25kHz
TCCR2B &= ~_BV(CS22);	// Increase the timer2 PWM frequency to 31.25kHz
TCCR2B |= _BV(CS20);

I see this from the PWM link mentioned in post 10. So to get it out of audible hearing range, these lines should do the trick it seems. And the data sheet for my motor drivers says it works with PWM up to 100khz, so this should work great for me. Thanks.

//---------------------------------------------- Set PWM frequency for D9 & D10 ------------------------------
//TCCR1B = TCCR1B & B11111000 | B00000001;    // set timer 1 divisor to     1 for PWM frequency of 31372.55 Hz

//---------------------------------------------- Set PWM frequency for D3 & D11 ------------------------------
//TCCR2B = TCCR2B & B11111000 | B00000001;    // set timer 2 divisor to     1 for PWM frequency of 31372.55 Hz

There is no free lunch.
Decreasing driver PWM resolution with increasing PWM frequency.
Leo..

The settings above give to OP a standard Arduino PWM resolution 8 bit

1 Like

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.