Forum: Mikrocontroller und Digitale Elektronik Anfängerfrage: Motor Rechts-, Linkslauf mit PWM


von Rocco L. (kaufparkangucker)


Lesenswert?

Hallo,

zur Zeit versuche ich einen Motor über eine H-Brücke mit einem ATmega8 
anzusteuern. Ziel soll es sein das der motor in einer "for" schleife 
eine bestimmte Zeit rechts herum und anschliesend in einer zweiten 
Schleife links herum dreht.

Ich habe mir schon einige Tutorials, das Datenblatt usw. durchgelesen. 
Leider begreife ich nicht warum OCR1A und OCR1B nicht in die Schleife 
dürfen. Steht nur einer dieser beiden Werte auserhalb der While geht 
alles wuderbar - zumindest in eine Richtung. Stehen beide auserhalb der 
while beinflussen sie sich gegenseitig entsprechend.

Wie mus man das machen das der Motor erst rechts und dann links herum 
läuft?

Hier mein bisheriger Code - zum größten Teil aus dem Tutorial von dieser 
Seite:
1
/* Standard Includes */
2
//#include <avr/interrupt.h>
3
#include <avr/io.h>
4
#define F_CPU 8000000UL     /* Quarz mit 8 Mhz */
5
#include <util/delay.h>
6
#include <stdint.h>
7
#include <inttypes.h>
8
 
9
10
#define Draht1     _BV(PB1)    //Motor vorwärts    
11
#define Draht2     _BV(PB2)    //Motor rückwärts    
12
    
13
14
15
int main(void)
16
{
17
  DDRB = 0xff;                      /* alle B Port Pins sind Outputs */
18
  PORTB = 0x00;                     /* fangen alle bei 0V an */
19
20
  const uint16_t delay = 1000;
21
  // OC1A auf Ausgang
22
  //DDRB = (1 << PB1 );    // OC1A auf Ausgang
23
  //DDRB = (1 << PB2 );  // OC1B auf Ausgang
24
  //
25
  // Timer 1 einstellen
26
  //  
27
  // Modus 14:
28
  //    Fast PWM, Top von ICR1
29
  //
30
  //    WGM13    WGM12   WGM11    WGM10
31
  //      1        1       1        0
32
  //
33
  //    Timer Vorteiler: 1
34
  //     CS12     CS11    CS10
35
  //       0        0       1
36
  //
37
  //  Steuerung des Ausgangsport: Set at BOTTOM, Clear at match
38
  //     COM1A1   COM1A0
39
  //       1        0
40
 
41
  TCCR1A = (1<<COM1A1) | (1<<WGM11)| (1<<COM1B1);
42
  TCCR1B = (1<<WGM13) | (1<<WGM12) | (1<<CS10);
43
 
44
  //  den Endwert (TOP) für den Zähler setzen
45
  //  der Zähler zählt bis zu diesem Wert
46
 
47
  ICR1 = 0x6FFF;
48
  
49
50
  //OCR1A = 0x3FFF;     //Geschwindigkeit für vorwärts
51
  //OCR1B = 0x6FFF;    //Geschwindigkeit für rückwärts
52
53
54
 while( 1 ){
55
    
56
   for (int i=0; i<100; i++){
57
  OCR1A = 0x3FFF;
58
  _delay_ms(delay);
59
  PORTB |= Draht1;      //Motor vorwärts
60
  _delay_ms(delay);
61
   }
62
63
   for (int k=0; k<100; k++){
64
  OCR1A = 0x6FFF;
65
  _delay_ms(delay);
66
  PORTB |= Draht2;      //Motor rückwärts
67
  _delay_ms(delay);
68
   }
69
70
 }  
71
}

Danke für die Hilfe

fg Rocco

von STK500-Besitzer (Gast)


Lesenswert?

_delay mit Variablen funktioniert - soweit ich das weiß - nicht (ich 
benutze es nicht).

von Falk B. (falk)


Lesenswert?


von Rocco L. (kaufparkangucker)


Lesenswert?

Hallo,

STK500-Besitzer schrieb:
> _delay mit Variablen funktioniert - soweit ich das weiß - nicht (ich
> benutze es nicht).

_delay_xs funktioniert mit Variablen. Hat bei einer älteren Bastelei 
auch funktioniert. Trotzdem habe ich die Variable erst mal gegen eine 
1000 getauscht.

Falk Brunner schrieb:
> http://www.mikrocontroller.net/articles/AVR-GCC-                           > 
Tutorial#Warteschleifen_.28delay.h.29

Daher habe ich das Grundgerüst für meinen Code. Ich Habe das Register 
TCCR1A um COM1B1 erweitert damit auch der Pin PB2 als OC1B angesprochen 
wird. Damit müsste ich ja eigentlich 2 Pins haben die über den Timer1 
angesprochen werden. Jetzt, dachte ich mir, braucht jeder Pin nur noch 
einen Vergleichswert, um so unterschiedliche Geschwindigkeiten zu 
erzeugen.
Somit hatte ich in der zweiten for Schleife ursprünglich OCR1B = 0x6FFF; 
statt OCR1A = 0x6FFF; stehen - aber das alles funktioniert nicht. der 
Motor dreht dabei entweder immer nur in eine Richtung - oder alles ist 
so falsch, dass der Motor garnicht dreht.

Danke für weitere Tips

fg Rocco

von Christian T. (shuzz)


Lesenswert?

Ohne jetzt die Timer-Einstellungen genau angeschaut zu haben:
Ich verstehe Deine while-Schleife nicht.

Du willst doch die H-Brücke per Hardware-PWM ansteuern, richtig?
Dazu setzt Du in die OCR1A/B Register einfach den jeweiligen Wert und 
gehst danach in eine delay-schleife.

Draht1 und Draht2 scheinen die Drehrichtung anzugeben, vllt. solltest Du 
einen der Ports einfach abschalten bevor Du den anderen aktivierst?
Nicht dass Dir noch die Brücke durchschiesst.

btw, ein Schaltplan Deiner H-Brücke wäre hilfreich. Hast Du ein 
Treiber-IC genommen oder diskret aufgebaut?

von Rocco L. (kaufparkangucker)


Angehängte Dateien:

Lesenswert?

Christian T. schrieb:
> Ohne jetzt die Timer-Einstellungen genau angeschaut zu haben:
> Ich verstehe Deine while-Schleife nicht.
>
> Du willst doch die H-Brücke per Hardware-PWM ansteuern, richtig?
> Dazu setzt Du in die OCR1A/B Register einfach den jeweiligen Wert und
> gehst danach in eine delay-schleife.
>
> Draht1 und Draht2 scheinen die Drehrichtung anzugeben, vllt. solltest Du
> einen der Ports einfach abschalten bevor Du den anderen aktivierst?
> Nicht dass Dir noch die Brücke durchschiesst.
>
> btw, ein Schaltplan Deiner H-Brücke wäre hilfreich. Hast Du ein
> Treiber-IC genommen oder diskret aufgebaut?

Hallo Christian,

Das abschalten des Drahtes habe ich jetzt so gelöst:
1
 while( 1 ){
2
    
3
   for (int i=0; i<100; i++){
4
    OCR1A = 0x3FFF;
5
    _delay_ms(delay);
6
    PORTB |= Draht1;      //Motor vorwärts
7
    _delay_ms(delay);
8
   }
9
    PORTB &= ~Draht1;
10
   for (int k=0; k<100; k++){
11
   
12
    OCR1B = 0x6FFF;
13
    _delay_ms(delay);
14
    PORTB |= Draht2;      //Motor rückwärts
15
    _delay_ms(delay);
16
   }
17
    PORTB &= ~Draht2;
18
 }

Leider dreht der Motor trotzdem nur in eine Richtung. Was auch seltsamm 
ist ist, dass der Motor ca 2-3 min langsamm und dann 2min schneller 
dreht

