Paste: 8 servos, 1 tiny2313

Author: Crest
Mode: c
Date: Sun, 18 Jan 2009 12:12:59
Plain Text |
/*
 * Copyright (c) 2009, Jan Bramkamp
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY Jan Bramkamp ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL Jan Bramkamp BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <avr/interrupt.h>
#include <avr/io.h>
#include <util/atomic.h>
#include <inttypes.h>

#define ever (;;)

// each execution of TIMER1_COMPB_vect increments this
// providing the rest of the code with a strictly monotonic
// increasing tick counter.
volatile     uint16_t cycles   = 0;

/*
 * volatile uint8_t reads and writes are always atomic
 * their for don't require access to them
 * to be protected by a ATOMIC_BLOCK.
 */

// bitmask "pointing" to the currently procced pin
volatile     uint8_t  servo    = 0x01;
// index in delta[8] and servo_pos[8] corresponding to this pin
volatile     uint8_t  idx      = 0;
// length of each time slot
static const uint16_t slot_len = 2400;

// minimal length of each duty cycle
static const uint16_t range_min = 1000;
// maximal length of each duty cycle
static const uint16_t range_max = 2000;
// how many ticks to wait before updating servo_pos[0..7]
static const uint16_t delay     = 1*8;

// how fast to move each servo
volatile      int16_t delta[8] =
{
	1, 1, 1, 1,
	1, 1, 1, 1
};

// current duty cycle length for each servo.
volatile     uint16_t servo_pos[8] = 
{
	1500, 1500, 1500, 1500,
	1500, 1500, 1500, 1500
};

// ISR(TIMER1_COMPA_vect) has to be executed before
// ISR(TIMER1_COMPB_vect) in each pwm cycle.
// SEE ALSO: TIMER1_COMPB_vect
ISR(TIMER1_COMPA_vect)
{
	PORTB &= ~(servo);						// Disable current servo
											// and move to next one by
	servo  = (servo << 1) | (servo >> 7);	// rotating the bitmask
	idx    = (idx + 1) & 7;					// and incrementing the index.
}

// ISR(TIMER1_COMPA_vect) has to be executed before
// ISR(TIMER1_COMPB_vect) in each pwm cycle.
// SEE ALSO: TIMER1_COMPA_vect
ISR(TIMER1_COMPB_vect)
{
	PORTB |= servo;				// Activate current servo,
	OCR1A  = servo_pos[idx];	// load pwm duty cycle
	TCNT1  = 0x0000;			// and reset counter;
	cycles++;
}

inline static void init_portb
( ) {
	PORTB = 0x01;	// Port_B[0]    = high, Port_B[1..7] = low
	DDRB  = 0xFF;	// Port_B[0..7] = output
}

inline static void init_timer1
( ) {
	// Enable both Timer1 Compare Match Interrupts.
	TIMSK = (1 << OCIE1A) | (1 << OCIE1B);
	
	// Start with first servo;
	OCR1A  = servo_pos[idx];
	
	// All servo slots have the same length;
	OCR1B  = slot_len;
	
	// Timer0/1 Prescaler = F_CPU / 1
	TCCR1B = (1 << CS10);	
}

void main (void)
{
	init_portb();
	init_timer1();
	
	sei(); // Enable Interrupts.
	
	for ever
	{
		uint16_t cycles_snapshot;	// we need a copy of the global tick counter
		
		ATOMIC_BLOCK(ATOMIC_FORCEON)
		{
			cycles_snapshot = cycles; // a correct copy
		}
		
		if ( cycles_snapshot > delay )
		{
			uint8_t i;
			
			ATOMIC_BLOCK(ATOMIC_FORCEON)
			{
				cycles = 0x0000;
			}
			
			for ( i = 0; i < 8; i++ )
			{
				uint16_t pos = servo_pos[i];
				
				if ( ( ( pos + delta[i] ) > range_max ) |
				     ( ( pos + delta[i] ) < range_min ) ) ATOMIC_BLOCK(ATOMIC_FORCEON)
				{
					delta[i]     = -delta[i];
				}
				else ATOMIC_BLOCK(ATOMIC_FORCEON)
				{
					servo_pos[i] = pos + delta[i];
				}
			}
		}
	}
}

//20ms / 8 = 2.5ms

New Annotation

Summary:
Author:
Mode:
Body: