Ich habe hier verschiedene BLDC-Motoren rumfliegen (u.a. die Leyboldpumpe)
um ein wenig spielen und testen zu können, habe ich mir mal den BLMC vom Ulrich Radig zusammengefrickelt, das funktioniert auch alles soweit ganz gut, bis darauf, dass der die Sollgeschwindigkeit über I²C haben will.
Ok, soweit kein Problem, ich habe das TWI-Gerümpel aus dem Code geworfen und rx_pwm statisch auf irgendeinen Wert gesetzt.
Die nächste Komfortstufe sollte sein, einen der noch freien ADC mit einem Poti auszustatten.
ADC6 an Pin19 gefiel mir da am Besten.
Fragt nicht, wozu dieses Bild gut ist, aber ich mag den neuen Ablader!
Nun bin ich mir der Steuerung von BLDC-Motoren noch nicht ganz warm, habe aber durchaus schon verstanden, dass Timing eine Hauptrolle spielt...
und wenn ich den Hobel nun zwinge, den ADC für meine Zwecke zu initialisieren, kriegt der Motor eine epileptischen Anfall.
Da mein Programmierstil auch eher zweckorientiert einer Axt im Wald gleicht, steige ich durch den Code vom Herrn Radig nicht so richtig durch.
Code: Alles auswählen
/*----------------------------------------------------------------------------
Copyright: Ulrich Radig (mail@ulrichradig.de)
Author: Ulrich Radig
Remarks:
known Problems: none
Version: 23.06.2011
Description: Brushless Motor Controller for ATmega48/88/168
Dieses Programm ist freie Software. Sie können es unter den Bedingungen der
GNU General Public License, wie von der Free Software Foundation veröffentlicht,
weitergeben und/oder modifizieren, entweder gemäß Version 2 der Lizenz oder
(nach Ihrer Option) jeder späteren Version.
Die Veröffentlichung dieses Programms erfolgt in der Hoffnung,
daß es Ihnen von Nutzen sein wird, aber OHNE IRGENDEINE GARANTIE,
sogar ohne die implizite Garantie der MARKTREIFE oder der VERWENDBARKEIT
FÜR EINEN BESTIMMTEN ZWECK. Details finden Sie in der GNU General Public License.
Sie sollten eine Kopie der GNU General Public License zusammen mit diesem
Programm erhalten haben.
Falls nicht, schreiben Sie an die Free Software Foundation,
Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
------------------------------------------------------------------------------*/
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <avr/io.h>
//PHASE1 (U)
#define UH_DDR DDRB |= (1<<3);
#define UH_ON TCCR2A |= (1<<COM2A1);
#define UH_OFF TCCR2A &= ~(1<<COM2A1);
//PHASE1 (U)
#define UL_DDR DDRB |= (1<<1);
#define UL_ON PORTB |= (1<<1);
#define UL_OFF PORTB &= ~(1<<1);
//PHASE2 (V)
#define VH_DDR DDRD |= (1<<5);
#define VH_ON TCCR0A |= (1<<COM0B1);
#define VH_OFF TCCR0A &= ~(1<<COM0B1);
//PHASE2 (V)
#define VL_DDR DDRB |= (1<<2);
#define VL_ON PORTB |= (1<<2);
#define VL_OFF PORTB &= ~(1<<2);
//PHASE3 (W)
#define WH_DDR DDRD |= (1<<3);
#define WH_ON TCCR2A |= (1<<COM2B1);
#define WH_OFF TCCR2A &= ~(1<<COM2B1);
//PHASE3 (W)
#define WL_DDR DDRC |= (1<<3);
#define WL_ON PORTC |= (1<<3);
#define WL_OFF PORTC &= ~(1<<3);
#define PHASE_ALL_OFF UH_OFF;UL_OFF;VH_OFF;VL_OFF;WH_OFF;WL_OFF;
#define SENSE_U ADMUX = 0;
#define SENSE_V ADMUX = 1;
#define SENSE_W ADMUX = 2;
#define SENSE_H (ACSR&(1<<ACO))
#define START_PWM 6
//volatile unsigned long i2c_timeout = 0;
volatile unsigned char rx_pwm = 0;
volatile unsigned char rotor_state = 0;
volatile unsigned char rotor_run = 0;
//############################################################################
void next_commutate_state (unsigned char startup)
//############################################################################
{
switch (rotor_state)
{
case (0):
if(!SENSE_H || startup)
{
WH_OFF;
UH_ON;
SENSE_W;
rotor_state = 1;
TCNT1 = 1;
}
break;
case (1):
if(SENSE_H || startup)
{
VL_OFF;
WL_ON;
SENSE_V;
rotor_state = 2;
TCNT1 = 1;
}
break;
case (2):
if(!SENSE_H || startup)
{
UH_OFF;
VH_ON;
SENSE_U;
rotor_state = 3;
TCNT1 = 1;
}
break;
case (3):
if(SENSE_H || startup)
{
WL_OFF;
UL_ON;
SENSE_W;
rotor_state = 4;
TCNT1 = 1;
}
break;
case (4):
if(!SENSE_H || startup)
{
VH_OFF;
WH_ON;
SENSE_V;
rotor_state = 5;
TCNT1 = 1;
}
break;
case (5):
if(SENSE_H || startup)
{
UL_OFF;
VL_ON;
SENSE_U;
rotor_state = 0;
TCNT1 = 1;
}
break;
}
}
//############################################################################
//back EMF zero crossing detection
ISR (ANALOG_COMP_vect)
//############################################################################
{
if(rotor_run == 200) next_commutate_state (0);
rotor_run++;
if(rotor_run > 200)
{
rotor_run = 200;
}
}
//############################################################################
ISR (TIMER1_OVF_vect)
//############################################################################
{
next_commutate_state (1);
rotor_run = 0;
OCR2A = START_PWM;
OCR2B = START_PWM;
OCR0B = START_PWM;
}
//############################################################################
//Hauptprogramm
int main (void)
//############################################################################
{
//Watchdog on
WDTCSR = (1<<WDCE) | (1<<WDE);
//Motordriver output
UH_DDR;
VH_DDR;
WH_DDR;
UL_DDR;
VL_DDR;
WL_DDR;
//PWM for UH, VH and WH (>32KHz)
TCCR0A |= (1<<COM0B1|1<<WGM01|1<<WGM00);
TCCR0B |= (1<<CS00);
TCCR2A |= (1<<COM2A1|1<<COM2B1|1<<WGM21|1<<WGM20);
TCCR2B |= (1<<CS20);
//TIMER1 for start commutation or Back EMF lost
TCCR1B |= (1<<CS11);
TIMSK1 |= (1<<TOIE1);
PHASE_ALL_OFF;
//Comperator init for back EMF
ADCSRB |= (1<<ACME);
DIDR1 |= (1<<AIN0D);
ACSR |= (1<<ACIE);
//Interrupts enabel
sei();
rx_pwm = 140;
while(1)
{
asm("wdr");
if(rx_pwm > START_PWM)
{
ACSR |= (1<<ACIE);
TIMSK1 |= (1<<TOIE1);
if(rotor_run == 200)
{
OCR2A = rx_pwm;
OCR2B = rx_pwm;
OCR0B = rx_pwm;
}
}
else
{
PHASE_ALL_OFF;
ACSR&=~(1<<ACIE);
TIMSK1 &= ~(1<<TOIE1);
}
}
}
Was ich nicht hinkriege ist, den ADC6 irgendwann -wenn mal ein bischen Zeit- ist zum einlesen des aktuellen Wertes zu überreden, ohne dass gleich alles drunter und drüber geht... Wie kann ich dem Lümmel beibiegen, dass er ADC6 anders nutzen soll?
Also der Atmega 88 müsste doch performant genug sein um seine ADCs mehrfach nutzen zu können, oder irre ich mich da und der ist mit seiner Aufgabe völlig ausgelastet?
An dieser Stelle schon mal besten Dank an Eure Hilfe!