0,0 → 1,197 |
/* |
* Copyright (C) 2004 Darren Hutchinson (dbh@gbdt.com.au) |
* |
* This program is free software; you can redistribute it and/or modify |
* it under the terms of the GNU Library General Public License as published by |
* the Free Software Foundation; either version 2 of the License, or (at your |
* option) any later version. |
* |
* This program is distributed in the hope that it will be useful, but |
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY |
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public |
* License for more details. |
* |
* You should have received a copy of the GNU Library General Public License |
* along with this software; see the file COPYING. If not, write to |
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, |
* MA 02111-1307, USA. |
* |
* $Id: driver.c,v 1.4 2004/04/04 06:54:05 dbh Exp $ |
*/ |
|
/* This file contains the code that takes the excitation values for each |
* coil (determined by the stepper module) and performs a crude PWM |
* and generates the output values for the stepper drivers. |
* |
* This module has it's own timer at a high frequency timer, so efficiency |
* is an important issue. |
*/ |
|
#include <avr/io.h> |
#include <avr/interrupt.h> |
#include <inttypes.h> |
|
#include "eq6.h" |
#include "stepper.h" |
#include "driver.h" |
|
/* Like the stepper.h module this one is heavily table-driven. Basically |
* each excitation value corresponds to 4, 8 bit lists of values are used |
* for 8 clock cycles. This gives a crude PWM system. |
* |
* The hardware is orginized such that both "ends" of a coil are in |
* adjacent bits, so each pair of 8 bits is interleaved into a single |
* 16 bit word where the bits are used two at a time. |
* |
* Note: The high side and low side tables here were different - basically if |
* a high side was driven for one coil, the low side should be driven for the |
* other. |
* |
* However the low side bits are reversed because of the bit numbers running |
* in the opposite direction for Port A (low side) vs Port C (high side), so |
* it turns out that the same table can be used for both! |
* |
* Of course the bits for the coils are composed in the opposite direction |
* too, but that happens below |
*/ |
uint8_t driveTbl[] = |
{ |
[EX_M_1] = 0xaa, |
[EX_M_0_67] = 0x2a, |
[EX_M_0_4] = 0x22, |
[EX_M_0_2] = 0x02, |
[EX_0] = 0x00, |
[EX_P_0_2] = 0x01, |
[EX_P_0_4] = 0x11, |
[EX_P_0_67] = 0x15, |
[EX_P_1] = 0x55 |
}; |
|
#define PWM_RATE 48000 /* PWM update speed */ |
#define PWM_STEPS 4 /* Steps per PWM cycle */ |
|
/* driverInit() initializes the port used by the stepper motors and the timer |
* used to update the stepper driver state |
* |
* Passed: |
* Nothing |
* |
* Returns: |
* Nothing |
* |
*/ |
void |
driverInit(void) |
{ |
/* Set up port A and C as outputs and set them to a safe default state |
* i.e. no outputs driven |
*/ |
PORTA = 0x00; // Disable high-side drivers |
DDRA = 0xff; // Set as outputs |
|
PORTC = 0xff; // Disable low-side drivers |
DDRC = 0xff; // Set as outputs |
|
/* Setup the "magic" relay bit as an output */ |
|
MAGIC_PORT &= ~_BV(MAGIC_BIT); |
MAGIC_DDR |= _BV(MAGIC_BIT); |
|
/* Setup timer 0 to generate interrupts for PWM */ |
OCR0 = (CLK_RATE / PWM_RATE / 8); |
TCCR0 = _BV(WGM01) | _BV(CS01); // Divied by 8, CTC mode |
|
/* Enable interrupt generation from timer 0 */ |
TIMSK |= _BV(OCIE0); |
} |
|
/* driverInt() is called whenever a timer 0 overflow interrupt occurs |
* |
* Passed: |
* Nothing |
* |
* Returns: |
* Nothing |
*/ |
SIGNAL(SIG_OUTPUT_COMPARE0) |
{ |
static uint8_t raCoil1States; |
static uint8_t raCoil2States; |
|
static uint8_t decCoil1States; |
static uint8_t decCoil2States; |
|
static uint8_t ctr = PWM_STEPS - 1; |
|
uint8_t highSidePort; |
uint8_t lowSidePort; |
|
// PORTA = ~excitation.ra; |
// PORTC = ~excitation.dec; |
|
/* Increment the step count. Reinitialize the states entries |
* if the counter wraps around |
*/ |
if (++ctr == PWM_STEPS) |
{ |
uint8_t tmp; |
|
ctr = 0; |
|
/* Update states */ |
tmp = _GET_C1(raExcitation.excitation); |
raCoil1States = driveTbl[tmp]; |
|
tmp = _GET_C2(raExcitation.excitation); |
raCoil2States = driveTbl[tmp]; |
|
tmp = _GET_C1(decExcitation.excitation); |
decCoil1States = driveTbl[tmp]; |
|
tmp = _GET_C2(decExcitation.excitation); |
decCoil2States = driveTbl[tmp]; |
|
/* Update magic relay state */ |
if (raExcitation.useRelay) |
MAGIC_PORT &= ~_BV(MAGIC_BIT); |
else |
MAGIC_PORT |= _BV(MAGIC_BIT); |
} |
|
/* Build the high_side driver output value */ |
highSidePort = decCoil2States & 0x3; |
highSidePort <<= 2; |
|
highSidePort |= decCoil1States & 0x3; |
highSidePort <<= 2; |
|
highSidePort |= raCoil2States & 0x3; |
highSidePort <<= 2; |
|
highSidePort |= raCoil1States & 0x3; |
|
/* Build the low-side driver states. Note that, due to the |
* reverse pin pordering for Port A vs Port C that these are built in |
* the opposite direction |
*/ |
lowSidePort = raCoil1States & 0x3; |
lowSidePort <<= 2; |
raCoil1States >>= 2; |
|
lowSidePort |= raCoil2States & 0x3; |
lowSidePort <<= 2; |
raCoil2States >>= 2; |
|
lowSidePort |= decCoil1States & 0x3; |
lowSidePort <<= 2; |
decCoil1States >>= 2; |
|
lowSidePort |= decCoil2States & 0x3; |
decCoil2States >>= 2; |
|
/* Potential safety check: rev (PortA) & PortC == 0 */ |
|
/* Write the values to the output ports */ |
PORTC = highSidePort; |
PORTA = ~lowSidePort; |
} |