Skip to content
Snippets Groups Projects
Commit c1b9b259 authored by Peter Gerwinski's avatar Peter Gerwinski
Browse files

Musterlösungen 3., 10. und 17.12.2018

parent 2dc847de
No related branches found
No related tags found
No related merge requests found
Showing with 1183 additions and 4 deletions
% hp-musterloesung-20181112.pdf - Solutions to the Exercises on Low-Level Programming / Applied Computer Sciences % hp-musterloesung-20181119.pdf - Solutions to the Exercises on Low-Level Programming / Applied Computer Sciences
% Copyright (C) 2013, 2015, 2016, 2017, 2018 Peter Gerwinski % Copyright (C) 2013, 2015, 2016, 2017, 2018, 2019 Peter Gerwinski
% %
% This document is free software: you can redistribute it and/or % This document is free software: you can redistribute it and/or
% modify it either under the terms of the Creative Commons % modify it either under the terms of the Creative Commons
......
% hp-musterloesung-20181112.pdf - Solutions to the Exercises on Low-Level Programming / Applied Computer Sciences % hp-musterloesung-20181126.pdf - Solutions to the Exercises on Low-Level Programming / Applied Computer Sciences
% Copyright (C) 2013, 2015, 2016, 2017, 2018 Peter Gerwinski % Copyright (C) 2013, 2015, 2016, 2017, 2018, 2019 Peter Gerwinski
% %
% This document is free software: you can redistribute it and/or % This document is free software: you can redistribute it and/or
% modify it either under the terms of the Creative Commons % modify it either under the terms of the Creative Commons
......
File added
% hp-musterloesung-20181203.pdf - Solutions to the Exercises on Low-Level Programming / Applied Computer Sciences
% Copyright (C) 2013, 2015, 2016, 2017, 2018, 2019 Peter Gerwinski
%
% This document is free software: you can redistribute it and/or
% modify it either under the terms of the Creative Commons
% Attribution-ShareAlike 3.0 License, or under the terms of the
% GNU General Public License as published by the Free Software
% Foundation, either version 3 of the License, or (at your option)
% any later version.
%
% This document is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
% GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with this document. If not, see <http://www.gnu.org/licenses/>.
%
% You should have received a copy of the Creative Commons
% Attribution-ShareAlike 3.0 Unported License along with this
% document. If not, see <http://creativecommons.org/licenses/>.
% README: XBM-Grafik, LED-Blinkmuster
\documentclass[a4paper]{article}
\usepackage{pgscript}
\usepackage{sfmath}
\begin{document}
\section*{Hardwarenahe Programmierung\\
Musterlösung zu den Übungsaufgaben -- 3.\ Dezember 2018}
\exercise{XBM-Grafik}
Bei einer XBM-Grafikdatei handelt es sich
um ein als C-Quelltext abgespeichertes Array,
das die Bildinformationen enthält:
\begin{itemize}\itemsep0pt
\item Jedes Bit entspricht einem Pixel.
\item Nullen stehen für Weiß, Einsen für Schwarz.
\item Das Bit mit Zahlenwert 1 steht für einen Bildpunkt ganz links im Byte,
das Bit mit Zahlenwert 128 für einen Bildpunkt ganz rechts.
(Diese Bit-Reihenfolge heißt \newterm{LSB first}.)
\item Jede Zeile des Bildes wird auf ganze Bytes aufgefüllt.
\item Breite und Höhe des Bildes sind als Konstantendefinitionen
(\lstinline{#define}) in der Datei enthalten.
\end{itemize}
Sie können eine XBM-Datei sowohl mit einem Texteditor
als auch mit vielen Grafikprogrammen öffnen und bearbeiten.
Beispiel (\gitfile{hp}{20181203}{aufgabe-1.xbm}):\hfill
\makebox(0,0)[tr]{\framebox{\includegraphics[scale=3]{aufgabe-1.png}}}
\begin{lstlisting}
#define aufgabe_1_width 14
#define aufgabe_1_height 14
static unsigned char aufgabe_1_bits[] = {
0x00, 0x00, 0xf0, 0x03, 0x08, 0x04, 0x04, 0x08, 0x02, 0x10, 0x32, 0x13,
0x22, 0x12, 0x02, 0x10, 0x0a, 0x14, 0x12, 0x12, 0xe4, 0x09, 0x08, 0x04,
0xf0, 0x03, 0x00, 0x00 };
\end{lstlisting}
Ein C-Programm, das eine XBM-Grafik nutzen will,
kann die \file{.xbm}-Datei mit \lstinline{#include "..."} direkt einbinden.
Schreiben Sie ein Programm, das die XBM-Datei als ASCII-Grafik ausgibt, z.\,B.:
\begin{lstlisting}[style=terminal,lineskip=-4pt]
******
* *
* *
* *
* ** ** *
* * * *
* *
* * * *
* * * *
* **** *
* *
******
¡ ¿
\end{lstlisting}
\points{8}
(Hinweis für die Klausur: Abgabe auf Datenträger ist erlaubt und erwünscht,
aber nicht zwingend.)
\solution
Siehe die Datei \gitfile{hp}{20181203}{loesung-1.c}.
Der Ausdruck \lstinline{(aufgabe_3_width + 7) / 8}
ist die auf ganze Bytes aufgerundete Breits des Bildes in Bytes.
Ohne das "`\lstinline{+ 7}"' in der Klammer würde stets abgerundet;
die Breite von Bildern, deren Breite kein Vielfaches von 8 ist,
wäre dann immer um 1 zu klein.
Dadurch daß wir $n - 1$ addieren, bevor wir durch $n$ dividieren,
machen wir aus dem Abrunden ein Aufrunden.
In jedem Durchlauf der äußeren Schleife
wird der Zeiger \lstinline{p} auf den Anfang der Zeile \lstinline{i} des Bildes gesetzt.
Innerhalb der Zeile gehen wir mit einer Bit-Maske \lstinline{mask}
die Bits innerhalb des Bytes \lstinline{*p} von rechts nach links durch (LSB first).
Wenn das Bit aus der Maske links herausgeschoben wird,
setzen wir die Maske auf \lstinline{0x01} zurück
und setzen den Zeiger \lstinline{p} auf das nächste Byte.
(Der Datentyp \lstinline{unsigned char}
ist eine vorzeichenlose ganze Zahl von der Größe einer Speicherzelle
und normalerweise identisch mit \lstinline{uint8_t}.
Da das XBM-Dateiformat den Datentyp \lstinline{unsigned char} verwendet,
geschieht dies auch im Programm \gitfile{hp}{20181203}{loesung-1.c};
ansonsten wäre \lstinline{uint8_t} eine bessere Wahl.)
\exercise{LED-Blinkmuster}
Wir betrachten das folgende Programm für einen ATmega32-Mikro-Controller
(Datei: \gitfile{hp}{20180108}{aufgabe-2.c}).
\begin{minipage}[t]{7cm}
\begin{lstlisting}[gobble=6]
#include <stdint.h>
#include <avr/io.h>
#include <avr/interrupt.h>
uint8_t counter = 1;
uint8_t leds = 0;
ISR (TIMER0_COMP_vect)
{
if (counter == 0)
{
leds = (leds + 1) % 8;
PORTC = leds << 4;
}
counter++;
}
\end{lstlisting}
\end{minipage}\hfill\begin{minipage}[t]{8.5cm}
\begin{lstlisting}[gobble=6]
void init (void)
{
cli ();
TCCR0 = (1 << CS01) | (1 << CS00);
TIMSK = 1 << OCIE0;
sei ();
DDRC = 0x70;
}
int main (void)
{
init ();
while (1)
; /* do nothing */
return 0;
}
\end{lstlisting}
\end{minipage}
An die Bits Nr.\ 4, 5 und 6 des Output-Ports C des Mikro-Controllers sind LEDs angeschlossen.\\
Sobald das Programm läuft, blinken diese in charakteristischer Weise:
\begin{quote}
\newcommand{\tdn}[1]{\raisebox{-2pt}{#1}}
\begin{tabular}{|c|c|c|c|}\hline
\tdn{Phase} & \tdn{LED oben (rot)} & \tdn{LED Mitte (gelb)} & \tdn{LED unten (grün)} \\[2pt]\hline
1 & aus & aus & an \\\hline
2 & aus & an & aus \\\hline
3 & aus & an & an \\\hline
4 & an & aus & aus \\\hline
5 & an & aus & an \\\hline
6 & an & an & aus \\\hline
7 & an & an & an \\\hline
8 & aus & aus & aus \\\hline
\end{tabular}
\end{quote}
% 8000000 / 64 = 125000
% 8000000 / 64 / 256 = 488.28125
% 8000000 / 64 / 256 / 256 = 1.9073486328125
Jede Phase dauert etwas länger als eine halbe Sekunde.
Nach 8 Phasen wiederholt sich das Schema.
Erklären Sie das Verhalten des Programms anhand des Quelltextes:
\vspace{-\medskipamount}
\begin{itemize}\itemsep0pt
\item[(a)]
Wieso macht das Programm überhaupt etwas,
wenn doch das Hauptprogramm nach dem Initialisieren lediglich eine Endlosschleife ausführt,
in der \emph{nichts} passiert?
\points{1}
\item[(b)]
Wieso wird die Zeile \lstinline|PORTC = leds << 4;| überhaupt aufgerufen,
wenn dies doch nur unter der Bedingung \lstinline|counter == 0| passiert,
wobei die Variable \lstinline|counter| auf 1 initialisiert,
fortwährend erhöht und nirgendwo zurückgesetzt wird?
\points{2}
\item[(c)]
Wie kommt das oben beschriebene Blinkmuster zustande?
\points{2}
\item[(d)]
Wieso dauert eine Phase ungefähr eine halbe Sekunde?
\points{2}
\item[(e)]
Was bedeutet "`\lstinline|ISR (TIMER0_COMP_vect)|"'?
\points{1}
\end{itemize}
Hinweis:
\vspace{-\medskipamount}
\begin{itemize}\itemsep0pt
\item
Die Funktion \lstinline|init()| sorgt dafür, daß der Timer-Interrupt Nr.\ 0 des Mikro-Controllers
etwa 488mal pro Sekunde aufgerufen wird.
Außerdem % schaltet sie eventuell an Port B angeschlossene weitere LEDs aus und
initialisiert sie die benötigten Bits an Port C als Output-Ports.
Sie selbst brauchen die Funktion \lstinline|init()| nicht weiter zu erklären.
% \item
% Während der Übung können Sie sich das Verhalten des Programms
% an einem RP6-Roboter vorführen lassen.
\end{itemize}
\clearpage
\solution
\begin{itemize}\itemsep0pt
\item[(a)]
\textbf{Wieso macht das Programm überhaupt etwas,
wenn doch das Hauptprogramm nach dem Initialisieren lediglich eine Endlosschleife ausführt,
in der \emph{nichts} passiert?}
Das Blinken wird durch einen Interrupt-Handler implementiert.
Dieser wird nicht durch das Hauptprogramm,
sondern durch ein Hardware-Ereignis (hier: Uhr) aufgerufen.
\item[(b)]
\textbf{Wieso wird die Zeile \lstinline|PORTC = leds << 4;| überhaupt aufgerufen,
wenn dies doch nur unter der Bedingung \lstinline|counter == 0| passiert,
wobei die Variable \lstinline|counter| auf 1 initialisiert,
fortwährend erhöht und nirgendwo zurückgesetzt wird?}
Die vorzeichenlose 8-Bit-Variable \lstinline{counter} kann nur
Werte von 0 bis 255 annehmen; bei einem weiteren
INkrementieren springt sie wieder auf 0 (Überlauf),
und die \lstinline{if}-Bedingung ist erfüllt.
\item[(c)]
\textbf{Wie kommt das oben beschriebene Blinkmuster zustande?}
In jedem Aufruf des Interrupt-Handlers wird die Variable
\lstinline{leds} um 1 erhöht und anschließend modulo 8 genommen.
Sie durchläuft daher immer wieder die Zahlen von 0 bis 7.
Durch die Schiebeoperation \lstinline{leds << 4} werden die 3 Bits
der Variablen \lstinline{leds} an diejenigen Stellen im Byte
geschoben, an denen die LEDs an den Mikro-Controller
angeschlossen sind (Bits 4, 5 und 6).
Entsprechend durchläuft das Blinkmuster immer wieder
die Binärdarstellungen der Zahlen von 0 bis 7
(genauer: von 1 bis 7 und danach 0).
\item[(d)]
\textbf{Wieso dauert eine Phase ungefähr eine halbe Sekunde?}
Der Interrupt-Handler wird gemäß Hinweis 488mal pro Sekunde aufgerufen.
Bei jedem 256sten Aufruf ändert sich das LED-Muster.
Eine Phase dauert somit $\frac{256}{488} \approx 0.52$ Sekunden.
\item[(e)]
\textbf{Was bedeutet "`\lstinline|ISR (TIMER0_COMP_vect)|"'?}
Deklaration eines Interrupt-Handlers für den Timer-Interrupt Nr.\ 0
\end{itemize}
\end{document}
File added
% hp-musterloesung-20181210.pdf - Solutions to the Exercises on Low-Level Programming / Applied Computer Sciences
% Copyright (C) 2013, 2015, 2016, 2017, 2018, 2019 Peter Gerwinski
%
% This document is free software: you can redistribute it and/or
% modify it either under the terms of the Creative Commons
% Attribution-ShareAlike 3.0 License, or under the terms of the
% GNU General Public License as published by the Free Software
% Foundation, either version 3 of the License, or (at your option)
% any later version.
%
% This document is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
% GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with this document. If not, see <http://www.gnu.org/licenses/>.
%
% You should have received a copy of the Creative Commons
% Attribution-ShareAlike 3.0 Unported License along with this
% document. If not, see <http://creativecommons.org/licenses/>.
% README: Trickprogrammierung, Thermometer-Baustein an I²C-Bus
\documentclass[a4paper]{article}
\usepackage{pgscript}
\usepackage{gensymb}
\usepackage{sfmath}
\newcommand{\ItwoC}{I\raisebox{0.5ex}{\footnotesize 2}C}
\newcommand{\ITWOC}{I\raisebox{0.5ex}{\normalsize 2}C}
\begin{document}
\section*{Hardwarenahe Programmierung\\
Musterlösung zu den Übungsaufgaben -- 10.\ Dezember 2018}
\exercise{Trickprogrammierung}
Wir betrachten das folgende Programm (Datei: \gitfile{hp}{20181210}{aufgabe-1.c}):
\begin{lstlisting}
#include <stdio.h>
#include <stdint.h>
int main (void)
{
uint64_t x = 4262939000843297096;
char *s = &x;
printf ("%s\n", s);
return 0;
}
\end{lstlisting}
Das Programm wird compiliert und auf einem 64-Bit-Little-Endian-Computer ausgeführt:
\begin{lstlisting}[style=terminal]
$ ¡gcc -Wall -O aufgabe-1.c -o aufgabe-1¿
aufgabe-1.c: In function `main':
aufgabe-1.c:7:13: warning: initialization from incompatible pointer type [...]
$ ¡./aufgabe-1¿
Hallo
\end{lstlisting}
\begin{itemize}
\item[(a)]
Erklären Sie die Warnung beim Compilieren. \points{2}
\item[(b)]
Erklären Sie die Ausgabe des Programms. \points{5}
\item[(c)]
Wie würde die Ausgabe des Programms auf einem 64-Bit-Big-Endian-Computer lauten? \points{3}
\end{itemize}
Hinweis: Modifizieren Sie das Programm
und lassen Sie sich Speicherinhalte ausgeben.
\solution
\begin{itemize}
\item[(a)]
\textbf{Erklären Sie die Warnung beim Compilieren.}
Zeile 7 des Programms enthält eine Zuweisung von \lstinline{&x}
an die Variable \lstinline{s}.
Der Ausdruck \lstinline{&x} steht für die Speicheradresse der Variablen \lstinline{x},
ist also ein Zeiger auf \lstinline{x},
also ein Zeiger auf eine \lstinline{uint64_t}.
Die Variable \lstinline{s} hingegen ist ein Zeiger auf \lstinline{char},
also ein Zeiger auf eine viel kleinere Zahl,
also ein anderer Zeigertyp.
\item[(b)]
\textbf{Erklären Sie die Ausgabe des Programms.}
Die 64-Bit-Zahl (\lstinline{uint64_t}) \lstinline{x}
belegt 8 Speicherzellen (Bytes) von jeweils 8 Bit.
Um herauszufinden, was diese enthalten,
lassen wir uns \lstinline{x} als Hexadezimalzahl ausgeben,
z.\,B.\ mittels \lstinline{printf ("%lx\n", x)}
(auf 32-Bit-Rechnern: \lstinline{"%llx\n"})
oder mittels \lstinline{printf ("%" PRIx64 "\n", x)}
(erfordert \lstinline{#include <inttypes.h>}
-- siehe die Datei \gitfile{hp}{20181210}{loesung-1-1.c}).
Das Ergebnis lautet:
\begin{lstlisting}[style=terminal,gobble=8]
3b29006f6c6c6148
\end{lstlisting}
Auf einzelne Bytes verteilt:
\begin{lstlisting}[style=terminal,gobble=8]
3b 29 00 6f 6c 6c 61 48
\end{lstlisting}
Auf einem Little-Endian-Rechner
ist die Reihenfolge der Bytes in den Speicherzellen genau umgekehrt:
\begin{lstlisting}[style=terminal,gobble=8]
48 61 6c 6c 6f 00 29 3b
\end{lstlisting}
Wenn wir uns diese Bytes als Zeichen ausgeben lassen
(\lstinline{printf()} mit \lstinline{%c} -- siehe die Datei \gitfile{hp}{20181210}{loesung-1-2.c}),
erhalten wir:
\begin{lstlisting}[style=terminal,gobble=8]
H a l l o ) ;
\end{lstlisting}
Das Zeichen hinter "`Hallo"' ist ein Null-Symbol (Zahlenwert 0)
und wird von \lstinline{printf ("%s")} als Ende des Strings erkannt.
Damit ist die Ausgabe \lstinline[style=terminal]{Hallo}
des Programms erklärt.
\goodbreak
\item[(c)]
\textbf{Wie würde die Ausgabe des Programms auf einem 64-Bit-Big-Endian-Computer lauten?}
Auf einem Big-Endian-Computer (egal, wieviele Bits die Prozessorregister haben)
ist die Reihenfolge der Bytes in den Speicherzellen genau umgekehrt
wie auf einem Little-Endian-Computer, hier also:
\begin{lstlisting}[style=terminal,gobble=8]
3b 29 00 6f 6c 6c 61 48
\end{lstlisting}
\lstinline{printf ("%s")} gibt in diesem Fall die Hexadezimalzahlen
\lstinline[style=terminal]{3b} und \lstinline[style=terminal]{29}
als Zeichen aus. Danach steht das String-Ende-Symbol mit Zahlenwert 0,
und die Ausgabe bricht ab.
Da, wie oben ermittelt, die Hexadezimalzahl \lstinline[style=terminal]{3b}
für das Zeichen \lstinline[style=terminal]{;}
und \lstinline[style=terminal]{29}
für das Zeichen \lstinline[style=terminal]{)} steht,
lautet somit die Ausgabe:
\begin{lstlisting}[style=terminal,gobble=8]
;)
\end{lstlisting}
Um die Aufgabe zu lösen, können Sie übrigens auch
auf einem Little-Endian-Computer (Standard-Notebook)
einen Big-Endian-Computer simulieren,
indem Sie die Reihenfolge der Bytes in der Zahl \lstinline{x} umdrehen
-- siehe die Datei \gitfile{hp}{20181210}{loesung-1-3.c}.
\end{itemize}
\exercise{Thermometer-Baustein an \ITWOC-Bus}
Eine Firma stellt einen elektronischen Thermometer-Baustein her,
den man über die serielle Schnittstelle (RS-232) an einen PC anschließen kann,
um die Temperatur auszulesen.
Nun wird eine Variante des Thermo"-meter-Bausteins entwickelt,
die die Temperatur zusätzlich über einen \ItwoC-Bus bereitstellt.
Um das neue Thermometer zu testen, wird es in ein Gefäß mit heißem Wasser gelegt,
das langsam auf Zimmertemperatur abkühlt.
Alle 10 Minuten liest ein Programm, das auf dem PC läuft,
die gemessene Temperatur über beide Schnittstellen aus
und erzeugt daraus die folgende Tabelle:
\begin{center}
\renewcommand{\arraystretch}{1.2}
\begin{tabular}{|c|c|c|}\hline
Zeit /\,min. & Temperatur per RS-232 /\,\degree C & Temperatur per \ItwoC\ /\,\degree C \\\hline\hline
\phantom{0}0 & 94 & 122 \\\hline
10 & 47 & 244 \\\hline
20 & 30 & 120 \\\hline
30 & 24 & \phantom{0}24 \\\hline
40 & 21 & 168 \\\hline
\end{tabular}
\end{center}
\begin{itemize}
\item[(a)]
Aus dem Vergleich der Meßdaten läßt sich
auf einen Fehler bei der \ItwoC-Übertragung schließen.\\
Um welchen Fehler handelt es sich,
und wie ergibt sich dies aus den Meßdaten?
\points{5}
\item[(b)]
Schreiben Sie eine C-Funktion \lstinline{uint8_t repair (uint8_t data)},
die eine über den \ItwoC-Bus empfangene fehlerhafte Temperatur \lstinline{data} korrigiert.
\points{5}
\end{itemize}
\solution
\begin{itemize}
\item[(a)]
\textbf{Aus dem Vergleich der Meßdaten läßt sich
auf einen Fehler bei der \ItwoC-Übertragung schließen.
Um welchen Fehler handelt es sich,
und wie ergibt sich dies aus den Meßdaten?}
Sowohl RS-232 als auch \ItwoC\ übertragen die Daten Bit für Bit.
Für die Fehlersuche ist es daher sinnvoll,
die Meßwerte als Binärzahlen zu betrachten:
\begin{center}
\renewcommand{\arraystretch}{1.2}
\begin{tabular}{|c|c|c|}\hline
Zeit /\,min. & Temperatur per RS-232 /\,\degree C & Temperatur per \ItwoC\ /\,\degree C \\\hline\hline
\phantom{0}0 & 94$_{10}$ = 01011110$_2$ & 122$_{10}$ = 01111010$_2$ \\\hline
10 & 47$_{10}$ = 00101111$_2$ & 244$_{10}$ = 11110100$_2$ \\\hline
20 & 30$_{10}$ = 00011110$_2$ & 120$_{10}$ = 01111000$_2$ \\\hline
30 & 24$_{10}$ = 00011000$_2$ & \phantom{0}24$_{10}$ = 00011000$_2$ \\\hline
40 & 21$_{10}$ = 00010101$_2$ & 168$_{10}$ = 10101000$_2$ \\\hline
\end{tabular}
\end{center}
Man erkennt, daß die Reihenfolge der Bits in den (fehlerhaften) \ItwoC-Meßwerten
genau die umgekehrte Reihenfolge der Bits in den (korrekten) RS-232-Mewßwerten ist.
Der Übertragungsfehler besteht also darin,
daß die Bits in der falschen Reihenfolge übertragen wurden.
Dies paßt gut damit zusammen,
daß die Bit-Reihenfolge von \ItwoC\ \emph{MSB First}, die von RS-232 hingegen \emph{LSB First\/} ist.
Offenbar haben die Entwickler der \ItwoC-Schnittstelle dies übersehen
und die \ItwoC-Daten ebenfalls \emph{LSB First\/} übertragen.
\goodbreak
\item[(b)]
\textbf{Schreiben Sie eine C-Funktion \lstinline{uint8_t repair (uint8_t data)},
die eine über den \ItwoC-Bus empfangene fehlerhafte Temperatur \lstinline{data} korrigiert.}
Die Aufgabe der Funktion besteht darin,
eine 8-Bit-Zahl \lstinline{data} entgegenzunehmen,
die Reihenfolge der 8 Bits genau umzudrehen
und das Ergebnis mittels \lstinline{return} zurückzugeben.
Zu diesem Zweck gehen wir die 8 Bits in einer Schleife durch
-- siehe die Datei \gitfile{hp}{20181210}{loesung-2.c}.
Wir lassen eine Lese-Maske \lstinline{mask_data} von rechts nach links
und gleichzeitig eine Schreib-Maske \lstinline{mask_result}
von links nach rechts wandern.
Immer wenn die Lese-Maske in \lstinline{data} eine 1 findet,
schreibt die Schreib-Maske diese in die Ergebnisvariable \lstinline{result}.
Da \lstinline{result} auf 0 initialisiert wurde,
brauchen wir Nullen nicht hineinzuschreiben.
Ansonsten wäre dies mit \lstinline{result &= ~mask_result} möglich.
Um die Schleife bis 8 zählen zu lassen,
könnte man eine weitere Zähler-Variable von 0 bis 7 zählen lassen,
z.\,B.\ \lstinline{for (int i = 0; i < 8; i++)}.
Dies ist jedoch nicht nötig, wenn man beachtet,
daß die Masken den Wert 0 annehmen,
sobald das Bit aus der 8-Bit-Variablen herausgeschoben wurde.
In \gitfile{hp}{20181210}{loesung-2.c} wird \lstinline{mask_data} auf 0 geprüft;
genausogut könnte man auch \lstinline{mask_result} prüfen.
Das \lstinline{return result} ist notwendig.
Eine Ausgabe des Ergebnisses per \lstinline{printf()} o.\,ä.\
erfüllt \emph{nicht\/} die Aufgabenstellung.
(In \gitfile{hp}{20181210}{loesung-2.c} erfolgt entsprechend \lstinline{printf()}
nur im Testprogramm \lstinline{main()}.)
\end{itemize}
\end{document}
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
int main (void)
{
uint64_t x = 4262939000843297096;
char *s = &x;
printf ("%lx\n", x);
printf ("%" PRIx64 "\n", x);
printf ("%s\n", s);
return 0;
}
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
int main (void)
{
uint64_t x = 4262939000843297096;
char *s = &x;
printf ("%lx\n", x);
printf ("%" PRIx64 "\n", x);
printf ("%c %c %c %c %c %c %c %c\n",
0x48, 0x61, 0x6c, 0x6c, 0x6f, 0x00, 0x29, 0x3b);
printf ("%s\n", s);
return 0;
}
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
int main (void)
{
uint64_t x = 0x48616c6c6f00293b;
char *s = &x;
printf ("%s\n", s);
return 0;
}
#include <stdio.h>
#include <stdint.h>
uint8_t repair (uint8_t data)
{
uint8_t result = 0;
uint8_t mask_data = 0x01;
uint8_t mask_result = 0x80;
while (mask_data)
{
if (data & mask_data)
result |= mask_result;
mask_data <<= 1;
mask_result >>= 1;
}
return result;
}
int main (void)
{
int data[] = { 122, 244, 120, 24, 168, -1 };
int i = 0;
while (data[i] >= 0)
printf ("%d\n", repair (data[i++]));
return 0;
}
File added
% hp-musterloesung-20181217.pdf - Solutions to the Exercises on Low-Level Programming / Applied Computer Sciences
% Copyright (C) 2013, 2015, 2016, 2017, 2018, 2019 Peter Gerwinski
%
% This document is free software: you can redistribute it and/or
% modify it either under the terms of the Creative Commons
% Attribution-ShareAlike 3.0 License, or under the terms of the
% GNU General Public License as published by the Free Software
% Foundation, either version 3 of the License, or (at your option)
% any later version.
%
% This document is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
% GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with this document. If not, see <http://www.gnu.org/licenses/>.
%
% You should have received a copy of the Creative Commons
% Attribution-ShareAlike 3.0 Unported License along with this
% document. If not, see <http://creativecommons.org/licenses/>.
% README: Fakultät, Lauflicht, Länge von Strings (Neuauflage)
\documentclass[a4paper]{article}
\usepackage{pgscript}
\usepackage{sfmath}
\begin{document}
\section*{Hardwarenahe Programmierung\\
Musterlösung zu den Übungsaufgaben -- 17.\ Dezember 2018}
\exercise{Fakultät}
Die Fakultät $n!$ einer ganzen Zahl $n \ge 0$ ist definiert als:
\begin{eqnarray*}
1 & \mbox{für} & n = 0, \\
n \cdot (n-1)! & \mbox{für} & n > 0.
\end{eqnarray*}
Mit anderen Worten: $n! = 1\cdot2\cdot3\cdot\dots\cdot n$.
Die folgende Funktion \lstinline{fak()} berechnet die Fakultät \emph{rekursiv}
(Datei: \gitfile{hp}{20181217}{aufgabe-1.c}):
\begin{lstlisting}
int fak (int n)
{
if (n <= 0)
return 1;
else
return n * fak (n - 1);
}
\end{lstlisting}
\begin{enumerate}[\quad(a)]
\item
Schreiben Sie eine Funktion, die die Fakultät \emph{iterativ} berechnet,\\
d.\,h.\ mit Hilfe einer Schleife anstelle von Rekursion.
\points{3}
\item
Wie viele Multiplikationen (Landau-Symbol)
erfordern beide Versionen der Fakultätsfunktion\\
in Abhängigkeit von \lstinline$n$?
Begründen Sie Ihre Antwort.
\points{2}
\item
Wieviel Speicherplatz (Landau-Symbol)
erfordern beide Versionen der Fakultätsfunktion\\
in Abhängigkeit von \lstinline$n$?
Begründen Sie Ihre Antwort.
\points{3}
\end{enumerate}
\solution
\begin{itemize}
\item[(a)]
\textbf{Schreiben Sie eine Funktion, die die Fakultät \emph{iterativ} berechnet,\\
d.\,h.\ mit Hilfe einer Schleife anstelle von Rekursion.}
Datei: \gitfile{hp}{20181217}{loesung-1.c}
\begin{lstlisting}[gobble=8]
int fak (int n)
{
int f = 1;
for (int i = 2; i <= n; i++)
f *= i;
return f;
}
\end{lstlisting}
\item[(b)]
\textbf{Wie viele Multiplikationen (Landau-Symbol)
erfordern beide Versionen der Fakultätsfunktion?}
In beiden Fällen werden $n$ Zahlen miteinander multipliziert --
oder $n - 1$, wenn man Multiplikationen mit 1 ausspart.
In jedem Fall hängt die Anzahl der Multiplikationen
linear von $n$ ab; es sind $\mathcal{O}(n)$ Multiplikationen.
Insbesondere arbeiten also beide Versionen gleich schnell.
\item[(c)]
\textbf{Wieviel Speicherplatz (Landau-Symbol)
erfordern beide Versionen der Fakultätsfunktion?}
Die iterative Version der Funktion benötigt 2 Variable vom Typ \lstinline{int},
nämlich \lstinline{n} und \lstinline{f}.
Dies ist eine konstante Zahl;
der Speicherplatzverbrauch ist daher $\mathcal{O}(1)$.
Die rekursive Version der Funktion erzeugt
jedesmal, wenn sie sich selbst aufruft,
eine zusätzliche Variable \lstinline{n}.
Es sind $n + 1$ Aufrufe; die Anzahl der Variablen \lstinline{n}
hängt linear von $n$ ab; der Speicherplatzverbrauch ist also $\mathcal{O}(n)$.
\end{itemize}
\exercise{Lauflicht}
\begin{minipage}[t]{8cm}
An die vier Ports eines ATmega16-Mikrocontrollers sind Leuchtdioden angeschlossen:
\begin{itemize}
\item
von links nach rechts\\
an die Ports A, B, C und D,
\item
von oben nach unten\\
an die Bits Nr.\ 0 bis 7.
\bigskip
\includegraphics[width=3cm]{leds.jpg}
\end{itemize}
\end{minipage}\hfill
\begin{minipage}[t]{7cm}
Wir betrachten das folgende C-Programm (Datei: \gitfile{hp}{20181217}{aufgabe-2.c})
für diesen Mikrocontroller:
\begin{lstlisting}[gobble=6]
#include <avr/io.h>
#include <avr/interrupt.h>
int counter = 0;
ISR (TIMER0_COMP_vect)
{
PORTA = 1 << ((counter++ >> 6) & 7);
}
int main (void)
{
cli ();
TCCR0 = (1 << CS01) | (1 << CS00);
TIMSK = 1 << OCIE0;
sei ();
DDRA = 0xff;
while (1);
return 0;
}
\end{lstlisting}
\end{minipage}
\medskip
Das Programm bewirkt ein periodisches Lauflicht in der linken Spalte von oben nach unten.
Eine Animation davon finden Sie in der Datei \gitfile{hp}{20181217}{aufgabe-2.gif}.
\begin{itemize}
\item[(a)]
Wieso bewirkt das Programm überhaupt etwas, wenn doch das Hauptprogramm
nach dem Initialisieren lediglich eine Endlosschleife ausführt,
in der \emph{nichts\/} passiert? \points 3
\item[(b)]
Erklären Sie, wie die Anweisung
\begin{lstlisting}[gobble=8]
PORTA = 1 << ((counter++ >> 6) & 7);
\end{lstlisting}
\vspace{-\medskipamount}
das LED-Blinkmuster hervorruft. \points 6
Hinweis: Zerlegen Sie die eine lange Anweisung in mehrere kürzere.\\
Wenn nötig, verwenden Sie zusätzliche Variable für Zwischenergebnisse.
\item[(c)]
Was bedeutet "`\lstinline{ISR (TIMER0_COMP_vect)}"'? \points 1
\item[(d)]
Wieso leuchten die Leuchtdioden PB2 und PD1? \points 2
\end{itemize}
\solution
\begin{itemize}
\item[(a)]
\textbf{Wieso bewirkt das Programm überhaupt etwas, wenn doch das Hauptprogramm
nach dem Initialisieren lediglich eine Endlosschleife ausführt,
in der \emph{nichts\/} passiert?}
Die Funktion \lstinline{ISR (TIMER0_COMP_vect)}
ist ein \emph{Interrupt-Handler\/}
(oder \emph{Interrupt Service Routine -- ISR\/})
und wird nicht durch das Hauptprogramm oder andere Funktionen aufgerufen,
sondern durch ein Hardware-Ereignis.
In diesem Fall sorgt die Initialisierung dafür,
daß die Funktion regelmäßig durch eine Uhr (\emph{Timer\/}) aufgerufen wird.
\bigskip
\goodbreak
\item[(b)]
\textbf{Erklären Sie, wie die Anweisung}
\begin{lstlisting}[gobble=8]
PORTA = 1 << ((counter++ >> 6) & 7);
\end{lstlisting}
\vspace{-\medskipamount}
\textbf{das LED-Blinkmuster hervorruft.}
Um die Anweisung zu verstehen, zerlegen wir sie in mehrere kürzere.
Dabei ist es wichtig, die Reihenfolge zu erkennen,
in der die einzelnen Operationen ausgeführt werden.
Zunächst stellen wir fest, daß der Post-Inkrement-Operator \lstinline{++}
erst nach der Anweisung -- also als letztes -- ausgeführt wird.
Die Anweisung ist somit äquivalent zu den folgenden zwei Anweisungen:
\begin{lstlisting}[gobble=8]
PORTA = 1 << ((counter >> 6) & 7);
counter++;
\end{lstlisting}
Von der verbleibenden langen Anweisung wird als erstes
die innerste Klammer ausgeführt:
\begin{lstlisting}[gobble=8]
int temp1 = counter >> 6;
PORTA = 1 << (temp1 & 7);
counter++;
\end{lstlisting}
Das Verschieben um 6 Binärstellen nach rechts
entspricht einer Division durch $2^6 = 64$.
Die Bedeutung der neu eingeführten Variablen ist somit ein Zähler,
der 64mal langsamer zählt als der ursprüngliche:
\begin{lstlisting}[gobble=8]
int slow_counter = counter >> 6;
PORTA = 1 << (slow_counter & 7);
counter++;
\end{lstlisting}
(Ohne diese Verlangsamung wäre das Lauflicht zu schnell,
um es es mit bloßem Auge wahrnehmen zu können.)
Als nächste Operation wird die bitweise Und-Verknüpfung ausgeführt:
\begin{lstlisting}[gobble=8]
int slow_counter = counter >> 6;
int temp2 = slow_counter & 7;
PORTA = 1 << temp2;
counter++;
\end{lstlisting}
Die Binärdarstellung von 7 sind drei Einsen: $7_{10} = 111_2$.
Eine Und-Verknüpfung mit 7 isoliert also die untersten drei Bits
von \lstinline{slow_counter}.
Mit drei Bits lassen sich Zahlen von 0 bis 7 darstellen.
Wenn also \lstinline{slow_counter} immer größer wird,
läuft \lstinline{slow_counter & 7} immer wieder von 0 bis 7:
\begin{lstlisting}[gobble=8]
int slow_counter = counter >> 6;
int slow_counter_0_to_7 = slow_counter & 7;
PORTA = 1 << slow_counter_0_to_7;
counter++;
\end{lstlisting}
(Bemerkung: Eine bitweise Und-Verknüpfung mit 7 ist gleichbedeutend
zur Berechnung des Rests bei Division durch 8:
\lstinline{slow_counter_0_to_7 = slow_counter % 8}.
Auch dadurch wird klar, daß das Ergebnis der Operation
immer wieder die Zahlen von 0 bis 7 durchläuft,
wenn \lstinline{slow_counter} immer größer wird.)
Die letzte Operation \lstinline{1 << slow_counter_0_to_7}
erzeugt eine Bitmaske, in der ein Bit von Position 0 bis Position 7,
also von rechts nach links wandert:
\begin{lstlisting}[style=terminal,gobble=8]
00000001
00000010
00000100
00001000
00010000
00100000
01000000
10000000
\end{lstlisting}
Die zyklische Zuweisung dieser Bitmasken an \lstinline{PORTA}
erzeugt das Lauflicht.
\bigskip
\textbf{Zusammenfassung:}
\begin{itemize}
\item
Das jeweils zum Schluß ausgeführte \lstinline{++}\\
läßt die Variable \lstinline{counter} bei jedem Aufruf um 1 hochzählen.
\item
Durch die Bit-Verschiebung um 6 nach rechts\\
erzeugen wir einen um den Faktor 64 langsameren Zähler.
\item
Die bitweise Und-Verknüpfung mit 7
isoliert die untersten 3 Bit des langsameren Zählers\\
und erzeugt so einen neuen Zähler,
der immer wieder von 0 bis 7 zählt.
\item
Die Linksverschiebung einer 1 um diesen neuen Zähler
erzeugt Bit-Masken mit jeweils einem Bit,
das von rechts nach links läuft.
\item
Die zyklische Zuweisung dieser Bitmasken an \lstinline{PORTA}
erzeugt das Lauflicht.
\end{itemize}
\bigskip
\item[(c)]
\textbf{Was bedeutet "`\lstinline{ISR (TIMER0_COMP_vect)}"'?}
Es bezeichnet einen \emph{Interrupt-Handler\/}
(\emph{Interrupt Service Routine -- ISR\/}) für einen \emph{Timer-Interrupt},
also eine Funktion,
die durch ein von einer Uhr periodisch ausgelöstes Hardware-Ereignis
aufgerufen wird.
\bigskip
\item[(d)]
\textbf{Wieso leuchten die Leuchtdioden PB2 und PD1?}
Der Mikro-Controller enthält kein Betriebssystem.
Auf ihm läuft kein Programm außer dem, das wir auf ihn herunterladen.
Beim Start enthalten alle Ports \textbf{zufällige Werte}.
Wenn wir wünschen, daß eine LED aus ist,
muß unser Programm sie explizit ausschalten.
In diesem Fall ließe sich dies durch die Zeilen
\begin{lstlisting}[gobble=8]
DDRB = 0xff;
DDRC = 0xff;
DDRD = 0xff;
PORTB = 0;
PORTC = 0;
PORTD = 0;
\end{lstlisting}
im Hauptprogramm erreichen.
(Wegen der \lstinline{volatile}-Eigenschaft
der \lstinline{DDR}- und \lstinline{PORT}-Variablen
ist dies übrigens \emph{nicht\/} äquivalent zu den folgenden Zeilen:
\begin{lstlisting}[gobble=8]
DDRB = DDRC = DDRD = 0xff;
PORTB = PORTC = PORTD = 0;
\end{lstlisting}
Für Details siehe:
\url{http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_assign_chain})
\end{itemize}
\exercise{Länge von Strings}
Diese Aufgabe ist eine Neuauflage von Aufgabe 3 der
Übung vom 5.\ November 2017,\\
ergänzt um die Teilaufgaben (f) und (g).
\medskip
Strings werden in der Programmiersprache C durch Zeiger auf \lstinline{char}-Variable realisiert.
Beispiel: \lstinline{char *hello_world = "Hello, world!\n"}
Die Systembibliothek stellt eine Funktion \lstinline{strlen()} zur Ermittlung der Länge von Strings\\
zur Verfügung (\lstinline{#include <string.h>}).
\begin{enumerate}[\quad(a)]
\item
Auf welche Weise ist die Länge eines Strings gekennzeichnet?
\points 1
\item
Wie lang ist die Beispiel-String-Konstante \lstinline{"Hello, world!\n"},
und wieviel Speicherplatz belegt sie?\\
\points 2
\item
Schreiben Sie eine eigene Funktion \lstinline{int strlen (char *s)},
die die Länge eines Strings zurückgibt.\\
\points 3
\end{enumerate}
Wir betrachten nun die folgenden Funktionen (Datei: \gitfile{hp}{20181217}{aufgabe-3.c}):
\begin{center}
\begin{minipage}{8cm}
\begin{lstlisting}[gobble=8]
int fun_1 (char *s)
{
int x = 0;
for (int i = 0; i < strlen (s); i++)
x += s[i];
return x;
}
\end{lstlisting}
\end{minipage}%
\begin{minipage}{6cm}
\vspace*{-1cm}
\begin{lstlisting}[gobble=8]
int fun_2 (char *s)
{
int i = 0, x = 0;
int len = strlen (s);
while (i < len)
x += s[i++];
return x;
}
\end{lstlisting}
\vspace*{-1cm}
\end{minipage}
\end{center}
\begin{enumerate}[\quad(a)]\setcounter{enumi}{3}
\item
Was bewirken die beiden Funktionen?
\points 2
\item
Schreiben Sie eine eigene Funktion,
die dieselbe Aufgabe erledigt wie \lstinline{fun_2()},\\
nur effizienter.
\points 4
\item
Von welcher Ordnung (Landau-Symbol) sind die beiden Funktionen
hinsichtlich der Anzahl ihrer Zugriffe auf die Zeichen im String?
Begründen Sie Ihre Antwort.
Sie dürfen für \lstinline{strlen()} Ihre eigene Version der Funktion voraussetzen.
\points 3
\item
Von welcher Ordnung (Landau-Symbol) ist Ihre effizientere Funktion?\\
Begründen Sie Ihre Antwort.
\points 1
\end{enumerate}
\solution
\begin{itemize}
\item[(a)]
\textbf{Auf welche Weise ist die Länge eines Strings gekennzeichnet?}
Ein String ist ein Array von \lstinline{char}s.
Nach den eigentlichen Zeichen des Strings enthält das Array
\textbf{ein Null-Symbol} (Zeichen mit Zahlenwert 0,
nicht zu verwechseln mit der Ziffer \lstinline{'0'}) als Ende-Markierung.
Die Länge eines Strings ist die Anzahl der Zeichen
\emph{vor\/} diesem Symbol.
\item[(b)]
{\bf Wie lang ist die Beispiel-String-Konstante \lstinline{"Hello, world!\n"},
und wieviel Speicherplatz belegt sie?}
Sie ist 14 Zeichen lang (\lstinline{'\n'} ist nur 1 Zeichen;
das Null-Symbol, das das Ende markiert, zählt hier nicht mit)
und belegt Speicherplatz für 15 Zeichen
(15 Bytes -- einschließlich Null-Symbol / Ende-Markierung).
\item[(c)]
\textbf{Schreiben Sie eine eigene Funktion \lstinline{int strlen (char *s)},
die die Länge eines Strings zurückgibt.}
Siehe die Dateien \gitfile{hp}{20181105}{loesung-3c-1.c} (mit Array-Index)
und \gitfile{hp}{20181105}{loesung-3c-2.c} (mit Zeiger-Arithmetik).
Beide Lösungen sind korrekt und arbeiten gleich schnell.
Die Warnung \lstinline[style=terminal]{conflicting types for built-in function "strlen"}
kann normalerweise ignoriert werden;
auf manchen Systemen (z.\,B.\ MinGW) hat jedoch die eingebaute Funktion \lstinline{strlen()}
beim Linken Vorrang vor der selbstgeschriebenen,
so daß die selbstgeschriebene Funktion nie aufgerufen wird.
In solchen Fällen ist es zulässig, die selbstgeschriebene Funktion
anders zu nennen (z.\,B.\ \lstinline{my_strlen()}).
\item[(d)]
\textbf{Was bewirken die beiden Funktionen?}
Beide addieren die Zahlenwerte der im String enthaltenen Zeichen
und geben die Summe als Funktionsergebnis zurück.
Im Falle des Test-Strings \lstinline{"Hello, world!\n"}
lautet der Rückgabewert 1171 (siehe \gitfile{hp}{20181105}{loesung-3d-1.c} und \gitfile{hp}{20181105}{loesung-3d-2.c}).
\item[(e)]
\textbf{Schreiben Sie eine eigene Funktion,
die dieselbe Aufgabe erledigt wie \lstinline{fun_2()},
nur effizienter.}
Die Funktion wird effizienter,
wenn man auf den Aufruf von \lstinline{strlen()} verzichtet
und stattdessen die Ende-Prüfung in derselben Schleife vornimmt,
in der man auch die Zahlenwerte der Zeichen des Strings aufsummiert.
Die Funktion \lstinline{fun_3()} in der Datei \gitfile{hp}{20181105}{loesung-3e-1.c}
realisiert dies mit einem Array-Index,
Die Funktion \lstinline{fun_4()} in der Datei \gitfile{hp}{20181105}{loesung-3e-2.c}
mit Zeiger-Arithmetik.
Beide Lösungen sind korrekt und arbeiten gleich schnell.
\textbf{Bemerkung:} Die effizientere Version der Funktion
arbeitet doppelt so schnell wie die ursprüngliche,
hat aber ebenfalls die Ordnung $\mathcal{O}(n)$ -- siehe unten.
\item[(f)]
\textbf{Von welcher Ordnung (Landau-Symbol) sind die beiden Funktionen
hinsichtlich der Anzahl ihrer Zugriffe auf die Zeichen im String?
Begründen Sie Ihre Antwort.
Sie dürfen für \lstinline{strlen()} Ihre eigene Version der Funktion voraussetzen.}
Vorüberlegung: \lstinline{strlen()} greift in einer Schleife
auf alle Zeichen des Strings der Länge $n$ zu,
hat also $\mathcal{O}(n)$.
\lstinline{fun_1()} ruft in jedem Schleifendurchlauf
(zum Prüfen der \lstinline{while}-Bedingung) einmal \lstinline{strlen()} auf
und greift anschließend auf ein Zeichen des Strings zu,
hat also $\mathcal{O}\bigl(n\cdot(n+1)\bigr) = \mathcal{O}(n^2)$.
\lstinline{fun_2()} ruft einmalig \lstinline{strlen()} auf
und greift anschließend in einer Schleife auf alle Zeichen des Strings zu,
hat also $\mathcal{O}(n+n) = \mathcal{O}(n)$.
\item[(g)]
\textbf{Von welcher Ordnung (Landau-Symbol) ist Ihre effizientere Funktion?\\
Begründen Sie Ihre Antwort.}
In beiden o.\,a.\ Lösungsvarianten
-- \gitfile{hp}{20181105}{loesung-3e-1.c}
und \gitfile{hp}{20181105}{loesung-3e-2.c} --
arbeitet die Funktion mit einer einzigen Schleife,
die gleichzeitig die Zahlenwerte addiert und das Ende des Strings sucht.
Mit jeweils einer einzigen Schleife
haben beide Funktionen die Ordnung $\mathcal{O}(n)$.
\end{itemize}
\end{document}
#include <stdio.h>
int fak (int n)
{
int f = 1;
for (int i = 2; i <= n; i++)
f *= i;
return f;
}
int main (void)
{
for (int n = 0; n <= 5; n++)
printf ("%d\n", fak (n));
return 0;
}
...@@ -60,6 +60,9 @@ Musterlösungen: ...@@ -60,6 +60,9 @@ Musterlösungen:
* [12.11.2018: Text-Grafik-Bibliothek, Datum-Bibliothek, Kondensator](https://gitlab.cvh-server.de/pgerwinski/hp/raw/master/20181112/hp-musterloesung-20181112.pdf) * [12.11.2018: Text-Grafik-Bibliothek, Datum-Bibliothek, Kondensator](https://gitlab.cvh-server.de/pgerwinski/hp/raw/master/20181112/hp-musterloesung-20181112.pdf)
* [19.11.2018: Arrays mit Zahlen, hüpfender Ball](https://gitlab.cvh-server.de/pgerwinski/hp/raw/master/20181119/hp-musterloesung-20181119.pdf) * [19.11.2018: Arrays mit Zahlen, hüpfender Ball](https://gitlab.cvh-server.de/pgerwinski/hp/raw/master/20181119/hp-musterloesung-20181119.pdf)
* [26.11.2018: Zahlensysteme, Mikrocontroller](https://gitlab.cvh-server.de/pgerwinski/hp/raw/master/20181126/hp-musterloesung-20181126.pdf) * [26.11.2018: Zahlensysteme, Mikrocontroller](https://gitlab.cvh-server.de/pgerwinski/hp/raw/master/20181126/hp-musterloesung-20181126.pdf)
* [03.12.2018: XBM-Grafik, LED-Blinkmuster](https://gitlab.cvh-server.de/pgerwinski/hp/raw/master/20181203/hp-musterloesung-20181203.pdf)
* [10.12.2018: Trickprogrammierung, Thermometer-Baustein an I²C-Bus](https://gitlab.cvh-server.de/pgerwinski/hp/raw/master/20181210/hp-musterloesung-20181203.pdf)
* [17.12.2018: Fakultät, Lauflicht, Länge von Strings (Neuauflage)](https://gitlab.cvh-server.de/pgerwinski/hp/raw/master/20181217/hp-musterloesung-20181217.pdf)
Tafelbilder: Tafelbilder:
------------ ------------
......
No preview for this file type
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment