#2 Abend 3: Alexander Neumann <@koeln.ccc.de> e.v. http://koeln.ccc.de Köln, 3.11.2008
Gliederung 1 Zahlendarstellung n-adische Darstellung natürlicher Zahlen negative Zahlen 2 Logikfunktionen 3 Register 4 Eingänge 5 Infrarot senden 6 TSOP-Effekte 7 Weiterführendes
Jetzt: Vortrag von Jens Ohlig Was macht der CCC eigentlich?
n-adische Darstellung natürlicher Zahlen Zahlendarstellung Normalerweise: Dezimalsystem (Basis: 10) Wichtige andere Systeme: Binär (Basis 2) und Hexadezimal (Basis 16) Notation für binär: Präfix 0b (zb. 0b01) Notation für hexadezimal: Präfix 0x (zb. 0x01) Notation für dezimal: kein Präfix (zb. 10 ) Normalerweise: Weglassen führender Nullen
n-adische Darstellung natürlicher Zahlen b-adische Darstellung natürlicher Zahlen Eine Zahl n lässt sich bezüglich einer Basis b mit r Stellen ausdrücken durch: n = r a i b i = a r b r + a r 1 b r 1 + + a 1 b 1 + a 0 b 0 i=0 (0 a i b 1) Beispiel: 13 zur Basis 2 13 = 1 2 3 + 1 2 2 + 0 2 1 + 1 2 0 = 0b1101
n-adische Darstellung natürlicher Zahlen Spezialfall: Hexadezimalsystem Basis: 16, Darstellung von 10-15 durch a f Beispiel: 13 = 13 16 0 = 0x0d
negative Zahlen Zahlendarstellungen Frage: Wie stellt man negative Zahlen dar? Antwort: Garnicht, man umgeht das Problem durch geschickte Interpretation von Zahlen. Wir gehen in den folgenden Beispielen immer von 8 Bit Zahlen aus (also zwischen 0 und 255)
negative Zahlen Einführung Zweierkomplement Es soll mit dem Mikrocontroller 16-7 berechnet werden. Vorgehen: Umwandlung der Zahl -7 ins Zweierkomplement, anschliessend rechnen mit einer positiven Zahl. Vorgehen: 1 Berechnung der Binärdarstellung der Zahl 2 Berechnung des (Einer)komplements durch Invertieren (alle Bits kippen ) 3 Addition von 1 4 Fertig
negative Zahlen Beispiel Zweierkomplement 1 7 = 0b00000111, 2 also ist das (Einer)komplement (bezogen auf 8 Bits): 0b11111000 = 248. 3 1 addiert: 249 = 0b11111001 Was bringt uns das? Jetzt können wir mit positiven Zahlen rechnen! (Und uns den Überlauf zunutze machen)
negative Zahlen Beispiel Zweierkomplement #2 Wir wollten berechnen: 16-7, also 16+ (-7) Berechnen nun: 16+249 = 265 = 9 (Überlauf bei 256) = 16-7 (Juhu!)
negative Zahlen Problem: Interpretation Problem: Wie ist 249 zu interpretiern? Als 249? Als (-7)? Antwort: Das kommt drauf an TM Gut: Im Zweierkomplement kann (wenn klar ist, das eine Zahl auch einen negativen Wert darstellen kann) am höchstwertigsten Bit abgelesen werden, ob die Zahl negativ ist. Beispiel: 249 = 0b11111001 negative Zahl (Für die Rückumwandlung benutzt man einfach den gleichen Weg, Einerkomplement, 1 Addieren, fertig)
negative Zahlen Einschub: Variablentypen in C Wichtig: Dem Compiler muss mitgeteilt werden, ob eine Variable ein signed oder unsigned Typ ist. Beispiel: signed int foobar = -4; Problem: Die Größe von int ist abhängig von der Architektur, auf i386 4 Byte (32 Bit), auf AVR 2 Byte (16 Bit). Deshalb wenn möglich, immer fixe Typen benutzen: ohne Vorzeichen: uint8_t, uint16_t, uint32_t,... mit Vorzeichen: int8_t, int16_t,... (Nicht vergessen: #include <stdint.h>)
Wertetabellen Wichtige (bitweise) Logikfunktionen: AND OR XOR NOT Wertetabelle AND 0 0 0 1 0 0 0 1 0 1 1 1 Wertetabelle OR 0 0 0 1 0 1 0 1 1 1 1 1 Wertetabelle XOR 0 0 0 1 0 1 0 1 1 1 1 0
Anwendung Aber wozu braucht man diese Funktionen? Antwort: Um in C einzelne Bits zu manipulieren! In Assembler: sbi PORTC, 0 setzt ein Bit cbi PORTC, 0 löscht ein Bit Wie geht das C-Style?
Anwendung Logikfunktionen, Bitweise In C werden Logikfunktionen bitweise angewandt: AND (&) 23 0b00010111 42 0b00101010 0b00000010 2 OR ( ) 23 0b00010111 42 0b00101010 0b00111111 63 XOR ( ) 23 0b00010111 42 0b00101010 0b00111101 61 Auch wichtig: Negation in C: NEG ( ) 23 0b00010111 23 0b11101000
Anwendung C-Style Bitmanipulation: Bits setzen Bit 0 in einer Variable (Register werden genau wie Variablen behandelt) foobar vom Typ uint8_t soll gesetzt werden. OR-Verknüpfung des alten Inhalts von foobar mit einem Wert, wo nur Bit 0 gesetzt ist: foobar = foobar 0b00000001; Abkürzung: foobar = 0b00000001;
Anwendung C-Style Bitmanipulation: Bits löschen Bit 3 in foobar soll gelöscht werden. AND-Verknüpfung des alten Inhalts von foobar mit einem mit einem Wert, wo nur Bit 3 Null ist: foobar = foobar & 0b11110111; Abkürzung: foobar &= 0b11110111; Besser: Negation benutzen! foobar &= 0b00001000;
_BV()-Makro Problem: Konstanten im Sourcecode (0b110101, 0x42) sind unübersichtlich. Lösung: Konstanten vom Präprozessor via Bit-Shift bauen lassen: (1«3) bedeutet: Nehme die Zahl 1, und schiebe diese (binär) um 3 Stellen nach links (Auffüllen mit 0). Beispiel: Konstante, wo nur Bit 3 gesetzt ist: (1«3) == 8 == 0b1000 Weitere Bits lassen sich durch logisches OR hinzufügen: (1«2) (1«3) == 0b1100 == 12 Beispiel: Konfiguriere PC2, PC5 als Ausgang: DDRC = (1«2) (1«5); /* == 0b100100 */
_BV()-Makro Das geht auch noch eleganter, die avr-libc stellt Konstanten für die Pins zur Verfügung: #define PC5 5 #define PC2 2 Diese lassen sich schön verwenden: DDRC = (1«PC2) (1«PC5); Wenn man doch mit Zahlen arbeitet, gibts einen unschönen Fall: 0b10 == (1«1) Deshalb definiert die avr-libc ein Makro (in avr/sfr_defs.h): #define _BV(bit) (1 «(bit)) Damit lässt sich das ganze noch schöner schreiben: DDRC = _BV(PC2) _BV(PC5); Die Bedeutung ist sofort klar: PC2 und PC5 Ausgang
Bitmanipulation #2 C-Style Bits setzen #2 Vorher: Bit 5 in einer Variable foobar vom Typ uint8_t soll gesetzt werden. OR-Verknüpfung des alten Inhalts von foobar mit einem Wert, wo nur Bit 5 gesetzt ist: foobar = 0b00100000; Jetzt schicker: foobar = _BV(5);
Bitmanipulation #2 C-Style Bits löschen #2 Vorher: Bit 3 in foobar soll gelöscht werden. AND-Verknüpfung des alten Inhalts von foobar mit einem mit einem Wert, wo nur Bit 3 Null ist: foobar &= 0b11110111; Mit Negation: foobar &= 0b00001000; Jetzt schicker: foobar &= _BV(3);
Anwendungsbeispiel Setze Prescaler von Timer1 auf 1024
Anwendungsbeispiel Also: In TCCR1B müssen die Bits CS12 und CS10 gesetzt werden: Sehr hässlich: TCCR1B = 5; oder TCCR1B = 0x05; Unschön: TCCR1B = 0b101; oder TCCR1B = (1«0) (1«2); Schick (und portabel!): TCCR1B = _BV(CS12) _BV(CS10);
Beschreibung Register Speicherstelle innerhalb der CPU Flüchtig (Initial immer 0) Speichert ein Datenwort (Hier: 8 Bit, also 1 Byte) Schneller, direkter Zugriff Entweder Arbeits- oder Konfigurationsregister Beispiel Arbeitsregister: r0-r31 (in C direkt nicht zugänglich) Beispiel Konfigurationsregister: DDRC
Beschreibung Register: Beispiel Der C-Code: DDRC = _BV(PC0) _BV(PC2) _BV(PC4); /* 0b1101 == 13 */ Könnte in Assembler so aussehen: ldi r16, 13 mov DDRC, r16 Bedeutung: Lade die Binärdarstellung der Zahl 13 in die Bits des Konfigurationsregisters DDRC.
Beispiel Konfigurationsregister Konfigurationsregister: DDRC Register Dezimalwert Bits 7 6 5 4 3 2 1 0 r16 0 0 0 0 0 0 0 0 0 DDRC 0 0 0 0 0 0 0 0 0 ldi r16, 13 (Binär: 1101) Register Dezimalwert Bits 7 6 5 4 3 2 1 0 r16 13 0 0 0 0 1 1 0 1 DDRC 0 0 0 0 0 0 0 0 0
Beispiel Konfigurationsregister Konfigurationsregister: DDRC Register Dezimalwert Bits 7 6 5 4 3 2 1 0 r16 13 0 0 0 0 1 1 0 1 DDRC 0 0 0 0 0 0 0 0 0 ldi r16, 13 (Binär: 1101) mov DDRC, r16 Register Dezimalwert Bits 7 6 5 4 3 2 1 0 r16 13 0 0 0 0 1 1 0 1 DDRC 13 0 0 0 0 1 1 0 1
Beispiel Konfigurationsregister Konfigurationsregister: DDRC Register Dezimalwert Bits 7 6 5 4 3 2 1 0 DDRC 13 0 0 0 0 1 1 0 1 Was bedeutet dies? DDRC konfiguriert die Datenrichtung der Pins PC0-PC5 (PC6 und PC7 sind im DIP-Gehäuse nicht rausgeführt) Bit ist 1 Pin ist Ausgang, Bit ist 0 Pin ist Eingang. Also: PC0, PC2, PC3 Ausgänge, PC1, PC4, PC5 Eingänge.
Konfiguration von Eingängen Pins als Eingang benutzen Konfiguriere PC0 als Eingang, um Taster 1 einzulesen. (Beispielcode ab jetzt in C) Bit im DDR-Register muss 0 sein, also: DDRC = 0b00000000; Besserer Stil (der die anderen Bits in Ruhe lässt): DDRC &= _BV(PC0); Schaltplan sagt: Taster verbinden Pins mit GND Problem: Wenn der Taster offen (=nicht gedrückt) ist, liegt kein definierter Pegel (=Spannung) am Pin an Messwerte können sowohl 0 als auch 1 sein. unschön! Lösung: Benutze einen Pull-Up Widerstand, der den Pegel auf 5V (also 1) legt, wenn Taster offen.
Konfiguration von Eingängen Praktisch: AVRs haben Pull-Up Widerstände eingebaut (man muss sie nur aktivieren.) Das geht über das PORT-Register (wenn das entsprechende Bit im DDR-Register 0 ist): PORTC = _BV(PC0); Messergebnis kann im entsprechenden Bit im PIN-Register ausgelesen werden: if (PINC & _BV(PC0)) foo(); else bar();
Infrarot senden Schaltplan sagt: Infrarot-Diode hängt (über Jumper IRTX) am Pin PD5. Deshalb zuerst: Konfiguriere PD5 als Ausgang. PD5/OC0B ist ein spezieller Pin, denn auf diesem kann Timer 0 ein Rechtesignal mit einstellbarer Frequenz und High-Pegel-Zeit generieren (PWM). Wir benötigen: 38kHz, bei 12.5% Duty-Cycle Modus: Phase-Correct PWM, TOP = OCR0A, Clear OC0B on Match, Prescaler 8 Also: TCCR0A: Bits WGM00 und COM0B1 TCCR0B: Bits WGM02 und CS01
Datenblatt sagt, PWM-Frequenz in diesem Modus ist: f pwm = f cpu 2 Prescaler OCR0A Deshalb: 33 ist ein guter Wert für OCR0A (f pwm = 38787Hz) Duty-Cycle von 25%: OCR0B = OCR0A / 4; /* 8 */
Funktionen: IR Senden 1 / set up timer 0 to generate a c a r r i e r using pwm at freq on pin OC0B (PD5) / 2 static void ir_enable ( uint8_t freq ) { 3 / t i m e r 0: phase c o r r e c t pwm mode, c l e a r OC0B on match, p r e s c a l e r 8 / 4 TCCR0A = _BV (WGM00 ) _BV (COM0B1 ) ; 5 TCCR0B = _BV (CS01 ) _BV (WGM02 ) ; 6 7 / set frequency / 8 OCR0A = freq ; 9 10 / set duty cycle to 50% / 11 OCR0B = freq / 2 ; 12 } 13 14 / d i s a b l e t i m e r 0 and pwm generation / 15 static void ir_disable ( void ) { 16 TCCR0A = 0; 17 TCCR0B = 0; 18 }
Oszilloskop, 12.5% Duty-Cycle
Oszilloskop, 50% Duty-Cycle
Der IR-Empfänger TSOP filtert das empfangene Signal, das führt zu: Einschalt- und Ausschaltverzögerung
Jetzt: Pizza Basteln, IR-Analyzer von unter http://w8n.koeln.ccc.de/media/slides/u23/ 2008-2/ir-analyzer.tar.gz, später im rumpus-software-repository unter http://www.lochraster.org/rumpus Aufgabe: Baut einen IR-Jammer, möglichst energiesparsam