224 lines
5.8 KiB
C
224 lines
5.8 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. *
|
|
***************************************************************************/
|
|
/*
|
|
* Status LED_GN LED_RT
|
|
* No Error, Motor off ON -
|
|
* No Error, Motor spinup FAST -
|
|
* No Error, Motor running SLOW -
|
|
* Current Limit F/S ON
|
|
* i2c Timeout ON ON
|
|
* Undervoltage OFF SLOW
|
|
* Overcurrent (Hard Limit) OFF FAST
|
|
* SELFTEST failed FAST FAST (not implemented yet)
|
|
* EEPROM invalid SLOW SLOW
|
|
*/
|
|
|
|
#include <avr/io.h>
|
|
#include <avr/interrupt.h>
|
|
|
|
#include "main.h"
|
|
#include "blmc.h"
|
|
#include "eeprom.h"
|
|
|
|
#define LED_OFF 0x00
|
|
#define LED_SLOW 0x01
|
|
#define LED_FAST 0x10
|
|
#define LED_ON 0x11
|
|
|
|
extern struct blmc_ blmc;
|
|
|
|
static uint8_t led[2];
|
|
|
|
ISR(TIMER1_OVF_vect)
|
|
{
|
|
static uint8_t rpm_cnt = 0;
|
|
static uint8_t adc_chan = SENSE_CURRENT;
|
|
|
|
/* Come back in 20ms */
|
|
TCNT1 = (0xFFFF - 20000);
|
|
|
|
/* commutations during last 20ms */
|
|
uint16_t diff = blmc.rpm_tmp - blmc.rpm_tmp_old;
|
|
blmc.rpm_tmp_old = blmc.rpm_tmp;
|
|
|
|
if ((blmc.flags & FLAG_RUN_MASK) == (FLAG_PWM_NORMAL | FLAG_COM_NORMAL)) {
|
|
/* too few commutations while running -> do a spinup */
|
|
if (diff < 0x08) {
|
|
blmc.flags &= ~(FLAG_RUN_MASK);
|
|
blmc.flags |= FLAG_PWM_SPINUP | FLAG_COM_SPINUP;
|
|
}
|
|
|
|
/* no i2c cmd in the last 20ms */
|
|
if (!(blmc.flags & FLAG_I2C_ACTIVE)) {
|
|
/* already in i2c timeout, turn off motor */
|
|
if (blmc.flags & FLAG_I2C_TIMEOUT)
|
|
blmc.pwm = 0;
|
|
|
|
blmc.flags |= FLAG_I2C_TIMEOUT;
|
|
|
|
} else {
|
|
blmc.flags &= ~FLAG_I2C_TIMEOUT;
|
|
}
|
|
|
|
// TODO: currently disabled
|
|
// blmc.flags &= ~FLAG_I2C_ACTIVE;
|
|
}
|
|
|
|
/* set pwm again (adjust current limit) */
|
|
setpwm(blmc.pwm);
|
|
|
|
/* calc rpm every second */
|
|
rpm_cnt++;
|
|
if (rpm_cnt == 50) {
|
|
rpm_cnt = 0;
|
|
|
|
blmc.rpm = blmc.rpm_tmp;
|
|
blmc.rpm_tmp = 0;
|
|
}
|
|
|
|
/* trigger adc by hand when not running */
|
|
if (!(blmc.flags & FLAG_COM_NORMAL)) {
|
|
trigger_adc(adc_chan);
|
|
if (adc_chan == SENSE_CURRENT)
|
|
adc_chan = SENSE_VOLTAGE;
|
|
else
|
|
adc_chan = SENSE_CURRENT;
|
|
}
|
|
|
|
/* led blink timer */
|
|
static uint8_t led_timer = 0;
|
|
led_timer = (led_timer +1) & 0x1F;
|
|
|
|
/* green LED */
|
|
if (((led[0] == LED_SLOW) && (led_timer & 0x10)) ||
|
|
(led[0] == LED_FAST && (led_timer & 0x04)) ||
|
|
(led[0] == LED_ON)) {
|
|
PORTB |= LED_GN;
|
|
|
|
} else {
|
|
PORTB &= ~LED_GN;
|
|
}
|
|
|
|
/* red LED */
|
|
if (((led[1] == LED_SLOW) && !(led_timer & 0x10)) ||
|
|
(led[1] == LED_FAST && !(led_timer & 0x04)) ||
|
|
(led[1] == LED_ON)) {
|
|
PORTB |= LED_RT;
|
|
|
|
} else {
|
|
PORTB &= ~LED_RT;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* For newer devices (mega88) the watchdog timer remains active even after a
|
|
* system reset. So disable it as soon as possible.
|
|
* automagically called on startup
|
|
*/
|
|
void get_mcusr(void) __attribute__((naked, section(".init3")));
|
|
void get_mcusr(void)
|
|
{
|
|
MCUSR = 0;
|
|
WDTCSR = (1<<WDCE) | (0<<WDE);
|
|
}
|
|
|
|
int main(void)
|
|
{
|
|
DDRB = PHASE_A_EN | PHASE_A_PWM | LED_RT | LED_GN;
|
|
DDRD = PHASE_B_EN | PHASE_B_PWM | PHASE_C_EN | PHASE_C_PWM;
|
|
|
|
PORTB = 0x00;
|
|
PORTD = 0x00;
|
|
|
|
/* timer0: running with F_CPU, 8bit Phase Correct PWM (16kHz) */
|
|
TCCR0A = (1<<WGM00);
|
|
TCCR0B = (1<<CS00);
|
|
|
|
/* timer1: running with F_CPU/8 */
|
|
TCCR1B = (1<<CS11);
|
|
|
|
/* timer2: running with F_CPU, 8bit Phase Correct PWM (16kHz) */
|
|
TCCR2A = (1<<WGM20);
|
|
TCCR2B = (1<<CS20);
|
|
|
|
/* enable Timer1 OVF Interrupt */
|
|
TIMSK1 = (1<<TOIE1);
|
|
|
|
/* Enable Analog Comparator Multiplexer */
|
|
ADCSRB |= (1<<ACME);
|
|
|
|
/* I2C Init: keep Address from bootloader, Auto ACKs with Interrupts */
|
|
TWCR = (1<<TWEA) | (1<<TWEN) | (1<<TWIE);
|
|
|
|
blmc.flags = 0x00;
|
|
if (read_parameters())
|
|
blmc.flags |= FLAG_INVALID_EEPROM;
|
|
|
|
sei();
|
|
|
|
while (1) {
|
|
uint8_t ledX[2] = { 0x00, 0x00 };
|
|
|
|
/* get motor status: spinup, running or off */
|
|
if (blmc.flags & FLAG_RUN_MASK) {
|
|
if (blmc.flags & (FLAG_COM_SPINUP | FLAG_PWM_SPINUP))
|
|
ledX[0] = LED_FAST;
|
|
else
|
|
ledX[0] = LED_SLOW;
|
|
} else {
|
|
ledX[0] = LED_ON;
|
|
}
|
|
|
|
/* soft errors (current limit, i2c timeout) */
|
|
if (blmc.flags & FLAG_SOFTERR_MASK)
|
|
ledX[1] = LED_ON;
|
|
|
|
/* hard errors */
|
|
if (blmc.flags & FLAG_HARDERR_MASK) {
|
|
if (blmc.flags & FLAG_CURRENT_LIMIT) {
|
|
ledX[0] = LED_OFF;
|
|
ledX[1] = LED_FAST;
|
|
|
|
} else if (blmc.flags & FLAG_UNDERVOLTAGE) {
|
|
ledX[0] = LED_OFF;
|
|
ledX[1] = LED_SLOW;
|
|
|
|
} else if (blmc.flags & FLAG_SELFTEST_FAILED) {
|
|
ledX[0] = LED_FAST;
|
|
ledX[1] = LED_FAST;
|
|
|
|
} else if (blmc.flags & FLAG_INVALID_EEPROM) {
|
|
ledX[0] = LED_SLOW;
|
|
ledX[1] = LED_SLOW;
|
|
}
|
|
}
|
|
|
|
if (!(blmc.flags & (FLAG_SOFTERR_MASK | FLAG_HARDERR_MASK)))
|
|
ledX[1] = LED_OFF;
|
|
|
|
led[0] = ledX[0];
|
|
led[1] = ledX[1];
|
|
|
|
/* do a spinup from main loop (blocking for > 200ms) */
|
|
if (blmc.flags & FLAG_COM_SPINUP)
|
|
spinup();
|
|
};
|
|
return 0;
|
|
}
|