Software BLDC Controller umstricken

Der chaotische Hauptfaden

Moderatoren: Heaterman, Finger, Sven, TDI, Marsupilami72, duese

Antworten
Benutzeravatar
Arndt
Beiträge: 2589
Registriert: Fr 28. Jun 2013, 13:42
Wohnort: einen Schritt über den Abgrund hinaus

Software BLDC Controller umstricken

Beitrag von Arndt »

Ich brauche da mal professionelle Hilfe.

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.
BildFragt 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);
		}
		
	}
}

Definition, usw. habe ich soweit durchdrungen und offensichtlich werden die ADC0,1 und 2 als Analogcomparator genutzt um die Rotorstellung zu erkennen. (stimmt doch hoffentlich :? )

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!
Benutzeravatar
felixh
Beiträge: 593
Registriert: So 11. Aug 2013, 13:46

Re: Software BLDC Controller umstricken

Beitrag von felixh »

Hi!

der analog comparator verwendet auch das ADMUC register.
dh, man kann den analog comparator entweder auf den AIN0 und AIN1 pins verwenden, ODER mit dem analogmultiplexer des ADCs.

wenn du den multiplexer des ADCs nimmst (ist hier der Fall) kannst du natürlich mit dem ADC nix mehr messen ;)

Eine idee wäre es, vll. alle x sec alle phasen abzuschalten, und eine ADC messung durchzuführen.

Oder wenn das Timingmässig klappt, direkt nach dem umschalten. Hängt aber von der Motordrehzahl ab.
bei 200khz ADC frequenz braucht so ne wandlung ja ihre 65us. (AFAIK. waren doch 13 taktzyklen für ne 10 bit wandlung, oder?)
Für diese zeit musst du natürlich das ANALOG_COMP_vect interrupt deaktivieren, also ACSR &= ~(1<<ACIE);
Benutzeravatar
Arndt
Beiträge: 2589
Registriert: Fr 28. Jun 2013, 13:42
Wohnort: einen Schritt über den Abgrund hinaus

Re: Software BLDC Controller umstricken

Beitrag von Arndt »

Wow, das haut mich fast aus den Latschen hier!

Ich hatte schon befürchtet, dass entweder nur das Eine, oder nur das Andere geht... meinte irgendwo im Datenblatt etwas gelesen zu haben, dass nicht alle ADCs vom comparator verhaftet werden, aber Du hast vollkommen recht, nur so ergibt das Sinn!

Ich bin nicht sicher, ob der Controller im laufenden Betrieb, nach dem ab bzw. zuschalten aller Phasen wieder in den Rytmus findet, ich hatte das Gefühl, dass der sich auch so gerne mal verschluckt und dann abschaltet (wie es in dem Video passiert ist)

Das mit dem Timing ist jetzt die interessanteste Frage...
Die Motordrehzahl soll schon -wenn es mit dieser Applikation umsetzbar ist- richtig hoch sein.
Ich bezweifle zwar, dass die TMP ihre 43.000 U/min noch einmal schafft, aber es wäre nett, das prinzipiell zu können :D

Wenn ich das jetzt richtig verstehe, müsste in einer passenden Pause grob folgendes passieren:

Code: Alles auswählen


//irgendwo in der Initialisierung 
ADCSRA |= (1 << ADPS2) | (1 << ADPS1); //ADC Abtastfreq. 125kHz -> müssten ja eigentlich reichen.

// wenn Zeit ist
ACSR &= ~(1<<ACIE); //ANALOG_COMP_vect interrupt deaktivieren	
	ADCSRA |= (1 << ADEN);                  // ADC aktivieren
        ADMUX = 6; // Kanal wählen
	ADCSRA |= (1 << ADSC);                  // eine ADC-Wandlung
	while (ADCSRA & (1 << ADSC)) {     // auf Abschluss der Konvertierung warten
	}	
	rx_pwm = ADCW;
       ADCSRA &= ~(1 << ADEN); // ADC deaktivieren
ACSR &= ~(1<<ACIE); //ANALOG_COMP_vect interrupt aktivieren
Wenn das jetzt nicht vollkommener murks ist,
wie finde ich denn am besten raus, wann Zeit für so etwas ist?

Edith sagt, ADMUX vergessen...
Benutzeravatar
licht_tim
Beiträge: 1472
Registriert: Fr 28. Jun 2013, 09:40
Wohnort: Ganderkesee

Re: Software BLDC Controller umstricken

Beitrag von licht_tim »

