Hi,
ich nutze in meiner Steuerung für die IDS2 den folgenden Code für die Rasten. Das ganze ist als Zustandsautomat der je nach Status und Bedingung weiterspring. Alles bisserl Denglisch, das muss ich noch verbessern, so das es einheitlich ist.
Als erstes hab ich Definitionen angelegt, damit ich das schnell um eine Stufe oder Rast erweitern kann ohne das ich an vielen Stellen was drehen muss :
Code: Alles auswählen
#define number_of_rasts 7
#define number_of_hop_timers 7
#define number_of_hop_flame_out_timers 2
#define status_mash_start 0
#define status_rast_0 1
#define status_rast_1 2
#define status_rast_2 3
#define status_rast_3 4
#define status_rast_4 5
#define status_rast_5 6
#define status_rast_6 7
#define status_jodnormal 8
Dann hab ich mir einen Struct mit dem ganzen Rezept angelegt, Damit hab ich alles beisammen.
Code: Alles auswählen
typedef struct Recipe
{
uint8_t ID;
float temperatur_mash;
float rast_temp[number_of_rasts];
uint16_t rast_time[number_of_rasts];
uint8_t dekoktion[number_of_rasts];
uint16_t hop_time[number_of_hop_timers];
uint16_t hop_time_flame_out[number_of_hop_flame_out_timers];
float hop_temp_flame_out[number_of_hop_flame_out_timers];
uint8_t cooler;
uint8_t Irish_moos;
uint8_t yeast;
float cooling_temp;
uint16_t boil_time;
float lauter_temp;
uint32_t crc;
} Recipe_t, *p_Recipe_t;
Für dich wichtig ist der Punkt der Rasten und die Zeiten dazu :
Code: Alles auswählen
float rast_temp[number_of_rasts];
uint16_t rast_time[number_of_rasts];
Die sind als Arrays angelegt.
Ich hab eine Variable für den Typ Recipe_t und einen Zeiger auf diese Variable angelegt. Ich arbeite nur mit dem Zeiger. Für alle
Übergaben nutze ich den Zeiger, das spart Speicher, da nur die Bytes vom Zeiger übergeben werden und nicht der ganze Block. Zudem muss beim Ändern nichts zurück geschrieben werden, da hier direkt im Struct gerarbeitet wird. Die Daten vom FRAM oder EEPROM hole ich Byteweise und speicher sie an der Adresse auf die der Zeiger zeigt. Damit hab ich den ganzen Block auf einmal. Zudem wird automatisch die Länge des Struct bestimmt. Wenn ich was ändere muss ich mich hier auch um nichts kümmern. Das Arbeiten mit Zeigern klingt erstmal komisch bringt aber viele Vorteile und kann deutlich RAM sparen wenn es knapp wird.
Es gibt noch eine Variable die den aktuellen Status des Automodes beinhaltet, das ist ein einfacher uint8_t. 1 Byte reicht hier.
Über ein Switch Case wird dann alles abgefragt. Das spart viele If und ist mit Copy und Paste schnell erweiterbar wenn ich merke 8 Rasten wären besser.
Code: Alles auswählen
switch(status_automode)
{
case status_mash_start:
{
if(check_rast)
{
setpoint = p_recipe->temperatur_mash;
check_rast = false;
}
if(get_key_long( PORT_PA05))
{
if(!mash_in_running)
{
mash_in_running = true;
}
else
{
overide_status = true;
}
}
if(!wait_temp_drop && !overide_status)
{
if(delta >= -0.5 && delta <= 0.5 )
{
wait_temp_drop = true;
Buzzer_trigger |= mash_buzzer;
blink_overide_green = true;
//if(!(p_system_->skip_pH%2) && automode_time == 0)
//{
//pH_timer = p_system_->pH_delay;
//}
}
}
else
{
if((delta >= 2 && auto_mash_mode ) || overide_status)
{
if(!(p_system_->skip_pH%2))
{
pH_timer = p_system_->pH_delay;
run_pH_timer = true;
}
p_documentation_data->mash_time = timestamp;
overide_status = false;
blink_overide_green = false;
wait_temp_drop = false;
status_automode++;
mash_in_running = false;
if(p_recipe->dekoktion[status_automode-1]%2)
{
dekoktion = true;
dekoktion_setpoint = p_recipe->temperatur_mash;
}
else
{
dekoktion = false;
}
automode_time = p_recipe->rast_time[status_automode-1];
check_rast = true;
run_timer = false;
}
}
}
break;
case status_rast_0:
{
if(get_key_short(PORT_PA05))
{
if(!pause)
{
pause = true;
}
else
{
pause = false;
}
}
if(check_rast)
{
if(p_recipe->rast_temp[status_automode-1] > 25.0)
{
check_rast = false;
last_rast = status_automode-1;
min_temp = p_recipe->rast_temp[status_automode-1];
max_temp = p_recipe->rast_temp[status_automode-1];
setpoint = p_recipe->rast_temp[status_automode-1];
delta = setpoint - temp;
}
else
{
status_automode++;
automode_time = p_recipe->rast_time[status_automode-1];
check_rast = true;
}
}
if(delta <= 0.5 && delta >= -0.5 && gradient > -0.1)
{
run_timer = true;
dekoktion = false;
}
if(run_timer)
{
if(min_temp > temp)
min_temp = temp;
if(max_temp < temp)
max_temp = temp;
}
if(automode_time == 0)
{
p_documentation_data->rast_time[status_automode-1] = timestamp;
p_documentation_data->rast_temp[status_automode-1] = setpoint;
p_documentation_data->rast_max[status_automode-1] = max_temp;
p_documentation_data->rast_min[status_automode-1] = min_temp;
status_automode++;
if(p_recipe->dekoktion[status_automode-1]%2)
{
dekoktion = true;
dekoktion_setpoint = p_recipe->rast_temp[last_rast];
}
else
{
dekoktion = false;
}
automode_time = p_recipe->rast_time[status_automode-1];
check_rast = true;
run_timer = false;
}
}
break;
default:
break;
}
Das ist erstmal ein Riesenblock und deckt auch nur einen kleinen Teil ab , da ich hier alles abfahre bis zum Läutern.
Das erste wichtige ist die erste Zeile
Die Bedinung für die Fälle ( Case ) ist der Inhalt der Variable status_automode.
Je nach Wert wird dann der Block ab dem passenden Wert bis zum nächsten
abgearbeitet.
Magic Numbers sind im Code immer schlecht und switch case, kann nur mit
Zahlen arbeiten. Daher die Definitionen weiter oben. Beim Compilieren wird der Text "status_mash_start" in den Wert 0 übersetzt. Wenn ich nun in der Mitte einen Schritt brauche änder ich einfach die nachfolgenden Definitionen ab. Beim Switch Case ist es egal wie die Reihenfolge ist, es wird immer der Fall abgearbeitet der passt. Es darf aber keinen Wert doppelt geben.
Der nächste wichtige Block ist die überprüfung ob die Rast angefahren wird.
Code: Alles auswählen
if(check_rast)
{
if(p_recipe->rast_temp[status_automode-1] > 25.0)
{
check_rast = false;
last_rast = status_automode-1;
min_temp = p_recipe->rast_temp[status_automode-1];
max_temp = p_recipe->rast_temp[status_automode-1];
setpoint = p_recipe->rast_temp[status_automode-1];
delta = setpoint - temp;
}
else
{
status_automode++;
automode_time = p_recipe->rast_time[status_automode-1];
check_rast = true;
}
}
Die Überprüfung findet nur einmal statt, da nach dem ausführen das Flag check_rast auf false gesetzt wird, wenn die Rast angefahren wird.
Wenn nicht bleibt sie true. Zudem wird der status auto_mode um einen um eins erhöht und im nächsten Durchlauf der Schleife wird die nächste Rast angefahren. Läuft also automatisch weiter. Wenn die Rast angefahren wird dann werden alle Paramter in die Variablen für den Regler geladen und es wird geprüft ob der Sollbereich erfolgt ist.
Code: Alles auswählen
if(delta <= 0.5 && delta >= -0.5 && gradient > -0.1)
{
run_timer = true;
dekoktion = false;
}
if(run_timer)
{
if(min_temp > temp)
min_temp = temp;
if(max_temp < temp)
max_temp = temp;
}
if(automode_time == 0)
{
p_documentation_data->rast_time[status_automode-1] = timestamp;
p_documentation_data->rast_temp[status_automode-1] = setpoint;
p_documentation_data->rast_max[status_automode-1] = max_temp;
p_documentation_data->rast_min[status_automode-1] = min_temp;
status_automode++;
if(p_recipe->dekoktion[status_automode-1]%2)
{
dekoktion = true;
dekoktion_setpoint = p_recipe->rast_temp[last_rast];
}
else
{
dekoktion = false;
}
automode_time = p_recipe->rast_time[status_automode-1];
check_rast = true;
run_timer = false;
}
Wenn das delta zum Sollwert kleiner als 0,5 K ist , startet der Timer. Wenn der Timer abgelaufen ist wird automatisch die nächste Rast angefahren.
Ich hab das direkt in der Main damit muss ich nichts übergeben.
Wenn du nun da ganze an ein Void übergeben willst musst du den Status für den Zugriff auf das Array bei dir übergeben. Wenn du folgendes machts
Code: Alles auswählen
void meine_raste(uint8_t status)
{
Temperatur = Rast[status];
status++
]
dann wird beim Aufruf der Wert der übergebenen Variable kopiert und mit der Kopie gearbeitet. In der Main bleibt der Wert aber erhalten.
Wenn du nun mit einem Zeiger den Wert übergibst, dann wird das direkt auch in der Main verändert.
Code: Alles auswählen
void meine_raste(uint8_t *status)
{
Temperatur = Rast[*status];
*status++
]
Du musst dann den Aufruf wie folgt machen
Du musst die Adresse der Variable übergeben da du nun mit einem Zeiger arbeitest. Damit kannst du den Status in dein Void ziehen und hier über die Arrays auf die Daten zugreifen.
Switch Case ist jedoch leicht erweiterbar und auch wieder leichter verständlich wenn du mal was in einem Jahr ändern musst.
Gruß JackFrost