Pages: [1] 2   Go Down
Author Topic: How to get 20khz PWM on pin D9  (Read 3347 times)
0 Members and 1 Guest are viewing this topic.
South East USA
Offline Offline
God Member
*****
Karma: 5
Posts: 651
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

This is out of my league, but I really need to increase the PWM to 20khz on digital pin 9. I'm powering a motor that is used in conjunction with audio equipment so I need the PWM to be out of hearing range.
The motor driver datasheet says it supports up to 20 KHz PWM. It's the VNH2SP30. Data sheet here:
http://www.pololu.com/file/0J52/vnh2sp30.pdf

I've read a few different pages here at Arduino.cc on PWM regarding changing the default (500) to something higher.  If I understand it right, it's somewhat easy to change it to some standard frequencies but to get 20Khz, I don't know. My atmega328 chip really won't be doing a whole lot, so it won't matter if it messes up my millis() function. I understand that is somewhat compromised by changing the PWM setting?
Thanks for the support. 
Logged

Massachusetts, USA
Offline Offline
Tesla Member
***
Karma: 201
Posts: 8701
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Pin D9 is the output from the Timer/Counter 1 Output Compare Register A (ORC1A).  The highest speed you can run it at is 16 million steps per second or 62.5 kHz for 9-bit PWM (16 MHz / 256 steps). The way to get 20 kHz is to have the timer reset at 800 (16,000,000 / 20,000) instead of 256.  This give you a PWM range from 0 to 799. 

That's in the Fast PWM mode.  For PWM modes where the timer counts up and then down (Phase Correct or Phase and Frequency Correct PWM) you would count up to 400 and back down to 0.

Read all Timer/Counter 1 and PWM modes in the ATmega328p datasheet.
Logged

Send Bitcoin tips to: 1L3CTDoTgrXNA5WyF77uWqt4gUdye9mezN
Send Litecoin tips to : LVtpaq6JgJAZwvnVq3ftVeHafWkcpmuR1e

Offline Offline
Edison Member
*
Karma: 64
Posts: 2464
Now, More Than Ever
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1235060559/4
Logged

"Hello, I must be going..."
"You gotta fight -- for your right -- to party!"
Don't react - Read.
"Who is like unto the beast? who is able to make war with him?"

South East USA
Offline Offline
God Member
*****
Karma: 5
Posts: 651
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Need to get 20kHz PWM on pin D9.

I've spent an hour reading through pages like "secretOfArduinoPWM", "PwmFrequency", "TimerPWNCheatsheet", About all I could get was that something called TCCR1B affects the timer1 which operates pin 9.  This is WAY over my head.  I've seen different examples, but I don't even know which of them to start with.

 
Logged

Global Moderator
Dallas
Offline Offline
Shannon Member
*****
Karma: 199
Posts: 12768
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset


Is a higher frequency acceptable?

Do you need more resolution or is 0-255 acceptable?

Do you need phase-correct PWM or is fast PWM acceptable?
Logged

South East USA
Offline Offline
God Member
*****
Karma: 5
Posts: 651
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset


Is a higher frequency acceptable?

Do you need more resolution or is 0-255 acceptable?

Do you need phase-correct PWM or is fast PWM acceptable?

The data sheet for the VNH2SP30 says, "PWM OPERATION UP TO 20 KHz".  So I assume if it were higher it might jitter or something, which would definitely not be acceptable

0-255 resolution is plenty.

I don't need phase-corect PWM.


Thanks.
Logged

Global Moderator
Dallas
Offline Offline
Shannon Member
*****
Karma: 199
Posts: 12768
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset


Do you need PWM output on pin 10?
Logged

South East USA
Offline Offline
God Member
*****
Karma: 5
Posts: 651
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset


Do you need PWM output on pin 10?


No, just pin 9.  The VHN2SP30 only uses 1 pin for PWM, then 2 other pins for enabling each side.  I already sent off for my pcboard, or I'd say any PWM pin would work.  I guess I could make a new board if 20Khz PWM isn't possible on pin 9.
Logged

Global Moderator
Dallas
Offline Offline
Shannon Member
*****
Karma: 199
Posts: 12768
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset


Call this from setup to initialize the timer...

Code:
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 to...
  //
  // COM1A1  COM1A0  Description
  // ------  ------  -----------------------------------------------------------
  // 1       0       Clear OC1A/OC1B on Compare Match (Set output to low level).
 
  TCCR1A =
      (1 << COM1A1) | (0 << COM1A0) |   // COM1A1, COM1A0 = 1, 0
      (0 << 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 B is irrelevant
 
  OCR1B = 0;
 
  // Ensure Channel A starts at zero / off
 
  OCR1A = 0;
 
  // We don't need no stinkin interrupts
 
  TIMSK1 = (0 << ICIE1) | (0 << OCIE1B) | (0 << OCIE1A) | (0 << TOIE1);

  // Ensure the Channel A pin is configured for output
  DDRB |= (1 << DDB1);

  // 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);
}