Ich habs jetzt mit den Controllern nicht so, eigentlich garnicht, aber wäre es nicht einfacher den Code einfach zu lassen wie er ist und einen zweiten zu nehmen und den dann das Poti auswerten und über i2c weiter geben zu lassen. Also im Sinne der 80%, vielleicht nicht das allerschönste aber man muss sich den anderen Code nicht reintun.
Benutzeravatar
Alexander470815
Beiträge: 2371
Registriert: So 11. Aug 2013, 15:42
Wohnort: D:\Hessen\Gießen

Re: Software BLDC Controller umstricken

Beitrag von Alexander470815 »

Ist die Pumpe denn überhaupt ein BLDC Motor?
Irgendwie sieht der Rotor mir stark nach Kurschlussläufer aus.
Ist er denn magnetisch (also selbst ein Magnet)?
Benutzeravatar
Arndt
Beiträge: 2589
Registriert: Fr 28. Jun 2013, 13:42
Wohnort: einen Schritt über den Abgrund hinaus

Re: Software BLDC Controller umstricken

Beitrag von Arndt »

Ähm ja, MIST
Alexander470815, Du hast recht! Schubladendenken meinerseits.
Der wird im Original natürlich mit einem FU angesteuert. :oops:
Zum Testen reicht der BLDC-treiber...

Die Herausforderung des regelbaren BLDC-Treibers bleibt aber trotzdem.
Die 80%-Lösung ist da natürlich ein Schlagkräftiges Argument,
Christopher hat mir schon erzählt, dass er und Ferdi das mal gemacht haben, aber aus irgendeinem Grund hat mich der Ehrgeiz gepackt.
Da wären ja noch der Keksentgrater, Nord-Süd-Ausrichter (noch geheim) und ein Pedelec-Motor mit 1Kw.
Eine passende Endstufe befindet sich gerade im Aufbau.
Benutzeravatar
felixh
Beiträge: 593
Registriert: So 11. Aug 2013, 13:46

Re: Software BLDC Controller umstricken

Beitrag von felixh »

prinzipiell sieht das schon ganz gut aus!
ABER du musst dir den ursprünglichen zustand des ADMUX registers merken! sonst aktivierst du den interrupt ja wieder, und lässt ADMUX auf 6 stehen, und der Motor wird zucken ;)

den einwand, dass der Controller ja auch die synchronisation zum Motor widerfinden muss, ist berechtigt. Da bin ich gerade überfragt, wie man das am besten anstellen könnte.
Das intelligenteste wäre dann warscheinlich, während des Betriebs die Frequenz zu messen, und dann mitzuzählen...

Mess doch mal mit nem Scope den zeitrahmen, den wir haben. Ein ADC lässt sich nämlich auch übertakten. Und hat vll Sogar ein S/H Glied, das man nutzen könnte... //EDIT: nein, hat er nicht. schade...

//EDIT 2:
Eben hatte ich die Erleuchtung!
DREHENCODER! funktionieren rein digital, und man hat was zum dran drehen...

//EDIT 3:
*hüstel* ausserdem ist ADCW ein 16 bit Register, und rx_pwm ein 8 bit register.
Standartmässig werden hier also die MSB weggeschmissen, NICHT die LSB.
deswegen müsstest du sowas hier nehmen: rx_pwm = ADCW >> 2;
Benutzeravatar
Arndt
Beiträge: 2589
Registriert: Fr 28. Jun 2013, 13:42
Wohnort: einen Schritt über den Abgrund hinaus

Re: Software BLDC Controller umstricken

Beitrag von Arndt »

felixh hat geschrieben:ABER du musst dir den ursprünglichen zustand des ADMUX registers merken! sonst aktivierst du den interrupt ja wieder, und lässt ADMUX auf 6 stehen, und der Motor wird zucken ;)
Öh ja natürlich!!
DREHENCODER! funktionieren rein digital, und man hat was zum dran drehen...
Ahaaahahaaaaaa mal ein Trauma mit dem R8C von Renesas erlebt....
Ne, die Idee ist klasse, habe heut noch erst meinen Encoder in der Hand gehabt!
Klingt versaut, ist aber so, ich habe nur einen :D und der will verfrickelt werden, da sollten wir mal drüber nachdenken.
Der hat sogar eine knopf drin zum an und abschalten, oder so, aber eigentlich ist der auch zu schön zum verbasteln............
//EDIT 3:
*hüstel* ausserdem ist ADCW ein 16 bit Register, und rx_pwm ein 8 bit register.
Standartmässig werden hier also die MSB weggeschmissen, NICHT die LSB.
deswegen müsstest du sowas hier nehmen: rx_pwm = ADCW >> 2;
[/quote]

Öh, mist! warum fällt mir sowas nicht auf *grummel* da gehe ich aber erst morgen bei, ok
Benutzeravatar
Sven
Beiträge: 4421
Registriert: Fr 28. Jun 2013, 12:52
Wohnort: Sechsundzwanzigdreisechzehn

