;Datei ASMHELP.A66: ;Hilfsroutinen für das C-Programm des A5-Projekts ;(Mikromechanischer Positionssensor) ; $SEGMENTED $CASE $DEBUG $ERRORPRINT $EXPDECNUM $MOD167 ;167er Befehle aktivieren $NOPAGING ;störende Header weglassen $NOSYMBOLS ;Symboltabelle im Listing weglassen ;$NOLIST ;ein Bug schaltet Listing komplett ab! $INCLUDE (REG167.INC) $LIST NCODE CGROUP ?PR?ASMHELP ;Codesegment ;NCONST DGROUP ?NC?ASMHELP ;Konstanten (in DGROUP??) NDATA DGROUP ?ND?ASMHELP ;Datensegment SDATA DGROUP ?ID?ASMHELP,SYSTEM ;ASSUME DPP1: NCONST ASSUME DPP2: NDATA ASSUME DPP3: SDATA ?PR?ASMHELP SECTION CODE PUBLIC 'NCODE' ;=============================== Allgemein ============================= PUBLIC BitReverse, MulDiv, MulDivL, DivR, DivRL PUBLIC MulDivW, MulDivWL, DivRW, DivRWL ; Alle denkbaren Traps zum Reset führen lassen NmiTrap PROC INTERRUPT NMITRAP=2 NmiTrap ENDP StoTrap PROC INTERRUPT STOTRAP=4 StoTrap ENDP StuTrap PROC INTERRUPT STUTRAP=6 StuTrap ENDP BTrap PROC INTERRUPT BTRAP=0Ah ; der Assembler 3.04 frisst kein "0x0A" srst BTrap ENDP BitReverse PROC NEAR ; R8=Wort mov r5,#16 ; Schleifenzähler br?1: ror r8,#1 ; Quellbit addc r4,#1 ; ins Ergebnis schaffen sub r5,#1 jmpr cc_nz,br?1 ret ; R4=bit-gedrehtes Wort BitReverse ENDP DivRL: ; R9:R8=Dividend, R10=Divisor DivR PROC NEAR ; Was passiert bei negativen Zahlen? mov mdl,r8 mov mdh,r9 jmp dr?1 ; weiter wie bei MulDiv DivR ENDP MulDivL: MulDiv PROC NEAR ; R8,R9=Faktoren, R10=Divisor mul r8,r9 dr?1: mov r4,r10 ; Divisor ashr r4,#1 ; halbieren add mdl,r4 ; halben Divisor draufaddieren ashr r4,#14 ; nur noch Vorzeichenbits in R4 addc mdh,r4 ; LongInt-Addition divl r10 mov r4,mdl mov r5,mdh ; ungenutzt bei Rückgabe von ret ; R4=Quotient, R5=Rest MulDiv ENDP DivRWL: ; R9:R8=Dividend, R10=Divisor DivRW PROC NEAR ; Was passiert bei negativen Zahlen? mov mdl,r8 mov mdh,r9 jmp dr?2 ; weiter wie bei MulDiv DivRW ENDP MulDivWL: MulDivW PROC NEAR ; R8,R9=Faktoren, R10=Divisor mulu r8,r9 dr?2: mov r4,r10 ; Divisor shr r4,#1 ; halbieren add mdl,r4 ; halben Divisor draufaddieren addc mdh,#0 ; LongInt-Addition divlu r10 mov r4,mdl mov r5,mdh ; ungenutzt bei Rückgabe von ret ; R4=Quotient, R5=Rest MulDivW ENDP ;=============================== Flash ============================= PUBLIC ProgSector ;AMD-Flash-Routinen (es wird stets wortweise in beide Flash-Chips gebrannt) ;Diese dürfen nur im RAM ausgeführt werden! Dafür sorgt ProgSector(). RamCodeStart: preamble PROC NEAR ; R5 = drittes Ausgabe-Wort, R9 = Segment bclr IEN mov r4,#0AAAAh ; Beachte im Folgenden: Adress-Verschiebung exts r9,#3 ; ...um 1 bit wegen 16-bit-Datenbus mov [r4],r4 ; "Schreibe AA auf Adresse xx555" ror r4,#1 ; R4 = #05555h mov 5554h,r4 ; "Schreibe 55 auf Adresse xxAAA" cmp r5,#0 ; (...Adresse [r4]=5555h gäbe Busfehler!) jmpr cc_Z,pre?1 exts r9,#1 mov 0AAAAh,r5 ; "Schreibe Befehl auf Adresse xx555" pre?1: ret preamble ENDP flashreset PROC NEAR ; R9 = Segment mov r4,#0F0F0h exts r9,#1 mov [r4],r4 ; Adresse ist ja egal! bset IEN bclr r4.0 ret ; liefert stets FALSE flashreset ENDP polling PROC NEAR ; R9:R8 = Adresse, R10 = Vergleichswert mov r4,r10 ; R4 für Byte-Adressierbarkeit po?1: exts r9,#1 ; "DATA# Polling" lt. Datenblatt mov r5,[r8] ; lese Flash-Adresse R9:R8 cmp r5,r4 jmpr cc_Z,po?ok ; stimmt überein exts r9,#1 mov r6,[r8] ; lese noch einmal (möglicherweise Glitch) jnb r5.5,po?2 ; ohne Bit 5 kein Byte-Vergleich cmpb rl6,rl4 jmpr cc_NZ,flashreset ; Fehler wenn Byte ungleich po?2: jnb r5.13,po?1 ; dito für den High-Teil cmpb rh6,rh4 jmpr cc_NZ,flashreset jmpr cc_UC,po?1 po?ok: bset r4.0 bset IEN ret polling ENDP ProgWord PROC FAR ; R9:R8 = Adresse, R10 = Wert mov r5,#0A0A0h callr preamble exts r9,#1 mov [r8],r10 callr polling ret ProgWord ENDP EraseSector PROC FAR ; R9:R8 = Adresse (vorzugsweise R8=0 oder 8000h) mov r5,#8080h callr preamble mov r5,#0 callr preamble mov r5,#3030h exts r9,#1 mov [r8],r5 ; geht nicht ins "preamble" wegen r8 mov r10,#0FFFFh callr polling ; auf FFFF warten ret EraseSector ENDP IsAMD256K PROC FAR ; R9 = Segment (svw. R9:R8 = Chip-Adresse) mov r5,#9090h callr preamble exts r9,#2 mov r6,0 mov r5,2 callr flashreset ; liefert das FALSE gleich mit(!) cmp r6,#0101h jmpr cc_NZ,is?err cmp r5,#2020h jmpr cc_NZ,is?err bset r4.0 ; OK bei Gleichheit is?err: ret IsAMD256K ENDP RamCodeEnd: ;Ende des Codes, der nur im RAM ausführbar ist; er enthält nur relative Sprünge ?ND?ASMHELP SECTION DATA PUBLIC 'NDATA' CodeBuf ds RamCodeEnd-RamCodeStart ;RAM-Bereich für den Flash-Code ?ND?ASMHELP ENDS %*DEFINE (CALLRAM(lbl)) ( DB 0DAh DB SEG CodeBuf DW SOF CodeBuf + (%lbl-RamCodeStart) ) ProgSector PROC NEAR ;R9:R8=Flash-Adresse, R11:R10=RAM-Adresse, R12=Länge mov r5,#CodeBuf mov r6,#SOF RamCodeStart mov r7,#RamCodeEnd-RamCodeStart ps?1: exts #SEG RamCodeStart,#1 mov r4,[r6+] mov [r5],r4 add r5,#2 sub r7,#2 jmpr cc_NZ,ps?1 ;calls SEG CodeBuf,SOF CodeBuf + (IsAMD256K-RamCodeStart) %CALLRAM(IsAMD256K) ;der Assembler 3.04 frisst das Konstrukt nicht mov rh4,#1 ;Fehler: Kein unterstütztes Flash jnb r4.0,ps?e mov r7,r10 ;ab jetzt R11:R7 = RAM-Adresse ;calls SEG CodeBuf,#SOF CodeBuf + (EraseSector-RamCodeStart) %CALLRAM(EraseSector) mov rh4,#2 ;Fehler beim Löschen jnb r4.0,ps?e bclr r12.0 ;nur gerade Anzahl Bytes nehmen cmp r12,#0 jmpr cc_Z,ps?e ;fertig, nur löschen ps?l: exts r11,#1 mov r10,[r7+] ;HUGE-RAM-Adresse lesen ;calls SEG CodeBuf,SOF CodeBuf + (ProgWord-RamCodeStart) %CALLRAM(ProgWord) mov rh4,#3 ;Fehler beim Brennen jnb r4.0,ps?e add r8,#2 sub r12,#2 jmpr cc_NZ,ps?l ps?e: ret ProgSector ENDP ;=============================== I²C ============================= PUBLIC i2c_init,i2c_start,i2c_stop,i2c_send,i2c_recv,i2c_bit1 SCL EQU P2.1 ; zum Glück ist Port2 bit-geschützt, das SDA EQU P2.2 ; erspart das Kopfzerbrechen bei Bitbefehlen DSCL EQU DP2.1 ; Aber bei der Konstruktion des Open-Drain- DSDA EQU DP2.2 ; Modus' waren Idioten am Werk!! Weglassen! CPUCLK EQU 20000000 ; CPU-Taktfrequenz in Hz I2CCLK EQU 100000 ; maximale I²C-Busfrequenz: 100 kHz I2CTMR EQU T8 ; verwendeter Timer-Kanal I2CTMRV EQU 8 ; gesetzter Vorteiler für Timer-Kanal ;Normalerweise ist SCL=H und SDA=H - an den "Übergabepunkten" zur Bitausgabe ;ist SCL=H und SDA=letzter Zustand, vor der nächsten Flanke ist zu warten. ?ID?ASMHELP SECTION DATA PUBLIC 'IDATA' i2ct dsw 1 ;letzter Stand der Uhr ?ID?ASMHELP ENDS i2c_wait PROC NEAR ; wartet mind. 5 µs, ohne Verschwendung bei Interrupts mov r6,I2CTMR ;Wehe, wenn I2CTMR(T8) nicht arbeitet! mov r5,r6 sub r6,i2ct cmp r6,#CPUCLK/I2CCLK/2/I2CTMRV ;Vorteiler einbeziehen! jmpr cc_ULT,i2c_wait mov i2ct,r5 ret i2c_wait ENDP i2c_start PROC NEAR bfldl P2,#6,#0 ;Port2-Ausgabetreiber auf LOW (Wegen Blinker?) call i2c_wait bset DSDA ret i2c_start ENDP i2c_init PROC NEAR ;führt sogleich eine Stop-Sequenz aus extr #1 mov i2ct,I2CTMR bfldl DP2,#6,#0 ;beides Eingänge, vorerst bfldl P2,#6,#0 ;Port2-Ausgabetreiber auf LOW ;hineinlaufen lassen, kein RET i2c_init ENDP i2c_stop PROC NEAR ;VR: R4,R5,R6 bclr r4.0 call i2c_bit call i2c_wait bclr DSDA ret i2c_stop ENDP i2c_send PROC NEAR ;PE: R8, PA: R4.0, VR: R4,R5,R6 mov r4,r8 ;Low-Teil=Bits mov rh4,#8 ;High-Teil=Bit-Zähler i2c?s: add rl4,rl4 ;Es gibt keinen byteweisen -Befehl addc rl4,#0 ;und so könnte man's emulieren call i2c_bit sub rh4,#1 jmp cc_NZ,i2c?s ;hineinlaufen lassen, kein RET i2c_send ENDP i2c_bit1 PROC NEAR bset r4.0 ;hochohmig machen (lassen) i2c_bit: ; ein Bit in R4.0 ausgeben und Ergebnis wieder in R4.0 liefern call i2c_wait bset DSCL nop ;Angst-NOP bmovn DSDA,r4.0 call i2c_wait bclr DSCL nop ;nicht unbedingt ein Angst-NOP jnb SCL,$ ;möglicher Hänger bei defekter I²C-Peripherie! bmov r4.0,SDA ret i2c_bit1 ENDP i2c_recv PROC NEAR ;PE: R15.0, PA: RL4, VR: R4,R5,R6 mov rh4,#8 i2c?r: add rl4,rl4 ;1 Bit Platz machen call i2c_bit1 ;macht R4.0 schon passend sub rh4,#1 jmp cc_NZ,i2c?r rol r4,#1 ;unten ein Bit Platz machen bmov r4.0,r15.0 call i2c_bit ;ACK-Bit schicken ror r4,#1 ret i2c_recv ENDP ?PR?ASMHELP ENDS END