This commit is contained in:
Olaf Rempel 2007-09-20 22:54:11 +02:00
commit eea082635e
7 changed files with 599 additions and 0 deletions

6
.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
*.o
*.elf
*.bin
*.hex
*.lst
*.map

64
Makefile Normal file
View File

@ -0,0 +1,64 @@
PRG = bl_test
OBJ = blmc.o i2c-slave.o main.o
MCU_TARGET = atmega8
OPTIMIZE = -Os
DEFS =
LIBS =
# You should not have to change anything below here.
CC = avr-gcc
# Override is only needed by avr-lib build system.
override CFLAGS = -g -Wall $(OPTIMIZE) -mmcu=$(MCU_TARGET) $(DEFS)
override LDFLAGS = -Wl,-Map,$(PRG).map
OBJCOPY = avr-objcopy
OBJDUMP = avr-objdump
SIZE = avr-size
all: $(PRG).elf lst text eeprom
$(SIZE) -x -A $(PRG).elf
$(PRG).elf: $(OBJ)
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ $(LIBS)
clean:
rm -rf *.o *.lst *.map $(PRG).elf *.hex *.bin
lst: $(PRG).lst
%.lst: %.elf
$(OBJDUMP) -h -S $< > $@
# Rules for building the .text rom images
text: hex bin
hex: $(PRG).hex
bin: $(PRG).bin
%.hex: %.elf
$(OBJCOPY) -j .text -j .data -O ihex $< $@
%.bin: %.elf
$(OBJCOPY) -j .text -j .data -O binary $< $@
# Rules for building the .eeprom rom images
eeprom: ehex ebin
ehex: $(PRG)_eeprom.hex
ebin: $(PRG)_eeprom.bin
%_eeprom.hex: %.elf
$(OBJCOPY) -j .eeprom --change-section-lma .eeprom=0 -O ihex $< $@
%_eeprom.bin: %.elf
$(OBJCOPY) -j .eeprom --change-section-lma .eeprom=0 -O binary $< $@
install: text
uisp -dprog=avr910 -dserial=/dev/ttyS0 -dspeed=115200 -dpart=M8 --erase --upload if=$(PRG).hex
# avrdude -p m8 -c butterfly -b 19200 -P /dev/ttyUSB0 -u -e -V -U flash:w:$(PRG).hex

226
blmc.c Normal file
View File

@ -0,0 +1,226 @@
/***************************************************************************
* 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"
struct blmc_ blmc;
static uint8_t next_sense;
void trigger_adc(uint8_t channel)
{
/* Disable Analog Comperator */
ACSR &= ~(1<<ACIE);
/* set channel (Internal reference, 2.56V) */
ADMUX = (1<<REFS1) | (1<<REFS0) | channel;
/* turn on ADC with interrupts, start conversion with 1/32 of F_CPU */
ADCSRA = (1<<ADEN) | (1<<ADSC) | (1<<ADIE) | (1<<ADIF)| (1<<ADPS2) | (1<<ADPS0);
}
void next_phase(void)
{
static uint8_t phase;
static uint8_t phase_adc;
/* Disable Analog Comperator */
ACSR &= ~(1<<ACIE);
TCNT1 = 0x00;
TCNT2 = 0x00;
switch (phase) {
case 0: PORTD = (PORTD & ~PHASE_L_MASK) | PHASE_B_L;
TCCR1A = (TCCR1A & ~(1<<COM1B1)) | (1<<COM1A1);
TCCR2 &= ~(1<<COM21);
ACSR = (1<<ACIS1);
next_sense = SENSE_C;
break;
case 1: PORTD = (PORTD & ~PHASE_L_MASK) | PHASE_B_L;
TCCR1A &= ~((1<<COM1A1) | (1<<COM1B1));
TCCR2 |= (1<<COM21);
ACSR = (1<<ACIS1) | (1<<ACIS0);
next_sense = SENSE_A;
break;
case 2: PORTD = (PORTD & ~PHASE_L_MASK) | PHASE_A_L;
TCCR1A &= ~((1<<COM1A1) | (1<<COM1B1));
TCCR2 |= (1<<COM21);
ACSR = (1<<ACIS1);
next_sense = SENSE_B;
break;
case 3: PORTD = (PORTD & ~PHASE_L_MASK) | PHASE_A_L;
TCCR1A = (TCCR1A & ~(1<<COM1A1)) | (1<<COM1B1);
TCCR2 &= ~(1<<COM21);
ACSR = (1<<ACIS1) | (1<<ACIS0);
next_sense = SENSE_C;
break;
case 4: PORTD = (PORTD & ~PHASE_L_MASK) | PHASE_C_L;
TCCR1A = (TCCR1A & ~(1<<COM1A1)) | (1<<COM1B1);
TCCR2 &= ~(1<<COM21);
ACSR = (1<<ACIS1);
next_sense = SENSE_A;
break;
case 5: PORTD = (PORTD & ~PHASE_L_MASK) | PHASE_C_L;
TCCR1A = (TCCR1A & ~(1<<COM1B1)) | (1<<COM1A1);
TCCR2 &= ~(1<<COM21);
ACSR = (1<<ACIS1) | (1<<ACIS0);
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)
ACSR |= (1<<ACIE) | (1<<ACI);
}
phase++;
if (phase == 6)
phase = 0;
blmc.rpm_tmp++;
}
void spinup(void)
{
uint16_t time = 1000;
while (time > 50) {
next_phase();
uint16_t i;
for (i = 0; i < time; i++)
_delay_us(25);
time -= (time / 24 +1);
}
blmc.flags = FLAG_PWM_SPINUP | FLAG_COM_NORMAL;
next_phase();
for (time = 0; time < 10; time++)
_delay_ms(20);
blmc.flags = FLAG_PWM_NORMAL | FLAG_COM_NORMAL;
}
void setpwm(uint8_t pwm)
{
if (pwm >= 8) {
if (blmc.pwm == 0)
blmc.flags = FLAG_PWM_SPINUP | FLAG_COM_SPINUP;
blmc.pwm = pwm;
} else {
blmc.flags = 0x00;
blmc.pwm = 0;
}
if (blmc.flags & FLAG_PWM_SPINUP)
pwm = 0x0f;
else
pwm = blmc.pwm;
if (blmc.current > 120)
blmc.pwm_limit++;
else if (blmc.pwm_limit > 0)
blmc.pwm_limit--;
if (blmc.pwm_limit > pwm)
blmc.pwm_limit = pwm;
pwm -= blmc.pwm_limit;
OCR1A = pwm;
OCR1B = pwm;
OCR2 = pwm;
}
ISR(ANA_COMP_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)
ACSR |= (1<<ACIE) | (1<<ACI);
sei();
if (channel == SENSE_CURRENT) {
current_tmp += value;
current_cnt++;
if (current_cnt == 6) {
blmc.current = current_tmp;
current_tmp = 0;
current_cnt = 0;
}
// TODO: ueberstrom-abschaltung
} else {
voltage_tmp += value;
voltage_cnt++;
if (voltage_cnt == 6) {
blmc.voltage = voltage_tmp;
voltage_tmp = 0;
voltage_cnt = 0;
}
// TODO: unterspannungs-abschaltung
}
}

47
blmc.h Normal file
View File

@ -0,0 +1,47 @@
#ifndef _BLMC_H_
#define _BLMC_H_
#include <avr/io.h>
#define PHASE_A_H (1<<PORTB1)
#define PHASE_B_H (1<<PORTB2)
#define PHASE_C_H (1<<PORTB3)
#define PHASE_H_MASK (PHASE_A_H | PHASE_B_H | PHASE_C_H)
#define PHASE_A_L (1<<PORTD4)
#define PHASE_B_L (1<<PORTD3)
#define PHASE_C_L (1<<PORTD2)
#define PHASE_L_MASK (PHASE_A_L | PHASE_B_L | PHASE_C_L)
#define SENSE_A 0
#define SENSE_B 1
#define SENSE_C 2
#define SENSE_VOLTAGE 3
#define SENSE_CURRENT 6
#define FLAG_PWM_SPINUP 0x01
#define FLAG_PWM_NORMAL 0x02
#define FLAG_COM_SPINUP 0x10
#define FLAG_COM_NORMAL 0x20
struct blmc_ {
uint8_t flags;
uint8_t pwm; // pwm setpoint
uint8_t pwm_limit; // increased by current-limit
uint16_t rpm;
uint16_t rpm_tmp;
uint16_t rpm_tmp2;
uint16_t current;
uint16_t voltage;
};
void trigger_adc(uint8_t channel);
void setpwm(uint8_t pwm);
void spinup(void);
#endif

130
i2c-slave.c Normal file
View File

@ -0,0 +1,130 @@
/***************************************************************************
* 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 <avr/wdt.h>
#include "main.h"
#include "blmc.h"
extern struct blmc_ blmc;
#define CMD_NONE 0x00
#define CMD_SETPWM 0x10
#define CMD_GETSTAT 0x20
#define CMD_REBOOT 0x80
ISR(TWI_vect)
{
static uint8_t cmd = CMD_NONE;
switch (TWSR & 0xF8) {
/* SLA + W received, ACK returned -> receive Data and ACK */
case 0x60:
cmd = CMD_NONE;
TWCR |= (1<<TWINT) | (1<<TWEA);
break;
/* prev. SLA + W, data received, ACK returned -> receive Data and ACK */
case 0x80:
switch (cmd & 0xF0) {
/* First bytes -> Command */
case CMD_NONE:
cmd = TWDR;
break;
/* set pwm */
case CMD_SETPWM:
setpwm(TWDR);
cmd = CMD_NONE;
break;
/* set parameters */
case CMD_REBOOT:
if (TWDR == 0x42)
wdt_enable(WDTO_15MS);
cmd = CMD_NONE;
break;
/* rest invalid */
default:
cmd = CMD_NONE;
break;
}
TWCR |= (1<<TWINT);
break;
/* SLA+R received, ACK returned -> send data */
case 0xA8:
/* prev. SLA+R, data sent, ACK returned -> send data */
case 0xB8:
switch (cmd & 0xF0) {
/* get Current */
case CMD_GETSTAT:
switch (cmd++ & 0x0F) {
case 0: TWDR = blmc.pwm - blmc.pwm_limit;
break;
case 1: TWDR = blmc.pwm;
break;
case 2: TWDR = (blmc.rpm >> 8);
break;
case 3: TWDR = (blmc.rpm & 0xFF);
break;
case 4: TWDR = (blmc.current >> 8);
break;
case 5: TWDR = (blmc.current & 0xFF);
break;
case 6: TWDR = (blmc.voltage >> 8);
break;
case 7: TWDR = (blmc.voltage & 0xFF);
cmd = CMD_NONE;
break;
}
break;
/* rest invalid */
default:
cmd = CMD_NONE;
break;
}
TWCR |= (1<<TWINT);
break;
/* STOP or repeated START */
case 0xA0:
/* Data transmitted, NACK returned */
case 0xC0:
TWCR |= (1<<TWINT) | (1<<TWEA);
break;
/* Illegal state -> reset Hardware */
case 0xF8:
TWCR |= (1<<TWINT) | (1<<TWSTO) | (1<<TWEA);
break;
}
}

114
main.c Normal file
View File

@ -0,0 +1,114 @@
/***************************************************************************
* 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. *
***************************************************************************/
/*
* Application:
* LED_GN blinking -> motor stop
* LED_GN on -> motor running
* LED_GN on & LED_RT blinking -> pwm soft limit
* LED_GN off & LED_RT blinking -> current to high / undervoltage
*
* startup:
* - selftest
*
* cmdloop:
* - set motor (pwm)
* - get status (motor speed, voltage, current)
* - reboot (with cookie)
*/
#include <avr/io.h>
#include <avr/interrupt.h>
#include "main.h"
#include "blmc.h"
extern struct blmc_ blmc;
ISR(TIMER0_OVF_vect)
{
static uint8_t timer0_cnt = 0;
static uint8_t adc_chan = SENSE_CURRENT;
/* Come back in 20ms */
TCNT0 = 0xFF - 156;
/* current-limiting */
setpwm(blmc.pwm);
uint16_t diff = blmc.rpm_tmp - blmc.rpm_tmp2;
blmc.rpm_tmp2 = blmc.rpm_tmp;
/* too low rpm while running -> do a spinup */
if (diff < 0x8 && blmc.flags == (FLAG_PWM_NORMAL | FLAG_COM_NORMAL))
blmc.flags = FLAG_PWM_SPINUP | FLAG_COM_SPINUP;
timer0_cnt++;
if (timer0_cnt == 50) {
timer0_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;
}
}
int main(void)
{
DDRB = PHASE_H_MASK | LED_RT | LED_GN;
DDRD = PHASE_L_MASK;
PORTB = LED_GN;
PORTD = 0x00;
/* timer0: running with F_CPU/1024 */
TCCR0 = (1<<CS02) | (1<<CS00);
/* timer1: running with F_CPU, 8bit Fast PWM (32kHz) */
TCCR1B = (1<<CS10);// | (1<<WGM12);
TCCR1A = (1<<WGM10);
/* timer2: running with F_CPU, 8bit Fast PWM (32kHz) */
TCCR2 = (1<<WGM20) | (1<<CS20);// | (1<<WGM21);
/* enable Timer0 OVF Interrupt */
TIMSK = (1<<TOIE0); // | (1<<OCIE2); // | (1<<TOIE2)
/* Enable Analog Comparator Multiplexer */
SFIOR |= (1<<ACME);
/* I2C Init: keep Address from bootloader, Auto ACKs with Interrupts */
TWCR = (1<<TWEA) | (1<<TWEN) | (1<<TWIE);
sei();
while (1) {
if (blmc.flags & FLAG_COM_SPINUP)
spinup();
};
return 0;
}

12
main.h Normal file
View File

@ -0,0 +1,12 @@
#ifndef _MAIN_H_
#define _MAIN_H_
#include <avr/io.h>
#define F_CPU 8000000
#include <util/delay.h>
#define LED_RT (1<<PORTB4)
#define LED_GN (1<<PORTB5)
#endif