Forum: Mikrocontroller und Digitale Elektronik mit pic18f4550 2 PWM signale getrennt erzeugen


von Jonas K. (johnnyk)


Lesenswert?

Hallo,

habe einen PIC18f4550 mit 20MHz quarz.

Ich versuche mit diesem nun 2 voneinander unabhängig arbeitende 
PWM-Signale zu erzeugen. Die PWM Signale gehen auf einen L293d welcher 
wiederum 2 Motoren ansteuert (3v Gleichstrom). Durch das PWM will ich 
nun die Drehzahl der Motoren regeln allerdings unabhängig voneinander.
Wie erzeuge ich nun 2 voneinander unabhängige PWM Signale, welche ich 
allerding noch durch analoge Signale steuern kann (Fototransistoren).
Die Signale der 2 Sensoren habe ich bereits eingelesen über den pin a0 & 
a1.


Quellcode der sensoren:

void main(void)  // Beginn Hauptprogramm
{
ini();
    while(1)
    {
    sensorBan=an;

    if (ADCON0bits.GO_DONE==0)
    {
      sbr=ADRESH;


      ADCON0bits.CHS0=1;// schaltet AN0 ein (sensor boden rechts)
      ADCON0bits.GO_DONE=1;
    }

    Delay10KTCYx(1);

    if (ADCON0bits.GO_DONE==0)
    {
      sbl=ADRESH; // schaltet AN1 ein (sensor boden links)


      ADCON0bits.CHS0=0;
      ADCON0bits.GO_DONE=1;
    }

    Delay10KTCYx(1);

}



Ich hoffe mir kann jemand helfen.
mfg

von Martin S. (drunkenmunky)


Lesenswert?

Hi,
hast du das Programm getestet? Wenn ja wie?

Du verwendest nur das High-Byte des Ergebnisses vom AD-Wandler. Hast du 
das Ergebnis linksbündig in ADRESH und ADRESL drinstehen?
Du verschenkst damit Auflösung. Lese doch beide Register aus und 
speichere sie dann in einer Variable, z.B. so
unsigned short sbr;
sbr = ( ADRESH << 8 ) | ADRESL;

Dazu muß es aber rechtsbündig drinstehen.

Zur PWM:
Der Controller hat doch 2 Hardware PWM Module. Diese musst du zuerst 
konfugieren  mit den richtigen Einstellungen wie z.B. Frequenz und so. 
Dann kannst du das PWM Verhältnis so wie du es willst in Abhängigkeit 
von deinen AD Werten immer wieder aktualisieren.

Im Datenblatt steht es relativ genau drinnen, wie man vorgehen muss.

Wenn du konkrete Fragen hast, schreibe wieder!

von Jonas K. (johnnyk)


Lesenswert?

Danke für die schnelle Antwort.

ich habe das ergebnis des ad linksbündig stehen und lese nur high aus.
Dass mit der Auflösung ist grundsätzlich eine gute idee allerdings sehe 
ich für meine zwecke keinen großen sinn darin. Die 2 sensoren sollen nur 
eine schwarze linie erkennen aber trotzdem danke schonma für diese idee 
vllt werde ich sie noch brauchen.

Ich habe mich schon durch das Datenblatt gekämpft allerdings komme ich 
da nicht weiter auf welcher seite steht es den beschrieben?

mfg

von Martin S. (drunkenmunky)


Lesenswert?

Kapitel 15.4 PWM Mode (Seite 150)

Die Ausgänge sind dann CCP1 (RC2) und CPP2 (RC1 oder RB3).

Du hast ein "normales" PWM Modul, das nicht so kompliziert ist und ein 
enhanced PWM Module.

Aber steht alles drinnen:
15.4.4 SETUP FOR PWM OPERATION
The following steps should be taken when configuring
the CCPx module for PWM operation:
1. Set the PWM period by writing to the PR2
register.
2. Set the PWM duty cycle by writing to the
CCPRxL register and CCPxCON<5:4> bits.
3. Make the CCPx pin an output by clearing the
appropriate TRIS bit.
4. Set the TMR2 prescale value, then enable
Timer2 by writing to T2CON.
5. Configure the CCPx module for PWM operation.

des englischen solltest du schon halbwegs mächtig sein...

von Martin S. (drunkenmunky)


Lesenswert?

hab mir grad nochmal des Programm angeschaut. Für was sind die 
Wartezeiten? Wahrscheinlich dafür, dass die Wandlung sicher zu Ende ist, 
ode? Ist so nicht schön gemacht. Du kannst auch das GOnDone Bit so lange 
abfragen bis es wieder null ist und dann erst weiter gehen. So wie ich 
es gemacht hab. Nennt sich polling.
Noch schöner wäre es natürlich, wenn du das Ergebnis in einer Interrput 
Service Routine abspeicherst. Dann verbräts du nicht so viel Rechenzeit

1
void main(void)  // Beginn Hauptprogramm
2
{
3
ini();
4
    
5
while(1)
6
{
7
    sensorBan=an;          // ??
8
9
    ADCON0bits.CHS0=0;     // Kanal einstellen
10
    ADCON0bits.GO_DONE=1;  // Wandlung starten
11
12
    while (ADCON0bits.GO_DONE==0) continue;   // warten bis Wandlung fertig
13
14
    sbr=ADRESH; // Ergebnis speichern
15
16
    ADCON0bits.CHS0=1;     // Kanal wechseln
17
    ADCON0bits.GO_DONE=1;  // Wandlung starten
18
19
    while (ADCON0bits.GO_DONE==0) continue;   // warten bis Wandlung fertig
20
21
    sbl=ADRESH; // schaltet AN1 ein (sensor boden links)
22
23
}

von Jonas K. (johnnyk)


Lesenswert?

Hallo,
Hier mein Programm zur Erzeugung von PWM-Signalen an CCP1 pin RC0.
Aber wie bekomme ich jetzt parallel dazu an CCP2 pin RC1 ein weiteres 
PWM-Signal?
Über das CCPRxL Register kann ich ja jeweils die dc einstellen ?!?



void main(void)             // Beginn Hauptprogramm
{
TRISD=0x00;          //Port D ausgang (L293d ports)
TRISC=0x00;          //Port C ausgang (L293d enableports)

PORTB=0x00;          //Port B clear (8 LEDs)

PR2=0b11111111;       // maximale periodendauer

CCPR1L=0b00111111;      //dc ccp1 pin
CCPR2L=0b00111111;      //dc ccp2 pin


CCP1CON=0x0C;      //PWM Mode ausgewählt
CCP2CON=0x0C;      //PWM Mode ausgewählt

T2CON=0b01111001;    //1:16 prostscale, Timer 2 off, Prescale = 4
T2CONbits.TMR2ON = 1;   // Timer2 an

    while(1)
    {          //anfang dauerschleife

    vorM1();      //motor1 vorwärts


  while (TNR2==CCPR1L) continue;    //warten bis dc ccp1 erreicht

    einszweiEN=0;        //Motor 1 enabled RC0


  while (TNR2==PR2) continue;    //warten bis maximale periodendauer 
erreicht

    einszweiEN=1;        //Motor 1 disabled RC0

    }  //ende dauerschleife
}

von Jonas K. (johnnyk)


Lesenswert?

hat keiner eine idee?

von Martin S. (drunkenmunky)


Lesenswert?

funktioniert denn schon der eine PWM Ausgang? Ich denke mal eher 
nicht...

Ich glaube du verstehst da was grundsätzlich falsch. Du hast eine 
Hardware PWM in diesem Controller, das heißt du stellst die notwendigen 
Register ein wie du sie haben willst und dann läuft die PWM "von 
alleine" ohne dass du ständig in der while(1) irgendwas dafür tun mußt. 
Die Frequenz läßt man ja meist konstant und ändert nur bei Bedarf in der 
while(1) Schleife den duty_cycle.

Über das CCPRxL und noch über 2 Bits im CCPXCON stellst du den 
duty_cycle ein (zusammen also 10-bit Auflösung). Du kannst auch nur über 
CCPRxL einstellen, hast dann halt nur 8 bit Auflösung.

und wenn du Code postest, dann alles. Es weiß keiner was hier gemacht 
wird:
vorM1();

Also, änder des erst mal ab und dann könnnen wir nach dem 2. PWM Ausgang 
schauen. Hast du die Möglichkeit des zu testen?

von Jonas Kiefer (Gast)


Lesenswert?

Hallo,
also vorM1(); ist für euch nicht von Interesse. Da wird lediglich die 
Richtung der Motoren bestimmt.
Also ich habe es schon mal getestet die Motoren drehen unterschiedlich 
schnell --> Funktioniert

Jetzt bin ich aber nicht mehr weiter gekommen bei folgendem.
Ich habe 2 Sensoren die über die 2 Eingänge A0 & A1 angeschlossen sind. 
Die Werte werden Analog ausgelesen und dann in einem 8 Bit Register 
gespeichert soweit richtig?

Jetzt ist das CCPRxL Register auch 8 Bit lang. Ich habe versucht die 
werte die mir der AD-Wandler liefert direkt in die CCPRxL Register zu 
schreiben leider nimmt er diese nicht an.


Hier mal der Quelltext:
1
void ini (void)
2
{
3
ADCON1=0b00001101;      // AN0 & AN1 Analog
4
5
TRISA=0xFF;          // PortA eingang
6
TRISB=0x00;          // PortB ausgang
7
TRISD=0x00;          // PortD ausgang
8
TRISC=0x00;          // PortC ausgang
9
10
PORTB=0x00;          // PortB gelöscht
11
12
PR2=0xFF;           // Maxiamale Periodendauer
13
14
CCPR1L=0b01010111;      // dc RC2-Pin Startwert
15
CCPR2L=0b01010111;      // dc RC1-Pin Startwert
16
17
CCP1CON=0b00111111;      // PWM Mode ausgewählt
18
CCP2CON=0b00111111;      // PWM Mode ausgewählt
19
20
T2CON=0b00000100;      // Timer 2 an, Vorteiler = 4
21
22
sensorBan=an;        // Sensoren Boden an (Transistor)
23
vorM1();          // motor1 vorwärts
24
vorM2();          // motor2 vorwärts
25
26
}
27
28
void main(void)                         // Beginn Hauptprogramm
29
{
30
ini();                        // aufruf der initialisierung;
31
32
    while(1)
33
    {                      // anfang dauerschleife
34
35
      ADCON0bits.CHS0==0;            // Kanal einstellen
36
      ADCON0bits.GO_DONE=1;          // Wandlung starten
37
38
       while (ADCON0bits.GO_DONE==0) continue;    // warten bis Wandlung fertig
39
    
40
      sbr=ADRESH;                // Ergebnis speichern
41
      PORTB=sbl;                // ausgabe der binärzahl an LED0..7
42
      CCPR1L=ADRESH;              // dc für Motor2 anpassen
43
        
44
45
      ADCON0bits.CHS0=1;            // Kanal einstellen
46
      ADCON0bits.GO_DONE=1;          // Wandlung starten
47
    
48
    while (ADCON0bits.GO_DONE==0) continue;   // warten bis Wandlung fertig
49
    
50
      sbl=ADRESH;               // Ergebnis speichern
51
      PORTB=sbl;                // ausgabe der binärzahl an LED0..7
52
      CCPR2L=ADRESH;              // dc für Motor1 anpassen
53
54
    }                      // ende dauerschleife
55
}

von Martin S. (drunkenmunky)


Lesenswert?

Jonas Kiefer schrieb:
> ADCON0bits.CHS0==0;            // Kanal einstellen

da ist wohl ein = zuviel. Dann stellt er den Kanal 0 nicht ein.

Ansonsten sieht auf den ersten Blick richtig aus.

Der zweite Motor hat auch nicht funktioniert? Mit der Ausgabe, die du 
gemacht hast, kannst du ja sehen ob die AD-Wandlung richtig 
funktioniert.

Mit welchen Gerät programmierst du?

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.