\documentclass[11pt,a4paper]{article} % 2006-07-07 EW Adventures-1.tex % 2006-07-22 EW Adventures-2.tex % 2006-08-14 EW Adventures-2a.tex % 2007-01-10 EW Adventures-3.tex % 2007-01-13 EW Adventures-4.tex % 2008-11-14 EW Adventures-5.tex % % Aufgebessert nachdem ich 'ne Reihe Optimierungen gemacht hatte % Und dann nochmal komplett umgeräumt! % % language support \usepackage[german]{babel} %\usepackage{german} %\usepackage[latin1]{inputenc} % can use Umlauts now "u instead of "u %\usepackage{lmodern} % better fonts %\usepackage{pslatex} % use native PostScript fonts \usepackage{cm-super} \usepackage{url} % \url{} \path{} with correct "hyphenation" %\usepackage{fancyvrb} % for code examples % \voffset-10mm % \pagestyle{empty} % \pagestyle{plain} % \pagestyle{headings} % \renewcommand{\baselinestretch}{1.08} %\usepackage{xspace} \parindent=0pt %\newcommand{\Forth}{\textsc{forth}\xspace} \begin{document} \title{Adventures in Forth 5} \author{Erich W"alde} \maketitle \begin{multicols}{2} % ==================================================================== \section{Umstieg auf \texttt{amforth}} \label{sec:amforth} Am Ende meiner kleinen Artikelserie mit Code f"ur den Renesas--\texttt{r8c}--Kontroller war ich ein wenig traurig: der Datenspeicher, in dem mein Programm aufbewahrt wird, war leider fast voll. Und dabei wollte ich jetzt erst richtig loslegen. Zun"achst habe ich ein wenig mit \texttt{ByteForth} von Willem Ouwerkerk [\ref{willem}] herumexperimentiert. Allerdings ben"otigt man zum Arbeiten ein DOS. In meiner GNU/Linux--Welt l"asst sich das mit \texttt{dosemu} zwar halbwegs komfortabel handhaben, aber eben nur halbwegs. Dann bekam ich Wind von einem gewissen Matthias Trute und \texttt{amforth} [\ref{amforth}]. Ein nagelneues Forth f"ur die \texttt{atmega}--Familie von Atmel, angelehnt an die Artikelserie von Ron Minke [\ref{ron}]. Einen arbeitslosen atmega32--Kontroller hatte ich noch herumliegen. Also flugs an die Arbeit. Es stellte sich heraus, dass ich an meinem Code f"ur den \texttt{r8c} nur wenig "Anderungen vornehmen musste. Der Umstieg war leicht. \begin{itemize} \item \texttt{amforth} ist in kleinen Buchstaben programmiert: \texttt{IF} ist schlecht, \texttt{if} ist gut. \item Etliche Worte sind erst durch Eintragen in \texttt{dict\_appl.inc} verf"ugbar. \item statt bit--bang i2c Code ein simples \texttt{include lib/twi.frt} \item \texttt{,} (comma) schreibt ins Flash, das muss man mit \texttt{i@} lesen. \item Nur \texttt{allot} reserviert RAM, das kann man dann mit \texttt{@} lesen und mit \texttt{!} beschreiben. \item \texttt{bset}, \texttt{bclr}, \texttt{btst} fehlen. \item Das Thema mit den \texttt{include}--Dateien habe ich anders gel"ost. Ich expandiere meine Forth--Dateien zuerst mit einem \texttt{perl}--Filter und "ubertrage dann den kompletten (um Kommentare erleichterten) Quelltext auf den Kontroller. Es gibt ein nettes python--Programm \texttt{amforth-upload.py}, welches das genauso gut erledigt. \item Ich habe zuerst die \texttt{timeup}--Uhr mit den "Uberl"aufen von \texttt{timer2} gef"uttert und damit meine Zeitverwaltung realisiert. Das ist in [\ref{Adv2}] beschrieben und mit sehr geringen "Anderungen auch auf \texttt{amforth} lauff"ahig. \end{itemize} \section{Bit Flags} Inspiriert durch den Artikel \emph{Named Bits} von M.~Kalus, M.~Trute [\ref{namedbits}] habe ich Bit--Variablen realisiert. Die Definition von \texttt{flag:} ist verbl"uffend einfach \begin{verbatim} : flag: create ( addr bit -- ) 1 swap lshift , , does> ( -- bitmask addr ) dup i@ swap 1+ i@ ; \end{verbatim} Die Bitnummer wird in eine Maske umgewandelt (\texttt{1 swap lshift}) und gespeichert. Die Adresse der Variablen, die die Bits speichert, wird ebenfalls in Flash geschrieben. Zur Laufzeit wird die Adresse der Variablen und die Bitmaske auf den Stapel gelegt. Premiere: das war meine erste eigene Verwendung von \texttt{create ... does>}. Zur Verwendung erzeugt man zun"achst eine Variable, in der 16 Bits gespeichert werden k"onnen, dann definiert man eine Bitvariable, etwa so: \begin{verbatim} variable FLAGS FLAGS 4 flag: Fdebug \end{verbatim} Au"serdem definiert man Worte, die die auf den Stapel gelegten Werte \texttt{bitmask} und \texttt{addr} benutzen, z.B.\ \texttt{fset}: \begin{verbatim} : fset ( bitmask addr -- ) dup @ ( -- mask addr value ) rot ( -- addr value mask ) or ( -- addr new-value ) swap ! ( -- ) ; \end{verbatim} Die Variable \texttt{FLAGS} wird in einer Lesen--"Andern--Schreiben--Sequenz bearbeitet, so dass die anderen Bits unver"andert bleiben. Die folgenden Worte habe ich definiert: \begin{itemize} \item \texttt{fset} --- Bit setzen \item \texttt{fclr} --- Bit l"oschen \item \texttt{fset?} --- wahr, wenn Bit gesetzt ist \item \texttt{fclr?} --- wahr, wenn Bit gel"oscht ist \item \texttt{ftgl} --- das Bit setzen, wenn es gel"oscht ist, und umgekehrt \end{itemize} Das l"asst sich dann auch gleich ausprobieren: \begin{verbatim} > variable FLAGS ok > 0 FLAGS ! ok > FLAGS @ . 0 ok > FLAGS 2 flag: F_debug ok > F_debug fset? . 0 ok > F_debug fset ok > FLAGS @ . 4 ok > F_debug fset? . -1 ok > F_debug ftgl ok > F_debug fset? . 0 ok > FLAGS @ . 0 ok \end{verbatim} \section{So 'ne Art Objekt: \texttt{sensor:}} Mein derzeitiges Kontrollerprogramm liest alle 10 Sekunden zweierlei Temperatursensoren aus, LM75-- und LM92--Sensoren. Der Datentransfer ist bei beiden exakt identisch, nur die Anzahl der signifikanten Bits in der Antwort ist verschieden. Es gibt ein Wort \texttt{lm.get}, welches 2 Byte von der angegebenen Adresse liest: \begin{verbatim} : lm.get ( i2c_addr -- xh xl ok | err ) dup twi.ping? if >r 0 1 r@ >i2c 2 r> i2c} und \texttt{i2c ( x1 .. xN.msB N addr -- ) ... ; : r >r >r >r , r> , r> , r> , r> , does> ( -- id skalierterWert ok | id err ) >r ( -- ) r@ i@ ( -- id ) 1 r@ + i@ ( -- id i2c-addr ) 2 r@ + i@ ( -- id i2c-addr 'get ) execute ( -- id xN..x1 ok | id err ) 0= if ( -- id xN..x1 ) 3 r@ + i@ ( -- id xN..x1 'decode ) execute ( -- id T*100 ) 4 r@ + i@ + ( -- id T*100+a_0 ) 0 ( -- id T*100+a_0 ok ) else ( -- id ) -1 ( -- id error ) then r> drop ; \end{verbatim} Beim Definieren eines \texttt{sensor:}--Objekts werden eine ID, die Adresse des Sensors, je ein Zeiger auf eine Funktion zum Lesen des Sensors, sowie zum Dekodieren der gelesenen Daten, und schlie"slich eine Korrektur (Offset) abgespeichert. Die Verwendung wird jetzt einfacher: \begin{verbatim} : .T ( id value ok | id err -- ) 0= if swap .id 2 +.f else .id ." *" then ; ... 01 a_th1 ' lm.get ' lm75.decode -150 sensor: T1 02 a_th2 ' lm.get ' lm92.decode 122 sensor: T2 ... T1 .T T2 .T ... \end{verbatim} Das ist jetzt so eine Art Objekt mit privaten Konstanten, ohne private Variablen und mit nur einer Methode (\texttt{does>}). Nachteilig ist, dass man hier nicht verschiedene Methoden basteln kann. Immerhin kann man mehrere Instanzen anlegen, die sich in ihrem Innenleben verschieden verhalten. Diese Verschiedenheit wird "uber Funktionszeiger transportiert. Die Instanzen lassen sich daher auch f"ur andere Sensoren verwenden, die komplett anders anzusprechen sind und/oder eine andere Art und/oder Anzahl von Ergebnissen produzieren. Diese m"ussen nur korrekt weiterverarbeitet werden (z.B.\ Anzeigefunktion) \section{So 'ne Art Objekt: \texttt{filter\_mean:}} Ich wollte die Messwerte "uber 10 Minuten mitteln und am Ende der 10 Minuten nur als Liste von Werten $N$, $x_{min}$, $x_{mean}$, $x_{max}$ ausgeben. Beim Mittelwert ist es einfach, ich muss nicht alle Werte aufheben, sondern nur die Summe und die Anzahl der Messwerte. $x_{mean} = x_{sum}/N$, $x_{min}$ und $x_{max}$ k"onnen fortlaufend bestimmt werden. Also brauche ich 4 Variablen im RAM: $N$, $x_{min}$, $x_{sum}$, $x_{max}$. Davon $x_{sum}$ besser mit doppelter L"ange. \texttt{filter\_mean:} bekommt eine ID und einen Zeiger auf den reservierten RAM--Bereich. \begin{verbatim} : filter_mean: create ( id -- ) , \ store id in dictionary entry heap , \ store next RAM addr 5 cells allot \ create RAM space does> ( -- id ram_addr ) dup i@ swap 1+ i@ ; \end{verbatim} \texttt{filter\_mean:} speichert beim Anlegen eines Filters zuerst eine ID. Dann wird die Adresse der n"achsten freien RAM--Zelle gespeichert, und anschlie"send im RAM die n"otige Anzahl von Zellen reserviert. Zur Laufzeit werden lediglich die ID und die RAM--Adresse auf den Stapel gelegt. Die Belegung im RAM--Speicher soll mit Konstanten lesbarer gemacht werden. Dazu kommen Worte, die das Offset zu einer gegebenen Adresse addieren. \begin{verbatim} \ offsets into ram_addr 0 constant mean_sum 2 constant mean_N 3 constant mean_min 4 constant mean_max : mean_sum+ ( mean_sum cells + ) noop ; : mean_N+ mean_N cells + ; : mean_min+ mean_min cells + ; : mean_max+ mean_max cells + ; \end{verbatim} Drei Methoden habe ich definiert, um mit dem RAM--Bereich zu arbeiten: \begin{itemize} \item \texttt{reset ( id ram\_addr -- )}\\ die Werte im Speicher auf Null setzen \item \texttt{addup ( x id ram\_addr -- )}\\ einen neuen Messwert in die Daten einf"ugen, Dabei $x_{min}$ und $x_{max}$ "uberpr"ufen und ggf.\ aktualisieren. \item \texttt{eval ( id ram\_addr -- id max d.sum min N>0 | id 0 )}\\ die vorhandenen Daten auswerten \end{itemize} Neu war f"ur mich an dieser Stelle nur die korrekte Bearbeitung von $x_{sum}$ als doppelt lange Variable. Daf"ur gibt es die Helferlein \texttt{2@}, \texttt{2!}, \texttt{s>d}, \texttt{d+} und \texttt{m*/}, die sich in der Datei \texttt{lib/ans94/2x.frt} befinden. \begin{verbatim} : mean_eval ( id ram_addr -- id max d.sum min N>0 | id 0 ) >r r@ mean_N+ @ 0= if 0 \ N=0, no data else r@ mean_max+ @ \ mean_max r@ 2@ \ mean_sum 1 r@ mean_N+ @ \ mean_N m*/ \ mean_sum*1/mean_N r@ mean_min+ @ \ mean_min r@ mean_N+ @ \ mean_N then r> drop \ ram_addr ; \end{verbatim} \begin{verbatim} > decimal ok > 10 filter_mean: F10 ok > F10 mean_reset ok > : .F10 cr . . d. . . ; ok > 1000 F10 mean_addup ok > 2000 F10 mean_addup ok > 3000 F10 mean_addup ok > 4000 F10 mean_addup ok > F10 mean_eval .F10 4 1000 2500 4000 10 ok \end{verbatim} Im Unterschied zu \texttt{sensor:} enth"alt \texttt{filter\_mean:} private Variablen und drei Methoden, die auf den Variablen operieren. Wahrscheinlich sind das keine \textit{Objekte nach der reinen Lehre}, aber als Beispiel erhellend. Die Methoden sind hier f"ur alle Instanzen gleich. %\section{Ausblick} %Ein paar Dinge k"onnte man jetzt noch tun: \section{Referenzen} \begin{enumerate} \item \label{Adv1} E. W"alde, Adventures in Forth, Die 4. Dimension 3/2006, Jahrgang 22 \item \label{Adv2} E. W"alde, Adventures in Forth 2, Die 4. Dimension 4/2006, Jahrgang 22 %\item \label{Adv3} E. W"alde, Adventures in Forth 3, Die 4. Dimension 1/2007, Jahrgang 23 %\item \label{Adv4} E. W"alde, Adventures in Forth 4, Die 4. Dimension 1/2007, Jahrgang 23 %\item \label{r8c-ds} Renesas R8C/13 datasheet auf \url{www.renesas.com} %\item \label{Koenig} A.\ K"onig und M.\ K"onig, Das PICmicro Profi Buch, Franzis % Verlag 1999, ISBN 3-7723-4284-1 %\item \label{SPelc} Stephen Pelc, Programming Forth, \\ \url{http://www.mpeforth.com/arena/ProgramForth.pdf} %\item \label{Deliano} \url{http://www.embeddedforth.de/emb3.pdf} S.\ 9 %\item \label{dcf} \url{http://de.wikipedia.org/wiki/DCF77} und Verweise darin \item \label{willem} ByteForth von Willem Ouwerkerk,\\ \url{http://www.forth.hccnet.nl/byteforth.htm} \item \label{amforth} \url{http://amforth.sourceforge.net/} \item \label{ron} Ron Minke, Forth von der Pike auf Die 4. Dimension 3+4/2005 \ldots 4/2006, sowie Sonderheft AVR und 3+4/2007 \item \label{namedbits} M. Kalus, M. Trute, Named Bits, Die 4. Dimension 2/2007, Jahrgang 23 \end{enumerate} \end{multicols} \newpage \section{Listings} \begin{quote} \begin{small} \begin{multicols}{2} \listinginput[1]{1}{2008-04/flags.fs} \listinginput[1]{1}{2008-04/i2c_lmXX.fs} \listinginput[1]{1}{2008-04/i2c.fs} \listinginput[1]{1}{2008-04/sensor.fs} \listinginput[1]{1}{2008-04/filter_mean.fs} \end{multicols} \end{small} \end{quote} \end{document}