Dies ist eine alte Version des Dokuments!
2. Sound und Timer
schnelles Takten für Anfänger
Ziele
Nach dieser Lektion sollten Sie:
- wissen, wie man im Atmel Studio eine Puls-Signal ausgibt
Video
Übung
- I. Vorarbeiten
-
- Laden Sie folgende Datei herunter:
- II. Analyse des fertigen Programms
-
- Initialisieren des Programms
- Laden Sie
2_sound.hex
als firmware auf den 328 Chip
- Betrachtung der neuen Komponenten
- In der Simulation sind nun neben der LED weitere Komponenten zu sehen, die im Folgenden erklärt werden sollen
- ein Display Hd44780
- zwei Frequenzmesser, mit der Anzeige 0Hz
- ein Lautsprecher mit Schalter
- ein Oszilloskop
- Starten Sie die Simulation
- Wenn Sie die Lautsprecher des PCs aktiv haben, werden Sie ein Knacken (und ggf. Brummen) vernehmen. Dies rührt von der unvollständigen Simulation her. Wenn Sie den den Schalter vor dem Lautsprecher schließen, so sollten Sie - zusätzlich zum Knacken - einen aufsteigenden und abfallenden Ton hören. Es ist möglich, dass dieser nach wenigen Sekunden aufhört, auch das ist eine Eigenschaft der unvollständigen Simulation.
- Am Frequenzmesser ist dennoch zu sehen, dass ein Signal mit Frequenzen zwischen $375...1600Hz$ ausgegeben wird.
- Im Gegensatz dazu scheint das Oszilloskopbild still zu stehen und keine variierende Frequenz anzuzeigen. Um dies zu beheben, sollte am Oszilloskop der Haken bei Aut bzw. Auto entfernt werden. Nun ist eine Einstellung der x- und y-Werte über die Drehregler möglich.
- Die LED zeigt nun an, ob es sich um einen Aufsteigenden oder abfallenden Ton handelt
- Das Display zeigt zusätzlich an, welches Experiment geladen ist
- Das Programm zu diesem Hexfile soll nun erstellt werden
- II. Eingabe in Atmel Studio
-
- öffnen Sie Atmel Studio
- legen Sie ein neues „GCC C Executable Project“ mit dem Namen
2_sound
für einen ATmega328 an
- Eingabe und Kompilieren des Code
- Ersetzen Sie den vorhandenen Code, durch den rechts stehenden Code
- Kompilieren Sie den Code durch
Build
»Build solution
- Im unteren Teil des Fensters sollte nun die Ausgabe des Kompilers sichtbar werden. Diese sollte
========== Build: 1 succeeded or up-to-date, 0 failed, 0 skipped ==========
lauten
- Auswählen der hex-Datei
- im Atmel Studio finden Sie rechts im Fenster den „Solution Explorer“
- gehen Sie dort im Solution Explorer zu
Solution
»Einfuehrung_v01
»Output Files
- klicken Sie mit rechter Maustaste auf
Einfuehrung_v01.hex
und wählen Sie Pfad und Name aus
- III. Ausführung in Simulide
-
- Öffnen Sie SimulIDE (unter …\bin\simulide.exe)
- links in SimulIDE sollten Sie den Komponenten Browser finden. Wählen Sie dort
Micro
»AVR
»atmega
»atmega328
- Ziehen Sie den Eintrag
atmega328
per Drag and Drop in den Arbeitsbereich (rechter, beiger Teil des Fensters) - Es sollte nun ein Chip names
atmega328-1
dargestellt sein
- Erstellen der Ausgangsschaltung
- Im Programm wurde im auf PortD das 6bit angesprochen. Entsprechend soll auch hier am Port D der Ausgang 6 genutzt werden. Am Chip ist dieser mit D6 gekennzeichnet
- Fügen Sie eine LED (im Komponenten Browser über Output LED) und ein Massepotential ein (Sources Ground)
- Die Komponenten können mit dem Kontextmenu (Rechtsklick) gedreht und gespiegelt werden. Außerdem ist mit der Auswahl von
Properties
im Kontextmenu die Änderung von - Verbinden Sie die LED mit Masse und mit Port D6. Achten Sie auf die richtige Richtung der LED. Die Verbindungen lassen sich dadurch erstellen, dass auf ein Komponenten-Pin geklickt wird und die Linie zu einem nächsten Komponenten-Pin gezogen wird.
- Flashen der Software
- Klicken Sie rechts auf den Microcontroller und wählen Sie
Load firmware
- Fügen Sie hier den Pfad und Name des oben erstellten
Einfuehrung_v01.hex
ein und öffnen Sie dieses
- Starten der Simulation
- klicken Sie im Menu den Power-on Button
- Die Simulation startet
- Bugfixing
- vermutlich ist bei Ihnen zu sehen, dass die Diode nicht gleichmäßig an und aus dargestellt wird. Dies ist kein Fehler des Simulationsprogramms. Es wurde noch eine wichtige Komponente vergessen, welche immer bei der Verwendung von diskreten LEDs verwendet werden muss. Fügen Sie diese ein und Testen Sie die Schaltung nochmal
Sie sollten sich nach der Übung die ersten Kenntnisse mit dem Umgang der Umgebung angeeignet haben. Zum Festigen des der Fähigkeiten bieten sich folgende Aufgaben an:
- Aufgaben
-
- Welche Vorgaben für die Softwareentwicklung wurden verletzt, trotzdem das Programm lauffähig ist? (Interrupts werden erst in späteren Übungen erklärt)
- Wie könnte ein Ampel-Licht-Abfolge oder Lauflicht aus 4 Dioden erstellt und programmiert werden? Welche Optimierungen könnten im Code vorgenommen werden? Welche Komponente in SimulIDE kann genutzt werden? Wie kann die Farbe der LEDs geändert werden?
- Lesen Sie auf Mikrocontroller.net im Kapitel Warteschleifen die „erste Seite“, also bis:
Abhängig von der Version der Bibliothek verhalten sich die Bibliotheksfunktionen etwas unterschiedlich.
- main.c
- /*=============================================================================
- LCD_LIB_de Funktionen fuer die LCD-Anzeige des MiniMEXLE
- =========================================================
- Dateiname: lcd_lib_de.h
- Bibliothek fuer das LCD-Display des MiniMexle Boards bzw. optimiert für LCD-Display HD44780
- Autor: Steffen Freihofer (2006)
- Adaption: Thomas Pospiech (2007, Timing-Probleme geloest)
- G. Gruhler, D. Chilachava (2009, Init Ports und Dokumentation)
- Peter Blinzinger Libary umgebaut für SimulIDE, kompatibel mit dem Mexle2020 Board
- Version: 1.3 vom 26.04.2020
- =============================================================================*/
- // Deklarationen ==============================================================
- // Festlegung der Quarzfrequenz
- #ifndef F_CPU // optional definieren
- #define F_CPU 12288000UL // MiniMEXLE mit 12,288 MHz Quarz
- #endif
- // Include von Header-Dateien
- #include <avr/io.h> // I/O Konfiguration (intern weitere Dateien)
- #include <avr/interrupt.h> // globale Interrupts definieren (sei() und cli())
- #include <stdint.h> // Definition von Typen (int, char, ...)
- #include <stdbool.h> // Definition von 1-Bit-Variablentypen (bool)
- #include <util/delay.h> // Definition von Delays (Wartezeiten)
- // Konstanten
- #define CLEAR_DISPLAY 1 // Instruction Code fuer LCD: Loeschen des Displays
- // Port-Bits
- #define E PD4 // Enable-Signal zum Display: Port D, PD4
- #define RS PD7 // Register-Select zum Display: Port D, PD5
- // Makros
- #define Datenregister DDRD //Datenrichtungsregister für Datenport zum LCD (Verwendung der Bits 0 bis 3)
- #define Steuerregister DDRD //Datenrichtungsregister für Steuerport zum LCD
- #define P_DATA PORTD // Port ist Datenport zum LCD (Verwendung der Bits 0 bis 3)
- #define P_STEUER PORTD // Port ist Steuerport zum LCD
- // Funktionsprototypen
- void lcd_enable (void);
- void lcd_write (unsigned char byte);
- void lcd_init (void);
- void lcd_putc (unsigned char zeichen);
- void lcd_putstr (char *string);
- void lcd_gotoxy (unsigned char line, unsigned char pos);
- void lcd_clearDisplay(void);
- void lcd_clearline (unsigned char line);
- void lcd_displayMessage(char *string, unsigned char, unsigned char);
- // LCD-Ansteuerfunktionen =====================================================
- // ----------------------------------------------------------------------------
- // LCD_ENABLE: Erzeugt einen HIGH-Impuls des Enable-Signals
- // ----------------------------------------------------------------------------
- void lcd_enable (void)
- {
- P_STEUER |= (1 << E); // E=1 1 +-+
- _delay_us (1);
- P_STEUER &= ~(1 << E); // E=0 0--+ +---
- }
- // ----------------------------------------------------------------------------
- // LCD_WRITE: Schreibt ein Byte im 4-Bit-Modus in das LCD
- // ----------------------------------------------------------------------------
- void lcd_write (unsigned char byte) // Ausgabewert 8 Bit in "byte"
- {
- unsigned char temp; // Definition lokale Variable "temp"
- temp = byte; // Ausgabewert zwischenspeichern
- P_DATA &= 0xF0; // Untere 4 Bit auf Datenport loeschen
- P_DATA |= (temp>>4); // Ausgabewert 4 Bit nach rechts schieben
- // oberes Nibble auf Daten-Port LCD schreiben
- lcd_enable(); // Erzeugung Enable-Impuls zur Datenspeicherung
- // keine Wartezeit zwischen Nibbles notwendig
- byte &= 0x0F; // Unteres Nibble Ausgabewert extrahieren
- P_DATA &= 0xF0; // Untere 4 Bit auf Datenport loeschen
- P_DATA |= byte; // unteres Nibble auf Daten-Port LCD schreiben
- lcd_enable(); // Erzeugung Enable-Impuls zur Datenspeicherung
- }
- // ----------------------------------------------------------------------------
- // LCD_INIT: Initialisiert das LCD
- // ----------------------------------------------------------------------------
- void lcd_init (void)
- {
- cli(); //globale Interrupts deaktivieren
- Datenregister |= 0x0f; // Bit 0..3 (LCD-Daten) auf Output
- Steuerregister |= ((1<<E) | (1<<RS)); // Bit RS: LCD Register Select und Bit E: LCD Enable auf Output
- P_DATA |= 0x0f; // Port, Bit0..3 (LCD-Daten) SET
- // Steuersignale auf LOW
- P_STEUER &= ~((1<<E) | (1<<RS)); // E und RS auf 0 setzen
- _delay_ms (50);
- // Display in 4-bit Modus initialisieren
- P_DATA &= 0xF0;
- P_DATA |= 0x03;
- lcd_enable();
- _delay_ms (10);
- lcd_enable ();
- _delay_ms (10);
- lcd_enable ();
- P_DATA &= 0xF0;//0xF1;
- P_DATA |=0x02;
- _delay_ms (2);
- lcd_enable();
- _delay_us (50);
- // 2 Zeilen, 5 x 8 Pixel
- lcd_write (0x28);
- _delay_us (40);
- // Display einschalten
- lcd_write (0x0C);
- _delay_us (50);
- //P_DATA &= 0xF1;
- //lcd_enable();
- //_delay_us (50);
- // Cursor auf "increment mode" schalten
- lcd_write (0x06);//(0x02);
- _delay_us (50);
- // Display loeschen (funktioniert nur, wenn es 2x aufgerufen wird)
- lcd_write (0x01);
- _delay_ms (3);
- lcd_write (0x01);
- _delay_ms (3);
- sei(); // globale Interrrupts aktivieren
- }
- // ----------------------------------------------------------------------------
- // LCD_PUTC: Schreibt ein Zeichen an die entsprechende Speicherstelle
- // ----------------------------------------------------------------------------
- void lcd_putc (unsigned char character) // Ausgabewert 8-Bit in "character"
- {
- P_STEUER |= (1<<RS); // Register Select auf HIGH: "Daten ausgeben"
- lcd_write (character); // Zeichen ausgeben
- _delay_us (55); // Verzoegerung 55 us (interne Verarbeitung)
- }
- // ----------------------------------------------------------------------------
- // LCD_PUTSTR: Schreibt einen String auf das LCD
- // ----------------------------------------------------------------------------
- void lcd_putstr (char *string) // Pointer auf String in "string"
- {
- while (*string) // solange nicht Stringendezeichen 0x00
- {
- lcd_putc (*string); // Zeichen aus String holen und ausgeben
- string ++; // Pointer auf naechstes Zeichen setzen
- }
- }
- // ----------------------------------------------------------------------------
- // LCD_GOTOXY: Verschiebt den Curser an die angegebene Position
- // ----------------------------------------------------------------------------
- void lcd_gotoxy (unsigned char line, unsigned char pos)
- {
- P_STEUER &= ~(1<<RS); // Register Select auf LOW: "Control ausgeben"
- lcd_write((1 << 7) + 0x40 * line + pos); // Adresse Cursor in DDRAM-Adresse
- _delay_us (50); // Verzoegerung 50 us fuer Interne Verarbeitung
- }
- // ----------------------------------------------------------------------------
- // LCD_CLEARDISPLAY: Loescht den gesamten Display-Inhalt
- // ----------------------------------------------------------------------------
- void lcd_clearDisplay(void)
- {
- P_STEUER &= ~(1 << RS); // Register Select auf LOW: "Control ausgeben"
- lcd_write(CLEAR_DISPLAY); // Befehlscode an Display ausgeben
- _delay_ms (2); // Verzoegerung 2 ms fuer interne Verarbeitung
- }
- // ----------------------------------------------------------------------------
- // LCD_CLEARLINE: Loescht den Inhalt einer Display-Zeile
- // ----------------------------------------------------------------------------
- void lcd_clearline (unsigned char line)
- {
- switch(line)
- {
- case 0: lcd_gotoxy(0,0); // Beginn erste Zeile addressieren
- lcd_putstr (" "); // Leerzeichen ausgeben
- break;
- case 1: lcd_gotoxy(1,0); // Beginn zweite Zeile addressieren
- lcd_putstr (" "); // Leerzeichen ausgeben
- break;
- }
- }
- // ----------------------------------------------------------------------------
- // LCD_DISPLAYMESSAGE: Anzeige einer Stringvariablen auf dem Display
- // ----------------------------------------------------------------------------
- void lcd_displayMessage(char* mess, unsigned char line, unsigned char pos)
- {
- lcd_gotoxy(line,pos); // Cursor positionieren
- lcd_putstr(mess); // String ausgeben
- }