; ; Oszi51 (für Siemens C504 @12Mhz) ; ; 8051 Code by Matthias Arndt ; ; Protokoll und Kommunikationsrevision E ; ; letzte Änderung: 23.5.2005 ; VERSION EQU 10h ; Versionsnummer (fortlaufend Major Minor ) ; ******************** ; Registerdefinitionen ; ******************** ; Hardwareregister AD-Wandler im C504 (im as31 vordefiniert) P1ANA DATA 0x90 P3ANA DATA 0xb0 ADCON0 DATA 0xd8 ADCON1 DATA 0xdc ADDATL DATA 0xda ADDATH DATA 0xd9 SYSCON DATA 0xb1 IEN1 DATA 0xa9 PWM BIT P1.3 ; Anzeige/Ausgang für PWM Signal ; Debug LEDs AD_DISPLAY BIT P1.4 ; Anzeige für AD messung ( wird nach jedem einzelnen gemessenen Kanal getoggelt) OSZI_DISPLAY BIT P1.7 ; Anzeige für Oszi Messung (0 -> langsamer Modus, 1 -> Oszimessung läuft, ; blinken -> Oszimessung wird übertragen) ; ********* ; Variablen ; ********* messpuffer DATA 0x80 ; 0x80-0x8f IRAM für eine Messung aller 8 Kanäle STACK_BOTTOM DATA 0xA0 ; temporäre Datenspeicher TMP DATA 0x60 TMPH DATA 0x5A TMPL DATA 0x5B ; Kontrollregister für die Hauptschleife, Ende zum Monitor, bzw. Moduswechsel MAINCTRL DATA 0x58 ; Steuervariablen und Register für PWM PWM_STATE DATA 0x22 ; Status der PWM (ob positives Signal oder negatives Signal generiert wird) HICOUNT DATA 0x23 ; zählt die steigenden PWM Flanken (für Triggerbedingung) PWM_CNT_H DATA 0x24 ; zentraler Zähler für PWM PWM_CNT_L DATA 0x25 ; Einstellung Anzahl der Hi-Blöcke auf 512 Messtakte via Tastverhältnis TASTV_PULS_H DATA 0x26 ; Tastverhältnis positives Signal (vom Frontend auszurechnen) TASTV_PULS_L DATA 0x27 TASTV_NOPULS_H DATA 0x28 TASTV_NOPULS_L DATA 0x29 BUF_TRIG_H DATA 0x2A ; Adresse im ext. RAM, ab wo nach Messung ausgegeben werden soll BUF_TRIG_L DATA 0x2B TMP_BUS DATA 0x2C TMP_LOOP DATA 0x2D ; Steuervariablen für Timerintervall (Schrittweite der Messung) TIMER_H DATA 0x63 TIMER_L DATA 0x64 TIMER_H_SHADOW DATA 0x65 ; die SIO liest nur ins Shadow-Reg und triggert in der Hauptschleife TIMER_L_SHADOW DATA 0x66 ; das Kopieren der Werte in die eigentlichen Kontrollregister TMOD_BACKUP DATA 0x67 ; DPTR für Speicherung der Messwerte im ext. RAM BUFPTR_H DATA 0x68 ; Adresse für ext. RAM BUFPTR_L DATA 0x69 BUF_H DATA 0x6A ; Zähler für Triggerposition BUF_L DATA 0x6B ; ********** ; Konstanten ; ********** PWM_POSITIV EQU 0xFF PWM_NEGATIV EQU 0x00 SIGNAL_TERMINATE EQU 0x0F SIGNAL_TIMER EQU 0x0E SIGNAL_STATUS EQU 0x0D SIGNAL_OSZI EQU 0x0C SIGNAL_OK EQU 0x00 ESC EQU 27 CR EQU 13 LF EQU 10 ; *********** ; Resetvektor ; *********** ORG 0x000 ajmp oszi51_entry ; ***************** ; Programmeinsprung ; ***************** ORG 0x100 oszi51_entry: clr IE clr ES ; SIO des Monitor deaktivieren ; setb TI ; FIXME: z.Z: Programmabsturz ; mov SP,STACK_BOTTOM ; SP initialisieren ; *********************************** ; Variablen und Pufferinitialisierung ; *********************************** ; Messpuffer im IRAM löschen ; (aktuelle Messwerte stehen immer im IRAM, die letzten 512 Messungen werden im ext. RAM gespeichert) mov r0,#messpuffer mov r1,#0x10 ; 8 Messwerte a 2 Byte = 16 Bytes Puffer clrloop1: mov @r0,#0 inc r0 djnz r1,clrloop1 mov MAINCTRL,#SIGNAL_OK ; Zustandskontrolle für Hauptschleife initialisieren ; PWM Basisinitialisierung ; FIXME: Basiswert für PWM ; 64 Hi-Pulse a 4 Takte auf 512 Messtakte mov TASTV_PULS_H,#0 mov TASTV_NOPULS_H,#0 mov TASTV_PULS_L,#4 mov TASTV_NOPULS_L,#4 acall init_pwm ; PWM programmieren ; Speicherkontrolle für Messung in ext. RAM initialisieren mov dptr,#buffer ; Adresse für Puffer im ext. RAM in den DPTR mov BUFPTR_H,DPH ; in unserem "Addresspointer" merken mov BUFPTR_L,DPL mov BUF_H,#0 mov BUF_L,#0 ; TODO: initialen Timer (Messgeschwindigkeit) programmieren mov TMOD_BACKUP,TMOD ; altes Timersetup retten mov TMOD,#0x1 ; Timer 0 in 16Bit Modus mit Maschinentakt ; *********************** ; Hardwareinitialisierung ; *********************** mov P1,#00h ; alle LEDs löschen mov ADCON1,#0x07 ; AD Prescaler auf ":4", wähle Analogkanal 7 ; 6 Eingabekanäle auf Analog einstellen (AN0-AN5) lcall set_rmap ;mov dptr,#save_analog ; Speicherbereich um Analogsettigns zu sichern ;mov A,P1ANA ; Analogsetting P1 ;movx @dptr,A ; ablegen ;inc dptr ;mov A,P3ANA ; dito für P3 ;movx @dptr,A ;inc dptr ; Analog nur an P3.5 mov P3ANA,#0x0c ; P3.5 / AN7 aktivieren lcall clr_rmap ; SIO Vektor backuppen: wir speichern den alten "ljmp" 3 Bytes hinter dem neuen ; im 8 Byte breiten Bereich mov dptr,#0x23 movx a,@dptr mov dptr,#0x26 movx @dptr,a mov dptr,#0x24 movx a,@dptr mov dptr,#0x27 movx @dptr,a mov dptr,#0x25 movx a,@dptr mov dptr,#0x28 movx @dptr,a ; neuen Sprung für SIO Interrupt kopieren mov dptr,#sio_jump movx A,@dptr mov dptr,#0x23 movx @dptr,A mov dptr,#sio_jump+1 movx A,@dptr mov dptr,#0x24 movx @dptr,A mov dptr,#sio_jump+2 movx a,@dptr mov dptr,#0x25 movx @dptr,A ; Einstellung Baudrate und SIO Protokoll wird vom Monitor vorgenommen (9600 Baud, 8N1, XON/OFF) setb ES ; eigenen SIO IRQ aktivieren setb REN ; Empfang via SIO zulassen ; Quittung für allerersten Timer von Hand setzen setb TF0 ; Analoginterrupt verbieten mov A,IEN1 anl A,#0xfe ; (Bit 0, EADC ausmaskieren) mov IEN1,A ; ; *********************** ; Ausgabe Programmversion ; *********************** mov A,#'V' ; Versionsnummer acall SendChar mov A,#VERSION acall SendHEX mov A,#CR acall SendChar mov A,#LF acall SendChar setb TI ; ************* ; Hauptschleife ; ************* ; ; Default: langsame kontinuierliche Messung main: ; auf Timer 0 Flag warten jnb TF0,main clr TF0 ; Timer quittieren acall set_timer_langsam acall messung_langsam ; kontinuierliche, langsame Messung durchführen (kein Protokoll in ext. RAM) ; aktuelle Messwerte (on-the-fly) ausgeben acall dumpinternal check_mainctrl: ; *** Status/Zustand der Kontrolle für die hauptschleife bearbeiten (MAINCTRL) mov A,MAINCTRL cjne A,#SIGNAL_OSZI,no_oszi_mode ; kontinuierliche Messung und auf Trigger warten acall oszimodus ; TODO: dauerhafter Oszi-Modus? ; ggfs. zurück zu check_mainctrl mov MAINCTRL,#SIGNAL_OK ; zurück zum langsamen Modus ajmp check_mainctrl no_oszi_mode: cjne A,#SIGNAL_STATUS,nostatusmsg ; Statuspaket verschicken? acall send_status ; Statuspaket erzeugen und verschicken mov MAINCTRL,#SIGNAL_OK ajmp main nostatusmsg: cjne A,#SIGNAL_TIMER,no_new_timer ; Timer-Shadow umkopieren? ; Timer SHADOW umkopieren mov TIMER_H,TIMER_H_SHADOW mov TIMER_L,TIMER_L_SHADOW mov MAINCTRL,#SIGNAL_OK ; MAINCTRL resetten, um Ausführung zu signalisieren ajmp main no_new_timer: ; last but not least: soll zurück zum Monitor gegangen werden? mov A,MAINCTRL subb A,#SIGNAL_TERMINATE jz endit ; raus? dann raus! ; sonst Hauptschleife wiederholen ajmp main ; ****************** ; Hauptprogramm Ende ; ****************** endit: clr TR0 ; stoppe T0 am Ende ; Quittung für erfolgreiche Rückkehr zum Monitor senden mov a,#'M' acall SendChar mov a,#'o' acall SendChar mov a,#'n' acall SendChar mov a,#CR acall SendChar mov a,#LF acall SendChar ; alten SIO ljmp restaurieren mov dptr,#0x26 movx a,@dptr mov dptr,#0x23 movx @dptr,a mov dptr,#0x27 movx a,@dptr mov dptr,#0x24 movx @dptr,a mov dptr,#0x28 movx a,@dptr mov dptr,#0x25 movx @dptr,a ; 8 Eingabekanäle wieder auf ursprüngliche Einstellung restaurieren lcall set_rmap ;mov dptr,#save_analog ;movx A,@dptr ;mov P1ANA,A ;inc dptr ;movx A,@dptr mov P1ANA,#0x0f mov P3ANA,#00111100b lcall clr_rmap ; zurück zum Tasking Monitor ljmp 0x918e ; ***** UNTERPROGRAMME ***** ; ********* ; Oszimodus ; ********* ; schnelle Messung mit Protokoll im int. RAM ; Test mit Triggerbedingung ; oszimodus: ; push ACC clr ES ; SIO aus (kann Messraster stören) setb OSZI_DISPLAY ; zeige an, daß OSzimessung läuft acall init_pwm ; PWM neukonfigurieren ; Messung und Synchronisation mit PWM (3 positive PWM Pulse müssen durchgelaufen sein) sync_with_pwm: jnb TF0,sync_with_pwm clr TF0 ; Timer quittieren acall do_pwm ; PWM durchführen acall set_timer_schnell ; Timer für schnelle Messung neuprogrammieren acall messung_schnell ; schnelle Messung mit Protokoll im ext. RAM jb TF0,oszimodus_fehler ; bei Timerüberlauf: melde Fehler mov A,HICOUNT ; gezählte HI-Flanken der PWM in den Akku cjne A,#4,sync_with_pwm ; weniger als 4? dann weiter syncen ; jetzt 510* messen + 2 alte Messungen als Pretrigger behalten ; Zählen in TMP mov TMP,#0xff oszi_messschleife: jnb TF0,oszi_messschleife ; auf T0 warten clr TF0 ; Timer quittieren acall do_pwm ; weiterhin die PWM durchführen acall set_timer_schnell ; Timer für schnelle Messung neuprogrammieren acall messung_schnell ; schnelle Messung mit Protokoll im ext. RAM jb TF0,oszimodus_fehler ; bei Timerüberlauf: melde Fehler oszi_messschleife1: jnb TF0,oszi_messschleife1 ; auf T0 warten clr TF0 ; Timer quittieren acall do_pwm acall set_timer_schnell ; Timer für schnelle Messung neuprogrammieren acall messung_schnell ; schnelle Messung mit Protokoll im ext. RAM jb TF0,oszimodus_fehler ; bei Timerüberlauf: melde Fehler ; schon 510 Messungen durchgeführt? (2 Messungen pro Schleifendurchlauf!!) djnz TMP,oszi_messschleife ; 512 Messungen durchgeführt -> Protokoll ausgeben ab wraparoundstelle (Messung Pretrigger) mov DPH,BUFPTR_H mov DPL,BUFPTR_L acall dumpexternal ; Messprotokoll ausgeben mov MAINCTRL,#SIGNAL_OK ; Kontrolle zurück an die Hauptschleife mit SIO setb ES ; SIO wieder an pop ACC ret ; *********************** ; Fehlerroutine Oszimodus ; *********************** ; falls die Messung länger dauerte, als das eingestellte Messintervall, so ; wird der Oszimodus hier beendet und als Fehlermeldung ein einzelnes "!" ; versendet oszimodus_fehler: clr OSZI_DISPLAY ; Anzeige "kein Oszimodus" mov MAINCTRL,#SIGNAL_OK ; Kontrolle zurück an die Hauptschleife mit SIO mov A,#'!' ; ! laden acall SendChar ; versenden setb ES ; SIO wieder an pop ACC ret ; ************ ; dumpinternal ; ************ ; Messwerte aus dem internen Speicher auf die SIO ausgeben ; Messwerte im IRAM sind RAW - werden hier konverteirt, bevor sie ausgegeben werden dumpinternal: push ACC mov A,#'a' ; aktuelle Messung acall SendChar mov r0,#messpuffer mov r1,#6 ; Anzahl der Kanäle, die ausgegeben werden sollen send_aktuell: mov A,#' ' acall SendChar mov a,@r0 ; Hi-byte lesen mov TMPH,a ; temporär ablegen inc r0 mov a,@r0 ; LO-Byte lesen inc r0 mov TMPL,a acall konv_readable ; Messwert ins lesbare Format bringen mov a,TMPH ; Hi-Byte lesen anl a,#0x0f ; oberen Teil merken acall Nybble2ASCII acall SendChar ; unteres Nybble in HEX ausgeben mov a,TMPL ; konvertiertes Lo-Byte lesen acall SendHEX ; in HEX ausgeben djnz r1,send_aktuell ; CR/LF senden mov A,#CR acall SendChar mov A,#LF acall SendChar clr TI stop_idump: pop ACC ret ; *********************************** ; 512 Messungen aus ext. RAM ausgeben ; *********************************** ; (nach erfüllter Triggerbedingung) ; DPTR muss vorher mit der gewünschten Startadresse im Ringpuffer geladen werden dumpexternal: push ACC setb OSZI_DISPLAY ; LED für "Oszimessung wird übertragen" setzen ; Paketzähler mov PWM_CNT_H,#0x00 ; da wir während der Ausgabe keine PWM generieren mov PWM_CNT_L,#0x00 ; kann der PWM Zähler als Paketzähler mitbenutzt werden send_record_extRAM: mov A,#'z' ; Dump einer schnellen Oszimessung acall SendChar mov r1,#4 ; Anzahl der Kanäle, die ausgegeben werden sollen (4 - nach schneller Messung) send_extRAM: mov A,#' ' acall SendChar movx a,@dptr ; Hi-Byte lesen inc dptr mov TMPH,a movx a,@dptr ; LO-Byte lesen inc dptr mov TMPL,a acall konv_readable ; rohe Messwerte lesbar machen mov a,TMPH ; konvertiertes Hi-Byte lesen anl a,#0x0f ; nur unteres Nybble acall Nybble2ASCII acall SendChar ; unteres Nybble in HEX ausgeben mov a,TMPL ; konvertiertes Lo-Byte... acall SendHEX ; in HEX ausgeben djnz r1,send_extRAM mov A,#' ' acall SendChar ; Paketzähler ausgeben mov A,PWM_CNT_H ; hi-Byte Zähler anl A,#0x0f ; unteres Nybble acall Nybble2ASCII acall SendChar mov A,PWM_CNT_L ; lo-Byte Zähler acall SendHEX ; CR/LF senden mov A,#CR acall SendChar mov A,#LF acall SendChar cpl OSZI_DISPLAY ; LED für Oszi-Modus toggeln während der Übertragung ; Pakete mitzählen mov A,PWM_CNT_L add A,#1 ; Lo-Byte des Zählers erhöhen mov PWM_CNT_L,A jnc nc_zaehler ; Übertrag? inc PWM_CNT_H ; ja, dann auch Hi-Byte erhöhen nc_zaehler: ; Ende des Puffers im ext. RAM erreicht? mov A,DPH cjne A,#0x30,no_dptr_wrap mov dptr,#buffer ; zurück an den Anfang vom Ringpuffer no_dptr_wrap: mov A,PWM_CNT_H ; Paketzähler=512? cjne A,#0x02,send_record_extRAM ; nein, dann nächstes Paket senden clr OSZI_DISPLAY ; Indikator LED löschen clr TI pop ACC ret ; ****************************************** ; Timer 0 neuprogrammieren - schneller Modus ; ****************************************** ; hält T0 an, programmiert den neuen Zählwert und startet den Timer neu set_timer_schnell: clr TR0 ; stop T0 mov TH0,TIMER_H ; neue Timereinstellung in Timerregister kopieren mov TL0,TIMER_L setb TR0 ; Timer starten ret ; *********************************************************** ; Timer 0 neuprogrammieren - langsamer/kontinuierlicher Modus ; *********************************************************** ; hält T0 an, programmiert den neuen Zählwert für langsame Messung und startet den Timer neu set_timer_langsam: clr TR0 ; stop T0 mov TH0,#0x04 mov TL0,#0x00 setb TR0 ; start T0 ret ; ************************************ ; AD Messung für einen Kanal (Polling) ; ************************************ ; Kanalnr. 0-7 des Multiplexers in A, schreibt Messwert nach erfolgter Wandlung in TMPH, TMPL ; A wird überschrieben ad_messung: ; mov ADCON0,#0x07 ; Kanalnr A/D Controllregister schreiben , Prescaler :4, Analogkanal 7 ; mov ADCON1,#0x07 mov ADDATL,A ; Messung starten (Dummy nach ADDATL schreiben) ; Analog Select BUS auf nächsten Kanal setzen ; etwas warten, damit Messung schon begonnen hat, bevor der nächste Kanal als Eingang ; genommen wird nop nop nop inc A acall select_analog ; schon mal nächsten Kanal anwählen für folgende Messung wait_sample: jb ADCON0.4,wait_sample ; Warten auf BSY Flag ; Messwerte lesen und in 3-Nybble-Format bringen mov TMPH,ADDATH ; Messwerte lesen.. mov A,ADDATL ; und in Konvertierungsregister schreiben anl A,#0xC0 ; unnötige Bits im AD-Lowbyte ausmaskieren mov TMPL,A ret ; ************************************************************************************ ; konv_readable - konverteirt rohe Messwerte vom AD Wandler isn lesbare 3-Nybbleformat ; ************************************************************************************ ; Ein- und Rückgabe in TMPH TMPL konv_readable: push ACC push PSW mov A,TMPH ; Hi-Byte des Messwertes rl A rl A anl A,#0xFC ; untere 6 Bit des AD-Highbytes hoch shiften... mov r5,A ; und maskiert merken mov A,TMPL swap A ; Nybbles tauschen rr A rr A orl A,r5 ; gemerkte 6 Bit mit den 2 gültigen Bit des AD-Lowbyte mischen mov TMPL,A ; als neues Low-byte speichern mov A,TMPH ; Hi-Byte runtershiften swap A ; Nybbles tauschen (spart 4 Shifts) rr A rr A anl A,#0x03 ; die beiden gültigen Bits ausmaskieren mov TMPH,A ; als neues Hi-byte speichern pop PSW pop ACC ret ; ********************************************************************* ; select_analog - Auswahl des zu messenden Kanals am Analog-Multiplexer ; ********************************************************************* ; A enthält Nummer des einzulesenden Analogkanals am Multiplexer ; (nicht am AD_Wandler, der liest immer EAN7) select_analog: push ACC push PSW anl A,#0x07 ; nur gültige Bits im ACC maskieren mov TMP_BUS,A ; temporär ablegen mov A,P1 ; Bus einlesen anl A,#11111000b ; Bus maskieren orl A,TMP_BUS ; neuen Kanal in den Bus odern mov P1,A ; Bussignale setzen pop PSW pop ACC ret ; ******************** ; AD Messung - langsam ; ******************** ; 8 Analogkanäle messen, im internen Speicher ablegen messung_langsam: push ACC clr A acall select_analog ; ersten Kanal (Q1) auswählen nop nop nop ; warten, bis Kanal im Multiplexer auch wirklich selektiert wurde mov r0,#messpuffer ; Index Register mov r1,#6 ; Schleifenzähler mov r2,#0 ; Vorwärtszähler für den auszumessenden Kanal messschleife: mov A,r2 ; Nummer des zu messenden Kanals in ACC inc r2 ; Kanalnr. für nächsten Schleifendurchgang erhöhen acall ad_messung ; Kanal (in A) messen ; Messwerte aus den temporären Registern in den IRAM schreiben mov @r0,TMPH inc r0 mov @r0,TMPL inc r0 djnz r1,messschleife ; nächsten Kanal messen ; Zähler für Messungen erhöhen mov A,BUF_L add A,#1 ; Lo-Byte des Zählers erhöhen mov BUF_L,A jnc nc_buf ; Übertrag? inc BUF_H ; ja, dann auch Hi-Byte erhöhen nc_buf: mov A,BUF_H cjne A,#2,no_buf_reset ; 512 Messungen durchgeführt? (0x2?? > 511) ; ja -> Zähler resetten mov BUF_L,#0 mov BUF_H,#0 no_buf_reset: pop ACC ret ; ******************** ; AD Messung - schnell ; ******************** ; 8 Analogkanäle messen, im internen und externen Speicher ablegen ; extern: Logging der Messwerte ; intern: für schnellen Zugriff und Vergleich mit Trigger messung_schnell: push ACC mov A,#0x2 ; ab Kanal 2 (M1-M4) messen acall select_analog nop nop nop ; Zeit vertrödeln, damit Kanal auch selektiert wird mov r0,#messpuffer ; Index Register mov r1,#4 ; Schleifenzähler - nur 4 Kanäle messen! mov r2,#2 ; Vorwärtszähler für den auszumessenden Kanal (Beginn ab Kanal 2) messschleife_schnell: mov A,r2 ; Nummer des zu messenden Kanals in ACC inc r2 ; Kanalnr. für nächsten Schleifendurchgang erhöhen acall ad_messung ; *** Messwert in den externen RAM schreiben *** ; DPTR laden mov DPH,BUFPTR_H mov DPL,BUFPTR_L mov A,TMPH movx @dptr,A ; Hi-byte des Messwertes im ext. RAM ablegen inc dptr mov A,TMPL movx @dptr,A ; Lo-Byte des Messwertes im ext. RAM ablegen inc dptr mov A,DPH cjne A,#0x30,nodptrwrap ; Hi-Byte DPTR mit 0x30 vergleichen ; ja: DPTR jetzt auf 0x30xx -> Wraparound des DPTR und neuladen mit Bufferbeginn mov dptr,#buffer nodptrwrap: ; DPTR schreiben (und merken), da er vom restlichen Hauptprogramm ggfs. verwendet wird mov BUFPTR_H,DPH mov BUFPTR_L,DPL djnz r1,messschleife_schnell ; nächsten Kanal messen ; Zähler für Messungen erhöhen mov A,BUF_L add A,#1 ; Lo-Byte des Zählers erhöhen mov BUF_L,A jnc nc_buf2 ; Übertrag? inc BUF_H ; ja, dann auch Hi-Byte erhöhen nc_buf2: mov A,BUF_H cjne A,#2,no_buf_reset2 ; 512 Messungen durchgeführt? (0x2?? > 511) ; ja -> Zähler resetten mov BUF_L,#0 mov BUF_H,#0 no_buf_reset2: pop ACC ret ; ********************** ; Generierung PWM Signal ; ********************** do_pwm: push ACC push PSW ; PWM-Hauptzähler inkrementieren clr C mov A,PWM_CNT_L add A,#1 ; WARNING: inc setzt C bei overflow nicht!!!!!!!!!!!!!!!!!!!! mov PWM_CNT_L,A jnc no_pwm_cnt_c ; c? inc PWM_CNT_H ; dann Hi-Byte der PWM mitzählen no_pwm_cnt_c: ; sonst nicht... mov A,PWM_STATE ; zählen wir Hi oder Lo Pulse? cjne A,#PWM_NEGATIV,pwm_positiv_zaehlen ; zählen der Loflanken der PWM pwm_negativ_zaehlen: mov r2,TASTV_NOPULS_H mov r3,TASTV_NOPULS_L mov r4,PWM_CNT_H mov r5,PWM_CNT_L acall subb16_16 acall test_r67_is_0 jc pwm_overflow ; sonst normal raus pop PSW pop ACC ret ; zählen der Hi-flanken der PWM pwm_positiv_zaehlen: mov r2,TASTV_PULS_H mov r3,TASTV_PULS_L mov r4,PWM_CNT_H mov r5,PWM_CNT_L acall subb16_16 acall test_r67_is_0 jc pwm_overflow ; sonst normal raus pop PSW pop ACC ret ; PWM-Zähler Überlauf? pwm_overflow: ; hier gehts weiter, wenn wir einen Überlauf des PWM Zählers festgestellt haben mov PWM_CNT_H,#0 ; Zentralen PWM zähler resetten mov PWM_CNT_L,#0 ; PWM status togglen mov A,PWM_STATE cpl A ; PWM Status negieren mov PWM_STATE,A cjne A,#PWM_POSITIV,pwm_flanke_negativ ; PWM Signal positiv? ; PWM positiv? ja inc HICOUNT ; Zähler für positive Flanken erhöhen pwm_flanke_negativ: ; da die PWM immer auf Beginn mit negativer Flanke eingestellt wird, ist die Logik korrekt ; d.h. PWM OUT wird hi beim ersten Overflow des PWM zählers und umgekehrt cpl PWM ; PWM Ausgang toggeln pop PSW pop ACC ret ; *********************** ; Test, ob r6 r7 = 0x0000 ; *********************** ; C gesetzt, falls r6=0x00 && r7=0x00 test_r67_is_0: clr C cjne r6,#0,nope cjne r7,#0,nope setb C nope: ret ; ****************** ; PWM initialisieren ; ****************** init_pwm: push ACC push PSW mov PWM_STATE,#PWM_NEGATIV ; PWM mit negativer Flanke beginnen mov HICOUNT,#0 ; Reset Zähler für Hi-Flanken mov PWM_CNT_H,#0 ; Zentralen PWM zähler resetten mov PWM_CNT_L,#0 clr PWM ; PWM Ausgang auf Lo initialisieren pop PSW pop ACC ret ; ************************************ ; Statuspaket erzeugen und verschicken ; ************************************ ; Format: s hhh hhh hhhh\r\n ; - aktive PWM Puls Länge ; - inaktive PWM Puls Länge ; - Timerschrittweite send_status: push ACC push PSW mov A,#'s' ; 's' für Statuspaket acall SendChar mov A,#' ' acall SendChar mov A,TASTV_PULS_H anl A,#0x0f acall Nybble2ASCII acall SendChar mov A,TASTV_PULS_L acall SendHEX mov A,#' ' acall SendChar mov A,TASTV_NOPULS_H anl A,#0x0f acall Nybble2ASCII acall SendChar mov A,TASTV_NOPULS_L acall SendHEX mov A,#' ' acall SendChar mov A,TIMER_H ; Hi-Byte Zeitschrittweite acall SendHEX mov A,TIMER_L ; Lo-Byte Zeitschrittweite acall SendHEX mov A,#CR acall SendChar mov A,#LF acall SendChar pop PSW pop ACC ret ; ******* ; SendHEX ; ******* ; sende Zeichen in A in Hexnotation SendHEX: push ACC ; Zeichen zwischenspeichern anl a,#0xF0 ; oberes Nybble ausmaskieren rr a ; 4 mal runtershiften rr a rr a rr a acall Nybble2ASCII ; untere 4 Bit als ASCII Zeichen kodieren acall SendChar ;... und versenden pop ACC ; originales Zeichen wiederherstellen anl a,#0x0F ; unteres Nybble ausmaskieren acall Nybble2ASCII ; nach ASCII konvertieren acall SendChar ; Zeichen versenden.... ret ; ************ ; Nybble2ASCII ; ************ ; bildet das untere Nybble in A als Hexzeichen in ASCII-Kodierung ab, Ausgabe in A ; Wichtig: A darf nur das untere Nybble beinhalten, sonst gibt es Schrott! ; vor acall Nybble2ASCII anl A,#0x0f machen (oberes Nybbel in A ausblenden) ; 0000 -> '0' ; 0001 -> '1' ; ... ; 0110 -> 'A' ; ... ; 1111 -> 'F' Nybble2ASCII: push ACC ; Zeichen merken clr c subb a,#0x0A ; 10 abziehen, C gesetzt, falls Zeichen <10 war pop ACC ; originales Zeichen wiederherstellen jc ZahlToASCII ; es war eine Zahl -> ASCII-Zeichen für Zahl erzeugen add a,#55 ; sonst: ASCII Zeichen A...F erzeugen ret ZahlToASCII: add a,#48 ; ASCII-Zeichen für Zahl erzeugen ret ; ******** ; SendChar ; ******** ; Zeichen in A über die SIO verschicken SendChar: clr ES mov SBUF,A jnb TI,$ ; auf SIO warten clr TI ; SIO freimachen setb ES ret ; ******************************** ; ein Hexzeichen von der SIO lesen ; ******************************** ; liest ein einzelnes ASCII-Zeichen, interpretiert dieses als Hexnybble und konvertiert dieses in ein normales Byte ; d.h. 'A' -> 0xa, '1' -> 0x1 ; FIXME: fehlerhafte (nicht-HEX) Zeichen führen zu fehlerhaften Rückgabenybbles! read_hex_nybble: jnb RI,read_hex_nybble ; auf SIO warten mov A,SBUF ; Zeichen in A lesen clr RI ; SIO freigeben convert_hex_nybble: anl A,#0x7f ; auf 7 Bit verkleinern jb ACC.6,chn_Buchstabe ; Buchstabenzeichen? jb ACC.4,chn_Zahl ; Zahlzeichen? mov A,#0xFF ; Fehlerbyte zurückgeben ret ; .... zurück mit Fehler chn_Buchstabe: anl A,#0xF ; relevanten Teil maskieren add A,#0x9 ; Zehnerdekade mitzählen (-1, da 'A' 65 statt 64) ret chn_Zahl: anl A,#0xF ; reinen Zahlenwert ausmaskieren ret ; und zurück... ; ************************************** ; Byte in HEX-Notation von der SIO lesen ; ************************************** ; Hi-Nybble,Lo-Nybble ; Wert des Bytes in A read_hex_byte: acall read_hex_nybble ; Hi-Nybble des Hi-Bytes rl A rl A rl A rl A ; Ergebnis 4 mal hochshiften mov r5,#0 mov r5,A ; temporär speichern acall read_hex_nybble ; Lo-Nybble des Hi-Bytes orl A,r5 ; mit Hi-nybble verodern ret ; *********** ; RMAP setzen ; *********** set_rmap: push ACC mov A,SYSCON ; RMAP setzen setb ACC.4 mov SYSCON,A pop ACC ret ; ************ ; RMAP löschen ; ************ clr_rmap: push ACC mov A,SYSCON ; RMAP setzen clr ACC.4 mov SYSCON,A pop ACC ret ; ******************************* ; Unterprogramm 16bit Subtraktion ; ******************************* ; Eingaben: Zahl a r2 r3 - b r4 r5 = r6 r7 ; nach Ausführen C gesetzt, falls r45 > r23 subb16_16: push ACC clr ES ; SIO abschalten währen der Subtraktion (damit kein anderes Unterprogramm die Register ändert) mov A,R3 ; lo-byte a clr c ; Carry löschen! subb A,R5 ; abziehen Lo-byte b mov r7,a ; Lo-byte Ergebnis abspeichern mov a,r2 ; hi-byte a subb A,r4 ; Hi-Byte b abziehen mov r6,a setb ES pop ACC ret ; ******************************** ; Serial Interrupt Service Routine ; ******************************** SIO_isr: clr TI setb P1.5 push PSW ; kritische Register retten push ACC jb RI,reciever ; Zeichen empfangen? dann ab in die Reciever-Routine ; Serieller Interrupt ohne RI (z.Z. Transmit nur mit Polling) pop ACC ; Register wiederherstellen pop PSW clr P1.5 reti ; *********** ; SIO Recieve ; *********** ; Empfangsroutine - wird bei SIO Interrupt ausgeführt ; SIO IRQ wird kurzfristig abgeschaltet (solange bis die Auswertung der Eingabe abgeschlossen ist) reciever: clr ES ; temporär SIO abschalten, bis wir alle Zeichen gelesen und gepollt haben mov A,SBUF ; erstes empfangenes Zeichen in A ; ** Abfrage auf ESC? cjne A,#ESC,noESC mov MAINCTRL,#SIGNAL_TERMINATE ; ja -> dann Signal für Rückkehr zum Monitor in der Hauptschleife setzen ajmp end_recieve noESC: ; ** Abfrage auf Setzen der Puls-Länge für aktiven PWM Puls cjne A,#'P',noP_ clr RI acall read_hex_nybble ; Hi_nybble lesen anl A,#0x0F ; Wert ausmaskieren mov TASTV_PULS_H,A ; HI-Byte für die Länge aktiver PWM-Puls acall read_hex_byte ; Lo-Byte lesen mov TASTV_PULS_L,A ; Lo-Byte mov MAINCTRL,#SIGNAL_OK ajmp end_recieve ; Auswertung ENDE noP_: ; ** Abfrage auf Setzen der Puls-Länge für inaktiven PWM Puls cjne A,#'N',noN clr RI acall read_hex_nybble ; Hi_nybble lesen anl A,#0x0F ; Wert ausmaskieren mov TASTV_NOPULS_H,A ; HI-Byte für die Länge eines inaktiven PWM-Puls acall read_hex_byte ; Lo-Byte lesen mov TASTV_NOPULS_L,A ; Lo-Byte mov MAINCTRL,#SIGNAL_OK ajmp end_recieve ; Auswertung ENDE noN: ; ** Abfrage auf Statusmeldung cjne A,#'S',noS clr RI ; SIO quittieren mov MAINCTRL,#SIGNAL_STATUS ; Signal für Sendung eines Statuspaketes absetzen ajmp end_recieve noS: ; ** Abfrage auf Zeitschrittweite (Messintervall) cjne A,#'Z',noZ clr RI acall read_hex_byte ; Hi-Byte lesen mov TIMER_H_SHADOW,A ; Hi-Byte Timer-Delay speichern acall read_hex_byte ; Lo-Byte lesen mov TIMER_L_SHADOW,A ; Lo-Byte Timer-Delay speichern mov MAINCTRL,#SIGNAL_TIMER ; der mainloop signalisieren, daß das Timer Shadow neugeladen wurde ajmp end_recieve noZ: ; ** Abfrage auf Umschaltung in schnellen (Oszi-)Modus cjne A,#'X',end_recieve mov MAINCTRL,#SIGNAL_OSZI ; Signal setzen für Umschaltung in den Oszi-Modus ; am Ende RI killen end_recieve: pop ACC pop PSW clr RI setb ES ; gaaaanz wichtig: SIO IRQ wieder zulassen!!!!! reti ; ....und SIO Interrupt beenden ; Datensegment sio_jump: ljmp SIO_isr ; dieser Befehl wird in den SIO Vektor kopiert ; Rettungspeicher für alte Analogsettings save_analog: DW 0,0,0 ; Adresse für unseren 4x512 Worte Messwerte Puffer ORG 0x2000 buffer: DW 0 ORG 0x3000 buffer_end: DW 0 END