NerdKits - electronics education for a digital generation

You are not logged in. [log in]

NEW: Learning electronics? Ask your questions on the new Electronics Questions & Answers site hosted by CircuitLab.

Microcontroller Programming » duty cycle controlled PWM with one counter?

July 05, 2012
by edb
edb's Avatar

I'm trying to set up a PWM motor control using hardware PWM to switch a transistor pair. The examples I've read on this site all seem to set up software PWM (which my project doesn't really allow processor time for, as all 6 processes are interrupt driven and 2 are time critical), or use both compare registers to set the frequency and duty cycle (x and y). The spec sheet (328P, page 102 “phase correct mode” seems to say that full PWM control can be had using just the one compare register (OCR0A for timer 1).

I don't have an o-scope available, but I'd like to be sure that I'm controlling my DC motor reasonably correctly. Any tips interpreting the manual on this?

BTW, before you bring up the second timer on the chip, a little background: I want timer0 (8 bit) to generate the motor control PWM (the duty cycle/ motor speed to be determined by a potentiometer reading) through a pair of 2N7000 like in the Nerdkit squirter tutorial, AND use the second compare register to generate specific frequency interrupts (not time critical, I'll deal with those 3 interrupts (read the ADC and change x and y, update the LCD, and read the button (+DIP switches) in function calls and with separate software counters). This will leave the 16 bit counter to do the 'important' stuff – specifically to measure motor speed (Hall effect sensor with a rotary interrupt switch) and send control signals to a pair of L293D H-bridge drivers through a shift register (both time critical to roughly 0.1ms).

Can I have control of both 'x' and 'y' for PWM with one compare register? I don't have a clue on the settings -- I'm thinking mode '5', but I've included the code I'm looking at that uses both OCR0A and OCR0B to set x and y for reference. My desired PWM output pin is PC1, and all other pins are taken (see picture – the extra shift registers are to allow for future expansion by adding more L293Ds. It's still missing some transistors and diodes, and probably a few caps...)

Thanks for any ideas (other than RTFM, I have a headache already from that thing! The spec sheet is great, but too detailed for my purposes and it skips through the modes to the point that I lose track of which register does what in which mode.)

Code that 'almost' does what I want, thanks to contributors on Nerdkits Forums: 1: Don't like because it only sets up 50% duty cycle (I think):

void pwm_set(uint16_t x) {
  OCR1B = x;
}

#define PWM_MIN 0
#define PWM_MAX 512
#define PWM_START 150
void pwm_init() {
  // setup Timer1 for Fast PWM mode, 16-bit
  // COM1B1 -- for non-inverting output
  // WGM13, WGM12, WGM11, WGM10 -- for Fast PWM with OCR1A as TOP value
  // CS10 -- for no clock prescaling

  OCR1A = 512;// set for 28.8Khz cycle
  pwm_set(PWM_START);
  TCCR1A = (1<<COM1B1) | (1<<WGM11) | (1<<WGM10);
  TCCR1B = (1<<WGM13) | (1<<WGM12) | (1<<CS10);
}

2: Don't like because it uses timer1 (minor issue – I think I can figure out the switch to the 8 bit counter) and both compare registers (major issue, this is the root of my question):

// PIN DEFINITIONS:
//
// PB2 - freq signal (OC1B)

void pwm_set(uint16_t x)
{
  OCR1B = x;
}

void pwm_Freqset(uint16_t y)
{
  OCR1A = y;
}

#define PWM_START 33
#define PWM_Freq 66
//~~~~~~~~~~~~~~~~~~~Start PWM Methods~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void pwm_init()
{
  // setup Timer1 for Fast PWM mode, 16-bit
  // COM1B1 -- for non-inverting output
  // WGM13, WGM12, WGM11, WGM10 -- for Fast PWM with OCR1A as TOP value
  // CS11 -- for CLK/8 prescaling

  // I want to drive my DC motor at 28kHz
            *[note: I'll be driving mine at a much lower freq.]
  // F=1/T and T=1/F
  // this means it will repeat every 35 microseconds (period)
  // each count is 8/14745600 = 0.5425us (speed)

  pwm_Freqset(PWM_Freq);
  pwm_set(PWM_START);
  TCCR1A = (1<<COM1B1) | (1<<WGM11) | (1<<WGM10);
  TCCR1B = (1<<WGM13) | (1<<WGM12) | (1<<CS11);
}

int main() {
  // Skip a few steps to focus...
  pwm_init();
  uint16_t HighDur = PWM_START;
  uint16_t freqNum = PWM_Freq;
  while(1){
    // Calcule freqNum Skipped...
    HighDur=freqNum/2; //2 can be changed to give a different duty cycle 2 gives about 50% duty

   pwm_Freqset(freqNum);
    pwm_set(HighDur);
}

![Spaghetti Anyone?](http://i.imgur.com/cue0d.jpg)
July 05, 2012
by edb
edb's Avatar

ummm...

Spaghetti Anyone?

July 05, 2012
by pcbolt
pcbolt's Avatar

Hi edb -

I'm not sure I completely understand the question, but do you need to control both the frequency and the duty cycle? The reason I ask is if you only need the duty cycle, you can easily control that with one variable and any of the WGM modes should work. If you are using Timer0, you will need one the corresponding output pins free (OC0A or OC0B) to automatically generate the pulse (hardware only). If that isn't possible, you could generate the output to PC1 with an interrupt.

If you could provide a simple drawing of the output waveforms complete with time 'ticks', it may help me understand the question better.

Good luck with your project...it looks like a rat's nest and I mean that in a good way :-)

July 06, 2012
by edb
edb's Avatar

I think that's the problem. I'm still trying to figure out what I absolutely 'need' to control. Just getting organized to post the question helped with some of the answers, I think.

I want to control the motor with PWM. Duty cycle 'should' be more important than frequency, since that's what's really going to 'motivate' the motor. My original thought was to keep the frequency around the same as my 'service' interrupts (things like updating the LCD and reading the ADC), but I've realized two things: 1) a second compare register on timer0 won't trigger independently (the freq. of it's matches will be the same as the motor freq.) 2) The motor under hardware control can have a higher frequency than my service routines, all I have to do is set a software count and tell it to service every nth cycle.

I'm thinking mode 5 because of the symmetrical wave form, it looks less likely to release trapped smoke from the motor and I can 'set and forget' the frequency and focus on the duty cycle. [p. 128: "due to the symmetric feature of the dual-slope PWM modes, these modes (dual slope) are preferred for motor control applications."]

So, more specific questions: 1. Is it possible to run PWM and use an interrupt from the timer (where in the cycle doesn't matter to me -- I see from p. 97 that the OCR0x registers are updated at TOP to prevent 'glitches'. Can it then generate an interrupt separate of the PWM signal?

  1. Are there preferred frequencies to run DC motors, or can I just pick something convenient? I'm thinking in the 1kHZ to 2kHZ range. I don't want to stress the motor but I also don't want to overheat my transistors.

  2. Is there an OCR0A and a different thing called an OCR0A register, or am I just confused again? I'm looking on page 103. The following reads as complete gibberish to me:

"The actual OC0x value will only be visible on the port pin if the data direction for the port pin is set as output. The PWM waveform is generated by clearing (or setting) the OC0x Register at the compare match between OCR0x and TCNT0 when the counter increments, and setting (or clearing) the OC0x Register at compare match between OCR0x and TCNT0 when the counter decrements."

I get that I have to have the pin set to output or I won't get the signal, and I get that the matches against the counter (both up and down) are what generates the signal, beyond that it's foggy to say the least.

Waveform as requested (duty cycle [50% shown] set by OCR0A): PWM signal control

July 06, 2012
by edb
edb's Avatar

rut-roh

I just got something you said and it's making me worry. The output for this signal can only go to pins OC0A or OC0B. I misinterpreted as PC0 or ADC0 or something... but I see it now on the pinout. Ouch! Beyond being under two layers of 'rats nest', which includes the display, those pins are used by the LCD.

Does that mean the LCD already uses timer0 and there's only one timer left? I'm using the Nerdkit LCD functions.

Plan for the day (has to be done regardless of the answer to that question: switch wires (and code) to PB1 and PC1, that will allow hardware PWM from the 16 bit counter.

My other questions are still 'out there', I'll just have to limit my timing to timer1. If I can read timer1 whenever I want (was PCINT1 moving to PCINT9) and generate a timer interrupt (as per my previous) then I can still make it work (I think).

July 06, 2012
by pcbolt
pcbolt's Avatar

edb -

I think we are getting somewhere here. First off OC0A and OC0B are pins that output the pulse train. OCR0A and OCR0B are registers that store the timer's compare value. In your diagram, the small horizontal ticks occur when the timer's count regisiter (TCNT0 I think) equals OCR0A or OCR0B depending on how it is set up. If you want to read an ADC of a potentiometer then lower your output power, lower the value of OCR0A (or OCR0B). This will bring the horizontal ticks lower and closer together therefore generate less percentage of +5v time.

The LCD is NOT using a timer but it is using one of timer0's PWM output pins. The easiest way to change this is to modify the pin assignments inside the lcd.c source code and re-compile it. If it is the same as the ATmega168, it is an easy switch for OC0A (OC0B is much harder). You'd have to re-wire one pin on the LCD, but that should not be too hard.

July 06, 2012
by edb
edb's Avatar

The fog seems to be lifting somewhat. Thanks for sticking with it...

I think part of it was I was confusing the mode I wanted and the mode the example used. Glad to hear that OC0A is not being used for the LCD, since that frees up timer0 for me (don't need to output a signal for that one, just signal the software to get to work).

You comments about ADC confuse me. I thought it used a different timer through the prescaler? I'm not planning on checking it very often (16ish times per second). The read itself looks pretty straight forward and I'll trigger another conversion each time I read it.

Anyway, I'll worry about that once I have this motor running... Got the wiring changes done today, moved the LCD mount from over the 328P in case I have to do any more, mounted transistors.

To do: apply what I've learned here and see if the motor runs.

Post a Reply

Please log in to post a reply.

Did you know that you can make a capacitive proximity sensor with some aluminum foil and paperclips? Learn more...