Die H Brücke habe ich mal fix geEAGLEed - siehe Anhang

von Rocco L. (kaufparkangucker)


Lesenswert?

OK, Habe es raus gefunden. Ich muste nur noch die Schleife verkürzen.

 while( 1 ){

   for (int i=0; i<1; i++){
    OCR1A = 0x3FFF;
    _delay_ms(delay);
    PORTB |= Draht1;      //Motor vorwärts
    _delay_ms(delay);
   }
    OCR1A = 0x0000;
    PORTB &= ~Draht1;
   for (int k=0; k<1; k++){

    OCR1B = 0x3FFF;
    _delay_ms(delay);
    PORTB |= Draht2;      //Motor rückwärts
    _delay_ms(delay);
   }
    OCR1B = 0x0000;
    PORTB &= ~Draht2;
 }

Danke an alle

fg Rocco

von Klaus (Gast)


Lesenswert?

Kannst du mir mal erklären, was die for-Schlaufen deiner Meinung nach da 
tun sollen?!?

von Stephan V. (orca)


Lesenswert?

Hi Rocco,

mir ist nicht ganz klar, was du eigentlich machen willst, aber ich 
glaube, dass du was anderes im Sinn hast, als du programmiert hast.
1
   for (int i=0; i<1; i++){  // ist wohl eine sehr kurze for-schleife von 0 bis 0???
2
    OCR1A = 0x3FFF;      // ok hier wird PWM für vorwärts mit 57% aktiviert (dein max ist ja laut erstem Posting 0x6FFF)
3
    _delay_ms(delay);    // dank PWM dreht sich der Motor jetzt mal selbstständig
4
    PORTB |= Draht1;     // => kurzes reinpfuschen in die PWM, wozu das???
5
    _delay_ms(delay);    // Motor dreht weiter
6
   }
7
    OCR1A = 0x0000;      // PWM ausschalten
8
9
   ... // und das ganze nochmal in die andere Richtung

Ist das wirklich das was du machen wolltest?
Probier mal zu Testzwecken ein kleines Programm, dass den Motor mal 5 
Sek. mit 50% vorwärts und anschließend 5 Sek. rückwärts dreht und dann 
den Motor abstellt.
(kleiner Tipp: du brauchst dafür keine for Schleifen und überleg dir wo 
dein while(1) hinkommt.)

by(e)
Stephan

von Thilo M. (Gast)


Lesenswert?

Die H-Brücke funktioniert so?
Sollten T1 und T2 nicht besser PNP sein?

von Rocco L. (kaufparkangucker)


Lesenswert?

Hallo,

das mit der zu kurzen for Schleife 0 bis <1 ist natürlich mathematisch 
totaler Unsinn. Habe jetzt noch mal mit meinen geringen 
Programmierkenntnissen an meinem Quelltext herum gefuscht.
1
#include <avr/io.h>
2
#define F_CPU 8000000UL     /* Quarz mit 8 Mhz */
3
#include <util/delay.h>
4
#include <stdint.h>
5
#include <inttypes.h>
6
 
7
8
#define Draht1     _BV(PB1)    //Motor vorwärts    
9
#define Draht2     _BV(PB2)    //Motor rückwärts    
10
    
