blmc/blmc.c

295 lines
6.9 KiB
C

/***************************************************************************
* Copyright (C) 09/2007 by Olaf Rempel *
* razzor@kopf-tisch.de *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; version 2 of the License, *
* *
* 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 General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include <avr/io.h>
#include <avr/interrupt.h>
#include "main.h"
#include "blmc.h"
#include "eeprom.h"
extern struct ee_param params;
struct blmc_ blmc;
/* Analog Comparator Channel */
static uint8_t next_sense;
void trigger_adc(uint8_t channel)
{
/* Disable Analog Comperator Interrupt */
TIMSK1 &= ~(1<<ICIE1);
/* set channel (external reference, 5V) */
ADMUX = (0<<REFS1) | (0<<REFS0) | channel;
/*
* turn on ADC with interrupts
* start conversion with 1/32 of F_CPU
* 1/250kHz * 25 => 100us conversion time
*/
ADCSRA = (1<<ADEN) | (1<<ADSC) | (1<<ADIE) | (1<<ADIF)| (1<<ADPS2) | (0<<ADPS0);
}
static void next_phase(void)
{
static uint8_t phase;
static uint8_t phase_adc;
/* Disable Analog Comperator Interrupt */
TIMSK1 &= ~(1<<ICIE1);
switch (phase) {
case 0: /* A: PWM, B: LOW, C: SENSE => disable EN_C, disable PWM_B, enable EN_B */
PORTD &= ~PHASE_C_EN;
TCCR0A &= ~PHASE_B_OC;
PORTD |= PHASE_B_EN;
/* C: falling edge */
TCCR1B &= ~(1<<ICES1);
next_sense = SENSE_C;
break;
case 1: /* A: SENSE, B: LOW, C: PWM => disable EN_A, enable PWM_C, enable EN_C */
PORTB &= ~PHASE_A_EN;
TCCR2A |= PHASE_C_OC;
PORTD |= PHASE_C_EN;
/* A: rising edge */
TCCR1B |= (1<<ICES1);
next_sense = SENSE_A;
break;
case 2: /* A: LOW, B: SENSE, C: PWM => disable EN_B, disable PWM_A, enable EN_A */
PORTD &= ~PHASE_B_EN;
TCCR2A &= ~PHASE_A_OC;
PORTB |= PHASE_A_EN;
/* B: falling edge */
TCCR1B &= ~(1<<ICES1);
next_sense = SENSE_B;
break;
case 3: /* A: LOW, B: PWM, C: SENSE => disable EN_C, enable PWM_B, enable EN_B */
PORTD &= ~PHASE_C_EN;
TCCR0A |= PHASE_B_OC;
PORTD |= PHASE_B_EN;
/* C: rising edge */
TCCR1B |= (1<<ICES1);
next_sense = SENSE_C;
break;
case 4: /* A: SENSE, B: PWM, C: LOW => disable EN_A, disable PWM_C, enable EN_C */
PORTB &= ~PHASE_A_EN;
TCCR2A &= ~PHASE_C_OC;
PORTD |= PHASE_C_EN;
/* A: falling edge */
TCCR1B &= ~(1<<ICES1);
next_sense = SENSE_A;
break;
case 5: /* A: PWM, B: SENSE, C: LOW => disable EN_B, enable PWM_A, enable EN_A */
PORTD &= ~PHASE_B_EN;
TCCR2A |= PHASE_A_OC;
PORTB |= PHASE_A_EN;
/* B: rising edge */
TCCR1B |= (1<<ICES1);
next_sense = SENSE_B;
break;
}
if (phase == (phase_adc & 0x0F)) {
if (phase_adc & 0x10)
trigger_adc(SENSE_VOLTAGE);
else
trigger_adc(SENSE_CURRENT);
phase_adc--;
if (phase_adc == 0xff)
phase_adc = 0x15;
else if (phase_adc == 0x0f)
phase_adc = 0x05;
} else {
/* restore channel */
ADMUX = next_sense;
/* enable Analog Comparator with Interrupts */
if (blmc.flags & FLAG_COM_NORMAL) {
TIFR1 |= (1<<ICF1);
TIMSK1 |= (1<<ICIE1);
}
}
phase++;
if (phase == 6)
phase = 0;
blmc.rpm_tmp++;
}
/*
* starts motor, must not run from interrupt
*/
void spinup(void)
{
/* see util/delay.h for details.. */
uint16_t tick = (((uint16_t)(F_CPU / 3e6)) << 8) * (params.spinup_tick & 0x3F);
uint16_t time = params.spinup_ticks;
while (time > 50) {
next_phase();
uint16_t i;
for (i = 0; i < time; i++)
_delay_loop_1(tick >> 8);
time -= (time / params.spinup_step +1);
}
/* manual spinup complete, analog comperator takes control */
blmc.flags &= ~(FLAG_RUN_MASK);
blmc.flags |= FLAG_PWM_SPINUP | FLAG_COM_NORMAL;
next_phase();
for (time = 0; time < params.spinup_wait; time++)
_delay_ms(20);
/* switch to desired pwm value */
blmc.flags &= ~(FLAG_RUN_MASK);
blmc.flags |= FLAG_PWM_NORMAL | FLAG_COM_NORMAL;
}
/*
* sets new pwm value
* called from i2c-slave (set new value) and from timer (recalc current limit)
*/
void setpwm(uint8_t pwm)
{
/* run motor *only* if there are no hard errors */
if (pwm >= params.pwm_min && !(blmc.flags & FLAG_HARDERR_MASK)) {
/* do a spinup */
if (blmc.pwm == 0) {
blmc.flags &= ~(FLAG_RUN_MASK);
blmc.flags |= FLAG_PWM_SPINUP | FLAG_COM_SPINUP;
}
} else {
blmc.flags &= ~FLAG_RUN_MASK;
pwm = 0;
}
/* save pwm value */
blmc.pwm = pwm;
/* do spinup with small pwm */
if (blmc.flags & FLAG_PWM_SPINUP)
pwm = params.spinup_pwm;
/* raise current-limit, set flag */
if (blmc.current > params.current_limit) {
blmc.flags |= FLAG_CURRENT_LIMIT;
blmc.pwm_limit++;
/* lower current-limit */
} else if (blmc.pwm_limit > 0) {
blmc.pwm_limit--;
} else if (blmc.pwm_limit == 0) {
blmc.flags &= ~FLAG_CURRENT_LIMIT;
}
/* prevent overflow */
if (blmc.pwm_limit > pwm)
blmc.pwm_limit = pwm;
/* set new value */
pwm -= blmc.pwm_limit;
/* limit pwm */
if (pwm > params.pwm_max)
pwm = params.pwm_max;
OCR0B = pwm;
OCR2A = pwm;
OCR2B = pwm;
/* disable PWM signales, enable all drivers -> brake */
if (pwm == 0) {
TCCR0A &= ~PHASE_B_OC;
TCCR2A &= ~(PHASE_A_OC | PHASE_C_OC);
PORTB |= PHASE_A_EN;
PORTD |= (PHASE_B_EN | PHASE_C_EN);
}
}
ISR(TIMER1_CAPT_vect)
{
next_phase();
}
ISR(ADC_vect)
{
static uint16_t current_tmp, voltage_tmp;
static uint8_t current_cnt, voltage_cnt;
uint8_t channel = ADMUX & 0x0F;
uint16_t value = ADCW;
/* restore channel */
ADMUX = next_sense;
/* turn off ADC, disable interrupt */
ADCSRA = 0x00;
/* enable Analog Comparator with Interrupts */
if (blmc.flags & FLAG_COM_NORMAL) {
TIFR1 |= (1<<ICF1);
TIMSK1 |= (1<<ICIE1);
}
if (channel == SENSE_CURRENT) {
current_tmp += value;
current_cnt++;
if (current_cnt == 6) {
blmc.current = current_tmp;
current_tmp = 0;
current_cnt = 0;
}
if (value > params.current_max)
blmc.flags |= FLAG_OVERCURRENT;
} else {
voltage_tmp += value;
voltage_cnt++;
if (voltage_cnt == 6) {
blmc.voltage = voltage_tmp;
voltage_tmp = 0;
voltage_cnt = 0;
if (blmc.voltage < params.voltage_min)
blmc.flags |= FLAG_UNDERVOLTAGE;
}
}
}