\documentclass[11pt,a4paper]{article}

% 2006-07-22 EW Adventures-2.tex

% needs to be first for some reason
\usepackage{epsfig}
%\psfigurepath{./images}

% language support
\usepackage[german]{babel}

%\usepackage[latin1]{inputenc} % can use Umlauts now "u instead of "u
%\usepackage{lmodern} % better fonts
%\usepackage{pslatex} % use native PostScript fonts

\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 2}

\author{Erich W"alde}

\maketitle

\begin{multicols}{2}

Im ersten Teil wurde gezeigt, wie man dem Renesas R8C13 Kontroller
beibringt, mit einem i2c--Bus zu reden. Inzwischen hat Bernd Paysan das
r8c--Forth und das zugeh"orige \texttt{terminal.fs} so aufgebohrt, da"s man
in einem Forth--Programm die Anweisung \texttt{include} verwenden kann.
Daher kann man ein l"angeres Programm in mehrere Dateien zerlegen. Man kann
die Dateien auch so schreiben, da"s sie von mehreren Projekten benutzt
werden k"onnen, eine angenehme Erleichterung.

Ich m"ochte mit dem R8C Kontroller eine Me"sstation aufbauen, die permanent
laufen soll. Um damit zeitliche Abl"aufe zu organisieren, w"are eine Art Uhr
nicht schlecht. Dann kann ich einfacher sagen \textit{alle 10 Minuten
  sollen die Messwerte verschickt werden}.

\section{Eine Forth Uhr}

Eine einfache M"oglichkeit, das Verstreichen der Zeit zu messen, besteht
durch die Verwendung von Timern. Man konfiguriert den Timer so, da"s er
immer nach einer (oder wenigen) Mikrosekunden einen Interrupt ausl"ost. Die
dazugeh"orige Interrupt Service Routine z"ahlt einen Z"ahler hoch und setzt
einen \textit{flag}, der dem "ubrigen Programm anzeigt, da"s eine
Mikrosekunde vergangen ist. Jetzt kann man sich im Quelltext von gForth
schlau machen und dem r8c--Forth eine andere Interrupt Service Routine
unterjubeln, aber das hab ich als Anf"anger nicht gen"ugend durchschaut. Es
geht auch ohne Durchblick.

Wenn man im Quelltext von r8c--Forth liest (\path{arch/r8c/prim.fs}), dann
findet man, da"s es eine Variable \texttt{timer} gibt, die von
\texttt{ms-irq} hochgez"ahlt wird. Es gibt also bereits einen
\textit{system tick}, den ich f"ur die Uhr verwenden kann.

In einem Buch "uber PIC Mikrokontroller (\ref{Koenig}) findet sich eine
geeignete Methode, um eine Zeitz"ahlung zu realisieren. Das Programm l"auft
durch eine Schleife, in der immer wieder "uberpr"uft wird, ob ein \textit{tick}
abgelaufen ist. Wenn ja, dann werden die Z"ahler, die die Zeit verwalten,
hochgez"ahlt (ticks oder Millisekunden, Sekunden, Minuten, Stunden \ldots).
Wenn eine ganze Sekunde verstrichen ist, dann wird der Sekundenz"ahler
ebenfalls hochgez"ahlt, und ein flag gesetzt, welcher ebendies anzeigt.
Diese Routine hei"st in dem o.g.\ Buch \texttt{timeup}. Wenn \texttt{timeup}
zur"uckkehrt, sind alle Z"ahler aktualisiert, und die flags gesetzt.

Im darauf folgenden Teil der Schleife werden die flags "uberpr"uft, die
dazugeh"origen Aufgaben (\textit{jobs)} aufgerufen, und danach die flags gel"oscht.
Wenn mehrere flags gesetzt sind, kann man entweder alle zugeh"origen
Aufgaben ausf"uhren und erst danach die Schleife von vorne beginnen --- oder
man kehrt in die Schleife nach jeder Aufgabe zur"uck. Das hat den Vorteil,
da"s man \texttt{timeup} schneller wieder aufruft, und so hoffentlich nichts
verpasst. Der Nachteil, da"s beispielsweise eine Stundenaufgabe erst 3 ticks
nach der Stunde aufgerufen wird, scheint dagegen klein.
In \textit{Pseudocode} sieht das etwa so aus:

\begin{verbatim}
while (1)
{   if (tick over?)
    {   timeup aufrufen
    }
    if      ( Millisekunden-flag? )
    {   Millisekunden-Aufgabe aufrufen
        Millisekunden-flag l"oschen
    }
    else if ( Sekunden-flag? )
    {   Sekunden-Aufgabe aufrufen
        Sekunden-flag l"oschen
    }
    else if ( Minuten-flag? )
    ...
}
\end{verbatim}

Es gilt also, die Funktion \texttt{timeup} nachzubilden, und die Schleife
zu schreiben.

\section{\texttt{timeup}, erster Versuch}

Um zu "Uberpr"ufen, ob die Idee "uberhaupt was taugt, schreibe ich eine
Schleife, in der immer nach einer Sekunde eine Ausgabe passiert. Das l"asst
sich dann einfach mit einer (Stop--)Uhr bewerten.

Ein tick ist vergangen, wenn \texttt{timer} seinen Wert ver"andert hat. Also
legen wir eine Kopie von \texttt{timer} in \texttt{otimer} an. In einer
Schleife, die aus reiner Bequemlichkeit per Tastendruck endet, testen wir,
ob sich die Werte von \texttt{timer} und \texttt{otimer} unterscheiden.
%(\verb,otimer @ timer @ - 0<>,). 
Das ist immer der Fall, wenn $1$
Millisekunde, also $1$ tick, verstrichen ist. Diese ticks z"ahlen wir. Nach
je $1000$ ticks, also nach $1$ Sekunde, soll eine Ausgabe gemacht werden.

\begin{verbatim}
Variable otimer
Variable msec
Variable sec
: run
  timer @ otimer !
  BEGIN
    otimer @ timer @ - 0<> IF  \ tickover
      1 msec +!
      msec @ 1000 >= IF
        0 msec !
        1 sec +!
        sec @ timer @ cr . .
      ENDIF
      1 otimer +!
    ENDIF
  key? UNTIL
;
\end{verbatim}

Versuch macht bekanntlich kluch:

\begin{verbatim}
run 
1951 1 
2951 2 
3951 3 
...
30951 30 
31951 31 
-32585 32  <== -32585 31951 - . 1000  ok
-31585 33 
...
-5585 59 
-4585 60
-3585 61 
-2585 62 
-1585 63 
-585 64 
415 65     <== 415 -585 - . 1000  ok
1415 66 
...
\end{verbatim}

Das funktioniert jetzt schon recht sch"on, auch an der Stelle, an der
\texttt{timer} "uberl"auft. Die Sekunden "uberschreiten die $59$, aber das
haben wir nicht anders gewollt \verb,:-),

\section{\texttt{timeup}, die Zweite}

Eine andere M"oglichkeit, die Zeit zu z"ahlen funktioniert so: zuerst lesen
wir \texttt{timer} aus, z"ahlen dann allerdings einen bestimmten Wert (z.B.\
500) dazu und speichern das Ergebnis in \texttt{newtimer}. Wenn
\texttt{timer} den Wert von \texttt{newtimer} erreicht oder "uberschritten
hat, dann ist ein bestimmtes Zeitinterval verstrichen. Dann rufen wir
\texttt{timeup} auf, um die Z"ahler zu aktualisieren. Anschlie"send wird zu
\texttt{newtimer} wieder der feste Wert addiert. Auf diese Art addieren
sich die Fehler durch Versp"atungen nicht. Ich glaube, das hab ich auch in
(\ref{Koenig}) gelesen.

Wir schreiben also erst mal ein abgespeckte Version von \texttt{timeup}.
TimerC gibt \texttt{cycles} vor, die in der Variablen \texttt{timer}
gespeichert werden. Die niedrigste Stufe von \texttt{timeup} z"ahlt diese
\texttt{cycles} zu \texttt{ticks}, vielleicht besser \textit{software
  ticks} oder \textit{slow ticks} genannt. Im Beispiel ist nach 500 cycles
ein slow tick vergangen, nach 2 slow ticks eine Sekunde. Mit diesen beiden
Stufen lassen sich auch jobs realisieren, die mehrfach pro Sekunde laufen
m"ussen. Das Programm befindet sich in der Datei \path{adv2_1.fs} und ist
in den Listings abgedruckt.

Zu Beginn der Schleife wird lediglich \texttt{newtimer} initialisiert.
Damit ist \texttt{tickover?} sofort erfolgreich und \texttt{timeup} wird
sogleich aufgerufen. Der zu einem abgelaufenen slow tick geh"orige
\texttt{job.tick} l"asst hier die LED an Pin P1.3 im Sekundentakt blinken.
Der Sekundenjob gibt die aktuellen Z"ahler aus, sowie den Abstand seit dem
letzten Sekundenjob in \texttt{cycles}.

\begin{verbatim}
include adv2_1.fs  ok
58 sec !  ok
run 
min sec timer timer-lastsec
0 59 -26991 1000 
1 0 -25991 1000 
running minute job ...
1 1 -24991 1000 
...  
\end{verbatim}

Die Z"ahler werden hochgez"ahlt, ganz so, wie man sich das vorstellt. Diese
Schleife l"asst sich schon mal gegen die Stopuhr testen. Man beachte auch,
da"s die Hauptschleife recht simpel aussieht. Die einzelnen Worte sind auch
"uberschaubar. 

\section{Uhr und Kalender}

\texttt{timeup} wird zwar l"anglich, wenn man Stunde, Tag, Monat und Jahr
noch dazupackt, aber nicht komplexer. \texttt{timeup} erh"alt im n"achsten
Schritt die restlichen Z"ahler. Sofort braucht man ein Wort, welches die
L"ange eines bestimmten Monats ausgibt, inklusive Behandlung der
Schaltjahre. F"ur die Monate habe ich eine Tabelle \texttt{MaxDay} als
\textit{byte array} angelegt (im RAM!). Der gesuchte Monat wird um $1$
vermindert und dient dann als Index in dieses array. Fehlt nur die korrekte
Behandlung des Februars in Schaltjahren.

Wenn man die "ubliche Aufz"ahlweise f"ur Schaltjahre umkehrt, wird eine
einfache Vorschrift daraus: Wenn ein Jahr durch 400 teilbar ist, ist es ein
Schaltjahr. Wenn es nicht durch 400 aber durch 100 teilbar ist, dann ist es
kein Schaltjahr. Wenn es nicht durch 400 und 100 aber durch 4 teilbar ist,
dann ist es ein Schaltjahr. Alle "ubrigen Jahre sind keine Schaltjahre.

\begin{verbatim}
ram
create MaxDay  31 c, 28 c, 31 c, 30 c, 31 c,
   30 c, 31 c, 31 c, 30 c, 31 c, 30 c, 31 c,
rom
: leap_year ( year -- f )
  dup 400 mod 0= IF     \ if year%400 == 0
    true                \   -> leap year
  ELSE                  \ else
    dup 100 mod 0= IF   \ if year%100 == 0
      false             \    -> no leap year
    ELSE                \ else
      dup 4 mod 0= IF   \ if year%4   == 0
        true            \    -> leap year
      ELSE              \ else
        false           \    -> no leap year
      ENDIF
    ENDIF
  ENDIF
  swap drop           \ remove year from stack
;
: length_of_month ( year month -- maxday )
  dup 1-                \ array starts at 0
  MaxDay + c@           \ get length
  swap 2 = IF           \ if month == 2
    swap leap_year IF   \ and leap_year
      1+                \   length += 1
    ENDIF
  ELSE
    swap drop           \ remove year
  ENDIF ;
\end{verbatim}

Diese Worte (siehe \path{adv2_timeup.fs}) lassen sich testen:

\begin{verbatim}
2000 leap_year . -1  ok
2001 leap_year . 0  ok
1996 leap_year . -1  ok
1997 leap_year . 0  ok
1900 leap_year . 0  ok
2004 leap_year . -1  ok
2006  2 length_of_month . 28  ok
2006 12 length_of_month . 31  ok
2004  2 length_of_month . 29  ok
2004  3 length_of_month . 31  ok
2004  4 length_of_month . 30  ok
2004 12 length_of_month . 31  ok
2000  2 length_of_month . 29  ok
1999  2 length_of_month . 28  ok
1900  2 length_of_month . 28  ok
\end{verbatim}

Bei \texttt{timeup} mu"s man noch darauf achten, da"s die Z"ahler f"ur
Sekunden, Minuten, Stunden gegen den ersten ung"ultigen Wert (60, 60, 24)
mit \verb,>=, getestet werden. Bei der L"ange des Monats produziert
\texttt{length\_of\_month} aber den h"ochsten g"ultigen Wert, also ist der
Vergleich ein \verb,>, und der Startwert 1 und nicht 0. Analog f"ur den
\texttt{month} Z"ahler:

\begin{verbatim}
: timeup ( -- )
...
  day @ year @ month @ length_of_month > IF
    1 day !             \ offset 1!
    1 monthflag !       \ month over
    1 month +!
  ENDIF
  month @ 12 > IF
    1 month !           \ offset 1!
    1 yearflag !        \ year over
    1 year +!
  ENDIF
;
\end{verbatim}

Der komplette Programmtext findet sich in \path{adv2_2.fs} und
\path{adv2_timeup.fs} und produziert etwa folgende Ausgabe:

\begin{verbatim}
include adv2_2.fs  ok
23 hour ! 59 min ! 57 sec ! 0 tick !  ok
2006 year ! 12 month ! 31 day !  ok
run 
year month day hour min sec timer...
2006 12 31 23 59 57 7812 7812 
2006 12 31 23 59 58 8813 1001 
2006 12 31 23 59 59 9813 1000 
2007 1 1 0 0 0 10813 1000 
running minute job ...
running hour job ...
running day job ...
running month job ...
running year job ...
2007 1 1 0 0 1 11813 1000 
2007 1 1 0 0 2 12813 1000 
2007 1 1 0 0 3 13813 1000
...
\end{verbatim}

\section{Uhrzeit auf's LCDisplay}

Sch"oner w"ar's nat"urlich, wenn die Uhrzeit auf dem Display erscheint. Dazu
verwenden wir ein paar einfache Worte, die die Angaben formatieren und
anzeigen. Das genaue Format ist nat"urlich Geschmacksache. Die daf"ur
notwendigen Worte sind in \path{adv2_lcd.fs}:

\begin{verbatim}
: p4!  s>d <# # # # # #> ;
: p2!  s>d <# # # #> ;

\ write string to lcd position col (0..15) 
\ row (0..1) with next lcdtype    
: lcdpos ( row col -- )
  swap $40 * + $80 + lcdctrl! &1 ms ;
: show.DT ( -- )
  year @  p4! lcdtype
  month @ p2! lcdtype
  day  @  p2! lcdtype s" -" lcdtype
  hour @  p2! lcdtype
  min  @  p2! lcdtype
  sec  @  p2! lcdtype ;
\end{verbatim}

F"ur die Anzeige habe ich entschieden, \texttt{show.DT} jede Minute
aufzurufen, und jede Sekunde lediglich die Sekundenanzeige zu
aktualisieren:

\begin{verbatim}
: job.sec ( -- )
  0 13 lcdpos sec @ p2! lcdtype 
;
: job.min ( -- )
  cr ." running minute job ..."
  lcdpage show.DT
;
\end{verbatim}

Au"serdem wird \texttt{show.DT} vor der Schleife einmal aufgerufen.
Der vollst"andige Programmtext findet sich in \path{adv2_3.fs}. Der
entscheidende Nachteil ist jetzt nur noch der, da"s beim Einschalten des
Kontrollers die Zeit erstmal die Zeit von Hand gestellt werden mu"s. Das ist
l"astig. Also habe ich die i2c--Bus Funktionen vom letzten Mal hergenommen
und eine batteriegepufferte PCF8583 Uhr an den i2c--Bus angeschlossen.

\section{i2c--Uhr}

Die Uhr anzusprechen, ist keine gro"se Kunst mehr, hier etwa um die Zeit zu
lesen. Der einzige Trick hier ist der, da"s die Uhr selbst keinen
Jahresz"ahler hat. Lediglich 4 Jahre werden gez"ahlt, um die (meisten)
Schaltjahre zu behandeln. Daher habe ich an Adresse 0x10 im EEPROM der Uhr
einen 2 Byte Z"ahler (nicht BCD, low Byte zuerst) f"ur das ganze Jahr
verwendet.

\begin{verbatim}
: get.rtc
  $01                   \ start address to rtc
  1 i2c_addr_rtc NB>i2c \ send start address
  6 i2c_addr_rtc NB<i2c \ read 6 Bytes
  $10                   \ YEAR address
  1 i2c_addr_rtc NB>i2c \ start address to rtc
  2 i2c_addr_rtc NB<i2c \ read 2 Bytes: xl xh
  8 lshift +            \ convert to YYYY
;
\end{verbatim}

Allerdings handelt man sich bei diesem Modell Zahlen im BCD--Format ein
(\textit{binary coded decimal}), d.h.\ die obere und die untere H"alfte
eines Bytes (\textit{nibbles}) enthalten je eine Ziffer von 0 bis 9. Die
Konvertierung k"onnte evtl.\ in Assembler gemacht werden, wenn der
Kontroller einen entsprechenden Befehl beinhaltet. Im r8c--gforth hab ich
aber kein Wort gefunden. Also basteln wir uns das geschwind selber.


\begin{verbatim}
: bcd>dec ( n.bcd -- n.dec )
  $ff and
  dup 
  4 rshift 10 *   \ extract high nibble as 10s
  swap
  $0f and         \ extract low nibble as 1s
  +               \ add
;
: dec>bcd ( n.dec -- n.bcd )
  &100 mod        \ 99 is largest number for
  &10 /mod        \    8 bit bcd 
  4 lshift
  +
  $ff and         \ truncate to 8 bit
;
\end{verbatim}

Meine L"osungen sind sicher verbesserungsf"ahig, aber f"ur diesen
Zweck ausreichend. Der vollst"andige Programmtext findet sich in
\path{adv2_4.fs} und den darin eingeschlossenen Dateien.

\begin{verbatim}
include adv2_4.fs  ok
2006 7 26 22 31 0 0 set.rtc  ok
run 
year month day hour min sec timer...
2006 7 26 22 31 4 22801 460 
2006 7 26 22 31 5 23801 1000 
2006 7 26 22 31 6 24801 1000 
...
\end{verbatim}

Die Uhrzeit wird zun"achst an die i2c--Uhr "ubertragen, die letzte Zahl vor
\texttt{set.rtc} sind die Hundertstelsekunden. So ausger"ustet "uberlebt
die Uhr jetzt auch das Ausschalten des Kontrollerboards. Ein
$0.47$\,F--GoldCap Kondensator kann meine Uhr l"anger als 2 Wochen
betreiben. Als Zugabe enth"alt das Programm noch das LM75--Thermometer von
der ersten Folge.

\section{Uhr als Task}

Jetzt bleibt nur noch, die Uhr als Hintergrunds--\textit{Task} laufen zu
lassen, so wie das in den Beispielen auf der WikiSeite schon geschieht.
Dazu habe ich bei Rafael Deliano (\ref{Deliano}) eine Erkl"arung gefunden,
wie ein \textit{PAUSE--Tasker} generell funktioniert. An der Stelle, an der
ein Task unterbrechbar ist, ruft er die Funktion \texttt{pause} auf. Dann
l"auft der n"achste Task. Der hier verwendete Tasker kann genau zwei Tasks
verwalten. In den Beispielen ist der zweite Task eine Variante von
\texttt{key}, der unsere Eingaben am Terminal bedient. Man kann also mit
dem System reden, obwohl die Uhr l"auft.

Die Umwandlung von \texttt{run} in ein Task ist fast trivial: \texttt{run}
wird als \texttt{task} gekennzeichnet, die \texttt{BEGIN ... UNTIL}
Schleife wird in eine \texttt{BEGIN ... AGAIN} Schleife umgewandelt, und
vor dem Ende der Schleife wird \texttt{pause} aufgerufen. Dort darf
\texttt{run} unterbrochen werden.

\begin{verbatim}
: run task
  ...
  BEGIN
    tickover? IF timeup ENDIF
    tickflag @ IF job.tick 0 tickflag ! ELSE
    ...
    ENDIF
    pause
  AGAIN
;
\end{verbatim}

Wenn man jetzt noch daf"ur sorgen will, da"s die Show sofort nach dem Einschalten
losgeht, dann helfen die folgenden Zeilen:

\begin{verbatim}
include adv2_5.fs
rom
' run IS bootmessge
ram
savesystem
\end{verbatim}

Das Wort \texttt{'} (\textit{sprich tick}) produziert die Adresse des
ausf"uhrbaren Teils vom folgenden Wort (\texttt{run}), und \texttt{is}
jubelt \texttt{bootmessage} ebendiesen Code unter (der Code, der vorher in
\texttt{bootmessage} steht, ist dann allerdings verloren).
\texttt{bootmessage} selbst wird beim Initialisieren des Forthsystems
aufgerufen, und damit \texttt{run}, und deswegen geht's sofort nach dem
Einschalten los. \texttt{savesystem} sorgt daf"ur, da"s die im RAM
angelegten Variablen ins ROM "ubertragen werden, sonst gehen sie verloren.

Fast "uberfl"ussig zu erw"ahnen \ldots mit den \texttt{job}-Worten kann ich
jetzt sehr bequem regelm"a"sige Aufgaben verteilen. Die Anzeige der Sekunde
jede Sekunde, das L"oschen und wiederbeschreiben des kompletten Displays
jede Minute und das Ein-/Ausschalten der LED und die Anzeige der Temperatur
alle halbe Sekunde (slow tick) sind die derzeit eingebauten regelm"a"sigen
Aufgaben. Mit etwas Phantasie l"a"st sich der Kontroller nun f"ur zum Schalten
t"aglicher Vorg"ange (Roll"aden, Licht, Kaffeemaschine) oder sensorgesteuerter
Dinge (Gew"achshausl"uftung, Dachfenster) gebrauchen. Der Haussteuerung in
Forth steht nichts mehr im Weg!

\section{Ausblick}

Der damit erreichte Stand der Dinge ist nicht schlecht. Dennoch verbleiben
eine Menge Dinge f"ur einen weiteren Artikel --- falls ich die Leser nicht
schon "uber Geb"uhr strapaziert habe.

Ein Blick auf den belegten und freien Speicher d"urfte sich lohnen. Wieviele
Variablen kann ich zur Me"swerterfassung noch benutzen? Und was passiert
mit Variablen, die mit \texttt{savesystem} gerettet werden? Liegen diese
nach einem Neustart im RAM oder im ROM? Falls im ROM, ist das eine schlaue
Idee (begrenzte Anzahl von Schreibzyklen), oder sollte man solche Variablen
besser beim Initialisieren der Schleife neu Anlegen?

Wie kann man im vorhandenen Code noch deutlich Platz sparen oder die
Teilung in Module verbessern? Wo und wie kann man Forth--spezifische
Programmiertechniken verwenden?

Messwerte wollen verschickt werden, am besten "uber die andere serielle
Schnittstelle (uart0). Wie genau stellt man das an?

Und als praktische Anwendung m"ochte ich einen Sensor aufbauen, der die
Sonnenscheindauer erfasst --- bitte ohne irgendwelche mechanisch
bewegten Teile.

Und der aufmerksame Leser hat l"angst gemerkt, da"s \texttt{job.year} den
im EEPROM der i2c--Uhr gespeicherten Wert f"ur das Jahr auch aktualisieren
sollte. 

\textit{Kontakt? \url{ew.forth@online.de} oder so?}

\section{Referenzen}

\begin{enumerate}
\item \label{Adv1} E. W"alde, Adventures in Forth, Die 4. Dimension 3/2006, Jahrgang 22
\item \label{Koenig} A.\ K"onig und M.\ K"onig, Das PICmicro Profi Buch, Franzis
  Verlag 1999, ISBN 3-7723-4284-1
\item \label{Deliano} \url{http://www.embeddedforth.de/emb3.pdf} S.\ 9
\end{enumerate}
\end{multicols}

\section{Listings}

\begin{quote}
\begin{small}
\begin{multicols}{2}
\listinginput[1]{1}{2006-04/adv2_1.fs}
\listinginput[1]{1}{2006-04/adv2_timeup.fs}
\listinginput[1]{1}{2006-04/adv2_2.fs}
\listinginput[1]{1}{2006-04/adv2_3.fs}
\listinginput[1]{1}{2006-04/adv2_lcd.fs}
\listinginput[1]{1}{2006-04/adv2_4.fs}
\listinginput[1]{1}{2006-04/adv2_5.fs}
\listinginput[1]{1}{2006-04/adv2_tasker.fs}
\listinginput[1]{1}{2006-04/adv2_i2c.fs}
\listinginput[1]{1}{2006-04/adv2_i2c_rtc.fs}
\listinginput[1]{1}{2006-04/adv2_i2c_lm75.fs}
\end{multicols}
\end{small}
\end{quote}

\end{document}
