PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : assembler: "kommazahlen" addieren



Goblin
06.07.2005, 14:48
moin!


für einen servocontroller brauch ich kommazahlen (um nicht nur die maximalpositionen bei einer pulslänge von 1 oder 2 anfahren zu können). das doofe ist, dass mein basic-compiler nur ganze zahlen bei einer wait-operation zulässt. ich hatte das so vor:



define var pls as byte 'variable für die pulslänge
low rb.0
let pls = 1
pulse:
wait 20
high rb.0
wait pls
low rb.0 'einmal 20 ms low und dann high für pulslänge
if rb.1 = high then goto increase 'wenn taste an rb.1 gedrückt
goto pulse

increase:
let pls = pls + 0.1 'zu pls 0,1 addieren
if pls > 2 then pls = 1 'pls nicht zu groß werden lassen
wait 500 'kurze pause
goto pulse


das prog soll bei tastendruck den servo ein stück weiterfahren und bei endposition wieder an die anfangsposition gehen.
der compiler macht allerdings aus dem "+0,1" "+1", wodurch mein servo bei tastendruck die jeweils andere endposition (1 oder 2) anfährt.
jetzt hab ich mir überlegt, ich könnte das ganze in assembler vielleicht beheben, so dass ich den assembler-code direkt bearbeite und da eintrage, dass er nicht 1 sondern 0,1 dazuaddieren soll. der assembler-code den der compiler erstellt sieht an der stelle so aus:



;ø~ø00039 let pls = pls + 0.1 'addiere 0,1ms zu pls
movf 64,w
movwf 16
movf 64+1,w
movwf ARG0+1
movlw 1
movwf 18
clrf ARG1+1
call $$ADD16
movf 16,w
movwf 64
movf ERG+1,w
movwf 64+1
clrwdt
;ø~ø00040 'fertig


das was hinter den kryptischen zeichen (zeilennummern vermute ich) steht, ist er basiccode, direkt drunter dann der assembler

nun hab ich von assembler wenig ahnung und wollte mal die freaks hier fragen, was ich verändern muss.... hoffe ihr werdet schlau aus dem problem! :)

Radian
06.07.2005, 16:47
Ich kenne den Befehlssatz für den Microcontroller nicht , aber:
Können die Befehle die du nutzt überhaupt mit Kommazahlen umgehen?
Als ich mich mal mit dem MIPS2000 beschäftigt habe, gabs extra Befehle+Register fuer float Operationen. Ganz einfach deshalb, weil Integerarithmetik und Floatarithmetik intern anders gehandhabt wird. So hat der MIPS dann auch ne extra Floatingpointeinheit neben der ALU für die Ganzahlarithmetik.

Goblin
06.07.2005, 17:40
naja, es würde vielleicht schon helfen, wenn der wait-befehl in assembler höher aufgelöst wäre (nicht 1ms sondern vielleicht 0,1 ms... oder 1 µs...) ich dachte wenn die "sprache" so hardwarenah ist, wäre das vielleicht möglich

Mobius
06.07.2005, 18:53
Also, es ist logisch, dass du bei einem byte keine Kommas kannst (bytes gehen schon von Definition aus von 0 - 256 oder -128 - 128).

Ansonsten kann der PIC selber gar keine Kommazahlen, außer vielleicht, wenn du für eine Zahl 2 Bytes definierst, einen für die Komma und einen für die Ganzzahl.

Und das wait ist nur in Basic (zumindest guckt das hart danach aus) definiert, wenn du mal den Codeabschnitt posten könntest, den das Befehl "wait" generiert könnte ich dir vielleicht sagen, wie du es verschnellen könntest.
MfG
Mobius

Goblin
07.07.2005, 10:51
;ø~ø00026 wait 20
movlw 20
movwf 16
movlw 0
movwf ERG+1
movlw 165
movwf 18
call $$WAIT
clrwdt



wäre ein 20 ms-wait. wirst du daraus schlau? ansonsten versuche ich es mal mit pulseout, der kann kürzere impulse, aber ist scheisse zu proggen, weil ich nicht genau weiss, was der für nen puls macht (nen oszi wäre hilfreich).

edit:

so ungefähr versteh ich den code, aber was heißt dieses '"ERG+1"

Mobius
07.07.2005, 16:03
Also, ich bräuchte noch ein Codeschnipsel (such im Source-Code nach $$Wait). Dieser Teil Ladet nur einige Zahlen in den Register und ruft dann die Funktion $$Wait auf, welches, mMn den Register D'16' ausliest und dann so lange zu durchlaufen, bis es zu Ende ist.

Das ERG ist wohl eine Zahl, die irgendwo im Asm-Quelltext vom Compiler definiert wurde.

Ich hoffe nur, dass der Compiler den fertigen Asm-Quelltext nicht löscht, nachdem die Hex-File erstellt wurde.
MfG
Mobius

Goblin
07.07.2005, 18:46
$$WAIT movf ERG,w
iorwf ERG+1,w
jz $$WEND
movf ARG1,w
movwf ARG1+1


und der $$WEND:



$$WEND retlw 0



sooo. und nun schau mal, ob du das irgendwie schneller hinbekommst.

wenn du kurz zeit hast, wäre ich auch über ein fertiges prog seeeeehr erfreut. ich will einfach nur an rb.0 nen servo mit nem puls zwischen 1 und 2 ms (100µs-schritte) ansteuern. die pulslänge soll über einen button an rb.4 jeweils um 100µs hochgesetzt werden. wenn pulslänge > 2000µs, dann pulslänge = 1000 µs

wenn das wirklich jemand machen sollte *hoff*, dann hätte ich gerne die hex-datei. der pic ist nen 16F84A mit 4 mHz.

ich denke das wäre das richtige. sag mir dann bitte, welchen wert ich da

Mobius
11.07.2005, 18:06
OK, so schlau werd ich aus diesem Ding auch net :S

Also, ich hab mich mal hingesetzt und etwas zusammengespielt:


;usefull Macros and defines

list p=16F84A ; list directive to define processor
#include <p16F84A.inc> ; processor specific variable definitions



movlf MACRO register, literare
movlw literare
movwf register
ENDM

#define clock D'156'

;memory
CBLOCK 20h
c_timer ;starts with 10 and ends with 20,
;and together with timer0 it'll
;generate "interrupts" in an intervalle between 1ms
;and 2ms
t_timer
W_TEMP
STATUS_TEMP
ENDC

;data
org 0x000
goto main

org 0x004
goto interrupt

interrupt
;backup important registers
movwf W_TEMP ; Copy W to TEMP register,
swapf STATUS, W ; Swap status to be saved into W
movwf STATUS_TEMP ; Save status to STATUS_TEMP register

ISR
;Was it a PORTB-Interrupt?
btfsc INTCON,0
call portb_int

;Was it the timer?
btfsc INTCON,2
call timer_int

swapf STATUS_TEMP, W ; Swap nibbles in STATUS_TEMP register
; and place result into W
movwf STATUS ; Move W into STATUS register
; (sets bank to original state)
swapf W_TEMP, F ; Swap nibbles in W_TEMP and place result in W_TEMP
swapf W_TEMP, W ; Swap nibbles in W_TEMP and place result into W
retfie

;the portb-interrupt function
portb_int
bcf INTCON,3
;test, if we have 20 in the c_timer register
movfw c_timer
sublw D'20'
BZ port_is_null
incf c_timer
return
port_is_null
movlf c_timer,D'10'
return


;the timer-interrupt function
timer_int
bcf INTCON,2
;test if t_temp = c_temp
movlf TMR0,clock
movfw t_timer
subwf c_timer,W
BNZ timer_not_ready
clrf t_timer
banksel PORTA

btfss PORTA,0
goto set_it
bcf PORTA,0
return

set_it
bsf PORTA,0
return

timer_not_ready
incf t_timer
return

;the main routine
main
;Because we are using a 4MHz PIC,
;we will get set timer0 to a prescale of
;to get an overflow every 100µs.
movlf c_timer,D'10'
banksel OPTION_REG
clrf OPTION_REG

;setup PORTB
banksel PORTB
clrf PORTB
clrf PORTA
banksel TRISB
movlf TRISB,B'00010000' ;all Ports except rb.4 are outputs
;and should be pulled to GND or VCC
clrf TRISA

;enable Interrupts for timer0 and Portb-changes
movlf INTCON,B'10101000'

banksel TMR0
movlf TMR0,clock

;do nothing, interrupts make the rest
loop
goto loop

end


Das ganze Programm beruht auf eine etwas andere Art und Weise, wie du es machen wolltest:
Ich benutzt den timer0 mit einem Prescale von 1 (d.h. alle 1 µs eine erhöhung des TMR0's) und lade 156 (d.h. 100 µs bis zu einem overflow) in den TRM0. Danach setzt ich zwei Interrupts, eines ist der Timer selber, das Andere PortB.4 low --> high (c_timer 10 --> 20).
Wenn ein Timer interrupt passiert, checke ich mit einer Laufvariable von (10 --> 20 in 1er Schritten), wie viele µs ich schon hinter mir habe und wenn es = c_timer (counter_timer) ist, invertire ich das Bit PORTA.0 und lösche den t_timer register (temporary_timer). Wenn es dies nicht ist, erhöhe t_timer einfach um eines und verlasse den Interrupt.
Wenn ein PortB-change interrupt auftritt, schaue ich, ob c_timer = 20 ist, wenn ja dann wird c_timer = 10, wenn nicht, erhöhe ich c_timer um eins (1).

Dadurch dass der timer jeden 100µs überläuft und der software-prescale (ich nenn solche Laufvariablen halt so ;) ) 10-20 ist, erreiche ich eine Wartezeit von 1 ms --> 2ms in 100µs Schritten.

Leider habe ich mit Servos nicht viel am Hut, aber ich skiziere mal, was für einen Bild du mit einem Osci am Pin PORTA.0 erhälst (PortA musste ich deshalb wählen, weil beim Simulator irgendwie kein Interrupt ankam, wenn ich immer PORTB.0 verändert habe... interessant, ist aber so :-/ )


+-----1000µs-----+-----1000µs-----+-----1000µs-----+
|----------------|
-----------------| |-----------------


MfG
Mobius

€dit: ich habe den Code nur im Simulator von MPLAB getestet, d.h. ich kann nicht wirklich viel über das Timing aussagen, außer, dass es stimmen müsste.

€€dit: na, wieder einmal ein Beispiel gebracht, wie ein Code NICHT aussehen sollte ^^ Layout war noch nie meine Stärke gewesen...

Goblin
12.07.2005, 17:07
hey, das ganze is total nett von dir. aber ich glaube du hast da nen fehler gemacht. mit dem von dir skizzierten spannungsverlauf kannst du keinen servo steuern. das muss vielmehr so aussehen:

http://www.electronicsplanet.ch/Schaltun/Servo/Impulse.jpg
da wo 1,5ms steht muss die pulslänge halt 1 - 2 ms betragen. 1,5 ist in diesem fall die servo-mittelstellung

ich werd erstmal sehen, ob ich meinen eigenen code auf meinem 16f870 zum laufen bringe. ansonsten diskutieren wir an dieser stelle nochmal deinen code.

nochmal danke für deine mühe!

Mobius
12.07.2005, 17:28
oh... ja :D, wie gesagt, hatte keinen Schimmer, wie ein Servo anzusteuern sei ^_^ aber das ist keine Große Änderung, man muss nur eine weitere Variable einführen und in diesen 200 reinschreiben. Danach den Timer abwechselnd bis c_time und halt 200 rechnen lassen, das geht auch irgendwie (einfach abfragen, was auf dem PORTA.0 anliegt, ist's 0 dann 200, ansonsten c_time)...

Keine Ursache, gestern war mir sowieso fast den ganzen Tag fad, da war es eine angenehme Abwechlung ^_^
MfG
Mobius