// This here program is to illustrate PWM on the Atmel ATMEGA8l AVR
// microcontroller.
//
// climber 2003

#include <inttypes.h>
#include <avr/io.h>
#include <stdio.h>

/*
 mega8l pinout for PWM test project 
 I put one of these in every AVR C program I make

               +---------------+
         reset |  1     pc5 28 |
     motor 1 A |  2 pd0 pc4 27 |
               |  3 pd1 pc3 26 |
     motor 1 B |  4 pd2 pc2 25 |
     motor 2 A |  5 pd3 pc1 24 |
     motor 2 B |  6 pd4 pc0 23 | 
           VCC |  7         22 | GND
           GND |  8         21 | AREF
          xtal |  9 pb6     20 | AVCC
          xtal | 10 pb7 pb5 19 | ISP
     motor 3 A | 11 pd5 pb4 18 | ISP
     motor 3 B | 12 pd6 pb3 17 | ISP, motor 3 PWM
               | 13 pd7 pb2 16 | motor 2 PWM
               | 14 pb0 pb1 15 | motor 1 PWM
               +---------------+
*/

//-------------------------------------------------------------------------
int main (void)
{
  uint8_t delay, reverse;
  uint16_t pwm;
  long count;

  // set up ports as outputs
  DDRB = _BV(DDB1)   // motor 1 PWM
       | _BV(DDB2)   // motor 2 PWM
       | _BV(DDB3);  // motor 3 PWM
  DDRD = _BV(DDD0)   // motor 1 A
       | _BV(DDD2)   // motor 1 B
       | _BV(DDD3)   // motor 2 A
       | _BV(DDD4)   // motor 2 B
       | _BV(DDD5)   // motor 3 A
       | _BV(DDD6);  // motor 3 B

  // set up PB1 and PB2 with 8bit PWM
  TCCR1A = _BV(WGM10)    // 8 bit fast PWM, see WGM12 below
         | _BV(COM1A1)   // set OC1A/B on compare match, clear them at top
         | _BV(COM1B1);
  TCCR1B = _BV(CS11)     // 1/8 prescale
         | _BV(WGM12) ;  // fast PWM       

  // set up PB3 for 8 bit PWM
  TCCR2 = _BV(WGM20)     // timer 2 fast PWM
        | _BV(WGM21)     // ditto
        | _BV(COM21)     // clear OC2 on compare match, set OC2 at TOP
        | _BV(CS21);     // 1/8 prescale

  reverse = 0;
  while (1)
  {
    if (reverse)
    {
      sbi(PORTD, PD0);
      cbi(PORTD, PD2);
      sbi(PORTD, PD3);
      cbi(PORTD, PD4);
      sbi(PORTD, PD5);
      cbi(PORTD, PD6);
      reverse = 0;
    }
    else
    {
      cbi(PORTD, PD0);
      sbi(PORTD, PD2);
      cbi(PORTD, PD3);
      sbi(PORTD, PD4);
      cbi(PORTD, PD5);
      sbi(PORTD, PD6);
      reverse = 1;
    }
    // ramp up/down the motors.  I start with pwm = 90 because our friend
    // friction tells us the motors won't turn with duty cycles much
    // lower than that.
    for (pwm=90; pwm<=255; pwm++) 
    {
      OCR1A = pwm;
      for (count=0; count<=100000; count++) delay--;
    }
    for (count=0; count<=1000000; count++) delay--; 
    for (pwm=254; pwm>=170; pwm--)
    {
      OCR1A = pwm;
      for (count=0; count<=100000; count++) delay--;
    }
    OCR1A = 0;
    sbi(PORTD, PD0);   // slam on the brakes
    sbi(PORTD, PD2);

    // motor 2
    for (pwm=90; pwm<=255; pwm++)
    {
      OCR1B = pwm;
      for (count=0; count<=100000; count++) delay--;
    }
    for (count=0; count<=1000000; count++) delay--;
    for (pwm=254; pwm>=170; pwm--)
    {
      OCR1B = pwm;
      for (count=0; count<=100000; count++) delay--;
    }
    OCR1B = 0;
    sbi(PORTD, PD3);   // slam on the brakes
    sbi(PORTD, PD4);

    // motor 3
    for (pwm=90; pwm<=255; pwm++)
    {
      OCR2 = pwm;
      for (count=0; count<=100000; count++) delay--;
    }
    for (count=0; count<=1000000; count++) delay--;
    for (pwm=254; pwm>=170; pwm--)
    {
      OCR2 = pwm;
      for (count=0; count<=100000; count++) delay--;
    }
    OCR2 = 0;
    sbi(PORTD, PD5);   // slam on the brakes
    sbi(PORTD, PD6);
  }
}

