1
\$\begingroup\$

For a while, I've been working with Arduino, but about a month ago, I switched over to using solely the ATmega328-PU microcontroller. I use the Atmel Studio IDE. So far, I have successfully blinked an LED, used programs involving buttons/switches, and used the Fast PWM function to dim an LED. Currently, I am attempting to drive a servo motor using Fast PWM, but have had little success. I have not found any source codes that work for the specific needs of my microcontroller, and I don't really have the ability to change around all the parameters, as I am still learning.

The below is the code which I have developed. It does not work, but I was hoping that someone may be able to fix it up. The servo moves in one direction, but does not stop, and just ends up grinding the motor shaft gear, indicating this could be an issue with the wave length. An additional question: is there an equation to convert degree to PWM wavelength? Thanks!

Microcontroller specifics:

  • Model - ATmega328-PU
  • 16,000,000 hz crystal
  • 28 pins
  • Using 8 bit timer

Servo:

  • Servo model: Tower Pro SG90
  • Servo Frequency 50 hz (is this standard?)

Code:

#define F_CPU 16000000UL
#include <avr/io.h>
#include <util/delay.h>

int main(void)
{

    DDRD |= (1 << DDD6);
    // PD6 is now an output - corresponds to OCR0A

    TCCR0A |= (1 << COM0A1);
    // set non-inverting mode

    TCCR0A |= (1 << WGM01) | (1 << WGM00);
    // set fast PWM Mode

    ICR1 = 39999;

    OCR0A = ICR1 - 2000;
    // gives servo motor position

    TCCR0B |= (1 << CS01);
    // set prescaler to 8 and starts PWM


    while(1)
    {
    }
}
\$\endgroup\$
  • \$\begingroup\$ Formally, you should read the data sheet. More practically, write an Arduino sketch that commands the desired position, and then reads back out and prints the hardware register values you've been trying to set. Then you can use those in your own AVR C program as you attempted. \$\endgroup\$ – Chris Stratton Jan 12 '18 at 6:06
  • \$\begingroup\$ @ChrisStratton Thank you! I did check out the Servo motor's datasheet, but unfortunately, it seemed to be more like an advertisement and carried very little useful data for non-Arduino users. \$\endgroup\$ – Mischief City Jan 12 '18 at 6:15
  • \$\begingroup\$ I meant the ATmega data sheet. It's admittedly complicated, but the information is there. Watching what the Arduiono sketches are actually doing with the registers and then looking those up in the data sheet may be more informative than tackling it cold. Also there have been a number of writeups along those lines online that you can easily find. \$\endgroup\$ – Chris Stratton Jan 12 '18 at 6:17
1
\$\begingroup\$

There are several problems with your code:-

  1. In non-inverting mode the PWM pulse starts at a count of 0 and ends on compare match, so you should load the pulse width directly into the compare register (not subtract it from the period).

  2. ICR1 is part of 16-bit Timer1, not 8-bit Timer0.

  3. An 8-bit timer will not accept 16 bit values.

To produce 16 bit PWM you must use Timer1, then the numbers 2000 (for 1ms pulse width) and 3999 (for 20ms period) will work properly. So change all Timer0 references to their Timer1 equivalents, ie. TCCR0A -> TCCR1A, COM0A1 -> COM1A1 etc. and set up PB1 to output the servo pulse.

\$\endgroup\$

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.