Re: Software BLDC Controller umstricken

Beitrag von Sven »

Wenn du einen Drehencode brauchst können wir morgen mal auf meinem Basteltisch gucken, da müsste noch einer aus der letzten Reichelt Order liegen, den ich dann doch noch nicht gebraucht habe :)
Irgendso ein Teil von ALPS.
Benutzeravatar
felixh
Beiträge: 593
Registriert: So 11. Aug 2013, 13:46

Re: Software BLDC Controller umstricken

Beitrag von felixh »

Ich hab mich nochmal in das DB eingelesen: es gibt anscheinend DOCH ein S/H glied!
im 2. ADC Takt nach Aktivierung einer Conversion wird dieses Glied getriggert.

dh, alles was wir tun müssen, ist:
next_commutate_state erweitern um:
Überprüfen, ob alte wandlung schon komplett ist. wenn ja:
interrupts (ja, mehrzahl. also cli() ) deaktivieren, ADMUX speichern & neu setzten, conversion starten, 3 ADC Takte warten, ADMUX widerherstellen, Interrupts aktivieren.

Ich würde, um die zeit, die das programm verschwendet zu reduzieren, den ADC im Interrupt-modus betreiben.
Ausserdem kann es nicht schaden, den ADC etwas zu übertakten. Verliert zwar etwas an genauigkeit, die man mit nem Poti aber eh nicht gehabt hätte.

bei 200kHz verlierst du so 15us. Mess oder rechne mal nach, ob du soviel zeit hast. (sollte aber Locker drin sein. bei 65k u/min macht das ja 1000 u/sec und damit 166us/phasenwechsel)
Benutzeravatar
Arndt
Beiträge: 2589
Registriert: Fr 28. Jun 2013, 13:42
Wohnort: einen Schritt über den Abgrund hinaus

Re: Software BLDC Controller umstricken

Beitrag von Arndt »

Heureka! Es tut :D

global die Interrupts unterbrechen war garnicht notwendig, der ANALOG_COMP_vect reicht.
Der casus-knacksus war nur das zwischenspeichern des ADMUX und der Vorteiler des ADC...
Zeit ist zwischen dem Phasenwechsel offensichtlich üppig, also spare ich mir -der Einfachheit und Ahnungslosigkeits halber- das warten und prüfen, ob die Wandlung fertig ist.
Der Vorteiler habe ich einfach mal auf 2 gestellt :) -> "LIiii, alles was geht!"

Code: Alles auswählen

	//Geschwindigkeitsvorgabe lesen
	ACSR &= ~(1<<ACIE); 				//ANALOG_COMP_vect interrupt deaktivieren   
    
	ADCSRA |= (1 << ADEN);      		// ADC aktivieren
  
	old_ADMUX =ADMUX;					//ADMUX zwischenspeichern
	ADMUX = 6;							// Kanal wählen ADMUX = 6;

   	ADCSRA |= (1 << ADSC);              // eine ADC-Wandlung
   	while (ADCSRA & (1 << ADSC)) {		// auf Abschluss der Konvertierung warten
   	}   
   	rx_pwm = ADCW >> 2;					// MSB in rx_pwm lesen

    ADCSRA &= ~(1 << ADEN); 			// ADC deaktivieren

	ADMUX = old_ADMUX;					// AMDUX zurücksetzen

	ACSR &= ~(1<<ACIE); 				// ANALOG_COMP_vect interrupt aktivieren
Dat steht nu alles unter- und außerhalb des switch-case im next_commutate_state.

Beim Gas geben muss man allerdings ein wenig sachte sein, sonst verschluckt er sich gern, aber damit kann ich leben.
Für den Tellerschleifer und zum Testen ist das eine sehr gute Lösung.
Danke an alle für die Tips und besonders an Dich felixh, dass Du Dich da so mit reingehängt hast!!

Ankündigung Folgeprojekt:
Beendet ist das aber nun doch noch nicht, da mit Ferdi und Licht_Tim den Floh mit dem 2.Controllör ins Ohr gesetzt haben.
Ich glaub als nächstes versuche ich mich -um des lernen willens- an die 80%-Lösung und bequatsch einen weiteren BLMC über I²C.


Rahmenbedingungen der Applikation:
Drehgeber mit Taster zum ein/aus-schalten und Geschwindigkeitsvorgabe,
der letzte Wert wird in den eeprom geschrieben und beim Neustart mit einer passenden Flanke angefahren.
Toll wäre jetzt, wenn der BLMC hin und wieder die Geschwindigkeit aus den EMF-Daten rüberbölken würde, dann könnte man noch ein nettes kleines Display mit dazuhäkeln, oder?
Mal gucken, Idee ist gespeichert und im nächsten Zeitfenster gehts dann weiter.
Antworten