Programm für einen simplen autonomen Roboter

  • Nabend Gemeinde,
    in den letzten Tagen war ich damit beschäftigt, einen kleinen autonomen Roboter zu bauen der mit Infrarotsensoren laufen sollte.

    Da sich vieleicht hier jemand mit geringen Elektronikkentnissen auch mal soetwas bauen möchte, dachte ich, stelle ich hier mal mein Programm rein.
    Das Programm ist in Assembler, und ausgelegt auf den Pic 16F870.
    Assemblerschreibsoftware ist MPlab von Microchip und Brennersoftware ist USBurn von Sprut.de

    Das Programm funktioniert auch mit allen anderen Pics, vorraussetzung:
    -AD Wandler
    -2-4Inputs
    -8Outputs
    und die config so wie die Speicherzeilen der Konstanten und Variablen müssen angepasst werden.

    Sensoren:
    Es sollten welche sein, die bei näherndem Objekt eine größerwerdende Signalspannung ausgeben, z.B Infrarotsensoren.
    Taster gehen aber auch Problemlos, dafür muss nichts im Porgramm geändert werden.

    -----------------------------------------------------------------------
    ;Autonom

    list p=16f870
    #include <p16f870.inc>

    __CONFIG _HS_OSC & _WDT_OFF & _PWRTE_ON & _CP_OFF & _BODEN_ON & _LVP_OFF & _CPD_OFF & _WRT_ENABLE_OFF & _DEBUG_OFF

    ; '__CONFIG' directive is used to embed configuration data within .asm file.
    ; The lables following the directive are located in the respective .inc file.
    ; See respective data sheet for additional information on configuration word.

    ; Konstanten festlegen
    freq Equ D'20' ; Frequenzangabe
    zschl1 Equ 0x20 ; Zähler für Zeit-Schleife
    zschl2 Equ 0x21 ; Zähler für Zeit-Schleife
    zschl3 Equ 0x22 ; Zähler für Zeit-Schleife
    motor Equ 0x23
    sensorschw Equ 0x24


    ;***** VARIABLE DEFINITIONS
    w_temp EQU 0x71 ; variable used for context saving
    status_temp EQU 0x72 ; variable used for context saving

    ; Macros festlegen
    b0 MACRO ; auf Bank 0 schalten
    bcf STATUS, RP0
    bcf STATUS, RP1
    ENDM

    b1 MACRO ; auf Bank 1 schalten
    bsf STATUS, RP0
    bcf STATUS, RP1
    ENDM

    b2 MACRO ; auf Bank 2 schalten
    bcf STATUS, RP0
    bsf STATUS, RP1
    ENDM

    b3 MACRO ; auf Bank 3 schalten
    bsf STATUS, RP0
    bsf STATUS, RP1
    ENDM

    b02 MACRO ; auf Bank 0 oder 2 schalten
    bcf STATUS, RP0
    ENDM

    b13 MACRO ; auf Bank 1 oder 3 schalten
    bsf STATUS, RP0
    ENDM


    ;**********************************************************************
    ORG 0x000 ; processor reset vector

    clrf PCLATH ; ensure page bits are cleared
    goto Init ; go to beginning of program


    ORG 0x004 ; interrupt vector location

    movwf w_temp ; save off current W register contents
    movf STATUS,w ; move status register into W register
    movwf status_temp ; save off contents of STATUS register


    Init

    b1
    movlw B'00000000'
    movwf TRISB
    movlw B'11111111'
    movwf TRISA

    bcf ADCON1,3
    bcf ADCON1,2
    bcf ADCON1,1
    bcf ADCON1,0
    b0

    ; ADC einschalten
    BSF ADCON0,0

    ; ADC speed für 20 MHz einstellen
    BSF ADCON0, 7 ; ADCS1=1
    BCF ADCON0, 6 ; ADCS0=0

    ; Daten rechtsbündig
    b1 ; Bank1
    BCF ADCON1, 7
    b0 ; Bank0
    ;---------------------------------------------------------
    movlw .xx ;<----xx durch gewünschten Wert ersetzen movwf sensorschw ; ( größer=frühere reaktion des bots
    ;******************************************

    main
    movlw .50 ;Wert bestimmt wie lange die Aktion ausgeführt wird
    call Zeit
    BCF motor,0
    BCF motor,1

    abfrage
    ; Sensor rechts auswerten
    BCF ADCON0, 5 ; ADCHS2=0
    BCF ADCON0, 4 ; ADCHS1=0
    BCF ADCON0, 3 ; ADCHS0=0
    movlw .2
    call Zeit
    BSF ADCON0, 2 ; ADC starten
    loop
    BTFSC ADCON0, 2 ; ist der ADC fertig?
    GOTO loop
    movlw sensorschw
    subwf ADRESH
    BTFSC STATUS,0
    BSF motor,0 ;Wenn Signal, dann bit setzen

    ; Sensor2 rechts auswerten
    BCF ADCON0, 5 ; ADCHS2=0
    BSF ADCON0, 4 ; ADCHS1=0
    BCF ADCON0, 3 ; ADCHS0=0
    movlw .2
    call Zeit
    BSF ADCON0, 2 ; ADC starten
    loop1
    BTFSC ADCON0, 2 ; ist der ADC fertig?
    GOTO loop1
    movlw sensorschw
    subwf ADRESH
    BTFSC STATUS,0
    BSF motor,0 ;Wenn Signal, dann bit setzen

    ; Sensor links auswerten
    BCF ADCON0,5 ; ADCHS2=0
    BCF ADCON0,4 ; ADCHS1=0
    BSF ADCON0,3 ; ADCHS0=0
    movlw .2
    call Zeit
    BSF ADCON0,2 ; ADC starten
    loop2
    BTFSC ADCON0,2 ; ist der ADC fertig?
    GOTO loop2
    movlw sensorschw
    subwf ADRESH
    BTFSC STATUS,0
    BSF motor,1 ;wenn Signal, dann bit setzen

    ; Sensor2 links auswerten
    BCF ADCON0,5 ; ADCHS2=0
    BSF ADCON0,4 ; ADCHS1=0
    BSF ADCON0,3 ; ADCHS0=0
    movlw .2
    call Zeit
    BSF ADCON0,2 ; ADC starten
    loop3
    BTFSC ADCON0,2 ; ist der ADC fertig?
    GOTO loop3
    movlw sensorschw
    subwf ADRESH
    BTFSC STATUS,0
    BSF motor,1 ;wenn Signal, dann bit setzen

    ;Motoren ansteuern
    BTFSC motor,0 ;Wenn kein Bit gesetzt wurde(kein Sig.) dann überspringen
    goto links ;linken Motor auf rückwärts
    ;Motor links vorwärts
    BSF PORTB, 0 ;N-Fet Rechtsunten der H-Brücke aus
    BCF PORTB, 1 ;P-Fet Linksoben der H-Brücke aus
    BSF PORTB, 2 ;P-Fet Rechtsoben der H-Brücke an
    BCF PORTB, 3 ;N-Fet Linksunten der H-Brücke an
    weiter ;Wenn linke Motoren auf rückwärts gestellt wurden, gehts hier weiter
    BTFSC motor,1 ;Wenn kein Bit gesetzt wurde, dann überspringen
    goto rechts ;rechte Motoren auf rückwärts
    ;Motor rechts vorwärts
    BSF PORTB, 4 ;N-Fet Rechtsunten der H-Brücke aus
    BCF PORTB, 5 ;P-Fet Linksoben der H-Brücke aus
    BSF PORTB, 6 ;P-Fet Rechtsoben der H-Brücke an
    BCF PORTB, 7 ;N-Fet Linksunten der H-Brücke an
    goto main

    links
    ;Motor links Rückwärts
    BCF PORTB, 2 ;P-Fet Rechtsoben der H-Brücke aus
    BSF PORTB, 3 ;N-Fet Linksunten der H-Brücke aus
    nop
    nop
    BCF PORTB, 0 ;N-Fet Rechtsunten der H-Brücke an
    BSF PORTB, 1 ;P-Fet Linksoben der H-Brücke an
    goto weiter
    rechts
    ;Motor rechts rückwärts
    BCF PORTB, 6 ;P-Fet Rechtsoben der H-Brücke aus
    BSF PORTB, 7 ;N-Fet Linksunten der H-Brücke aus
    nop
    nop
    BCF PORTB, 4 ;N-Fet Rechtsunten der H-Brücke an
    BSF PORTB, 5 ;P-Fet Linksoben der H-Brücke an
    goto main


    ;------------------------------------------------------------
    ; Unterprogramme

    ; Zeitschleife
    Zeit
    movwf zschl1 ; ms übertragen
    Zeit1
    nop ; warten
    nop
    nop
    nop
    nop
    nop
    nop
    nop
    nop
    nop
    nop
    nop
    nop
    nop
    nop
    movlw D'47' ; Zeitkonstante für 1ms
    movwf zschl2 ; übertragen
    Zeit2
    nop ; warten
    movlw freq ; Zeitkonstante für Frequenz
    movwf zschl3 ; übertragen
    Zeit3
    nop ; warten
    nop
    decfsz zschl3, F ; vorbei?
    goto Zeit3 ; nein, noch nicht
    decfsz zschl2, F ; vorbei?
    goto Zeit2 ; nein, noch nicht
    decfsz zschl1, F ; vorbei?
    goto Zeit1 ; nein, noch nicht
    return ; Zeit abgelaufen

    END ; directive 'end of program'


    Kleine Erklärung:
    Das Programm Schaltet nur zwischen Vorwärts und Rückwärts um, es wird dazwischen nich erst gebremst. Den kleinen Motoren macht das nichts. Bei großen Motoren, bzw einem schweren Bot muss eine Bremsfunktion eingefügt werden.


    Die
    "movlw .xx
    call Zeit"
    Befehle dienen dazu, eine bestimmte Zeit an millisekunden abzuwarten.


    Das Programm wandelt den analogen Sensorwert in einen Digitalen Wert zwischen 0 und 128 um. 0=0V,128=~5V

    Von diesem Wert wird dann ein frei wählbarer Wert subtrahiert.
    Haben wir nun ein negatives Ergebnis, ist das Sensorsignal unter dem grenzwert, und es wird ein Carrybit gesetzt.
    Ist es positiv, wird keines gesetzt.
    Nun wird geguckt ob das Carrybit gesetzt oder nicht gesetzt wurde.
    Theorie: Wurde eins gesetzt, ist noch genug Platz zwischen Wand und Roboter.
    Praxis: Seltsamerweise ist es in der Praxis genau andersherum. Wurde eins Gesetzt, ist der Abstand zur Wand unter dem Sollwert. Liegt eventuell an irgendwelchen Eigenheiten vom Chip. Sprich 0=5v, 128=0V


    Beschaltung:

    Sensoranschlüsse:
    An0=Sensorrechts
    An1=Sensorrechts
    An2=Sensorlinks
    An3=Sensorlinks

    Pinbelegung bzw Anschluss der H-Brücken:
    H-Brücke für links:
    PortB,0=PNP s1
    PortB,1=NPN s2
    PortB,2=NPN s1
    PortB,3=PNP s2

    H-brücke rechts:
    PortB,4=PNP s1
    PortB,5=NPN s2
    PortB,6=NPN s1
    PortB,7=PNP s2

    Es ist wichtig diese Belegung einzuhalten, da es sonst einen Kurzschluss gibt ;)

    s1=stufe1
    s2=stufe2

    Stufe 1 ist jeweils die linke Seite der H-Brücke
    und Stufe 2 jeweils die Rechte.

    Vorwiderstände:
    Als Basisvorwiderstände kann man 1k ohm nehmen, sofern man nur Minigetriebemotoren betreiben will.Wenn möglich größer, oder Transistoren mit hohem Verstärkungsfaktor.

    Pulldownwiderstände:
    Da die Sensoren ein positives Ausgangssignal haben, brauch man Pulldownwiderstände an den Eingängen.
    Diese dienen dazu, den Kondensator des ADCs wieder zu entladen.
    10Kohm reichen hier aus.


    Jeder der schonmal einen Pic programmiert hat, auch wenn es nur eine blinke Led war, sollte das Anschließen der H-Brücken hinbekommen.
    Falls fragen zum Programm oder zur Pinbelegung bestehen, fragt ruhig.


    Hoffe ich kann jemanden mit dem Programm etwas erfreuen :]

    Gruß Kai


    Ps: Die Aktionszeit nach main muss auf die Motorengeschwindigkeit angepasst werden. Umso schneller, umso kleiner.

    4 Mal editiert, zuletzt von panzerbot (24. Februar 2013 um 23:28)

    • Offizieller Beitrag

    Hab's mal durchgesehen. Soweit versteh' ich das, was hier passiert. Ein paar Verbesserungsvorschläge hätte ich noch. :D

    Zitat

    movlw .60 ;Umso größer, umso früher reagiert der Bot


    Dieser Wert ist die Vorgabe für die Schwelle. Wenn der Sensor 0x61 liefert, dann wird die Fahrtrichtung/Drehung umgeschaltet.
    Aber das hängt ganz gewaltig von den verwendeten Sensoren und der Umgebung ab. Mein Vorschlag wäre, diesen Wert zentral am Anfang des Programms zu definieren, damit er kurzfristig für alle Sensoren geändert werden kann, ohne dass man jedesmal im Programm einzeln suchen muss.
    SensSchw Equ 0x60 ; Schwellwert für den Sensor und die Richtungsumschaltung

    Im Programm dann:
    movlw SensSchw ; Vorgabewert für Schwellenauswertung laden

    Zitat

    ;Motor links vorwärts
    BSF PORTB, 0
    BCF PORTB, 1
    BSF PORTB, 2
    BCF PORTB, 3


    Ohne eine Kenntnis von der Beschaltung und der verwendeten Transistorart (z.B. High-Side sind P-FETs, ...) wird's hier schwierig. Die hast Du im Intro mitgeteilt. Aber mit der Vermischung der beiden Motoren und der jeweiligen Beschaltung an den Motoren wird's schwierig, die Übersicht zu behalten. Ich würde hier hinter jeder Zeile einen Hinweis setzen, was da gerade angesteuert wird.

    Nach meiner Erfahrung ist es günstig, zwischen dem Abschalten einer H-Seite und dem Zuschalten der anderen H-Seite eine kurze Verschnaufpause zu machen. (Stichwort: Ausräumzeiten der MOSFETs).

    Würde also dann so aussehen (Bezeichnungen natürlich falsch, weil ich die Beschaltung nicht genau gechecked habe und ehrlich gesagt auch keine Lust dazu hatte , hier "Re-Enginenering" zu betreiben):
    ;Motor links vorwärts
    BSF PORTB, 0 ; Abschalten P-FET links oben der H-Brücke
    BCF PORTB, 1 ; Abschalten N-FET rechts unten der H-Brücke
    NOP ; Pausenzeit für Abschaltung der FETs.
    NOP
    BSF PORTB, 2 ; Einschalten N-FET links unten der H-Brücke
    BCF PORTB, 3 ; Einschalten P-FET rechts oben der H-Brücke


    Das Unterprogramm "Zeit" ist hier starr integriert. Der Prozessor macht hier nichts anderes als auf der Stelle zu treten und zu warten, bis die vorgegebenen Zeitschleifen abgelaufen sind.
    Während dieser Zeit können Signalwechsel an anderen Sensoren nicht erfasst werden. Wenn's blöd läuft, verliert der Bot dann u.U. seine Orientierung oder knallt gegen ein Hindernis. (=> Details weis ich jetzt natürlich nicht, wie schnell dass der Bot fährt etc. . Aber nach meiner Meinung sollte das Abfragen der Analogeingänge immer so schnell wie möglich erfolgen, um auf Änderungen im Umfeld reagieren zu können.

    Verwundert bin ich, dass dir der Assembler beim Assemblieren keinen Alarm anschläg. Das Label "b1" kommt mehreremals vor. (?)

    .
    Interesse an Elektronik für Schaukampfroboter und Kettenfahrzeuge (Fahrtregler, ESC) ? => http://www.Robots.IB-Fink.de

    Leitspruch: "Wer will, findet Wege. Wer nicht will, findet Gründe."

  • Das mit als Variable festgelegten Subtraktionswert ist eine gute Idee, werde ich umändern.


    Die Erklärungen werde ich genau so einfügen.


    b1 und b0 sind einfach Macros, insofern dürfen die beliebig oft auftauchen. Spart einfach die beiden Status befehle ;)


    Edit: Meinst du das mit dem Unterprogramm Zeit, dass der das nach jedem Programmdurchlauf durchgeht oder wie? Nein, das stimmt nicht.
    Das wird nur zwischengeschaltet, z.B nach dem abfragen des ADCs, mit eine 2ms pause entsteht, da es sonst zu fehlern kommen kann.

    Einmal editiert, zuletzt von panzerbot (24. Februar 2013 um 23:23)