Rev 43 |
Rev 51 |
Go to most recent revision |
Blame |
Compare with Previous |
Last modification |
View Log
| RSS feed
/*############################################################################
+ Regler für Brushless-Motoren
+ ATMEGA8 mit 8MHz
+ (c) 01.2007 Holger Buss
+ Nur für den privaten Gebrauch
+ Keine Garantie auf Fehlerfreiheit
+ Kommerzielle Nutzung nur mit meiner Zustimmung
+ Der Code ist für die Hardware BL_Ctrl V1.0 entwickelt worden
+ www.mikrocontroller.com
/############################################################################*/
#include "main.h"
unsigned int PWM = 0;
unsigned int Strom = 0; //ca. in 0,1A
unsigned char Strom_max = 0;
unsigned char Mittelstrom = 0;
unsigned int Drehzahl = 0; // in 100UPM 60 = 6000
unsigned int KommutierDelay = 10;
unsigned int I2C_Timeout = 0;
unsigned int SIO_Timeout = 0;
unsigned int SollDrehzahl = 0;
unsigned int IstDrehzahl = 0;
unsigned int DrehZahlTabelle[256];//vorberechnete Werte zur Drehzahlerfassung
unsigned char ZeitFuerBerechnungen = 1;
unsigned char MotorAnwerfen = 0;
unsigned char MotorGestoppt = 1;
unsigned char MaxPWM = MAX_PWM;
unsigned int CntKommutierungen = 0;
unsigned int SIO_Drehzahl = 0;
unsigned char ZeitZumAdWandeln = 1;
unsigned char MotorAdresse = 1;
unsigned char PPM_Betrieb = 1;
//############################################################################
//
void SetPWM(void)
//############################################################################
{
unsigned char tmp_pwm;
tmp_pwm = PWM;
if(tmp_pwm > MaxPWM) // Strombegrenzung
{
tmp_pwm = MaxPWM;
PORTC |= ROT;
}
if(Strom > MAX_STROM) // Strombegrenzung
{
OCR1A = 0; OCR1B = 0; OCR2 = 0;
PORTC |= ROT;
Strom--;
}
else
{
#ifdef _32KHZ
OCR1A = tmp_pwm; OCR1B = tmp_pwm; OCR2 = tmp_pwm;
#endif
#ifdef _16KHZ
//OCR1A = 2 * (int)tmp_pwm; OCR1B = 2 * (int)tmp_pwm; OCR2 = tmp_pwm;
OCR1A = tmp_pwm; OCR1B = tmp_pwm; OCR2 = tmp_pwm;
#endif
}
}
//############################################################################
//
void PWM_Init(void)
//############################################################################
{
PWM_OFF;
TCCR1B = (1 << CS10) | (0 << CS11) | (0 << CS12) | (0 << WGM12) |
(0 << WGM13) | (0<< ICES1) | (0 << ICNC1);
/* TCCR1B = (1 << CS10) | (0 << CS11) | (0 << CS12) | (1 << WGM12) |
(0 << WGM13) | (0<< ICES1) | (0 << ICNC1);
*/
}
//############################################################################
//
void Wait(unsigned char dauer)
//############################################################################
{
dauer = (unsigned char)TCNT0 + dauer;
while((TCNT0 - dauer) & 0x80);
}
//############################################################################
//
void Anwerfen(unsigned char pwm)
//############################################################################
{
unsigned long timer = 300,i;
DISABLE_SENSE_INT;
PWM = 5;
SetPWM();
Manuell();
Delay_ms(200);
PWM = pwm;
while(1)
{
for(i=0;i<timer; i++)
{
if(!UebertragungAbgeschlossen) SendUart();
else DatenUebertragung();
Wait(100); // warten
}
timer-= timer/15+1;
if(timer < 25) { if(TEST_MANUELL) timer = 25; else return; }
Manuell();
Phase++;
Phase %= 6;
AdConvert();
PWM = pwm;
SetPWM();
if(SENSE)
{
PORTD ^= GRUEN;
}
}
}
/*
#define SENSE_A ADMUX = 0;
#define SENSE_B ADMUX = 1;
#define SENSE_C ADMUX = 2;
#define ClrSENSE ACSR |= 0x10
#define SENSE ((ACSR & 0x10))
#define SENSE_L (!(ACSR & 0x20))
#define SENSE_H ((ACSR & 0x20))
*/
void RotBlink(unsigned char anz)
{
sei(); // Interrupts ein
while(anz--)
{
PORTC |= ROT;
Delay_ms(300);
PORTC &= ~ROT;
Delay_ms(300);
}
Delay_ms(1000);
}
#define TEST_STROMGRENZE 100
unsigned char DelayM(unsigned int timer)
{
while(timer--)
{
FastADConvert();
if(Strom > TEST_STROMGRENZE)
{
STEUER_OFF;
return(1);
}
}
return(0);
}
unsigned char Delay(unsigned int timer)
{
while(timer--)
{
}
return(0);
}
void MotorTon(void)
//############################################################################
{
unsigned int timer = 300,i;
unsigned int t;
unsigned char MosfetKurzschluss = 0, MosfetOkay = 0,anz = 0;
DISABLE_SENSE_INT;
cli();//Globale Interrupts Ausschalten
PORTC &= ~ROT;
PWM = 0;
SetPWM();
uart_putchar('\n');
STEUER_OFF;
//#define STEUER_A_L {PORTD &= ~0x30; PORTD |= 0x08;}
//#define STEUER_B_L {PORTD &= ~0x28; PORTD |= 0x10;}
//#define STEUER_C_L {PORTD &= ~0x18; PORTD |= 0x20;}
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//+ High-Mosfets auf Kurzschluss testen
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Strom = 0;
PORTD |= 0x08; // Low A ein
PORTB |= 0x04; // High B ein; und A gegen B
//PORTB |= 0x08; // Test A
if(DelayM(3))
{
MosfetKurzschluss = 0x01;
anz = 1;
uart_putchar('A');
}
STEUER_OFF;
DelayM(100);
Strom = 0;
PORTD |= 0x10; // Low B ein
PORTB |= 0x02; // High C ein
//PORTB |= 0x04; // Test B
if(DelayM(3))
{
anz = 2;
MosfetKurzschluss = 0x02;
uart_putchar('B');
}
STEUER_OFF;
DelayM(100);
Strom = 0;
PORTD |= 0x20; // Low C ein
PORTB |= 0x04; // High B ein
//PORTB |= 0x0C; // High A+B ein
//PORTB |= 0x02; // Test C
if(DelayM(3))
{
anz = 3;
MosfetKurzschluss = 0x04;
uart_putchar('C');
}
STEUER_OFF;
DelayM(100);
PORTD |= 0x08; // Low A ein; und A gegen C
PORTB |= 0x02; // High C ein
//PORTB |= 0x08; // Test A
if(DelayM(3))
{
anz = 3;
MosfetKurzschluss = 0x01;
uart_putchar('C');
}
STEUER_OFF;
DelayM(100);
if(anz) while(1) RotBlink(anz);
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//+ Low-Mosfets auf Kurzschluss testen
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Strom = 0;
PORTB |= 0x08; // Test A
//PORTD |= 0x08; // Low A ein
if(DelayM(3))
{
anz = 4;
MosfetKurzschluss = 0x01;
uart_putchar('a');
}
STEUER_OFF;
Strom = 0;
PORTB |= 0x04; // Test B
//PORTD |= 0x10; // Low B ein
if(DelayM(3))
{
anz = 5;
MosfetKurzschluss = 0x02;
uart_putchar('b');
}
STEUER_OFF;
Strom = 0;
PORTB |= 0x02; // Test C
//PORTD |= 0x20; // Low C ein
if(DelayM(3))
{
anz = 6;
MosfetKurzschluss = 0x04;
uart_putchar('c');
}
STEUER_OFF;
if(anz) while(1) RotBlink(anz);
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//+ High-Mosfets auf Schalten testen
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
SENSE_A;
STEUER_OFF;
PORTD |= 0x10; // Low B ein
PORTD |= 0x20; // Low C ein
Strom = 0;
#define TONDAUER 100000
#define SOUND_E 5
#define SOUND2_A 500
for(i=0; i< (TONDAUER / SOUND2_A) ; i++)
{
PORTB |= 0x08; // Test A
Delay(SOUND_E);
if(SENSE_L) { MosfetOkay |= 0x01; } else { MosfetOkay &= ~0x01;}
PORTB = 0;
Delay(SOUND2_A);
}
STEUER_OFF;
SENSE_B;
PORTD |= 0x08; // Low A ein
PORTD |= 0x20; // Low C ein
#define SOUND1_A 600
for(i=0; i<(TONDAUER / SOUND1_A); i++)
{
PORTB |= 0x04; // Test B
Delay(SOUND_E);
if(SENSE_L) { MosfetOkay |= 0x02; } else { MosfetOkay &= ~0x02;}
PORTB = 0;
Delay(SOUND1_A);
}
STEUER_OFF;
SENSE_C;
PORTD |= 0x08; // Low A ein
PORTD |= 0x10; // Low B ein
#define SOUND3_A 700
for(i=0; i<(TONDAUER / SOUND3_A); i++)
{
PORTB |= 0x02; // Test C
Delay(SOUND_E);
if(SENSE_L) { MosfetOkay |= 0x04; } else { MosfetOkay &= ~0x04;}
PORTB = 0;
Delay(SOUND3_A);
}
STEUER_OFF;
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//+ Low-Mosfets auf Schalten testen
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
SENSE_B;
PORTD |= 0x08; // Low A ein
#define SOUND2_A 500
for(i=0; i< (TONDAUER / SOUND2_A) ; i++)
{
PORTB |= 0x04; // Test B
Delay(SOUND_E);
if(SENSE_L) { MosfetOkay |= 0x08; } else { MosfetOkay &= ~0x08;}
PORTB = 0;
Delay(SOUND2_A);
}
STEUER_OFF;
SENSE_C;
PORTD |= 0x10; // Low B ein
for(i=0; i<(TONDAUER / SOUND3_A); i++)
{
PORTB |= 0x02; // Test C
Delay(SOUND_E);
if(SENSE_L) { MosfetOkay |= 0x10; } else { MosfetOkay &= ~0x10;}
PORTB = 0;
Delay(SOUND3_A);
}
STEUER_OFF;
SENSE_B;
PORTD |= 0x20; // Low C ein
for(i=0; i<(TONDAUER / SOUND1_A); i++)
{
PORTB |= 0x04; // Test B
Delay(SOUND_E);
if(SENSE_L) { MosfetOkay |= 0x20; } else { MosfetOkay &= ~0x20;}
PORTB = 0;
Delay(SOUND1_A);
}
sei();//Globale Interrupts Einschalten
Delay_ms(250 * MotorAdresse);
#define SOUND8_E 7
#define SOUND8_A 650
for(i=0; i<(TONDAUER / SOUND8_A); i++)
{
for(timer = 0; timer < SOUND8_E; timer++) PORTB |= 0x04; // Test B
for(timer = 0; timer < SOUND8_A; timer++) PORTB = 0;
}
if(!(MosfetOkay & 0x01)) { anz = 1; uart_putchar('A'); } else
if(!(MosfetOkay & 0x02)) { anz = 2; uart_putchar('B'); } else
if(!(MosfetOkay & 0x04)) { anz = 3; uart_putchar('C'); } else
if(!(MosfetOkay & 0x08)) { anz = 4; uart_putchar('a'); } else
if(!(MosfetOkay & 0x10)) { anz = 5; uart_putchar('b'); } else
if(!(MosfetOkay & 0x20)) { anz = 6; uart_putchar('c'); }
Delay_ms(250 * (4-MotorAdresse));
if(MosfetOkay != 0x3f) Delay_ms(1000);
#define TONDAUER 80000
#define SOUND9_E 20
#define SOUND9_A 500
while(anz--)
{
PORTC |= ROT;
if(!(MosfetOkay & 0x04) || !(MosfetOkay & 0x20)) // Schalter C defekt
{
PORTD |= 0x10; // Low B ein
for(i=0; i< (TONDAUER / SOUND9_A) ; i++)
{
for(timer = 0; timer < SOUND9_E; timer++) PORTB |= 0x08; // Test A
for(timer = 0; timer < SOUND9_A; timer++) PORTB = 0;
}
}
else
if(!(MosfetOkay & 0x02) || !(MosfetOkay & 0x10)) // Schalter B defekt
{
PORTD |= 0x20; // Low C ein
for(i=0; i< (TONDAUER / SOUND9_A) ; i++)
{
for(timer = 0; timer < SOUND9_E; timer++) PORTB |= 0x08; // Test A
for(timer = 0; timer < SOUND9_A; timer++) PORTB = 0;
}
}
else
if(!(MosfetOkay & 0x01) || !(MosfetOkay & 0x08)) // Schalter A defekt
{
PORTD |= 0x20; // Low C ein
for(i=0; i< (TONDAUER / SOUND9_A) ; i++)
{
for(timer = 0; timer < SOUND9_E; timer++) PORTB |= 0x04; // Test B
for(timer = 0; timer < SOUND9_A; timer++) PORTB = 0;
}
}
PORTC &= ~ROT;
Delay_ms(300);
}
}
//############################################################################
//
unsigned char SollwertErmittlung(void)
//############################################################################
{
static unsigned int sollwert = 0;
unsigned int ppm;
if(!I2C_Timeout) // bei Erreichen von 0 ist der Wert ungültig
{
if(SIO_Timeout) // es gibt gültige SIO-Daten
{
sollwert = (MAX_PWM * (unsigned int) SIO_Sollwert) / 200; // skalieren auf 0-200 = 0-255
PPM_Betrieb = 0;
ICP_INT_DISABLE;
PORTC &= ~ROT;
}
else
if(anz_ppm_werte > 20) // es gibt gültige PPM-Daten
{
PPM_Betrieb = 1;
ppm = PPM_Signal;
if(ppm > 300) ppm = 0; // ungültiges Signal
if(ppm > 200) ppm = 200;
if(ppm <= MIN_PPM) sollwert = 0;
else
{
sollwert = (int) MIN_PWM + ((MAX_PWM - MIN_PWM) * (ppm - MIN_PPM)) / (190 - MIN_PPM);
}
PORTC &= ~ROT;
}
else // Kein gültiger Sollwert
{
if(!TEST_SCHUB) { if(sollwert) sollwert--; }
PORTC |= ROT;
}
}
else // I2C-Daten sind gültig
{
sollwert = I2C_RXBuffer;
PPM_Betrieb = 0;
PORTC &= ~ROT;
ICP_INT_DISABLE;
}
if(sollwert > MAX_PWM) sollwert = MAX_PWM;
return(sollwert);
}
void DebugAusgaben(void)
{
DebugOut.Analog[0] = Strom;
DebugOut.Analog[1] = Mittelstrom;
DebugOut.Analog[2] = SIO_Drehzahl;
DebugOut.Analog[3] = PPM_Signal;
}
//############################################################################
//Hauptprogramm
int main (void)
//############################################################################
{
char altPhase = 0;
int test = 0;
unsigned int MinUpmPulse,Blink,TestschubTimer;
unsigned int Blink2,MittelstromTimer,DrehzahlMessTimer,MotorGestopptTimer;
DDRC = 0x08;
PORTC = 0x08;
DDRD = 0xBA;
PORTD = 0x00;
DDRB = 0x0E;
PORTB = 0x31;
#if (MOTORADRESSE == 0)
PORTB |= (ADR1 + ADR2); // Pullups für Adresswahl
for(test=0;test<500;test++);
if (PINB & ADR1)
{
if (PINB & ADR2) MotorAdresse = 1;
else MotorAdresse = 2;
}
else
{
if (PINB & ADR2) MotorAdresse = 3;
else MotorAdresse = 4;
}
#else
MotorAdresse = MOTORADRESSE;
#endif
UART_Init();
Timer0_Init();
sei();//Globale Interrupts Einschalten
// Am Blinken erkennt man die richtige Motoradresse
/*
for(test=0;test<5;test++)
{
if(test == MotorAdresse) PORTD |= GRUEN;
Delay_ms(150);
PORTD &= ~GRUEN;
Delay_ms(250);
}
Delay_ms(500);
*/
// UART_Init(); // war doppelt
PWM_Init();
InitIC2_Slave(0x50);
InitPPM();
Blink = SetDelay(101);
Blink2 = SetDelay(102);
MinUpmPulse = SetDelay(103);
MittelstromTimer = SetDelay(254);
DrehzahlMessTimer = SetDelay(1005);
TestschubTimer = SetDelay(1006);
while(!CheckDelay(MinUpmPulse))
{
if(SollwertErmittlung()) break;
}
;
PORTD |= GRUEN;
PWM = 0;
SetPWM();
SFIOR = 0x08; // Analog Comperator ein
ADMUX = 1;
MinUpmPulse = SetDelay(10);
DebugOut.Analog[1] = 1;
PPM_Signal = 0;
if(!SollwertErmittlung()) MotorTon();
// zum Test der Hardware; Motor dreht mit konstanter Drehzahl ohne Regelung
if(TEST_MANUELL) Anwerfen(TEST_MANUELL); // kommt von dort nicht wieder
while (1)
{
if(!TEST_SCHUB) PWM = SollwertErmittlung();
//I2C_TXBuffer = PWM; // Antwort über I2C-Bus
if(MANUELL_PWM) PWM = MANUELL_PWM;
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
if(Phase != altPhase) // es gab eine Kommutierung im Interrupt
{
MotorGestoppt = 0;
ZeitFuerBerechnungen = 0; // direkt nach einer Kommutierung ist Zeit
MinUpmPulse = SetDelay(50); // Timeout, falls ein Motor stehen bleibt
altPhase = Phase;
}
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
if(!PWM) // Sollwert == 0
{
MotorAnwerfen = 0; // kein Startversuch
ZeitFuerBerechnungen = 0;
// nach 1,5 Sekunden den Motor als gestoppt betrachten
if(CheckDelay(MotorGestopptTimer))
{
DISABLE_SENSE_INT;
MotorGestoppt = 1;
STEUER_OFF;
}
}
else
{
if(MotorGestoppt) MotorAnwerfen = 1; // Startversuch
MotorGestopptTimer = SetDelay(1500);
}
if(MotorGestoppt && !TEST_SCHUB) PWM = 0;
SetPWM();
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
if(!ZeitFuerBerechnungen++)
{
if(MotorGestoppt) PORTD |= GRUEN; //else PORTD &= ~GRUEN;
if(SIO_DEBUG)
{
DebugAusgaben(); // welche Werte sollen angezeigt werden?
if(!UebertragungAbgeschlossen) SendUart();
else DatenUebertragung();
}
// Berechnen des Mittleren Stroms zur (langsamen) Strombegrenzung
if(CheckDelay(MittelstromTimer))
{
MittelstromTimer = SetDelay(50); // alle 50ms
if(Mittelstrom < Strom) Mittelstrom++;// Mittelwert des Stroms bilden
else if(Mittelstrom > Strom) Mittelstrom--;
if(Mittelstrom > LIMIT_STROM)// Strom am Limit?
{
MaxPWM--;// dann die Maximale PWM herunterfahren
PORTC |= ROT;
}
else
{
if(MaxPWM < MAX_PWM) MaxPWM++;
}
}
if(CheckDelay(DrehzahlMessTimer)) // Ist-Drehzahl bestimmen
{
DrehzahlMessTimer = SetDelay(10);
SIO_Drehzahl = (6 * CntKommutierungen) / (POLANZAHL / 2);
CntKommutierungen = 0;
if(PPM_Timeout == 0) // keine PPM-Signale
ZeitZumAdWandeln = 1;
}
if(CheckDelay(TestschubTimer))
{
TestschubTimer = SetDelay(1500);
if(TEST_SCHUB)
{
switch(test)
{
case 0: PWM = 50; test++; break;
case 1: PWM = 130; test++; break;
case 2: PWM = 60; test++; break;
case 3: PWM = 140; test++; break;
case 4: PWM = 150; test = 0; break;
default: test = 0;
}
}
}
// Motor Stehen geblieben
if((CheckDelay(MinUpmPulse) && SIO_Drehzahl == 0) || MotorAnwerfen)
{
MotorGestoppt = 1;
DISABLE_SENSE_INT;
MinUpmPulse = SetDelay(100);
if(MotorAnwerfen)
{
PORTC &= ~ROT;
MotorAnwerfen = 0;
Anwerfen(10);
PORTD |= GRUEN;
MotorGestoppt = 0;
Phase--;
PWM = 1;
SetPWM();
SENSE_TOGGLE_INT;
ENABLE_SENSE_INT;
MinUpmPulse = SetDelay(20);
while(!CheckDelay(MinUpmPulse)); // kurz Synchronisieren
PWM = 15;
SetPWM();
MinUpmPulse = SetDelay(300);
while(!CheckDelay(MinUpmPulse)); // kurz Durchstarten
// Drehzahlmessung wieder aufsetzen
DrehzahlMessTimer = SetDelay(50);
altPhase = 7;
}
}
} // ZeitFuerBerechnungen
} // while(1) - Hauptschleife
}