PicoBasic wird am PC programmiert und dann in den Controller übertragen. Dagegen kommt die
TPS
ganz ohne einen PC aus, weil man Programme direkt über Tasten
eingeben kann. Damit ist die Programmierung auch fernab aller
Zivilisation möglich. Das wäre auch für einen PicoBasic-Controller eine
gute Sache, dachte ich mir. Mit dem Rpi Pico wurde die
Tastenprogrammierung nun umgesetzt.
Der TPS-Controller braucht drei Tasten. Weil es ein 4-Bit-System ist,
werden Zahlen bis 15 mit bis zu 15 Tastendrücken eingeben. PicoBasic
ist aber ein 8-Bit-System. Bis zu 255 Tastendrücke wären unrealistisch.
Deshalb wird hier eine bitweise Eingabe verwendet. Die Bits 0…7 werden
nacheinander durch kurze und lange Tastendrücke programmiert. Kurz
bedeutet, das Bit bleibt unverändert, und ein langer Druck invertiert
es.
Für die Programmierung wird nur ein einziger Taster an GBIO15
gebraucht. Ein beliebig langer Tastendruck im Run-Modus schaltet in den
Programmiermodus um. Alle Anzeigen verwenden die acht LEDs über
Pullups, also mit reduzierter Helligkeit. Zuerst zeigen sie die
aktuelle Adresse 0 durch ein schnelles, schwaches Flimmern aller
LEDs an. Drückt man die Taste kurz, werden das Basic-Token und das
Datenbyte kurz sichtbar. Danach erscheint die Adresse 1, wobei die
erste LED heller ist. So kann man mit kurzen Tastendrücken durch ein
vorhandenes Programm scrollen.
Wenn an einer Adresse die Daten überschrieben werden sollen, drückt man
länger (ca. 0,5 s … 1 s) und löscht damit zugleich den Inhalt. Die
Zeile enthält nun 0x0000, sodass nacheinander das Token 00 und das
Datenbyte 00 programmiert werden müssen. Die Programmierung beginnt mit
dem Bit 0, das gerade schwach blinkt. Mit einem kurzen Tastendruck
bleibt es low, mit einem langen (ca. 0,5 s … 1 s) invertiert man es, es
wird also high. So geht man durch alle acht Bits, wobei das
jeweils veränderbare Bit durch ein schnelles Blinken angezeigt wird.
Das Byte wird nach und nach schichtbar. Falls man sich an einer Stelle
vertippt hat, kann man noch einmal durch alle Bits gegen und nur das
defekte Bit durch einen langen Druck invertieren. Wenn das Byte korrekt
eingegeben ist, wechselt man durch einen sehr langen Tastendruck (ca.
1,5 s) in die Programmierung des Datenbytes. Das ist an jeder Stelle im
Byte möglich.
Das Datenbyte wird genauso programmiert wie das Basic-Token. Mit einem
sehr langen Druck (>1,5 s) beendet man die Eingabe. Weil viele
Befehle kein Datenbyte brauchen, kann 00 stehen bleiben, und beendet
die Eingabe sofort. Damit gelangt man wieder in die Anzeige der
Adresse, die nun um eins erhöht wurde. So kann man nacheinander mehrere
Zeilen ändern. Mit einem sehr langen Druck wechselt man in den
Run-Modus und startet das aktuelle Programm.
So kann man kleine Änderungen an einem zuvor am PC erzeugten Programm
mit nur einer Taste eingeben. Und man kann ein im RAM vorhandenes
Programm ins EEPROM übertragen und damit Autostart-fähig machen. Dazu
muss man im Adressen-Anzeigemodus 4 Sekunden lang auf den Taster
drücken. Vom Run-Modus aus reichen zwei Tastendrücke, einmal kurz und
einmal sehr, sehr lang.
Die Tastenprogrammierung eines vollständigen PicoBasic-Programms ist
aufwendig und erfordert einige Übung. Man lernt dabei, in Binärzahlen
zu denken. Weil man bei der Eingabe mit dem Bit 0 beginnt, muss man
eine Binärzahl von rechts nach links übertragen. Das hat für kleine
Zahlen den Vorteil, dass man nur wenige Bits eingeben muss. 0x02 bracht
nur einen kurzen und einen längeren Tastendruck, danach kann man die
Eingabe durch einen sehr langen Druck beenden.
Ein Beispiel:
0x09FF Pdir = 255 wird binär zu
00001001 11111111 und erfordert die Eingaben
l k k l e l l l l l l l l e
(k = kurz, l = lang, e = sehr lang für „Ende“)
Bei der Entwicklung der Tasteneingabe wurde intensiv nach den optimalen
Zeiten für die Tastenbedienung gesucht. Am Ende kamen diese Werte dabei
heraus:
Kleiner als 0,25 s: kurz (weiter)
0,25 s bis 1,25 s: lang (programmieren)
1,25 s bis 3 s: sehr lang (beenden)
mehr als 3 s: sehr sehr lang (EEPROM)
In die aktuelle PicoBasic-Version für den Rpi Pico wurde die
Tastenprogrammierung in Form einer einzelnen Funktion tps eingebaut.
void tps(){ //Programmierung über eine Taste an GPIO15
gpio_set_dir_masked (mask, 0); //Eingänge
while (gpio_get(15) == 0){delay(100);} //Taste abwarten
delay (100);
adr = 0;
int tps_running = 1;
while (tps_running > 0){
int pressed = 0;
int done = 0;
while (done == 0){ //adr-Anzeige und Tastenabfrage 50 ms
for(int j=0; j<8; j++){gpio_set_pulls(j,1 &(255), 0);}
delay (5);
if (gpio_get(15) == 0){pressed++;}
for(int j=0; j<8; j++){gpio_set_pulls(j,1 &(adr>>j), 0);}
delay (45);
if ((gpio_get(15) == 1) & (pressed > 0)){done = 1;}
}
if (pressed < 5){ //Kurz < 250 ms: weiter
uint16_t kom2 = code[adr] >> 8;
uint16_t dat2 = code[adr] & 255;
for(int j=0; j<8; j++){gpio_set_pulls(j,1 &(kom2>>j), 0);}
delay (500);
for(int j=0; j<8; j++){gpio_set_pulls(j,1 &(dat2>>j), 0);}
delay (500);
}
if (pressed > 25){tps_running = 0;} // >1,5s: TPS beenden
if (pressed >
59){
// 3s: EEPROM + beenden
tps_running = 0;
for (uint16_t i=0;i<256;i++){
dat = code[i];
EEPROM.write(2*i, dat>>8);
EEPROM.write(2*i+1, dat&255);
if (i==31){EEPROM.commit();}
if (i==63){EEPROM.commit();}
if (i==95){EEPROM.commit();}
if (i==127){EEPROM.commit();}
if (i==159){EEPROM.commit();}
if (i==191){EEPROM.commit();}
if (i==223){EEPROM.commit();}
}
Serial.flush();
EEPROM.commit();
}
if (pressed>4 && pressed
<24){ // ca 1,5 s: kom
ändern
int done3 = 0;
//kom = code[adr] >> 8;
kom = 0;
while (done3==0){
for(int i=0; i<8;
i++){ //kom 0..7
pressed = 0;
int done2 = 0;
while (done2==0){
for(int j=0; j<8; j++){gpio_set_pulls(j,1 &(kom>>j), 0);}
delay (40);
if (gpio_get(15) == 0){pressed++;}
gpio_set_pulls(i,1 & (~kom>>i), 0);
delay (10);
if
(gpio_get(15)==1 && pressed>0 && pressed<6){done2
= 1;} //<250ms: next
bit
if
(gpio_get(15)==1 && pressed>5 &&
pressed<25){ //ca.0,5s: Bit invertieren
done2 = 1;
kom = kom ^ (1<<i);
}
if
(gpio_get(15)==1 && pressed>24){ //>1,5s: fertig
done2 = 1;
done3 = 1;
i = 8;
}
}
}
}
done3 = 0;
//dat = code[adr] & 8;
dat = 0;
while
(done3==0){
// dat editieren
for(int i=0; i<8;
i++){ //dat 0..7
pressed = 0;
int done2 = 0;
while (done2==0){
for(int j=0; j<8; j++){gpio_set_pulls(j,1 &(dat>>j), 0);}
delay (40);
if (gpio_get(15) == 0){pressed++;}
gpio_set_pulls(i,1 & (~dat>>i), 0);
delay (10);
if
(gpio_get(15)==1 && pressed>0 && pressed<6){done2
= 1;} //kurz: next
bit
if
(gpio_get(15)==1 && pressed>5 &&
pressed<25){ //ca.0,5s: Bit invertieren
done2 = 1;
dat = dat ^ (1<<i);
}
if
(gpio_get(15)==1 && pressed>24){ // >1,25: fertig,
next adr
done2 = 1;
done3 = 1;
i = 8;
}
}
}
}
code[adr] = kom*256 + dat; //Geänderte Daten speichern
}
adr++;
}
delay(300);
adr = 0;
a=0; b=0; c=0; d=0;
return_nr = 0;
rx = 1000;
weiter = 0; //keine Wartezeit
warten = 0;
}