11
uint8_t hallsensor;
12
13
int main(void)
14
{
15
  DDRB = ((1<<DDB1) | (1<<DDB2));   /* B Port Pins sind Outputs */
16
  DDRB&= ~(1<<DDB0);        /* B Port Pins sind Inputs*/
17
  PORTB |=(1<<PB0);          /*interne Pull up Wiederstände ein*/
18
  PORTB = 0x00;                     /* fangen alle bei 0V an */
19
20
  const uint16_t delay = 3000;
21
22
  hallsensor = PB0;          // Status von PB0 in hallsensor kopieren
23
24
  //-----PWM--TIMER--EINSTELLEN----------------------
25
                          //    |
26
  TCCR1A = (1<<COM1A1) | (1<<WGM11)| (1<<COM1B1);//  |
27
  TCCR1B = (1<<WGM13) | (1<<WGM12) | (1<<CS10);//   |
28
                          //    |
29
  ICR1 = 0x6FFF;// ENDWERT DES ZÄHLERS       //    |
30
  //-------------------------------------------------
31
32
// while( 1 ){
33
    
34
   for (float i=0; i<124000; i++){
35
    OCR1A = 8186;      //Geschwindigkeit
36
    PORTB |= Draht1;        //Motor vorwärts
37
   }
38
    PORTB &= ~Draht1;  //Motor aus
39
    OCR1A = 0x0000;    //Geschwindigkeit 0
40
    _delay_ms(delay);  //Warten wegen Trägheit des Motors
41
   for (float k=0; k<124000; k++){
42
    OCR1B = 8186;      //Geschwindigkeit
43
    PORTB |= Draht2;        //Motor rückwärts
44
   }
45
    PORTB &= ~Draht2;  //Motor aus
46
    OCR1B = 0x0000;    //Geschwindigkeit 0
47
    _delay_ms(delay);  //Warten wegen Trägheit des Motors
48
// }  
49
}

 Ich finde das mit der for Schleife ja auch nicht so toll, schließlich 
erzeugt diese ja bereits so was wie eine PWM. Wie ich das allerdings 
ganz ohne Schleifen erleding sollte weis ich leider nicht. Bin halt noch 
ein Anfänger. Allerdings würde ich mich über weitere Tips sehr freuen.

Später soll mal an irgend einem Pin "gehorcht" werden und je nachdem was 
für ein binäres Signal anliegt soll der Motor bzw das Getriebe eine 
bestimmte Anzahl an Umdrehungen links oder rechts herum drehen. Die 
Umdrehungen messe ich z.Z. mit einem Hallsensor am Ende des Getriebes.

Danke für Eure Hilfe

fg Rocco

von STK500-Besitzer (Gast)


Lesenswert?

Wieso lässt du nicht erst mal die PWM weg und steuerst die H-Brücke über 
den Controller per Tasten?
Die "Einschaltperiode", wie du sie mit den For-Schleifen machen willst, 
kannst du auch vom Timer ableiten.
float ist auf (8Bit-)Mikrocontrollern nicht wirklich zu empfehlen, vor 
allem nicht, wenn man damit nur zählen will.
Dafür gibt es genug ganzzahlige Datentypen.

von Rocco L. (kaufparkangucker)


Lesenswert?

Thilo M. schrieb:
>Die H-Brücke funktioniert so?
>Sollten T1 und T2 nicht besser PNP sein?

In der aufgebauten Schaltung sind es auch PNP Transistoren. Warum ich 
mich beim erstellen des Schaltplans in Eagle verguckt habe weis ich auch 
gerade nicht...




STK500-Besitzer schrieb:
> Wieso lässt du nicht erst mal die PWM weg und steuerst die H-Brücke über
> den Controller per Tasten?
> Die "Einschaltperiode", wie du sie mit den For-Schleifen machen willst,
> kannst du auch vom Timer ableiten.
> float ist auf (8Bit-)Mikrocontrollern nicht wirklich zu empfehlen, vor
> allem nicht, wenn man damit nur zählen will.
> Dafür gibt es genug ganzzahlige Datentypen.

Das Steuern per Tasten ist sehr einfach. So was habe ich bereits 
gemacht. Jetzt war es, wie ich finde, an der Zeit mich weiter zu 
entwickeln und mal was mit den Sonderfunktionen des Atmega8 zu 
probieren. PWM war dabei die erste Idee nachdem ich schon mit dem 
Analogkomperator und den D/A A/D Wandlern herum gespielt habe.

Hat vieleicht einer etwas passenden Code wo ich mir so etwas mit Timern 
statt der for Schleifen mal ansehen kann?

Danke Rocco

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.