Soooo ich habe die Steuerung zusammen und das ganze auf ATTiny geschrumpft.
Code:
#include <avr/sleep.h>
#define vibrationsstufen 6 //Stufen plus NULL
const bool debug = true;
const int rpmArray[vibrationsstufen+1] = {500,600,800,1200,1600,2000,2500};//Verbindliche drehzahl
int pwmArray[vibrationsstufen+1] = {0,20,25,30,35,40,50}; //Geschätze Pulsweite
//Startgeschwindigkeit NULL
volatile byte vibrationsstufe = 1;
// Zeitspeicher der Entprellung für PCINT
volatile unsigned long lastPcInterrupt;
// Zeitspeicher der Entprellung für INT0
volatile unsigned long lastHwInterrupt;
//RPM Variablen
#define messpunkte 10 // Anzahl der Messpunkte pro Umdrehung
const unsigned long debounceDelay = 12 / messpunkte; //Deboundzeit in abhägigkeit der Messpunkte
unsigned long previousMillis = millis(); // Zeitstempel der letzten RPM Messung
unsigned long elapsedTime = 0; // Seit letzter RPM Messung vergangene millis()
volatile unsigned long counter = 0; // Zählvariable für RPM ermittlung
float rpm = 0; // Zielariable für die RPM
//Perepherie Definieren
#define motor 1 //PB1
#define lichtschranke 2 //PB2 oder auch INT0
//TASTER Definieren
#define onOf 3 //PB3
#define up 4 //PB4
#define dwn 0 //PB0
//Flaggen Definieren
volatile bool onOfFlag = false;
volatile bool upFlag = false;
volatile bool dwnFlag = false;
void Interrupt_config() {
// Löschen des Global Interrupt Enable Bits (I) im Status Register (SREG)
cli();
// GIMSK – General Interrupt Mask Register
// Bit 5 – PCIE: Pin Change Interrupt Enable
// Bit 6 – INT0: External Interrupt Request 0 Enable
GIMSK |= (1 << PCIE) | (1 << INT0);
// PCMSK – Pin Change Mask Register
// Setzen des Pin Change Enable Mask für PB3 und PB4
PCMSK |= (1 << PCINT3) | (1 << PCINT4);
// PB0 separat einstellen, außer im Debug-Modus
if (!debug) {
PCMSK |= (1 << PCINT0); // PB0 aktivieren
}
// Externe Interrupts (INT0) konfigurieren
// MCUCR – MCU Control Register
// Bit 1 – ISC01: Interrupt Sense Control 0 Bit 1
// Bit 0 – ISC00: Interrupt Sense Control 0 Bit 0
//ISC01 ISC00 Description
//0 0 The low level of INT0 generates an interrupt request.
//0 1 Any logical change on INT0 generates an interrupt request.
//1 0 The falling edge of INT0 generates an interrupt request.
//1 1 The rising edge of INT0 generates an interrupt request.
// ISC01 = 1, ISC00 = 1: The rising edge of INT0 generates an interrupt request.
// MCUCR |= (1 << ISC01) | (1 << ISC00);
// ISC01 = 1, ISC00 = 0: The falling edge of INT0 generates an interrupt request.
// MCUCR |= (1 << ISC01);
// MCUCR &= ~(1 << ISC00);
// ISC01 = 0, ISC00 = 1: Any logical Change on INT0 generates an interrupt request.
MCUCR &= ~(1 << ISC01);
MCUCR |= (1 << ISC00);
// Setzen des Global Interrupt Enable Bits (I) im Status Register (SREG)
sei();
}
void setup()
{
if( debug) {
Serial.begin(9600);
while (!Serial) delay(10);
Serial.println("Starte debugging....Taste dwn ist aus");
}
if(!debug) {
// PB0 ist Eingang dwn
DDRB &= ~(1 << DDB0); //Beim Debuggen TX Pin
// PB1 ist PWM Ausgang zu MOSFET/Motor
DDRB = (1 << motor);
digitalWrite(motor, LOW);
}
// PB2 ist Eingang Lichtschranke
DDRB &= ~(1 << DDB2);
// PB3 ist Eingang onOf
DDRB &= ~(1 << DDB3);
// PB4 ist Eingang up
DDRB &= ~(1 << DDB4);
// Pull-Up-Widerstand für PB0 einschalten
if(!debug) PORTB |= (1 << PORTB0); //Beim Debuggen TX Pin
// Pull-Up-Widerstände für PB3 und PB4 einschalten
PORTB |= (1 << PORTB3) | (1 << PORTB4);
// Pull-Up-Widerstand für PB2 (INT0) explizit ausschalten
//PORTB &= ~(1 << PORTB2);
// Pull-Up-Widerstand für PB2 (INT0) explizit einschalten
//PORTB |= (1 << PORTB2); // Aktiviert den internen Pull-up-Widerstand für PB2
Interrupt_config();
if( debug ) {
Serial.print("DDRB: ");
Serial.println(DDRB, BIN);
Serial.print("PORTB: ");
Serial.println(PORTB, BIN);
}
}
// Interrupt Service Routine für INT0
ISR(INT0_vect) {
cli(); // keine weiteren Interrupts zulassen
unsigned long prellzeit = lastHwInterrupt + debounceDelay; //ca 2 millis entprellen
lastHwInterrupt = millis(); //der aktuelle ist nun der letzte
if(lastHwInterrupt < prellzeit) return; //
counter++;
sei();
}
//Aufruf der Interrupt Serviceroutine
ISR(PCINT0_vect)
{
cli(); // keine weiteren Interrupts zulassen
unsigned long prellzeit = lastPcInterrupt + 250; // 1/4 Sekunde entprellen
unsigned long currentTime = millis(); // Der aktuelle Zeitpunkt
if(currentTime < prellzeit) return; // Entprellen
lastPcInterrupt = currentTime; // Der aktuelle ist nun der letzte
// Flags setzen, wenn die entsprechenden Schalter zu Ground geschlossen sind
onOfFlag = !(PINB & (1 << onOf)); // Wenn onOf-Schalter zu Ground geschlossen
upFlag = !(PINB & (1 << up)); // Wenn up-Schalter zu Ground geschlossen
if(!debug) dwnFlag = !(PINB & (1 << dwn)); // Wenn dwn-Schalter zu Ground geschlossen - ausser beim debuggen
sei(); // Interrupts wieder freigeben
}
void flagsResponse(){
if (onOfFlag)
{
//Toggle on/off
if(vibrationsstufe > 0) vibrationsstufe=0;
else vibrationsstufe = 1;
}
else if (upFlag)
{
// mehr wenn möglich
if (vibrationsstufe < vibrationsstufen && vibrationsstufe > 0 )
vibrationsstufe++;
}
else if (dwnFlag)
{
// weniger wenn möglich
if (vibrationsstufe > 1)
vibrationsstufe--;
}
onOfFlag = upFlag = dwnFlag = 0;
}
void pwmAnpassung() {
byte pwmFreq = pwmArray[vibrationsstufe];
//pwmFreq = constrain(pwmFreq, 15, 55); // Begrenze PWM-Wert auf gültigen Bereich
analogWrite(motor, pwmFreq);
}
void pwmPID() {
// Anpassung der PWM in Abhängigkeit der RPM
byte toleranz = 20;
// Abbruch, wenn RPM zu niedrig ist (Defekter Sensor???)
if (rpm < 200) return;
// Anpassung der PWM basierend auf den RPM
if (rpm < rpmArray[vibrationsstufe] - toleranz) {
pwmArray[vibrationsstufe]++;
} else if (rpm > rpmArray[vibrationsstufe] + toleranz) {
pwmArray[vibrationsstufe]--;
}
// Begrenze den Wert von pwmArray[vibrationsstufe] auf den gültigen Bereich
pwmArray[vibrationsstufe] = constrain(pwmArray[vibrationsstufe], 15, 55);
}
void goToSleep()
{
byte adcsra;
adcsra = ADCSRA; // save ADC control and status register A
ADCSRA &= ~(1 << ADEN); // disable ADC
MCUCR |= (1 << SM1) & ~(1 << SM0); // Sleep-Modus = Power Down
MCUCR |= (1 << SE); // set sleep enable
sei();//sicherheitshalber soll interrupt immer aktiv sein
sleep_cpu(); // sleep
MCUCR &= ~(1 << SE); // reset sleep enable
ADCSRA = adcsra; // restore ADC control and status register A
}
void rpmCalc(){
cli(); // Deaktiviere Interrupt für kritische Berechnung
rpm = ( counter * 1000 * 60 ) / (messpunkte * elapsedTime) ; // Pulse pro Umdrehung;
rpm = rpm/2; //derzeit zälht jeder interrupt macht zwei bei fall & rise
counter = 0; // Zurücksetzen des Zählers für die nächste Messung
sei(); // Aktiviere Interrupt wieder
}
void loop() {
flagsResponse();
if(! debug ) pwmAnpassung();
elapsedTime = millis() - previousMillis;
if (elapsedTime >= 100){
rpmCalc();
previousMillis = millis(); // Aktualisierung des Zeitstempels
pwmPID();
if ( debug ) {
Serial.print("Stufe: ");
Serial.print(vibrationsstufe);
Serial.print(" PWM: ");
Serial.println(pwmArray[vibrationsstufe]);
Serial.print(" RPM: ");
Serial.println(rpm);
}
}
if(0 == vibrationsstufe) {
delay(1000);
goToSleep();
}
}
Allerdings muss ich dann doch einfach eingestehen, dass ich PCBs mit WESENTLICH fetteren Leitungen Ordern muss. MCU und Lichtschranke sowie alle anderen Bauteile haben es überlebt, aber der Hauptstrom ist schön weggeschmurgelt. So schön weihnachtlich hat es hier seit Dezember nicht gerochen.