I. Der (onboard) Flash-SpeicherDieses Tutorial befasst sich mit Funktionen die dazu dienen, sich mit der Ansteuerung des Flash-Speichers auf einem JControl-Modul vertraut zu machen. Es wird gezeigt, wie die Dimensionen des Flash-Speichers bestimmt werden, wie Daten in den Speicher geschrieben und wieder ausgelesen werden können. |
Die Speicherdimensionen setzen sich aus drei Komponenten zusammen: Bytes pro Sektor, Anzahl der Sektoren und Anzahl der Speicherbänke. Je nach Produktausstattung können diese Dimensionen variieren. Für einen problemlosen Zugriff auf den Speicher ist es also unerlässlich, die Dimensionen des Speichers zu bestimmen, auf den ein laufendes Programm zugreifen soll.
Ein Beispiel:. Bei einem JControl/SmartDisplay http://www.domologic.com/services/ch3/index_de.html enthält jede Speicherbank 64kB, womit ein solches Modul in der Standardausführung genau eine Speicherbank besitzt. Diese Bank ist aufgeteilt in 512 Sektoren und jeder Sektor besteht aus 128 Bytes.
1 | /** |
2 | * Java file created by JControl/IDE |
3 | * |
4 | * @author RSt |
5 | * @date 03.11.04 17:23 |
6 | * |
7 | */ |
8 | import java.io.IOException; |
9 | import jcontrol.comm.RS232; |
10 | |
11 | public class FlashDimensions { |
12 | |
13 | static String flashDimensions = ""; |
14 | static RS232 myRS232; |
15 | |
16 | public FlashDimensions() { |
17 | try{ |
18 | myRS232 = new RS232();//default = 19200 Baud |
19 | }catch(IOException e){ |
20 | } |
21 | flashDimensions = jcontrol.system.Management.getProperty("flash.format"); |
22 | } |
23 | |
24 | public static void main(String[] args) { |
25 | new FlashDimensions(); |
26 | myRS232.println(flashDimensions); |
27 | for(;;);//Stops execution of this program. |
28 | } |
29 | } |
Das obige Programm gibt (auf einem Standard-JControl/SmartDisplay) über die serielle Schnittstelle den String
"512x128x1
" ({Sektoren}x{Bytes/Sektor}x{Bänke}) aus, welcher die Speicherdimensionen des Beispiels (s.o.) widerspiegelt. Aus diesem String (die entsprechende Programmzeile ist hervorgehoben) kann ein Programm also während des Betriebs die Dimensionen des eingebauten Flash-Speichers bestimmen, sodass dem Benutzer die Daten zur Verfügung stehen, die er für den sicheren Zugriff auf den Speicher braucht.
Hinweis aus der Praxis:. Es gestaltet sich bei Speicheroperationen sehr einfach, in die 0-1-Falle zu geraten: Die Indizes der Bänke, Sektoren und Bytes beginnen bei 0 und reichen bis zu der entsprechenden Anzahl-1. Bei zwei Speicherbänken stehen also die Bänke 0 und 1 zur Verfügung. Dies ist zwar i.d.R. bekannt, bleibt aber dennoch eine häufige Ursache für Speicherzugriffsfehler.
Um Daten in den Speicher zu schreiben, werden zunächst dessen Dimensionen benötigt. Nun ist es notwendig etwas über die Methoden herauszufinden, die die JControl-API http://www.jcontrol.org/current/docs/api/index.html für den Zugriff auf den Flash-Speicher bereitstellt. Die folgenden Klassen beinhalten entsprechende Methoden:
jcontrol.io.Flash
jcontrol.storage.FlashStream
jcontrol.storage.FlashTlv
Als Beispiel wird hier die Klasse jcontrol.io.Flash
betrachtet. Um mit ihr überhaupt auf den Speicher zugreifen zu können, muss eine Instanz dieser Klasse (mit new) erzeugt werden. Dem Konstruktor (Flash()
) kann dabei die Speicherbank (als int
) übergeben werden, auf die bei Speicheroperationen zugegriffen werden soll (Flash(speicherbank)
). Mit der Flash-Instanz und den Speicherdimensionen ist nun alles vorhanden, was der Anwender braucht, um Daten zu speichern.
Für das Schreiben von Daten wird hier die Methode write der Klasse jcontrol.io.Flash
verwendet, welche 4 Paramter verlangt:
byte[]
).int
), ab dem die Daten aus dem Byte-Array (data) herausgelesen und in den Speicher übertragen werden sollen. (ACHTUNG: Erstes Byte hat den Index 0!)int
; Es werden dann die Bytes von startindex bis (startindex+length-1) aus dem angegebenen Byte-Array in den Speicher geschrieben.int
), in den geschrieben werden soll.
Tipp: Die Klasse jcontrol.io.Flash
verfügt außerdem über die Methode getUsableSectors(), die dem Benutzer anzeigt, wie viele Sektoren ihm für eigene Daten zur Verfügung stehen. Das ist besonders dann nützlich, wenn die zu beschreibende Speicherbank dieselbe ist, die das auszuführende Programm beherbergt. Es ist nicht in die Sektoren zu schreiben, die das Programm beinhalten.
Das folgende Beispielprogramm schreibt den String
"Welcome to JControl!!!" an den Anfang des 0ten Sektors von Bank 0.
1 | /** |
2 | * Java file created by JControl/IDE |
3 | * |
4 | * @author RSt |
5 | * @date 04.11.04 14:34 |
6 | * |
7 | */ |
8 | import java.io.IOException; |
9 | import jcontrol.io.Flash; |
10 | |
11 | public class FlashWrite { |
12 | |
13 | private static Flash myFlash; |
14 | |
15 | public FlashWrite() { |
16 | try{ |
17 | //Initiate Flash object with context to bank 0 |
18 | myFlash = new Flash(0); |
19 | }catch(IOException e){ |
20 | } |
21 | } |
22 | |
23 | public static void main(String[] args) { |
24 | new FlashWrite(); |
25 | String writeThis = "Welcome to JControl!!!"; |
26 | byte[] data = writeThis.getBytes(); |
27 | try{ |
28 | //write data to memory: |
29 | myFlash.write(data, 0, data.length, 0); |
30 | }catch(IOException e){ |
31 | } |
32 | for(;;);//Stops execution of this program. |
33 | } |
34 | } |
Ob die Daten korrekt geschrieben wurden, lässt sich bei diesem Programm nur mit dem Simulator der JControl/IDE überprüfen. Der Inhalt des entsprechenden Sektors kann in dessen Flash Viewer (Bild 1) sichtbar gemacht werden. Im folgenden Abschnitt (Daten aus dem Speicher lesen) wird das Beispielprogramm so erweitert werden, dass die geschriebenen Daten wieder eingelesen und auf der seriellen Schnittstelle ausgegeben werden.
Die oben erwähnten Klassen jcontrol.storage.FlashStream
und jcontrol.storage.FlashTlv
basieren auf der hier benutzten Klasse jcontrol.io.Flash
und implementieren einige hilfreiche Funktionen. So ermöglicht die Klasse FlashStream
z.B. das kontinuierliche Schreiben, bzw. Lesen einzelner Zeichen (char
), wohingegen die Klasse FlashTlv
spezielle Dateien erzeugt.
Wenn Daten aus dem Speicher gelesen werden sollen, ist es ebenfalls notwendig, die Speicherdimensionen zu kennen. Ebenso wie beim Schreiben von Daten muss eine Instanz der Klasse jcontrol.io.Flash
ezeugt werden, um auf den Speicher und die entsprechende Bank zugreifen zu können. Für das Lesen aus dem Speicher wird hier die Methode read der Klasse jcontrol.io.Flash
verwendet.
Wie die Methode write, so erwartet auch read 4 Paramter:
byte[]
).int
), ab dem das Byte-Array (data) gefüllt werden soll. (ACHTUNG: Erstes Byte hat den Index 0!)int
; Es werden dann die Bytes aus dem Speicher in die Bytes des Arrays mit den Indizes von startindex bis (startindex+length-1) übertragen.int
), aus dem gelesen werden soll.Folgendes Programm schreibt wiederum den String
"Welcome to JControl!!!" an den Anfang des 0ten Sektors von Bank 0. Allerdings werden diese Daten hier wieder ausgelesen und zum Zwecke der Überprüfung auf der seriellen Schnittstelle ausgegeben.
1 | /** |
2 | * Java file created by JControl/IDE |
3 | * |
4 | * @author RSt |
5 | * @date 04.11.04 15:49 |
6 | * |
7 | */ |
8 | import java.io.IOException; |
9 | import jcontrol.comm.RS232; |
10 | import jcontrol.io.Flash; |
11 | |
12 | public class FlashRead { |
13 | |
14 | static Flash myFlash; |
15 | static RS232 myRS232; |
16 | |
17 | public FlashRead() { |
18 | try{ |
19 | //Initiate Flash object with context to bank 0 |
20 | myFlash = new Flash(0); |
21 | //Initiate RS232 object for printing messages |
22 | myRS232 = new RS232();//default = 19200 baud |
23 | }catch(IOException e){ |
24 | } |
25 | } |
26 | |
27 | public static void main(String[] args) { |
28 | new FlashRead(); |
29 | String writeThis = "Welcome to JControl!!!"; |
30 | byte[] data = writeThis.getBytes(); |
31 | byte[] read = new byte[data.length]; |
32 | try{ |
33 | //write data to memory: |
34 | myFlash.write(data, 0, data.length, 0); |
35 | //read Flash content: |
36 | myFlash.read(read, 0, read.length, 0); |
37 | //write read data to serial port |
38 | myRS232.println(new String(read)); |
39 | }catch(IOException e){ |
40 | } |
41 | for(;;);//Stops execution of this program. |
42 | } |
43 | } |
Die Ausgabe des Programms kann nun mit der RS232-Konsole der JControl/IDE oder einem anderen Terminal-Programm überprüft werden.
In den vorigen Abschnitten wurde gezeigt, wie die Dimensionen des Flash-Speichers bestimmt und Daten in den Speicher geschrieben und wieder ausgelesen werden können. In diesem Abschnitt soll nun vermittelt werden, wie man mit diesen Kenntnissen derart auf den Speicher zugreift, dass zu schreibende Daten an einer bestimmten Stelle im Speicher stehen und beim Hineinschreiben keine Daten aus dem Speicher verloren gehen. Dieser Abschnitt bezieht sich speziell auf die Klassejcontrol.io.Flash
.
Die generelle Problematik: In den bisherigen Beispielprogrammen dieses Kapitels wurden die Daten (der String
"Welcome to JControl!!!") an den Anfang des 0ten Sektors in Bank 0 geschrieben. Wenn der Beispiel-String
nun nicht im 0ten Byte beginnen soll, sondern z.B. im 5ten, so kann dies der write
-Methode der Klasse jcontrol.io.Flash
nicht direkt mitgeteilt werden. Alle Bytes die geschrieben werden sollen, werden an den Anfang des Zielsektors geschrieben (der Parameter startindex bezieht sich nur auf das übergebene Byte
-Array "data
"). Die zu schreibenden Bytes müssen also schon im Byte
-Array an der Stelle stehen, an der sie später im Zielsektor erscheinen sollen. Soll also der Beispiel-String
im 5ten Byte beginnen, so muss dessen erstes Byte auch im Byte
-Array an 5ter Stelle stehen und data
ab (Start-)Index 0 in den Speicher übertragen werden. Dabei stellt sich die Frage: "Was ist mit den ersten 5 Bytes des Byte
-Arrays zu tun?" Diese Bytes werden ja ebenfalls in den Speicher übertragen und überschreiben die dort bereits vorhandenen Bytes, was nicht gerade wünschenswert ist.
Die zusätzliche Problematik der Hardware: Hierbei handelt es sich um die Art des Speicherzugriffs auf Hardware-Ebene. Bei Schreibzugriffen auf den Flash-Speicher wird jedes Mal der komplette Zielsektor geschrieben. Das bedeutet anschaulich, dass damit auch die Bytes des Speichers gelöscht werden, die sich hinter dem letzten geschriebenen Byte im Sektor befinden. Diese Bytes werden automatisch mit dem Wert 255 (0xFFh, bzw 11111111b) gefüllt.
Die Lösung: Für einen verlustfreien Schreibzugriff auf den Flash-Speicher müssen folgende Aktionen ausgeführt werden:
Um die Auswirkungen dieser Vorgehensweise zu verdeutlichen, schreibt das folgende Beispielprogramm (Listing 4) zuerst den String
"012345678901234567890123456789" in den Flash-Speicher. Das soll zeigen, dass die vorhandenen Bytes im Zielsektor nicht verändert werden. Außerdem werden am Schluss des Programms 10 Bytes mehr aus dem Speicher gelesen, als hineingeschrieben wurden. Das Programm ist außerdem für die Standardausführung der JControl-Module ausgelegt und benutzt daher eine Puffergröße von 128 Bytes (=Sektorgröße des Standard-Speichers).
1 | /** |
2 | * Java file created by JControl/IDE |
3 | * |
4 | * @author RSt |
5 | * @date 04.11.04 17:30 |
6 | * |
7 | */ |
8 | import java.io.IOException; |
9 | import jcontrol.comm.RS232; |
10 | import jcontrol.io.Flash; |
11 | |
12 | public class FlashBufferedWriteRead { |
13 | |
14 | static Flash myFlash; |
15 | static RS232 myRS232; |
16 | |
17 | public FlashBufferedWriteRead() { |
18 | try{ |
19 | //Initiate Flash object with context to bank 0: |
20 | myFlash = new Flash(0); |
21 | //Initiate RS232 object for printing messages: |
22 | myRS232 = new RS232();//default = 19200 baud |
23 | }catch(IOException e){ |
24 | } |
25 | } |
26 | |
27 | public static void main(String[] args) { |
28 | new FlashBufferedWriteRead(); |
29 | String firstWrite = "012345678901234567890123456789";//30 characters |
30 | byte[] firstData = firstWrite.getBytes(); |
31 | String writeThis = "Welcome to JControl!!!";//String to write |
32 | //Assumed usage of standard JControl/SmartDisplay or -/Sticker!!! |
33 | byte[] buffer = new byte[128];//Buffer for existing sector content |
34 | byte[] data = writeThis.getBytes();//Bytes representing the String |
35 | byte[] read = new byte[40];//For controlling written data |
36 | int targetPos = 5;//Position of the first byte of 'data' in memory |
37 | try{ |
38 | //write firstWrite into sector 0: |
39 | myFlash.write(firstData, 0, firstData.length, 0); |
40 | //read complete sector 0: |
41 | myFlash.read(buffer, 0, buffer.length, 0); |
42 | //integrate data into buffer: |
43 | for(int i=0 ; i < data.length ; i++){ |
44 | buffer[i+targetPos] = data[i]; |
45 | } |
46 | //write bytes into memory (complete sector): |
47 | myFlash.write(buffer, 0, buffer.length, 0); |
48 | //read Flash content: |
49 | myFlash.read(read, 0, read.length, 0); |
50 | //write read data to serial port: |
51 | myRS232.println(new String(read)); |
52 | }catch(IOException e){ |
53 | } |
54 | for(;;);//Stops execution of this program. |
55 | } |
56 | } |
Der Speicherinhalt kann nun entweder mit dem Flash Viewer des Simulators der JControl/IDE kontrollieren (Bild 2) oder anhand der Ausgabe auf der seriellen Schnittstelle.
Bild 3 zeigt die Daten, die auf der seriellen Schnittstelle ausgegeben werden. Der hintere Teil der Ausgabe besteht aus 10 gleichen Sonderzeichen (ÿ). Diese kommen daher, dass 10 Zeichen mehr aus dem Speicher gelesen wurden, als hinein geschrieben wurden. Jene Zeichen treten also dann auf, wenn unbeschriebene Speicherbereiche ausgelesen werden.
Achtung: In dem Beispielprogramm (Listing 4) wurde auf eine Überprüfung der Speichergrenzen verzichtet. In der Praxis ist unbedingt darauf zu achten, ob Daten über Sektor- oder gar Speicherbank-Grenzen hinaus reichen. Falls Daten über solche Bereichsgrenzen hinaus geschrieben werden, resultiert dies in einer OutOfMemoryException.