Analog to PWM

Introduction

Hardware

Code

The servo pulses are generated by running 8-bit Timer0 at 50Hz (the ISR triggers every 20 microseconds), then, an 8-bit ADC value is read from the ADCH register and used to set the period of 16-bit Timer1, which runs one shot to generate the output pulse. The ADC is free-running so we don’t need to initiate a conversion when it’s time to pulse the output; the most recent value is already in the ADCH register.

Here’s a code listing (while I figure out how to host this somewhere):
…note that you’ll also need to #include avr/io.h, stdint.h, and avr/interrupt.h


/*
* Analog_to_Servo_PWM.c
*
* Created    :    4/23/2012 12:25:54 PM
* Author     :    Daniel Kramnik
* Purpose    :    Converts a 0.8 - 3.6V hall effect sensor throttle signal to a 1000uS - 2000uS servo PWM waveform for a Turnigy ESC in an electric scooter
*/

//Internal oscillator = 8MHz
#define F_CPU 800000UL

#define hallMin 41                 // 0.8v
#define hallMax 184                // 3.6v

void setupADC();
void setupInterrupts();
void setupTimers();

volatile uint8_t Vin = 41;
volatile uint8_t onTime = 100;

int main(void)
{
setupADC();
setupInterrupts();
setupTimers();

PORTB |= (1 << PB4);        // Turn on the program start LED
while(1);                     // Run the ADC and timer 0, wait for an interrupt to occur
}

ISR(TIMER0_COMPA_vect)
{
uint8_t val = ADCH;             // Get ADC reading from high byte register

if (val < hallMin) {val = hallMin;}
if (val > hallMax) {val = hallMax;}

onTime = 100 + 100 * (val – hallMin) / (hallMax – hallMin);

PORTB |= (1 << PB0);      // Start the output pulse

// Start Timer 1 (1000 – 2000uS pulse generator)
TCCR1 =
(1 << CTC1) |             // Clear timer on compare match
(1 << CS12) ;             // Set prescaler to 8

OCR1A = onTime;                 // Set the top number of ticks (10uS/tick)
TCNT1 = 0;                     // Reset counter
};

ISR(TIMER1_COMPA_vect)
{
PORTB &= ~(1 << PB0); // End the output pulse

TCCR1 = 0;                     // Stop Timer 1
};

setupADC()
{
ADMUX =
(1 << MUX0)  |            // Select ADC1 (PB2, AVR pin 7)
(1 << ADLAR) ;            // Left-adjust reading (we only care about high 8 bits, put them in ADCH register)
// Use Vcc (5V) as the voltage reference (default)
ADCSRA =
(1 << ADEN)  |            // Enable ADC
(1 << ADATE) |            // Auto trigger
(1 << ADPS2) |            // Set prescaler to 64 for 125kHz SAR clock
(1 << ADPS1) ;

ADCSRB = 0;                     // Free-running mode

DIDR0 = (1 << ADC1D);     // Disable digital input buffer

ADCSRA |= (1 << ADSC);    // Start free-running conversion
};

setupInterrupts()
{
TIMSK =
(1 << OCIE0A) |            // Enable Timer 0 output compare interrupt
(1 << OCIE1A) ;            // Enable Timer 1 output compare interrupt

sei();                        // Enable global interrupts
};

setupTimers()
{
DDRB =
(1 << PB0) |            // Timer output pin
(1 << PB4) ;            // LED output pin

PORTB = 0x00;

// Timer 0 setup (~50Hz cycle generator)
TCCR0A =
(1 << WGM01) ;            // CTC operation (mode 2) with OC0A and OC0B disconnected

TCCR0B =
(1 << CS02) ;            // Set prescaler to 256

OCR0A = 78;                    // Set the top to ~20mS
TCNT0 = 0;                    // Reset counter
};

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s