Call this to change the output value...

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


   The range is zero to 799.   
Logged

Massachusetts, USA
Offline Offline
Tesla Member
***
Karma: 201
Posts: 8701
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I've spent an hour reading through pages like "secretOfArduinoPWM", "PwmFrequency", "TimerPWNCheatsheet", About all I could get was that something called TCCR1B affects the timer1 which operates pin 9.  This is WAY over my head.  I've seen different examples, but I don't even know which of them to start with.

That's a good start.  Each of the three timers has two PWM outputs (A and B).  For Pin 9 you have to look at which timer controls it:

https://spreadsheets.google.com/spreadsheet/pub?key=0AtfNMvfWhA_ccnRId19SNmVWTDE0MEtTOV9HOEdQa0E&gid=0

That spreadsheet shows that on an UNO the Pin 9 PWM comes from Timer 1 output A (T1A) and on the ATmega it's known as Port B Pin 1 (PB 1).  If you switch to a Mega you need to change to Timer 2 output B (T2B) which on the ATmega is known as Port H Pin 6 (PH 6).

Knowing you want Timer/Counter 1 you read that section of the datasheet.  It talks about the Timer Counter Registers: TCCR1A, TCCR1B, and TCCR1C (they had too many bit to fit in one byte so they made it into three registers). 
TIMSK1: The Interrupt Mask Register.  Since you aren't using interrupts this remains 0.
TIFR1: Interrupt Flag Register.  Since you aren't using interrupts this isn't important.
Since it is a 16-bit counter the other registers are two bytes each:
TCNT1:  The actual timer/counter count
OCR1A: The 'A' Output Compare Register that is associated with Pin 9
OCR1B: The 'B' Output Compare Register associated with a different pin.
ICR1: The Input Capture Register.  In out case we are using it to set the TOP of the counting range so the counter doesn't go all the way to 65535.
Logged

Send Bitcoin tips to: 1L3CTDoTgrXNA5WyF77uWqt4gUdye9mezN
Send Litecoin tips to : LVtpaq6JgJAZwvnVq3ftVeHafWkcpmuR1e

South East USA
Offline Offline
God Member
*****
Karma: 5
Posts: 651
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Thanks for the help on this!  I'll test it out. 

Quote
Call this to change the output value...
This won't be changing the frequency, but the motor speed?

So then instead of using
Code:
AnalogWrite(motorSpeed); //motorSpeed is 0 to 255

 I'll use this?
Code:
analogWriteSAH(motorSpeed); //motorSpeed is 0 to 799

Many thanks.

Logged

Global Moderator
Dallas
Offline Offline
Shannon Member
*****
Karma: 199
Posts: 12768
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Thanks for the help on this!

You are welcome.

Quote
I'll test it out. 

Please do (thoroughly test it).  I only had the time to perform a very basic test with an LED.

Quote
Call this to change the output value... This won't be changing the frequency, but the motor speed?

Exactly.  The frequency is set in analogWriteSAH_Init to 20K Hz (assuming I didn't make a mistake).

Quote
So then instead of using
Code:
analogWrite( 9, motorSpeed ); //motorSpeed is 0 to 255

 I'll use this?
Code:
analogWriteSAH( motorSpeed ); //motorSpeed is 0 to 799

Exactly.

Just to confirm, you have a VNH2SP30 (this is a snippet of the VNH3SP30 and VNH2SP30 Comparison from Pololu's website)...
         
VNH3SP30VNH2SP30
.........
Maximum PWM frequency10 kHz20 kHz
.........
« Last Edit: December 06, 2012, 01:42:57 pm by Coding Badly » Logged

South East USA
Offline Offline
God Member
*****
Karma: 5
Posts: 651
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

thanks.  Yes, I got the VNH2SP30 for that very reason, that it goes up to 20Khz.
I don't have a scope, but I do have a Fluke meter that measures frequency.  I've never used that function, but I'll have to test it out, it'll be good learning.
Logged

Marseille - FRANCE
Offline Offline
Newbie
*
Karma: 0
Posts: 8
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi guys!
Your answers are great  smiley and very useful as I have the very same VNH2SP30 chip on my motomonster Hbridge (sparkfun https://www.sparkfun.com/products/10182)

but I would like to drive two motors! I have read in the code that "Channel B is irrelevant" and I'm lost...

Could you please tell me what to implement in order to have D9 and D10 ultrasonic's POWER

Many thanks!
Mat
Logged

Global Moderator
Dallas
Offline Offline
Shannon Member
*****
Karma: 199
Posts: 12768
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset


Call from setup...

Code:
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);
}
Logged

Pages: [1] 2   Go Up
Jump to: