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

Vorbereitung 21.1.2021

parent 8c79e4d6
No related branches found
No related tags found
No related merge requests found
Showing
with 1813 additions and 0 deletions
%.elf: %.c
avr-gcc -Wall -Os -mmcu=atmega328p $< -o $@
%.hex: %.elf
avr-objcopy -O ihex $< $@
download:
./download.sh
#include <stdio.h>
#include <stdint.h>
int main (void)
{
uint64_t x = 4262939000843297096;
char *s = &x;
printf ("%s\n", s);
return 0;
}
#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++;
}
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;
}
#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdint.h>
#define F_CPU 16000000l
#include <util/delay.h>
volatile uint8_t key_pressed = 0;
ISR (INT0_vect) /* PD2 */
{
key_pressed = 1;
}
int main (void)
{
cli ();
EICRA = 1 << ISC00 | 1 << ISC01; /* INT0: steigende Flanke */
EIMSK = 1 << INT0; /* INT0 einschalten */
sei ();
DDRD = 0xfb; /* binär: 1111 1011 */
PORTD = 0x40; /* binär: 0100 0000 */
while (1)
{
while (!key_pressed)
; /* just wait */
PORTD ^= 0x40;
key_pressed = 0;
}
return 0;
}
#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdint.h>
#define F_CPU 16000000l
#include <util/delay.h>
volatile uint8_t key_pressed = 0;
ISR (INT0_vect) /* PD2 */
{
key_pressed = 1;
}
int main (void)
{
cli ();
EICRA = 1 << ISC00 | 1 << ISC01; /* INT0: steigende Flanke */
EIMSK = 1 << INT0; /* INT0 einschalten */
sei ();
DDRD = 0xfb; /* binär: 1111 1011 */
PORTD = 0x40; /* binär: 0100 0000 */
while (1)
{
while (!key_pressed)
; /* just wait */
_delay_ms (1);
PORTD ^= 0x40;
key_pressed = 0;
}
return 0;
}
#include <avr/io.h>
#define F_CPU 16000000
#include <util/delay.h>
int main (void)
{
DDRD = 0x01;
PORTD = 0x01;
while (1)
{
while ((PIND & 0x02) == 0)
; /* just wait */
PORTD ^= 0x01;
_delay_ms (200);
}
return 0;
}
#include <avr/io.h>
#include <avr/interrupt.h>
ISR (TIMER0_COMPB_vect)
{
PORTD ^= 0x40;
}
int main (void)
{
cli ();
TCCR0B = (1 << CS01) | (1 << CS00); /* Takt durch 64 dividieren */
TIMSK0 = 1 << OCIE0B; /* Interrupt einschalten */
sei ();
DDRD = 0xfd; /* binär: 1111 1101 */
PORTD = 0x40; /* binär: 0100 0000 */
while (1);
return 0;
}
#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdint.h>
ISR (TIMER0_COMPB_vect)
{
static uint8_t counter = 0;
if (counter++ == 0)
PORTD ^= 0x40;
}
int main (void)
{
cli ();
TCCR0B = (1 << CS01) | (1 << CS00); /* Takt durch 64 dividieren */
TIMSK0 = 1 << OCIE0B; /* Interrupt einschalten */
sei ();
DDRD = 0xfd; /* binär: 1111 1101 */
PORTD = 0x40; /* binär: 0100 0000 */
while (1);
return 0;
}
#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdint.h>
ISR (INT0_vect) /* PD2 */
{
PORTD ^= 0x40;
}
int main (void)
{
cli ();
EICRA = 1 << ISC00 | 1 << ISC01; /* INT0: steigende Flanke */
EIMSK = 1 << INT0; /* INT0 einschalten */
sei ();
DDRD = 0xfb; /* binär: 1111 1011 */
PORTD = 0x40; /* binär: 0100 0000 */
while (1);
return 0;
}
#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdint.h>
ISR (INT0_vect) /* PD2 */
{
PORTD ^= 0x40;
}
int main (void)
{
cli ();
EICRA = 1 << ISC00 | 1 << ISC01; /* INT0: steigende Flanke */
EIMSK = 1 << INT0; /* INT0 einschalten */
sei ();
DDRD = 0xff; /* binär: 1111 1111 */
PORTD = 0x40; /* binär: 0100 0000 */
while (1);
return 0;
}
#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdint.h>
#define F_CPU 16000000l
#include <util/delay.h>
uint8_t key_pressed = 0;
ISR (INT0_vect) /* PD2 */
{
key_pressed = 1;
}
int main (void)
{
cli ();
EICRA = 1 << ISC00 | 1 << ISC01; /* INT0: steigende Flanke */
EIMSK = 1 << INT0; /* INT0 einschalten */
sei ();
DDRD = 0xfb; /* binär: 1111 1011 */
PORTD = 0x40; /* binär: 0100 0000 */
while (1)
{
while (!key_pressed)
; /* just wait */
PORTD ^= 0x40;
key_pressed = 0;
}
return 0;
}
port=$(ls -rt /dev/ttyACM* | tail -1)
echo avrdude -P $port -c arduino -p m328p -U flash:w:$(ls -rt *.hex | tail -1)
avrdude -P $port -c arduino -p m328p -U flash:w:$(ls -rt *.hex | tail -1) 2>/dev/null
File added
% hp-20210121.pdf - Lecture Slides on Low-Level Programming
% Copyright (C) 2012, 2013, 2015, 2016, 2017, 2018, 2019, 2020, 2021 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: Hardwarenahe Programmierung: Interrupts, volatile-Variable, Byte-Reihenfolge – Endianness, Binärdarstellung negativer Zahlen, Speicherausrichtung – Alignment
\documentclass[10pt,t]{beamer}
\usepackage{pgslides}
\usepackage{pdftricks}
\usepackage{tikz}
\begin{psinputs}
\usepackage[utf8]{inputenc}
\usepackage[german]{babel}
\usepackage[T1]{fontenc}
\usepackage{helvet}
\renewcommand*\familydefault{\sfdefault}
\usepackage{pstricks,pst-grad}
\end{psinputs}
\title{Hardwarenahe Programmierung}
\author{Prof.\ Dr.\ rer.\ nat.\ Peter Gerwinski}
\date{21.\ Januar 2021}
\begin{document}
\maketitleframe
\nosectionnonumber{\inserttitle}
\begin{frame}
\shownosectionnonumber
\begin{itemize}
\item[\textbf{1}] \textbf{Einführung}
\hfill\makebox(0,0)[br]{\raisebox{2.25ex}{\url{https://gitlab.cvh-server.de/pgerwinski/hp}}}
\item[\textbf{2}] \textbf{Einführung in C}
\item[\textbf{3}] \textbf{Bibliotheken}
\item[\textbf{4}] \textbf{Algorithmen}
\item[\textbf{5}] \textbf{Hardwarenahe Programmierung}
\begin{itemize}
\color{medgreen}
\item[5.1] Bit-Operationen
\item[5.2] I/O-Ports
\color{red}
\item[5.3] Interrupts
\item[5.4] volatile-Variable
\item[5.6] Byte-Reihenfolge -- Endianness
\item[5.7] Binärdarstellung negativer Zahlen
\item[5.8] Speicherausrichtung -- Alignment
\end{itemize}
\vspace*{-\smallskipamount}
\item[\textbf{\dots}]
\end{itemize}
\end{frame}
\setcounter{section}{4}
\section{Hardwarenahe Programmierung}
\subsection{Bit-Operationen}
\subsubsection{Zahlensysteme}
\begin{frame}[fragile]
\showsection
\vspace*{-\smallskipamount}
\showsubsection
\vspace*{-\medskipamount}
\showsubsubsection
\begin{tabular}{rlrl}
Basis & Name & Beispiel & Anwendung \\[\smallskipamount]
2 & Binärsystem & 1\,0000\,0011 & Bit-Operationen \\
8 & Oktalsystem & \lstinline,0403, & Dateizugriffsrechte (Unix) \\
10 & Dezimalsystem & \lstinline,259, & Alltag \\
16 & Hexadezimalsystem & \lstinline,0x103, & Bit-Operationen \\
256 & (keiner gebräuchlich) & 0.0.1.3 & IP-Adressen (IPv4)
\end{tabular}
\bigskip
\begin{itemize}
\item
Computer rechnen im Binärsystem.
\item
Für viele Anwendungen (z.\,B.\ I/O-Ports, Grafik, \dots) ist es notwendig,\\
Bits in Zahlen einzeln ansprechen zu können.
\end{itemize}
\end{frame}
\begin{frame}[fragile]
\showsubsubsection
\begin{tabular}{rlrlrc}
\qquad 000 & \bf 0 \hspace*{1.5cm} & 0000 & \bf 0 & \quad 1000 & \bf 8\\
001 & \bf 1 & 0001 & \bf 1 & 1001 & \bf 9\\
010 & \bf 2 & 0010 & \bf 2 & 1010 & \bf A\\
011 & \bf 3 & 0011 & \bf 3 & 1011 & \bf B\\[\smallskipamount]
100 & \bf 4 & 0100 & \bf 4 & 1100 & \bf C\\
101 & \bf 5 & 0101 & \bf 5 & 1101 & \bf D\\
110 & \bf 6 & 0110 & \bf 6 & 1110 & \bf E\\
111 & \bf 7 & 0111 & \bf 7 & 1111 & \bf F\\
\end{tabular}
\medskip
\begin{itemize}
\item
Oktal- und Hexadezimalzahlen lassen sich ziffernweise\\
in Binär-Zahlen umrechnen.
\item
Hexadezimalzahlen sind eine Kurzschreibweise für Binärzahlen,\\
gruppiert zu jeweils 4 Bits.
\item
Oktalzahlen sind eine Kurzschreibweise für Binärzahlen,\\
gruppiert zu jeweils 3 Bits.
\item
Trotz Taschenrechner u.\,ä.\ lohnt es sich,\\
die o.\,a.\ Umrechnungstabelle \textbf{auswendig} zu kennen.
\end{itemize}
\end{frame}
\subsubsection{Bit-Operationen in C}
\begin{frame}[fragile]
\showsubsubsection
\begin{tabular}{lll}
C-Operator & Verknüpfung & Anwendung \\[\smallskipamount]
\lstinline,&, & Und & Bits gezielt löschen \\
\lstinline,|, & Oder & Bits gezielt setzen \\
\lstinline,^, & Exklusiv-Oder & Bits gezielt invertieren \\
\lstinline,~, & Nicht & Alle Bits invertieren \\[\smallskipamount]
\lstinline,<<, & Verschiebung nach links & Maske generieren \\
\lstinline,>>, & Verschiebung nach rechts & Bits isolieren
\end{tabular}
\bigskip
Numerierung der Bits: von rechts ab 0
\medskip
\begin{tabular}{ll}
Bit Nr.\ 3 auf 1 setzen: &
\lstinline,a |= 1 << 3;, \\
Bit Nr.\ 4 auf 0 setzen: &
\lstinline,a &= ~(1 << 4);, \\
Bit Nr.\ 0 invertieren: &
\lstinline,a ^= 1 << 0;,
\end{tabular}
\smallskip
~~Abfrage, ob Bit Nr.\ 1 gesetzt ist:\quad
\lstinline{if (a & (1 << 1))}
\end{frame}
\begin{frame}[fragile]
\showsubsubsection
C-Datentypen für Bit-Operationen:
\smallskip\par
\lstinline{#include <stdint.h>}
\medskip\par
\begin{tabular}{lllll}
& 8 Bit & 16 Bit & 32 Bit & 64 Bit \\
mit Vorzeichen & \lstinline,int8_t,
& \lstinline,int16_t,
& \lstinline,int32_t,
& \lstinline,int64_t, \\
ohne Vorzeichen & \lstinline,uint8_t,
& \lstinline,uint16_t,
& \lstinline,uint32_t,
& \lstinline,uint64_t,
\end{tabular}
\bigskip
\bigskip
Ausgabe:
\smallskip\par
\begin{lstlisting}
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
...
uint64_t x = 42;
printf ("Die Antwort lautet: %" PRIu64 "\n", x);
\end{lstlisting}
\iffalse
\bigskip
Aufgabe: Schreiben Sie C-Funktionen, die ein "`Array von Bits"' realisieren, z.\,B.
\smallskip
\begin{tabular}[t]{ll}
\lstinline|void set_bit (int i);| & Bei Index $i$ auf 1 setzen \\
\lstinline|void clear_bit (int i);| & Bei Index $i$ auf 0 setzen \\
\lstinline|int get_bit (int i);| & Bei Index $i$ lesen
\end{tabular}
\medskip
Hinweise:
\begin{itemize}
\item
Die Größe des Bit-"`Arrays"' (z.\,B.\ 1000) dürfen Sie als \emph{vorher bekannt\/} voraussetzen.
\item
Sie benötigen ein Array, z.\,B.\ von \lstinline|char|- oder \lstinline|int|-Variablen.
\item
Sie benötigen eine Division (\lstinline|/|) sowie den Divisionsrest (Modulo: \lstinline|%|).
\end{itemize}
\fi
\end{frame}
\subsection{I/O-Ports}
\begin{frame}[fragile]
% \showsection
\showsubsection
\vspace*{-1.5\medskipamount}
{\large\textbf{\color{structure}5.3\quad Interrupts}}
\bigskip
Kommunikation mit externen Geräten
\bigskip
\begin{center}
\includegraphics{io-ports-and-interrupts.pdf}
\end{center}
\end{frame}
\begin{frame}[fragile]
\showsubsection
In Output-Port schreiben = Aktoren ansteuern
Beispiel: LED
\medskip
\begin{lstlisting}
#include <avr/io.h>
...
DDRC = 0x70;
PORTC = 0x40;
\end{lstlisting}
\begin{picture}(0,0)
\put(3,0.67){\begin{minipage}{3cm}
\color{red}%
binär: 0111\,0000\\
binär: 0100\,0000
\end{minipage}}
\put(10,0.67){\makebox(0,0)[r]{\color{red}Herstellerspezifisch!}}
\end{picture}
\bigskip
\lstinline{DDR} = Data Direction Register\\
Bit = 1 für Output-Port\\
Bit = 0 für Input-Port
\bigskip
\emph{Details: siehe Datenblatt und Schaltplan}
\end{frame}
\begin{frame}[fragile]
\showsubsection
Aus Input-Port lesen = Sensoren abfragen
Beispiel: Taster
\medskip
\begin{lstlisting}
#include <avr/io.h>
...
DDRC = 0xfd;
while ((PINC & 0x02) == 0)
; /* just wait */
\end{lstlisting}
\begin{picture}(0,0)(-1.5,-0.42)
\put(3,0.67){\begin{minipage}{3cm}
\color{red}%
binär: 1111\,1101\\
binär: 0000\,0010
\end{minipage}}
\put(10,0.67){\makebox(0,0)[r]{\color{red}Herstellerspezifisch!}}
\end{picture}
\bigskip
\lstinline{DDR} = Data Direction Register\\
Bit = 1 für Output-Port\\
Bit = 0 für Input-Port
\bigskip
\emph{Details: siehe Datenblatt und Schaltplan}
\bigskip
Praktikumsaufgabe: Druckknopfampel
\end{frame}
\subsection{Interrupts}
\begin{frame}[fragile]
\showsubsection
Externes Gerät ruft (per Stromsignal) Unterprogramm auf
Zeiger hinterlegen: "`Interrupt-Vektor"'
Beispiel: eingebaute Uhr\hfill
\makebox(0,0)[tr]{%
\only<1->{\begin{minipage}[t]{4.7cm}
\vspace*{-0.3cm}%
statt Zählschleife (\lstinline{_delay_ms}):\\
Hauptprogramm kann\\
andere Dinge tun
\end{minipage}}%
}
\medskip
\begin{lstlisting}
#include <avr/interrupt.h>
...
ISR (TIMER0B_COMP_vect)
{
PORTD ^= 0x40;
}
\end{lstlisting}
\begin{picture}(0,0)
\color{red}
\put(1.9,3.1){\makebox(0,0)[tr]{\tikz{\draw[-latex](0,0)--(-1.4,-1.0);}}}
\put(2.0,3.2){\makebox(0,0)[l]{"`Dies ist ein Interrupt-Handler."'}}
\put(2.3,2.6){\makebox(0,0)[tr]{\tikz{\draw[-latex](0,0)--(-0.6,-0.55);}}}
\put(2.4,2.6){\makebox(0,0)[l]{Interrupt-Vektor darauf zeigen lassen}}
\end{picture}
Initialisierung über spezielle Ports:
\lstinline{TCCR0B}, \lstinline{TIMSK0}
\bigskip
\emph{Details: siehe Datenblatt und Schaltplan}
\vspace*{-2.5cm}\hfill
{\color{red}Herstellerspezifisch!}%
\hspace*{1cm}
\end{frame}
\begin{frame}[fragile]
\showsubsection
Externes Gerät ruft (per Stromsignal) Unterprogramm auf
Zeiger hinterlegen: "`Interrupt-Vektor"'
Beispiel: Taster\hfill
\makebox(0,0)[tr]{%
\begin{minipage}[t]{4.7cm}
\vspace*{-0.3cm}%
statt \newterm{Busy Waiting\/}:\\
Hauptprogramm kann\\
andere Dinge tun
\end{minipage}}
\medskip
\begin{lstlisting}
#include <avr/interrupt.h>
...
ISR (INT0_vect)
{
PORTD ^= 0x40;
}
\end{lstlisting}
\medskip
Initialisierung über spezielle Ports:
\lstinline{EICRA}, \lstinline{EIMSK}
\bigskip
\emph{Details: siehe Datenblatt und Schaltplan}
\vspace*{-2.5cm}\hfill
{\color{red}Herstellerspezifisch!}%
\hspace*{1cm}
\end{frame}
\subsection{volatile-Variable}
\begin{frame}[fragile]
\showsubsection
Externes Gerät ruft (per Stromsignal) Unterprogramm auf
Zeiger hinterlegen: "`Interrupt-Vektor"'
Beispiel: Taster
\vspace*{-2.5pt}
\begin{minipage}[t]{5cm}
\begin{onlyenv}<1>
\begin{lstlisting}[gobble=8]
¡#include <avr/interrupt.h>
...
uint8_t key_pressed = 0;
ISR (INT0_vect)
{
key_pressed = 1;
}¿
\end{lstlisting}
\end{onlyenv}
\begin{onlyenv}<2>
\begin{lstlisting}[gobble=8]
¡#include <avr/interrupt.h>
...
volatile uint8_t key_pressed = 0;
ISR (INT0_vect)
{
key_pressed = 1;
}¿
\end{lstlisting}
\end{onlyenv}
\end{minipage}\hfill
\begin{minipage}[t]{6cm}
\begin{lstlisting}[gobble=6]
¡int main (void)
{
...
while (1)
{
while (!key_pressed)
; /* just wait */
PORTD ^= 0x40;
key_pressed = 0;
}
return 0;
}¿
\end{lstlisting}
\end{minipage}
\pause
\begin{picture}(0,0)
\color{red}
\put(10.3,4.0){\makebox(0,0)[b]{\begin{minipage}{6cm}
\begin{center}
\textbf{volatile}:\\
Speicherzugriff\\
nicht wegoptimieren
\end{center}
\end{minipage}}}
\put(10.3,3.95){\makebox(0,0)[tr]{\tikz{\draw[-latex](0,0)--(-0.5,-0.9);}}}
\end{picture}
\end{frame}
\begin{frame}[fragile]
\showsubsection
Was ist eigentlich \lstinline{PORTD}?
\bigskip
\pause
\lstinline[style=cmd]{avr-gcc -Wall -Os -mmcu=atmega328p blink-3.c -E}
\bigskip
\pause
\lstinline{PORTD = 0x01;}\\
\textarrow\quad
\lstinline[style=terminal]{(*(volatile uint8_t *)((0x0B) + 0x20)) = 0x01;}\\
\pause
\begin{picture}(0,2)(0,-1.7)
\color{red}
\put(5.75,0.3){$\underbrace{\rule{2.95cm}{0pt}}_{\mbox{Zahl: \lstinline|0x2B|}}$}
\pause
\put(1.55,0.3){$\underbrace{\rule{4.0cm}{0pt}}_{\mbox{\shortstack[t]{Umwandlung in Zeiger\\
auf \lstinline|volatile uint8_t|}}}$}
\pause
\put(1.32,-1){\makebox(0,0)[b]{\tikz{\draw[-latex](0,0)--(0,1.3)}}}
\put(1.12,-1.1){\makebox(0,0)[tl]{Dereferenzierung des Zeigers}}
\end{picture}
\pause
\textarrow\quad
\lstinline|volatile uint8_t|-Variable an Speicheradresse \lstinline|0x2B|
\pause
\bigskip
\bigskip
\textarrow\quad
\lstinline|PORTA = PORTB = PORTC = PORTD = 0| ist eine schlechte Idee.
\end{frame}
\subsection{Byte-Reihenfolge -- Endianness}
\subsubsection{Konzept}
\begin{frame}[fragile]
\showsubsection
\showsubsubsection
Eine Zahl geht über mehrere Speicherzellen.\\
Beispiel: 16-Bit-Zahl in 2 8-Bit-Speicherzellen
\smallskip
Welche Bits liegen wo?
\pause
\bigskip
$1027 = 1024 + 2 + 1 = 0000\,0100\,0000\,0011_2 = 0403_{16}$
\pause
\bigskip
Speicherzellen:
\medskip
\begin{tabular}{|c|c|l}\cline{1-2}
\raisebox{-0.25ex}{04} & \raisebox{-0.25ex}{03} & \strut Big-Endian "`großes Ende zuerst"' \\\cline{1-2}
\multicolumn{2}{c}{} & \pause für Menschen leichter lesbar \pause \\
\multicolumn{3}{c}{} \\[-5pt]\cline{1-2}
\raisebox{-0.25ex}{03} & \raisebox{-0.25ex}{04} & \strut Little-Endian "`kleines Ende zuerst"' \\\cline{1-2}
\multicolumn{2}{c}{} & \pause bei Additionen effizienter
\end{tabular}
\pause
\medskip
\textarrow\ Geschmackssache
\pause\\
\quad\textbf{\dots\ außer bei Datenaustausch!}
% \pause
% \bigskip
%
% Aber: nicht verwechseln! \qquad $0304_{16} = 772$
\end{frame}
\begin{frame}
\showsubsection
\showsubsubsection
Eine Zahl geht über mehrere Speicherzellen.\\
Beispiel: 16-Bit-Zahl in 2 8-Bit-Speicherzellen
\smallskip
Welche Bits liegen wo?
\medskip
\textarrow\ Geschmackssache\\
\textbf{\dots\ außer bei Datenaustausch!}
\begin{itemize}
\item
Dateiformate
\item
Datenübertragung
\end{itemize}
\end{frame}
\subsubsection{Dateiformate}
\begin{frame}
\showsubsection
\showsubsubsection
Audio-Formate: Reihenfolge der Bytes in 16- und 32-Bit-Zahlen
\begin{itemize}
\item
RIFF-WAVE-Dateien (\file{.wav}): Little-Endian
\item
Au-Dateien (\file{.au}): Big-Endian
\pause
\item
ältere AIFF-Dateien (\file{.aiff}): Big-Endian
\item
neuere AIFF-Dateien (\file{.aiff}): Little-Endian
\end{itemize}
\pause
\bigskip
Grafik-Formate: Reihenfolge der Bits in den Bytes
\begin{itemize}
\item
PBM-Dateien: Big-Endian\only<4->{, MSB first}
\item
XBM-Dateien: Little-Endian\only<4->{, LSB first}
\end{itemize}
\only<4->{MSB/LSB = most/least significant bit}
\end{frame}
\subsubsection{Datenübertragung}
\begin{frame}
\showsubsection
\showsubsubsection
\begin{itemize}
\item
RS-232 (serielle Schnittstelle): LSB first
\item
I$^2$C: MSB first
\item
USB: beides
\pause
\medskip
\item
Ethernet: LSB first
\item
TCP/IP (Internet): Big-Endian
\end{itemize}
\end{frame}
\subsection{Binärdarstellung negativer Zahlen}
\begin{frame}[fragile]
\showsubsection
Speicher ist begrenzt!\\
\textarrow\ feste Anzahl von Bits
\medskip
8-Bit-Zahlen ohne Vorzeichen: \lstinline{uint8_t}\\
\textarrow\ Zahlenwerte von \lstinline{0x00} bis \lstinline{0xff} = 0 bis 255\\
\pause
\textarrow\ 255 + 1 = 0
\pause
\medskip
8-Bit-Zahlen mit Vorzeichen: \lstinline{int8_t}\\
\lstinline{0xff} = 255 ist die "`natürliche"' Schreibweise für $-1$.\\
\pause
\textarrow\ Zweierkomplement
\pause
\medskip
Oberstes Bit = 1: negativ\\
Oberstes Bit = 0: positiv\\
\textarrow\ 127 + 1 = $-128$
\end{frame}
\begin{frame}[fragile]
\showsubsection
Speicher ist begrenzt!\\
\textarrow\ feste Anzahl von Bits
\medskip
16-Bit-Zahlen ohne Vorzeichen:
\lstinline{uint16_t}\hfill\lstinline{uint8_t}\\
\textarrow\ Zahlenwerte von \lstinline{0x0000} bis \lstinline{0xffff}
= 0 bis 65535\hfill 0 bis 255\\
\textarrow\ 65535 + 1 = 0\hfill 255 + 1 = 0
\medskip
16-Bit-Zahlen mit Vorzeichen:
\lstinline{int16_t}\hfill\lstinline{int8_t}\\
\lstinline{0xffff} = 66535 ist die "`natürliche"' Schreibweise für $-1$.\hfill
\lstinline{0xff} = 255 = $-1$\\
\textarrow\ Zweierkomplement
\medskip
Oberstes Bit = 1: negativ\\
Oberstes Bit = 0: positiv\\
\textarrow\ 32767 + 1 = $-32768$
\bigskip
Literatur: \url{http://xkcd.com/571/}
\end{frame}
\begin{frame}[fragile]
\showsubsection
Frage: \emph{Für welche Zahl steht der Speicherinhalt\,
\raisebox{2pt}{%
\tabcolsep0.25em
\begin{tabular}{|c|c|}\hline
\rule{0pt}{11pt}a3 & 90 \\\hline
\end{tabular}}
(hexadezimal)?}
\pause
\smallskip
Antwort: \emph{Das kommt darauf an.} ;--)
\pause
\medskip
Little-Endian:
\smallskip
\begin{tabular}{lrl}
als \lstinline,int8_t,: & $-93$ & (nur erstes Byte)\\
als \lstinline,uint8_t,: & $163$ & (nur erstes Byte)\\
als \lstinline,int16_t,: & $-28509$\\
als \lstinline,uint16_t,: & $37027$\\
\lstinline,int32_t, oder größer: & $37027$
& (zusätzliche Bytes mit Nullen aufgefüllt)
\end{tabular}
\pause
\medskip
Big-Endian:
\smallskip
\begin{tabular}{lrl}
als \lstinline,int8_t,: & $-93$ & (nur erstes Byte)\\
als \lstinline,uint8_t,: & $163$ & (nur erstes Byte)\\
als \lstinline,int16_t,: & $-23664$\\
als \lstinline,uint16_t,: & $41872$\\ als \lstinline,int32_t,: & $-1550843904$ & (zusätzliche Bytes\\
als \lstinline,uint32_t,: & $2744123392$ & mit Nullen aufgefüllt)\\
als \lstinline,int64_t,: & $-6660823848880963584$\\
als \lstinline,uint64_t,: & $11785920224828588032$\\
\end{tabular}
\vspace*{-1cm}
\end{frame}
\subsection{Speicherausrichtung -- Alignment}
\begin{frame}[fragile]
\showsubsection
\begin{lstlisting}
#include <stdint.h>
uint8_t a;
uint16_t b;
uint8_t c;
\end{lstlisting}
\pause
\bigskip
Speicheradresse durch 2 teilbar -- "`16-Bit-Alignment"'
\begin{itemize}
\item
2-Byte-Operation: effizienter
\pause
\item
\dots\ oder sogar nur dann erlaubt
\pause
\arrowitem
Compiler optimiert Speicherausrichtung
\end{itemize}
\medskip
\pause
\begin{minipage}{3cm}
\begin{lstlisting}[gobble=6]
¡uint8_t a;
uint8_t dummy;
uint16_t b;
uint8_t c;¿
\end{lstlisting}
\end{minipage}
\pause
\begin{minipage}{3cm}
\begin{lstlisting}[gobble=6]
¡uint8_t a;
uint8_t c;
uint16_t b;¿
\end{lstlisting}
\end{minipage}
\pause
\vspace{-1.75cm}
\strut\hfill
\begin{minipage}{6.5cm}
Fazit:
\begin{itemize}
\item
\textbf{Adressen von Variablen\\
sind systemabhängig}
\item
Bei Definition von Datenformaten\\
Alignment beachten \textarrow\ effizienter
\end{itemize}
\end{minipage}
\end{frame}
\nosectionnonumber{\inserttitle}
\begin{frame}
\shownosectionnonumber
\begin{itemize}
\item[\textbf{1}] \textbf{Einführung}
\hfill\makebox(0,0)[br]{\raisebox{2.25ex}{\url{https://gitlab.cvh-server.de/pgerwinski/hp}}}
\item[\textbf{2}] \textbf{Einführung in C}
\item[\textbf{3}] \textbf{Bibliotheken}
\item[\textbf{4}] \textbf{Algorithmen}
\begin{itemize}
\item[4.1] Differentialgleichungen
\item[4.2] Rekursion
\item[4.3] Aufwandsabschätzungen
\end{itemize}
\item[\textbf{5}] \textbf{Hardwarenahe Programmierung}
\begin{itemize}
\item[5.1] Bit-Operationen
\item[5.2] I/O-Ports
\color{medgreen}
\item[5.3] Interrupts
\item[5.4] volatile-Variable
\item[5.6] Byte-Reihenfolge -- Endianness
\item[5.7] Binärdarstellung negativer Zahlen
\item[5.8] Speicherausrichtung -- Alignment
\end{itemize}
\item[\textbf{\dots}]
\end{itemize}
\end{frame}
\end{document}
File added
% hp-musterloesung-20210121.pdf - Solutions to the Exercises on Low-Level Programming
% Copyright (C) 2013, 2015, 2016, 2017, 2018, 2019, 2020, 2021 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, LED-Blinkmuster
\documentclass[a4paper]{article}
\usepackage{pgscript}
\usepackage{gensymb}
\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 -- 21.\ Januar 2021}
\exercise{Trickprogrammierung}
Wir betrachten das folgende Programm (Datei: \gitfile{hp}{2020ws/20210121}{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}{2020ws/20210121}{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}{2020ws/20210121}{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}{2020ws/20210121}{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}{2020ws/20210121}{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}{2020ws/20210121}{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}{2020ws/20210121}{loesung-2.c} erfolgt entsprechend \lstinline{printf()}
nur im Testprogramm \lstinline{main()}.)
\end{itemize}
\exercise{LED-Blinkmuster}
Wir betrachten das folgende Programm für einen ATmega32-Mikro-Controller
(Datei: \gitfile{hp}{2020ws/20210121}{aufgabe-3.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}
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}
\goodbreak
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 initialisiert sie die benötigten Bits an Port C als Output-Ports.
Sie selbst brauchen die Funktion \lstinline|init()| nicht weiter zu erklären.
\end{itemize}
\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-uebung-20210121.pdf - Exercises on Low-Level Programming
% Copyright (C) 2013, 2015, 2016, 2017, 2018, 2019, 2020, 2021 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, LED-Blinkmuster
\documentclass[a4paper]{article}
\usepackage{pgscript}
\usepackage{gensymb}
\newcommand{\ItwoC}{I\raisebox{0.5ex}{\footnotesize 2}C}
\newcommand{\ITWOC}{I\raisebox{0.5ex}{\normalsize 2}C}
\begin{document}
% \thispagestyle{empty}
\section*{Hardwarenahe Programmierung\\
Übungsaufgaben -- 21.\ Januar 2021}
Diese Übung enthält Punkteangaben wie in einer Klausur.
Um zu "`bestehen"', müssen Sie innerhalb von 80 Minuten
unter Verwendung ausschließlich zugelassener Hilfsmittel
14 Punkte (von insgesamt \totalpoints) erreichen.
\exercise{Trickprogrammierung}
Wir betrachten das folgende Programm (Datei: \gitfile{hp}{2020ws/20210121}{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.
\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}
\exercise{LED-Blinkmuster}
Wir betrachten das folgende Programm für einen ATmega32-Mikro-Controller
(Datei: \gitfile{hp}{2020ws/20210121}{aufgabe-3.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}
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 initialisiert sie die benötigten Bits an Port C als Output-Ports.
Sie selbst brauchen die Funktion \lstinline|init()| nicht weiter zu erklären.
\end{itemize}
\begin{flushright}
\textit{Viel Erfolg!}
\end{flushright}
\makeatletter
\immediate\write\@mainaux{\string\gdef\string\totalpoints{\arabic{points}}}
\makeatother
\end{document}
../common/io-ports-and-interrupts.pdf
\ No newline at end of file
#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;
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment