Created
August 7, 2012 21:30
-
-
Save pboehm/3289562 to your computer and use it in GitHub Desktop.
Lösungen für Aufgaben im Assemblerpraktikum
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/****************************************************************************** | |
* Projekt Template für einen Versuch im Assemblerpraktikum * | |
* * | |
* Versuch-Nr.: 1 (Digital-Voltmeter) * | |
* Gruppen-Nr.: * | |
* * | |
* Mitglieder der Gruppe: | |
* - Philipp Böhm | |
* - Mathias Perlet | |
* | |
* Zusammenfassung: | |
* Dieses Programm nutzt den Analog-Digital-Wandler des Boards um den am | |
* Potentiometer eingestellten Wert als Spannungswert zwischen 0 und 3,30V | |
* auf dem Display auszugeben | |
* | |
******************************************************************************/ | |
#include "praktikum.h" | |
/////////////////////////////////////////////////////////////////////////////// | |
// | |
// main | |
// | |
// Die Routine 'main' wird innerhalb der Versuchsanordnung durch den | |
// Startup-Code angesprungen. Sie ist somit die erste Methode in der Sie | |
// eigenen Quelltext einfügen können. | |
// | |
// Achten Sie darauf, dass 'main' niemals endet. | |
// | |
/////////////////////////////////////////////////////////////////////////////// | |
RSEG CODE32:CODE:NOROOT(2) | |
PUBLIC main | |
main: | |
// Initialisiert spezifische Teile des Praktikums. Bitte belassen Sie | |
// diesen Aufruf aus eigenem Interesse am Beginn der Methode main | |
RCALL praktikum_init | |
////////////////////////////////////////////////// | |
// Display initialisieren - Cursor in zweite Zeile | |
RCALL lcd_init | |
RCALL lcd_hide_cursor | |
MOV R12, 1 | |
MOV R11, 2 | |
RCALL lcd_set_cursor_position | |
////////////////////////////////////////////// | |
// Start-Button auslesen, ob er gedrückt wurde | |
// Wenn der Button gedrückt wurde, soll der | |
// Programmfluss weiterlaufen. | |
MOV R12, LWRD(hint) | |
RCALL lcd_write_string | |
// Eingabe vom Taster aktivieren | |
MOV R0, LWRD(AVR32_GPIO_ADDRESS | AVR32_GPIO_PORT_2) | |
ORH R0, HWRD(AVR32_GPIO_ADDRESS | AVR32_GPIO_PORT_2) | |
//Bit 24 ist der Taster PB0 | |
MOV R1, 0 | |
SBR R1, 24 | |
ST.W R0[AVR32_GPIO_GPERS], R1 | |
//Taster abfragen | |
nochma: LD.W R6, R0[AVR32_GPIO_PVR] | |
BLD R6, 24 | |
BREQ nochma | |
//////////////////////////////////////////////// | |
// Cursor für Spannungswertausgabe positionieren | |
// und Display clearen | |
RCALL lcd_clear_display | |
MOV R12, 4 | |
MOV R11, 2 | |
RCALL lcd_set_cursor_position | |
//////////////////////////////////////////////////////////// | |
// GPIO Port 0 aktivieren, weil das Potentiometer dort hängt | |
MOV R0, LWRD(AVR32_GPIO_ADDRESS | AVR32_GPIO_PORT_0) | |
ORH R0, HWRD(AVR32_GPIO_ADDRESS | AVR32_GPIO_PORT_0) | |
// Potentiometer an Port 0 aktivieren (22. Bit für Poti) | |
MOV R1, 0 // R1-Register auf 0 setzen | |
SBR R1, 22 // Bit für Poti setzen | |
ST.W R0[AVR32_GPIO_GPERS], R1 // Aktvieren des Potentiometers | |
CBR R1, 22 // Clear Bit in Register (22.Bit von R1) | |
/////////////////////////////////////// | |
// MUX auf Modus für AD-Wandler stellen | |
ST.W R0[AVR32_GPIO_PMR0S], R1 | |
ST.W R0[AVR32_GPIO_PMR1S], R1 | |
//////////////////// | |
// ADC konfigurieren | |
// Startup und Sample-Holdtime auf MAX setzen sowie den ADC zurücksetzen | |
// um ein sauberen Ausgangszustand zu haben | |
MOV R1, LWRD(AVR32_ADC_ADDRESS) | |
ORH R1, HWRD(AVR32_ADC_ADDRESS) | |
//AD-Wandler reseten | |
MOV R2, 0 | |
SBR R2, 0 //Reset-Bit 1 setzen | |
ST.W R1[AVR32_ADC_CR], R2 | |
// Start-UP und Sample-and-Hold-Time im ADC Mode Register setzen | |
MOV R3, LWRD(0xF1F0000) | |
ORH R3, HWRD(0xF1F0000) | |
ST.W R1[AVR32_ADC_MR], R3 //ADC-Modus einstellen | |
LD.W R4, R1[AVR32_ADC_MR] | |
//Channel enable register vom ADC auf Channel 1 setzen | |
MOV R3, 0 | |
SBR R3, 1 | |
ST.W R1[AVR32_ADC_CHER], R3 | |
MOV R3, 0 //Reset-Bit 0 setzen | |
SBR R3, 1 //Start-Bit auf 1 setzen | |
ST.W R1[AVR32_ADC_CR], R3 // ADC ist ab hoer aktiviert | |
///////////////////////////////////////////////////////////////// | |
// Den ADC aktivieren, sodass er einen neuen Wert konvertiert. | |
// Solange nachschauen, ob ein neuer Wert konvertiert wurde bis | |
// einer bereitsteht. | |
// | |
// Den Wert vom ADC aus dem LCDR-Register holen, umrechnen und | |
// dann auf dem Display ausgeben | |
///////////////////////////////////////////////////////////////// | |
loop: | |
// ADC dazu bringen, einen neuen Wert zu konvertieren | |
MOV R3, 0 | |
SBR R3, 1 | |
ST.W R1[AVR32_ADC_CR], R3 // Wandlungsvorgang starten | |
novalue: | |
// Statusregister des ADC laden und das DRDY Bit laden | |
LD.W R4, R1[AVR32_ADC_SR] | |
BLD R4, 16 // Das 16. Bit ist das DRDY (Data ready) Bit | |
BRNE novalue | |
// aktuellen Wert aus ADC laden | |
LD.W R10, R1[AVR32_ADC_LCDR] | |
// Wert umrechnen und ausgeben | |
RCALL calc_value | |
RCALL display_value | |
RJMP loop | |
/////////////////////////////////////////////////////////////////////////////// | |
// Dieses Unterprogramm holt sich den aktuellen Wert aus dem | |
// Last Converted Data Register (LCDR) und berechnet die notwendigen | |
// STellen zur Azeige und schreibt diese in spezielle Register | |
// | |
// VKT -> R8 | |
// 1.NKT -> R9 | |
// 2.NKT -> R10 | |
/////////////////////////////////////////////////////////////////////////////// | |
calc_value: | |
PUSHM LR | |
// Konstanten laden | |
MOV R6, 1023 // maximale Auflösung des ADC | |
MOV R8, 3300 // maximaler Spannungswert (3,3 V * 100) | |
MOV R5, 48 // ASCII-Offset | |
// Spannungswert ohne Komma berechnen (nicht in ASCII) | |
MUL R8, R10 | |
DIVU R8, R8, R6 | |
// Vorkommateil berechnen durch restlose Division durch 1000 | |
MOV R6, 1000 | |
DIVU R8, R8, R6 // WICHTIG R9 enthält den Rest der Division | |
ADD R8, R5 // ASCII-Offset aufaddieren | |
// Wert für VKT auf Stack sichern | |
MOV R10, R8 // weil man R8 nicht einzeln pushen kann | |
PUSHM R10 | |
// 1. Nachkommateil berechnen | |
MOV R6, 100 // 1. Nachkommastelle | |
MOV R10, R9 // Nachkommawert aus erster Division laden | |
DIVU R10, R10, R6 // Division durch 100, Rest in R11 | |
ADD R10, R5 // ASCII-Offset aufaddieren | |
PUSHM R10 // Sichern des Wertes auf dem Stack | |
// 2. Nachkommateil (analog zu 1. NKT) | |
MOV R6, 10 // Division durch 10 für 2. NKT | |
MOV R10, R11 | |
DIVU R10, R10, R6 | |
ADD R10, R5 | |
PUSHM R10 // Sichern des Wertes auf dem Stack | |
// berechnete Werte vom Stack holen und in die Register R8-R10 | |
// schreiben um sie dann an display_value weiterzugeben | |
POPM R8-R10 | |
POPM LR | |
RET 0 | |
/////////////////////////////////////////////////////////////////////////////// | |
// Ausgeben des aktuellen Spannungswertes, welcher vorher berechnet wurde. | |
// Die jeweiligen Register R8-R10 müssen mit den Werten gefüllt sein. | |
// | |
// R8 = VKT | |
// R9 = 1. NKT | |
// R10 = 2. NKT | |
/////////////////////////////////////////////////////////////////////////////// | |
display_value: | |
PUSHM LR | |
// Cursor positionieren (2. Zeile fünfte Spalte) | |
MOV R11, 2 | |
MOV R12, 5 | |
RCALL lcd_set_cursor_position | |
// VKT ausgeben | |
MOV R12, R8 | |
RCALL lcd_write_data | |
// Komma ausgeben | |
MOV R12, LWRD(comma) | |
RCALL lcd_write_string | |
// 1.NKT ausgeben | |
MOV R12, R9 | |
RCALL lcd_write_data | |
// 2.NKT ausgeben | |
MOV R12, R10 | |
RCALL lcd_write_data | |
// Einheit ausgeben | |
MOV R12, LWRD(unit) | |
RCALL lcd_write_string | |
POPM LR | |
RET 0 | |
/////////////////////////////////////////////////////////////////////////////// | |
// | |
// Das Segment DATA32_C dient Ihnen zur Festlegung eigener Constanten, d. h. | |
// der permanenten Speicherung von Daten. Ein Beispiel, das den Wert '200' unter | |
// dem Namen 'constant' im Speicher konstant (unveränderbar) ablegt, ist Ihnen | |
// gegeben. | |
// | |
/////////////////////////////////////////////////////////////////////////////// | |
RSEG DATA32_C:CONST:REORDER:NOROOT(2) | |
comma: | |
DC8 "," | |
unit: | |
DC8 "V" | |
hint: | |
DC8 "Press button PB0 ;-)" | |
/////////////////////////////////////////////////////////////////////////////// | |
// | |
// Das Segment DATA32_Z dient Ihnen zur Festlegung von Speicherbereichen für | |
// Ihre Variablen. Ein Beispiel, dass 16 Byte Platz unter dem Namen 'variable' | |
// reserviert, ist Ihnen gegeben. | |
// | |
/////////////////////////////////////////////////////////////////////////////// | |
RSEG DATA32_Z:DATA:REORDER:NOROOT(2) | |
variable: | |
DS8 16 | |
END |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/****************************************************************************** | |
* Projekt Template für einen Versuch im Assemblerpraktikum * | |
* * | |
* Versuch-Nr.: 2 * | |
* Gruppen-Nr.: * | |
* * | |
* Mitglieder der Gruppe: | |
* Philipp Böhm | |
* Mathias Perlet | |
* * | |
* Zusammenfassung: | |
* Implementation einer Wetterstation, welche die Helligkeit sowie | |
* die Temperatur entsprechend auf dem Display anzeigt. | |
* * | |
******************************************************************************/ | |
#include "praktikum.h" | |
/////////////////////////////////////////////////////////////////////////////// | |
// | |
// main | |
// | |
// Die Routine 'main' wird innerhalb der Versuchsanordnung durch den | |
// Startup-Code angesprungen. Sie ist somit die erste Methode in der Sie | |
// eigenen Quelltext einfügen können. | |
// | |
// Achten Sie darauf, dass 'main' niemals endet. | |
// | |
/////////////////////////////////////////////////////////////////////////////// | |
RSEG CODE32:CODE:NOROOT(2) | |
PUBLIC main | |
main: | |
// Initialisiert spezifische Teile des Praktikums. Bitte belassen Sie | |
// diesen Aufruf aus eigenem Interesse am Beginn der Methode main | |
RCALL praktikum_init | |
////////////////////////////////////////////////// | |
// Display initialisieren - Cursor in erste Zeile | |
RCALL lcd_init | |
RCALL lcd_hide_cursor | |
MOV R12, 1 | |
MOV R11, 1 | |
RCALL lcd_set_cursor_position | |
MOV R12, LWRD(welcome) | |
RCALL lcd_write_string | |
MOV R12, 1 | |
MOV R11, 3 | |
RCALL lcd_set_cursor_position | |
MOV R12, LWRD(press_button) | |
RCALL lcd_write_string | |
//////////////////////////////////////////////////////// | |
// GPIO Port 2 aktivieren, weil dort Taster hängen | |
MOV R0, LWRD(AVR32_GPIO_ADDRESS | AVR32_GPIO_PORT_2) | |
ORH R0, HWRD(AVR32_GPIO_ADDRESS | AVR32_GPIO_PORT_2) | |
MOV R1, 0 | |
SBR R1, 24 | |
SBR R1, 21 | |
SBR R1, 18 | |
ST.W R0[AVR32_GPIO_GPERS], R1 | |
// Start-Taster abfragen und nur wenn gedrückt fortfahren | |
nochma: LD.W R6, R0[AVR32_GPIO_PVR] | |
BLD R6, 24 | |
BREQ nochma | |
RCALL lcd_clear_display | |
//////////////////////////////////////////////////////// | |
// GPIO Port 0 aktivieren, weil dort LDR und Temp hängen | |
MOV R0, LWRD(AVR32_GPIO_ADDRESS | AVR32_GPIO_PORT_0) | |
ORH R0, HWRD(AVR32_GPIO_ADDRESS | AVR32_GPIO_PORT_0) | |
// LDR (23. Bit) und Temp (21. Bit) | |
MOV R1, 0 // R1-Register auf 0 setzen | |
SBR R1, 23 // Bit für LDR setzen | |
SBR R1, 21 // Bit für Temp setzen | |
ST.W R0[AVR32_GPIO_GPERS], R1 // Aktvieren der Komponenten | |
CBR R1, 23 // Clear Bit in Register (23.Bit von R1) | |
/////////////////////////////////////// | |
// MUX auf Modus für AD-Wandler stellen | |
ST.W R0[AVR32_GPIO_PMR0S], R1 | |
ST.W R0[AVR32_GPIO_PMR1S], R1 | |
//////////////////// | |
// ADC konfigurieren | |
// Startup und Sample-Holdtime auf MAX setzen sowie den ADC zurücksetzen | |
// um ein sauberen Ausgangszustand zu haben | |
MOV R1, LWRD(AVR32_ADC_ADDRESS) | |
ORH R1, HWRD(AVR32_ADC_ADDRESS) | |
//AD-Wandler reseten | |
MOV R2, 0 | |
SBR R2, 0 //Reset-Bit 1 setzen | |
ST.W R1[AVR32_ADC_CR], R2 | |
// Start-UP und Sample-and-Hold-Time im ADC Mode Register setzen | |
MOV R3, LWRD(0xF1F0000) | |
ORH R3, HWRD(0xF1F0000) | |
ST.W R1[AVR32_ADC_MR], R3 //ADC-Modus einstellen | |
LD.W R4, R1[AVR32_ADC_MR] | |
/////////////////////////////////////////// | |
// Standardfunktion einstellen Bit 0 = Helligkeit | |
MOV R5, 0 | |
MOV R6, LWRD(mode) | |
ORH R6, HWRD(mode) | |
ST.W R6, R5 | |
///////////////////////////////////////////////////////////////// | |
// Nachfolgend befindet sich die Schleife, wo die jeweiligen | |
// Funktionen ausgeführt werden und mittels Button Druck | |
// der Mode umgeschaltet werden kann | |
///////////////////////////////////////////////////////////////// | |
loop: | |
// Taster auslesen und mode toggeln | |
MOV R5, LWRD(AVR32_GPIO_ADDRESS | AVR32_GPIO_PORT_2) | |
ORH R5, HWRD(AVR32_GPIO_ADDRESS | AVR32_GPIO_PORT_2) | |
// Taster abfragen | |
LD.W R6, R5[AVR32_GPIO_PVR] | |
BLD R6, 24 | |
BREQ no_toggle | |
// aktuelen Modus umschalten | |
RCALL toggle_mode | |
// hier einmal das Display löschen | |
RCALL lcd_clear_display | |
// ADC reseten | |
MOV R1, LWRD(AVR32_ADC_ADDRESS) | |
ORH R1, HWRD(AVR32_ADC_ADDRESS) | |
MOV R2, 0 | |
SBR R2, 0 //Reset-Bit 1 setzen | |
ST.W R1[AVR32_ADC_CR], R2 | |
// eine Sekunde warten um das Doppeltoggeln zu unterbinden | |
MOV R10, LWRD(2500000) | |
ORH R10, HWRD(2500000) | |
RCALL wait | |
no_toggle: | |
// Mode auslesen und entsprechend handeln | |
// | |
// wenn das Mode-Bit den Wert 0 hat dann wird die | |
// Temperatur ausgegeben, bei 1 die Helligkeit | |
MOV R5, LWRD(mode) | |
ORH R5, HWRD(mode) | |
LD.W R6, R5 | |
BLD R6, 0 | |
BREQ temp // Mode = 1 | |
// Helligkeit | |
RCALL light | |
RJMP loop | |
// Temperatur | |
temp: RCALL temperature | |
RJMP loop | |
/////////////////////////////////////////////////////////////////////////////// | |
// Temperatursensor verarbeiten | |
// | |
////////////////////////////////////////////////////////////////////////////// | |
temperature: | |
PUSHM LR | |
// Funktion ausgeben | |
MOV R12, 1 | |
MOV R11, 1 | |
RCALL lcd_set_cursor_position | |
MOV R12, LWRD(temp_hint) | |
RCALL lcd_write_string | |
// ADC Channel auf den für den Temperatursensor stellen | |
// Channel enable register vom ADC auf Channel 0 setzen | |
MOV R3, 0 | |
SBR R3, 0 | |
ST.W R1[AVR32_ADC_CHER], R3 | |
// nächsten Wert holen -> R10 | |
RCALL get_next_adc_value | |
/////////////////////////////////////////////////////////////////////// | |
// Berechnung des Temperaturwertes und Umwandlung in ASCII, sowie | |
// die Ausgabe in Prozent | |
/////////////////////////////////////////////////////////////////////// | |
// Konstanten laden | |
MOV R5, 48 // ASCII-Offset | |
MOV R6, 1023 // maximale Auflösung des ADC | |
MOV R8, 3300 | |
// aktuellen Wert (nicht in ASCII) berechnen | |
MUL R8, R10 | |
DIVU R8, R8, R6 | |
// Gradwert = (258900 - R8 * 100) / 3766 | |
MOV R4, 100 | |
MUL R8, R4 | |
MOV R4, 258900 | |
SUB R8, R4, R8 | |
MOV R4, 3766 | |
DIVU R8, R8, R4 | |
// Zehnerstelle berechnen ( /10) | |
MOV R6, 10 | |
DIVU R8, R8, R6 // WICHTIG R9 enthält den Rest der Division | |
// Wert für Schwellwert-Betrachtung in Variable speichern | |
MOV R10, LWRD(temp_level) | |
ORH R10, HWRD(temp_level) | |
ST.W R10, R8 | |
ADD R8, R5 // ASCII-Offset aufaddieren | |
// Wert ausgeben | |
MOV R12, R8 | |
RCALL lcd_write_data | |
// Einerstelle in ASCII umwandeln und ausgeben | |
ADD R9, R5 | |
MOV R12, R9 | |
RCALL lcd_write_data | |
// °C ausgeben | |
MOV R12, 0x80 | |
RCALL lcd_write_data | |
MOV R12, 0x43 | |
RCALL lcd_write_data | |
// Unterprogramm zur Ausgabe des Levels aufrufen | |
RCALL output_advice | |
// entsprechend lange warten um das Flackern etwas zu reduzieren | |
// komplett wird es nie gehen | |
MOV R10, 350000 | |
RCALL wait | |
POPM LR | |
RET 0 | |
////////////////////////////////////////////////////////////////////////////// | |
// LDR verarbeiten | |
// | |
////////////////////////////////////////////////////////////////////////////// | |
light: | |
PUSHM LR | |
//////////////////// | |
// Funktion anzeigen | |
MOV R12, 1 | |
MOV R11, 1 | |
RCALL lcd_set_cursor_position | |
MOV R12, LWRD(light_hint) | |
RCALL lcd_write_string | |
// ADC Channel auf den für den LDR stellen | |
// Channel enable register vom ADC auf Channel 2 setzen | |
MOV R3, 0 | |
SBR R3, 2 | |
ST.W R1[AVR32_ADC_CHER], R3 | |
// nächsten Wert holen -> R10 | |
RCALL get_next_adc_value | |
/////////////////////////////////////////////////////////////////////// | |
// Berechnung des Helligkeitswerts und Umwandlung in ASCII, sowie | |
// die Ausgabe in Prozent | |
/////////////////////////////////////////////////////////////////////// | |
// Konstanten laden | |
MOV R6, 1023 // maximale Auflösung des ADC | |
MOV R8, 100 | |
MOV R5, 48 // ASCII-Offset | |
// aktuellen Wert (nicht in ASCII) berechnen | |
MUL R8, R10 | |
DIVU R8, R8, R6 | |
// Zehnerstelle berechnen ( /10) | |
MOV R6, 10 | |
DIVU R8, R8, R6 // WICHTIG R9 enthält den Rest der Division | |
ADD R8, R5 // ASCII-Offset aufaddieren | |
// Wert ausgeben | |
MOV R12, R8 | |
RCALL lcd_write_data | |
// Einerstelle in ASCII umwandeln und ausgeben | |
ADD R9, R5 | |
MOV R12, R9 | |
RCALL lcd_write_data | |
// Prozentzeichen ausgeben | |
MOV R12, 0x25 | |
RCALL lcd_write_data | |
// entsprechend lange warten um das Flackern etwas zu reduzieren | |
// komplett wird es nie gehen | |
MOV R10, 350000 | |
RCALL wait | |
POPM LR | |
RET 0 | |
/////////////////////////////////////////////////////////////////////////////// | |
// nächsten gewandelten Wert holen | |
// | |
// konvertierter Wert --> R10 | |
/////////////////////////////////////////////////////////////////////////////// | |
get_next_adc_value: | |
PUSHM LR | |
// ADC dazu bringen, einen neuen Wert zu konvertieren | |
MOV R3, 0 | |
SBR R3, 1 | |
ST.W R1[AVR32_ADC_CR], R3 // Wandlungsvorgang starten | |
novalue: | |
// Statusregister des ADC laden und das DRDY Bit laden | |
LD.W R4, R1[AVR32_ADC_SR] | |
BLD R4, 16 // Das 16. Bit ist das DRDY (Data ready) Bit | |
BRNE novalue | |
// aktuellen Wert aus ADC laden | |
LD.W R10, R1[AVR32_ADC_LCDR] | |
POPM LR | |
RET 0 | |
/////////////////////////////////////////////////////////////////////////////// | |
// mode umschalten | |
/////////////////////////////////////////////////////////////////////////////// | |
toggle_mode: | |
PUSHM LR | |
MOV R5, LWRD(mode) | |
ORH R5, HWRD(mode) | |
LD.W R6, R5 | |
COM R6 | |
ST.W R5, R6 | |
POPM LR | |
RET 0 | |
/////////////////////////////////////////////////////////////////////////////// | |
// Hinweis für die Länge der Hose ausgeben | |
// < 20 Lange Hose | |
// < 30 && >= 20 Kurze Hose | |
// >= 30 Keine Hose | |
/////////////////////////////////////////////////////////////////////////////// | |
output_advice: | |
PUSHM LR | |
// Aktuellen Schwellenwert (Zehnerstelle) für die Temperatur laden | |
MOV R4, LWRD(temp_level) | |
ORH R4, HWRD(temp_level) | |
LD.W R5, R4 | |
CP.W R5, 2 | |
BRCC greaterthan20 | |
// wenn kleiner 20 | |
MOV R10, LWRD(weather_long) | |
RJMP output | |
greaterthan20: | |
CP.W R5, 3 | |
BRCC greaterthan30 | |
// kleiner als 30 und größer gleich 20 | |
MOV R10, LWRD(weather_short) | |
RJMP output | |
greaterthan30: | |
// größer gleich 30 | |
MOV R10, LWRD(weather_no) | |
output: | |
// Cursor positionieren und Wert in R10 ausgeben | |
MOV R12, 1 | |
MOV R11, 4 | |
RCALL lcd_set_cursor_position | |
MOV R12, R10 | |
RCALL lcd_write_string | |
POPM LR | |
RET 0 | |
/////////////////////////////////////////////////////////////////////////////// | |
// Zeit Schleife - ca 1sek | |
// | |
// R10 muss die Länge der Zeitschöeife enthalten | |
/////////////////////////////////////////////////////////////////////////////// | |
wait: | |
PUSHM LR | |
MOV R8, R10 | |
subi: SUB R8, 0x1 | |
BRNE subi | |
POPM LR | |
RET 0 | |
/////////////////////////////////////////////////////////////////////////////// | |
// | |
// Das Segment DATA32_C dient Ihnen zur Festlegung eigener Constanten, d. h. | |
// der permanenten Speicherung von Daten. Ein Beispiel, das den Wert '200' unter | |
// dem Namen 'constant' im Speicher konstant (unveränderbar) ablegt, ist Ihnen | |
// gegeben. | |
// | |
/////////////////////////////////////////////////////////////////////////////// | |
RSEG DATA32_C:CONST:REORDER:NOROOT(2) | |
constant: | |
DC8 200 | |
welcome: | |
DC8 "Und nun das Wetter" | |
press_button: | |
DC8 "Press Button PB0" | |
temp_hint: | |
DC8 "Temperatur: " | |
light_hint: | |
DC8 "Helligkeit: " | |
percent: | |
DC8 "%" | |
weather_long: | |
DC8 "Lange Hosen Wetter" | |
weather_short: | |
DC8 "Kurze Hosen Wetter" | |
weather_no: | |
DC8 "Keine Hosen Wetter" | |
/////////////////////////////////////////////////////////////////////////////// | |
// | |
// Das Segment DATA32_Z dient Ihnen zur Festlegung von Speicherbereichen für | |
// Ihre Variablen. Ein Beispiel, dass 16 Byte Platz unter dem Namen 'variable' | |
// reserviert, ist Ihnen gegeben. | |
// | |
/////////////////////////////////////////////////////////////////////////////// | |
RSEG DATA32_Z:DATA:REORDER:NOROOT(2) | |
// Da 0. Bit entscheidet welche Funktion ausgeführt wird | |
// 0 -> Helligkeit | |
// 1 -> Temperatur | |
mode: | |
DS32 1 | |
temp_level: | |
DS8 0 | |
END |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/****************************************************************************** | |
* Projekt Template für einen Versuch im Assemblerpraktikum * | |
* * | |
* Versuch-Nr.: 3 * | |
* Gruppen-Nr.: * | |
* * | |
* Mitglieder der Gruppe: * | |
* - Philipp Böhm | |
* - Mathias Perlet | |
* Zusammenfassung: | |
* Mittels integrierten Timer und Interrupts soll eine Stoppuhr | |
* entwickelt werden, welche eine Auflösung im Millisekunden-Bereich | |
* besitzen soll. Mittels Taster soll das Umschalten sowie das Zurücksetzen | |
* möglich sein. | |
* * | |
******************************************************************************/ | |
#include "praktikum.h" | |
/////////////////////////////////////////////////////////////////////////////// | |
// | |
// main | |
// | |
// Die Routine 'main' wird innerhalb der Versuchsanordnung durch den | |
// Startup-Code angesprungen. Sie ist somit die erste Methode in der Sie | |
// eigenen Quelltext einfügen können. | |
// | |
// Achten Sie darauf, dass 'main' niemals endet. | |
// | |
/////////////////////////////////////////////////////////////////////////////// | |
RSEG CODE32:CODE:NOROOT(2) | |
PUBLIC main | |
main: | |
// Initialisiert spezifische Teile des Praktikums. Bitte belassen Sie | |
// diesen Aufruf aus eigenem Interesse am Beginn der Methode main | |
RCALL praktikum_init | |
///////////////////// | |
// LEDs konfigurieren | |
MOV R0, LWRD(AVR32_GPIO_ADDRESS | AVR32_GPIO_PORT_1) | |
ORH R0, HWRD(AVR32_GPIO_ADDRESS | AVR32_GPIO_PORT_1) | |
MOV R1, 0 | |
SBR R1, 27 | |
SBR R1, 28 | |
SBR R1, 29 | |
SBR R1, 30 | |
SBR R1, 19 | |
SBR R1, 20 | |
ST.W R0[AVR32_GPIO_GPERS], R1 | |
ST.W R0[AVR32_GPIO_ODER], R1 | |
// LC-Display initialisieren und Cursor abschalten | |
RCALL lcd_init | |
RCALL lcd_hide_cursor | |
// Initialisierung Tasten-Interrupt an Port2 über PB2 des EVK1100 | |
// Autovector für GPIO-Controller setzen, IntLevel = 0 | |
// bei gleichem IntLevel gewinnt höchste Gruppennummer | |
// also sollte der TimerInt(14) vor dem GPIO-TastenInt(2) sein | |
// Interrupts aktivieren | |
CSRF AVR32_SR_GM | |
MOV R0, LWRD(AVR32_INTC_ADDRESS) | |
ORH R0, HWRD(AVR32_INTC_ADDRESS) | |
//////////////////// | |
// ISRs registrieren | |
// Button (Gruppe 2) ISR an Adresse 0x204 registrieren | |
MOV R1, 0x204 | |
ST.W R0[AVR32_INTC_IPR + 2*4], R1 // Gruppe 2 (IPR2) | |
// Timer (Gruppe 14) ISR an Adresse 0x208 registrieren | |
MOV R1, 0x208 | |
ST.W R0[AVR32_INTC_IPR + 14*4], R1 // Gruppe 14 (IPR14) | |
// Laden der Basisadresse des GPIO-Controllers in Register R0 | |
MOV R0, LWRD(AVR32_GPIO_ADDRESS) | |
ORH R0, HWRD(AVR32_GPIO_ADDRESS) | |
MOV R1, 0 | |
SBR R1, 21 | |
SBR R1, 24 | |
// PB 0 und PB 1 aktivieren (Pin 21 und 24) | |
ST.W R0[AVR32_GPIO_PORT_2 + AVR32_GPIO_GPERS], R1 | |
// PB0/1 von GPIO2 Interrupt enable | |
ST.W R0[AVR32_GPIO_PORT_2 + AVR32_GPIO_IERS], R1 | |
// Interrupt Mode Register IMR1 und IMR0 | |
// 00 - Pin Change, 01 - Rising Edge, 10 - Falling Edge | |
// 11 - Reserved | |
ST.W R0[AVR32_GPIO_PORT_2 + AVR32_GPIO_IMR0S], R1 | |
// Rising Edge | |
ST.W R0[AVR32_GPIO_PORT_2 + AVR32_GPIO_IMR1C], R1 | |
// Glitch Filter Enable Register, GFER enabled für PIN 18 | |
// GFER nach RESET auf 1, d.h. Glitch Filter aktiviert | |
ST.W R0[AVR32_GPIO_PORT_2 + AVR32_GPIO_GFERS], R1 | |
////////////////////////////////////////////////////////////////////// | |
// Timer-Konfiguration /////////////////////////////////////////////// | |
////////////////////////////////////////////////////////////////////// | |
// R4 als Basis für den Timer | |
MOV R4, LWRD(AVR32_TC_ADDRESS) | |
ORH R4, HWRD(AVR32_TC_ADDRESS) | |
// R5 als Basis für Interruptcontroller | |
MOV R5, LWRD(AVR32_INTC_ADDRESS) | |
ORH R5, HWRD(AVR32_INTC_ADDRESS) | |
// Timer Modi konfigurieren | |
MOV R1, 0 | |
SBR R1, 15 // Setze WAVE-Mode | |
SBR R1, 14 // Zählen bis zu einem bestimmten Wert dann Interrupt | |
CBR R1, 13 // ---- | |
SBR R1, 1 // Timer Quelle auf Clock4 setzen | |
SBR R1, 0 // ---- | |
ST.W R4[AVR32_TC_CMR0], R1 | |
// Autovektor | |
MOV R1, 0 | |
SBR R1, 4 // Interrupt wenn Vergleich mit Register | |
ST.W R4[AVR32_TC_IER0], R1 | |
MOV R1, 0 | |
// Dezimalen Zählerwert eintragen, ab dem der Timer einen | |
// Interrupt auslöst. Um alle 1ms einen Intzerrupt zu bekommen | |
// ist dies bei einem 12 MHz Oszillator und Clock4 = 375 | |
MOV R6, LWRD(375) | |
ORH R6, HWRD(375) | |
ST.W R4[AVR32_TC_RC0], R6 | |
// Timer-Counter initial auf 0 setzen | |
MOV R5, LWRD(timer_count) | |
ORH R5, HWRD(timer_count) | |
MOV R6, 0 | |
ST.W R5, R6 | |
// Clock aktivieren und starten im Channel Control Register | |
// | |
// Bit 0 ist CLKEN welches die Clock zuschaltet aber nicht startet | |
// Bit 2 ist SWTRG welches die Clock startet, nachdem der | |
// Timer zurückgesetzt wurde | |
MOV R6, 0 | |
SBR R6, 0 | |
SBR R6, 2 | |
ST.W R4[AVR32_TC_CCR0], R6 | |
// Timer ist standardmäßig aus timer_active=0 | |
MOV R5, 0 | |
MOV R6, LWRD(timer_active) | |
ORH R6, HWRD(timer_active) | |
ST.W R6, R5 | |
// 00:00:00:00 ausgeben | |
MOV R12, 2 | |
MOV R11, 2 | |
RCALL lcd_set_cursor_position | |
MOV R12, LWRD(initial) | |
ORH R12, HWRD(initial) | |
RCALL lcd_write_string | |
// Register clearen | |
MOV R5, 0 | |
MOV R10, 0 | |
MOV R7, 0 | |
MOV R6, 0 | |
RJMP $ | |
/////////////////////////////////////////////////////////////////////// | |
// Toggle LEDs //////////////////////////////////////////////////////// | |
/////////////////////////////////////////////////////////////////////// | |
LedToggle: | |
PUSHM LR | |
PUSHM R0-R3 | |
MOV R0, LWRD(AVR32_GPIO_ADDRESS | AVR32_GPIO_PORT_1) | |
ORH R0, HWRD(AVR32_GPIO_ADDRESS | AVR32_GPIO_PORT_1) | |
// Anwählen der zu toggelnden LEDs | |
MOV R1, 0 | |
SBR R1, 27 | |
SBR R1, 28 | |
SBR R1, 29 | |
SBR R1, 30 | |
SBR R1, 19 | |
SBR R1, 20 | |
// Hier geschieht das eigentliche Toggeln | |
ST.W R0[AVR32_GPIO_OVRT], R1 | |
POPM R0-R3 | |
POPM LR | |
RET 0 | |
//////////////////////////////////////////////////////////////////////// | |
// Timer Interrupt Service Routine ///////////////////////////////////// | |
//////////////////////////////////////////////////////////////////////// | |
TimerInt: | |
PUSHM R0-R3 | |
// Statusregister lesen um es zu leeren | |
MOV R1, LWRD(AVR32_TC_ADDRESS) | |
ORH R1, HWRD(AVR32_TC_ADDRESS) | |
LD.W R0, R1[AVR32_TC_SR0] | |
/////////////////////////////////////////////////////////////// | |
// Testen ob Timer zurzeit aktiv ist sonst nicht inkrementieren | |
MOV R7, LWRD(timer_active) | |
ORH R7, HWRD(timer_active) | |
LD.W R8, R7 | |
BLD R8, 0 | |
BRNE timer_ende | |
// Initialiserung | |
MOV R0, 1 | |
ADD R7, R0 | |
MOV R12, 2 | |
MOV R11, 2 | |
RCALL lcd_set_cursor_position | |
//////////////////////////// | |
// Timer Wert inkrementieren | |
MOV R4, 1 | |
MOV R5, LWRD(timer_count) | |
ORH R5, HWRD(timer_count) | |
LD.W R6, R5 | |
ADD R6, R4 | |
ST.W R5, R6 | |
////////////////////////////////////// | |
// Aktuellen Wert umrechnen und darstellen | |
// | |
// R6 enthält den aktuellen Timer-Wert | |
// R0 -> ist aktueller Teilerwert | |
// R1 -> ASCII Offset | |
MOV R1, 48 // ASCII Offset | |
// Stunde Zehner (36 000 000 ms) | |
MOV R0, LWRD(36000000) | |
ORH R0, HWRD(36000000) | |
DIVU R6, R6, R0 // R7 rest | |
ADD R6, R1 | |
MOV R12, R6 | |
RCALL lcd_write_data | |
MOV R6, R7 | |
// Stunde Einer (3 600 000 ms) | |
MOV R0, LWRD(3600000) | |
ORH R0, HWRD(3600000) | |
DIVU R6, R6, R0 // R7 rest | |
ADD R6, R1 | |
MOV R12, R6 | |
RCALL lcd_write_data | |
MOV R6, R7 | |
// Doppelpunkt | |
MOV R12, LWRD(colon) | |
ORH R12, HWRD(colon) | |
RCALL lcd_write_string | |
// Minute Zehner (600 000 ms) | |
MOV R0, 600000 | |
DIVU R6, R6, R0 // R7 rest | |
ADD R6, R1 | |
MOV R12, R6 | |
RCALL lcd_write_data | |
MOV R6, R7 | |
// Minute Einer (60 000 ms) | |
MOV R0, 60000 | |
DIVU R6, R6, R0 // R7 rest | |
ADD R6, R1 | |
MOV R12, R6 | |
RCALL lcd_write_data | |
MOV R6, R7 | |
// Doppelpunkt | |
MOV R12, LWRD(colon) | |
ORH R12, HWRD(colon) | |
RCALL lcd_write_string | |
// Sekunde Zehner (10 000 ms) | |
MOV R0, 10000 | |
DIVU R6, R6, R0 // R7 rest | |
ADD R6, R1 | |
MOV R12, R6 | |
RCALL lcd_write_data | |
MOV R6, R7 | |
// Sekunde Einer (1000 ms) | |
MOV R0, 1000 | |
DIVU R6, R6, R0 // R7 rest | |
ADD R6, R1 | |
MOV R12, R6 | |
RCALL lcd_write_data | |
// Jede Sekunde LEDs toggeln | |
MOV R0, 0 | |
CP.W R0, R7 | |
BRNE notoggle | |
RCALL LedToggle | |
notoggle: | |
MOV R6, R7 | |
// Doppelpunkt | |
MOV R12, LWRD(colon) | |
ORH R12, HWRD(colon) | |
RCALL lcd_write_string | |
// Sekunde Zehntel (100 ms) | |
MOV R0, 100 | |
DIVU R6, R6, R0 // R7 rest | |
ADD R6, R1 | |
MOV R12, R6 | |
RCALL lcd_write_data | |
MOV R6, R7 | |
// Sekunde Hunderstel (10 ms) | |
MOV R0, 10 | |
DIVU R6, R6, R0 // R7 rest | |
ADD R6, R1 | |
MOV R12, R6 | |
RCALL lcd_write_data | |
MOV R6, R7 | |
timer_ende: | |
POPM R0-R3 | |
RETE | |
//////////////////////////////////////////////////////////////////////// | |
// Button Interrupt Service Routine //////////////////////////////////// | |
//////////////////////////////////////////////////////////////////////// | |
ButtonInt: | |
PUSHM R0-R3 | |
MOV R0, LWRD(AVR32_GPIO_ADDRESS) | |
ORH R0, HWRD(AVR32_GPIO_ADDRESS) | |
// GPIO Interrupt Flag Register einlesen | |
LD.W R3, R0[AVR32_GPIO_PORT_2 + AVR32_GPIO_IFR] | |
// welche Taste, welcher PIN aktiviert? | |
BLD R3, 24 | |
BREQ handle_start_stop | |
BLD R3, 21 | |
BREQ handle_reset | |
RJMP ende | |
handle_start_stop: | |
///////////////////////////////// | |
// Toggle des timer_active-Wertes | |
MOV R0, LWRD(timer_active) | |
ORH R0, HWRD(timer_active) | |
LD.W R1, R0 | |
COM R1 | |
ST.W R0, R1 | |
RJMP ende | |
handle_reset: | |
////////////////////////// | |
// Timer-Wert auf 0 setzen | |
MOV R5, LWRD(timer_count) | |
ORH R5, HWRD(timer_count) | |
MOV R6, 0 | |
// 0000000 ausgeben | |
MOV R12, 2 | |
MOV R11, 2 | |
RCALL lcd_set_cursor_position | |
MOV R12, LWRD(initial) | |
ORH R12, HWRD(initial) | |
RCALL lcd_write_string | |
ST.W R5, R6 | |
ende: | |
// GPIO Interrupt Flag Register clean | |
MOV R0, LWRD(AVR32_GPIO_ADDRESS) | |
ORH R0, HWRD(AVR32_GPIO_ADDRESS) | |
ST.W R0[AVR32_GPIO_PORT_2 + AVR32_GPIO_IFRC], R3 | |
POPM R0-R3 | |
RETE // Return from Event/Interrupt | |
/////////////////////////////////////////////////////////////////////////////// | |
// | |
// Das Segment DATA32_C dient Ihnen zur Festlegung eigener Constanten, d. h. | |
// der permanenten Speicherung von Daten. Ein Beispiel, das den Wert '200' unter | |
// dem Namen 'constant' im Speicher konstant (unveränderbar) ablegt, ist Ihnen | |
// gegeben. | |
// | |
/////////////////////////////////////////////////////////////////////////////// | |
RSEG DATA32_C:CONST:REORDER:NOROOT(2) | |
start_timer: | |
DC8 "Timer gestartet" | |
stop_timer: | |
DC8 "Timer gestoppt" | |
reset: | |
DC8 "Timer Reset" | |
timer: | |
DC8 "Timer Interrupt" | |
colon: | |
DC8 ":" | |
initial: | |
DC8 "00:00:00:00" | |
/////////////////////////////////////////////////////////////////////////////// | |
// | |
// Interrupt Tabelle | |
// | |
////////////////////////////////////////////////////////////////////////////// | |
COMMON EVTAB:CODE:ROOT(9) | |
ORG 0x204 // GPIO (Button) Interrupt | |
_handle_gpio_int: | |
RJMP ButtonInt | |
ORG 0x208 // Timer Interrupt | |
_handle_timer_int: | |
RJMP TimerInt | |
/////////////////////////////////////////////////////////////////////////////// | |
// | |
// Das Segment DATA32_Z dient Ihnen zur Festlegung von Speicherbereichen für | |
// Ihre Variablen. Ein Beispiel, dass 16 Byte Platz unter dem Namen 'variable' | |
// reserviert, ist Ihnen gegeben. | |
// | |
/////////////////////////////////////////////////////////////////////////////// | |
RSEG DATA32_Z:DATA:REORDER:NOROOT(2) | |
timer_count: | |
DS32 1 | |
timer_active: | |
DS32 1 | |
END |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment