Forum: Mikrocontroller und Digitale Elektronik Motoransteuerung mit PWM


von Alex (Gast)


Lesenswert?

Hallo Leute,

ich habe ein Problem mit der µC Programmierung.
Ich bin leider noch ein ziemlicher Anfänger in dieser Geschichte.

Ich würde gerne einen Motor mit einem PWM ansteuern.
Die Drehzahl soll durch einen Analogeingang erfolgen.
Dieser hat eine Spannung von 0-5V.
Bei 10Bit sozusagen von 0-1023 Werte.

Das einlesen der Analogwerte funktioniert soweit.
Es wäre super wenn mir einer Helfen könnte diese
Werte auf ein PWM-Signal zu übertragen für
eine Geschwindigkeitsregelung.

Als µC verwende ich einen at90can. Der PWM Ausgang liegt auf
dem PINB6 und der Analog eingang ist der PINF0.

Gruß

Alex

von Leonhard K. (leonhard_k)


Lesenswert?

Also grob durch 4 teilen geht immer recht gut. Damit kommst du in etwa 
auf 8bit PWM-Werte.

1023 / 4  = 255.75

Programmablauf wäre dann in etwa so:

1. ADC einlesen
2. Wert durch 4 teilen
3. Wert ausgeben
4. Zurück zu 1

MfG Leonhard

von Karl H. (kbuchegg)


Lesenswert?

Alex schrieb:

> Das einlesen der Analogwerte funktioniert soweit.
> Es wäre super wenn mir einer Helfen könnte diese
> Werte auf ein PWM-Signal zu übertragen für
> eine Geschwindigkeitsregelung.

Regeln oder Stellen?

Für eine Regelung brauchst du eine Rück-'Meldung' vom Motor. Sprich du 
müsstest die effektive Motordrehzahl messen und mit der Vorgabe aus dem 
ADC Wert vergleichen. Ist die effektive Drehzahl zu niedrig, dann muss 
die PWM erhöht werden, damit der Motor die Vorgabedrehzahl erreicht.

Ein Steller hingegen nimmt einfach den ADC Wert und errechnet sich 
daraus einen Wert den er auf die PWM gibt. Wie schnell dann auch immer 
der Motor mit dieser Einstellung dreht.


Und ach ja. Die PWM machst du in Hardware, wobei ich jetzt nicht weiß, 
an welchem Pin bei deinem µC die PWM abgegriffen werden kann.
FAQ: Timer

von Alex (Gast)


Lesenswert?

Danke für die Antowrten.

Ehm ne nicht regeln sonder stellen.
Ok danke werd mir mal des mit den Timern durchlesen.

Gruß Alex

von Alex (Gast)


Lesenswert?

Hallo Leute,

also es tut immer noch nicht. Leider habe ich kein Oszi da um den
PWM Ausgang zu testen.

Mein Code sieht wie folgt aus:

#include <avr/io.h>
#include <avr/interrupt.h>
#include "lcd_ks0066_4bit_io.h"
#include "Define_Inputs.h"
#include "Define_Outputs.h"
#include <stdlib.h>
#include <inttypes.h>


void initial(void)
{
  // **** Einrichten von Ein- und Ausgängen 
*******************************************************
  DDRB = 0x40;        // PortD Ausgänge
  // **** Einrichten von PWM Betrieb 
***************************************************************
  // Der Timer 0 wird im Fast PWM Modus betrieben

  TCCR0A  = ((1 <<WGM01)|(1<<WGM00)) ;// waveform generation mode auf 
"Fast PWM" setzen
  // WGM02 in TCCR0B bleibt auf 0 (default)
  TCCR0A |=(1<<COM0A1 ) ;     // set compare output mode to non 
inverting mode)
  //  COM0A0, COM0B1 and COM0B0 bleiben auf 0 (default)
  //TCCR0B =(1<<CS01|1<<CS00);// set clk prescaler to 64 (macht 122,1Hz 
PWM Frequenz)
  TCCR0A =(1<<CS02 ) ;   // set clk prescaler to 256 (macht 15,3Hz PWM 
Frequenz)
  // WGM02, FOC0A and FOC0B bleiben auf 0 (default)
  OCR0A=0;       // set initial value of OCR0A (0x00 erzeugt einen spike 
am output)
  TCNT0 = 0 ;     // reset timer / counterregister

  sei() ;

}

void ADC_Init(void) {

  uint16_t result;

  ADMUX = (1<<REFS0);            // AVCC als  Referenzspannung nutzen
  ADCSRA = (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0);       // 
Frequenzvorteiler 8 ==> 1.000.000/8 = 125.000Hz
  ADCSRA |= (1<<ADEN);                    // ADC aktivieren

  /* nach Aktivieren des ADC wird ein "Dummy-Readout" empfohlen, man 
liest
     also einen Wert und verwirft diesen, um den ADC "warmlaufen zu 
lassen" */

  ADCSRA |= (1<<ADSC);                  // eine ADC-Wandlung
  while (ADCSRA & (1<<ADSC) );          // auf Abschluss der 
Konvertierung warten
  /* ADCW muss einmal gelesen werden, sonst wird Ergebnis der nächsten
     Wandlung nicht übernommen. */
  result = ADCW;
}

// *ADC Einzelmessung 
************************************************************************ 
******
uint16_t ADC_Read( uint8_t ch )
{
  // select the corresponding channel 0~7
  // ANDing with '7' will always keep the value
  // of 'ch' between 0 and 7
  ch &= 0b00000111;  // AND operation with 7
  ADMUX = (ADMUX & 0xF8)|ch;     // clears the bottom 3 bits before 
ORing

  // start single conversion
  // write '1' to ADSC
  ADCSRA |= (1<<ADSC);

  // wait for conversion to complete
  // ADSC becomes '0' again
  // till then, run loop continuously
  while(ADCSRA & (1<<ADSC));

  return (ADC);
}

// 
************************************************************************ 
**********
//  Display integer 0-9999
//  Durch geschicktes multiplizieren und dividieren wird die 
Integer-Zahl
//  in ihre einzelnen Stellen zerlegt und diese dann am LCD ausgegeben.
//  Es wird nicht abgefragt, ob die Zahl größer als 9999 ist!
//  Währe dies der Fall, wird ein falsches Zeichen am LCD angezeigt.
// 
************************************************************************ 
**********
void print4(unsigned int x)
{
  unsigned int y;
  y=x/1000;lcd_data(y+0x30);x-=(y*1000);
  y=x/100;lcd_data(y+0x30);x-=(y*100);
  y=x/10;lcd_data(y+0x30);x-=(y*10);
  lcd_data(x+0x30);
}




int main(void)
{

    //++++++++++ INPUTS +++++++++++++

    //########## Digital ############

    DDRA |= 0x00;
    PORTA = 0xF8;
    //########## Analog #############

    DDRF |= (0<<PINF0) | (0<<PINF1);
    PORTF = 0x00;

    //++++++++++ OUTPUTS ++++++++++++


    DDRB |= (1<<PINB4) | (1<<PINB5);                  // High-Side 
Ausgang X13/14 als Ausgang deklariert
    DDRC |= (1<<PINC5) | (1<<PINC6);                  // High-Side 
Ausgang X15/16 als Ausgang deklariert
    DDRE |= (1<<PINE4) | (1<<PINE5) |(1<<PINE6) |(1<<PINE7);      // Die 
Pins E4,5,6,7 wurden als Outputs deklariert
    // H-Brücke
    DDRG |= (1<<PING2);
    DDRC |= (1<<PINC7);
    DDRB |= (1<<PINB6);
    DDRE |= (1<<PINE6) | (1<<PINE7);


    //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    // H-Bridge Ausgabe


    // Enable
    if(STATE_DI3_INPUT_X5)
    {
      PORTC &= ~(1<<ENABLE_H_Bridge_A_PINC7);
      PORTG &= ~(1<<ENABLE_H_Bridge_B_PING2);
    }
    else
    {
      PORTC |= 0x80; // PINC7
      PORTG |= 0x04; // PING2
    }

    // Motor links rechts lauf
    if (STATE_DI4_INPUT_X6)
    {
      PORTE &= ~(1<<OUT_H_Bridge_Right_A_PINE6);
      PORTE |= 0x80;  //PINE7
    }
    else
    {
      PORTE &= ~(1<<OUT_H_Bridge_Left_B_PINE7);
      PORTE |= 0x40; //PINE6
    }




    //###############################################
    // AD Wandlung


    int variable = 0;
    char pwm_sollwert = 0;

    initial();  // Initialisierung Ports etc.
    ADC_Init(); // Initialisierung Analogeingang
    lcd_init(LCD_DISP_ON);
    lcd_clrscr();


    while(1)
    {

    // *LCD Anzeige 
********************************************************************

    variable = ADC_Read(0);

    lcd_gotoxy(0,0);
    lcd_data( 'A' );  // Ausgabe einzelner Zeichen am LCD
    lcd_data( 'D' );
    lcd_data( 'C' );
    lcd_data( '0' );
    lcd_data( '=' );

    print4(variable);
  // Ausgabe einer vierstelligen Integer-Zahl am LCD
    _delay_us(20);

    // 
************************************************************************ 
************************

    // *PWM Sollwert setzen 
********************************************************************

    pwm_sollwert = variable/4;  // hätte man auch direkt nach OCR0A 
speichern können
    OCR0A=(pwm_sollwert);

    // 
************************************************************************ 
************************
   } // Ende while(1)Endlosschleife

   return 0;


}

Mich würde jetzt interressieren ob dies funktioniert sprich
ob der PWM Ausgang PB6 ein PWM ausgibt mit dem Wert "pwm_sollwert".

Danke schon mal für die Hilfe

Gruß

Alex

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Alex schrieb:
> TCCR0A =(1<<CS02 ) ;   // set clk prescaler to 256 (macht 15,3Hz PWM
> Frequenz)

Wenn ich da durch deine auskommentierten Zeilen richtig durchblicke, 
machst du mit dieser Zeile die gesamte vorige Initialisierung wieder 
platt.

Als PWM Test kannst du an den Ausgang eine LED anschliessen (mit 220R 
-330R Vorwiderstand).

Übrigens ist PB7 der Ausgang für OC0A, nicht PB6.

von Alex (Gast)


Lesenswert?

Ok danke. Ja hab des im Inet gefunden und
mal ein bisschen umgeschrieben.
Werde gleich mal des testen bzw umschreiben.

DANKE

von Alex (Gast)


Lesenswert?

also bis jetzt tut sich noch nix.

Mal dumm gefragt:

Kann ich Theoretisch auch mit einem Multimeter das PWM testen?
Einfach um zu schauen ob der Ausgang schaltet.

Ah ja ich habe keine 1Mhz Taktfrequenz sonder 16. Die Auskomentierung
habe ich noch nicht geändert da ich dieses Programm aus dem inet habe.

Zum Thema testen:

Also ich habe einen Motor angeschlossen an eine H-Brücke. Somit kann ich 
die Sache testen. Dies funktioniert auch. Ich habe Einfach von hand 5V 
angeschlossen und der Motor hat sich gedreht.

Gruß

Alex

von Karl H. (kbuchegg)


Lesenswert?

Alex schrieb:
> also bis jetzt tut sich noch nix.

Wirf den Code weg und schreib ihn neu.
Aber diesmal schreibst du ihn selber und holst dir maximal von einem 
einfachen PWM-Beispiel den Code zur Ansicht.

Dein erstes Programm hat keinen ADC, keine UART, kein LCD, kein gar 
nichts, sondern nur die PWM.
1
int main()
2
{
3
  Pins auf Ausgang stellen
4
5
  Timer konfigureieren
6
  und auf PWM einstellen
7
8
  while( 1 ) {
9
    OCR0A = 128;
10
  }
11
}

(die fehlenden Teile schreibst DU!)

und damit testest du erst mal, ob deine Hardware überhaupt funktioniert, 
ob sich also dein Motor dreht.

Danach veränderst du die 128 im COde auf andere Werte und beobachtest 
deinen Motor, ob er jetzt mit anderer Drehzahl dreht.

Und erst dann, wenn dieser Punkt abgehakt ist, erst dann nimmst du die 
ADC mit dazu!

Nicht alles auf einmal. Sonst stehst du mit einem Haufen Code da, der 
nicht funktioniert, und von dem du keine Ahnung hast, wo du mit 
Fehlersuche anfangen sollst. Immer in Schritten arbeiten, wobei man mit 
ganz einfachem Code anfängt, den testet und dann nach und nach in die 
richtige Richtung ausbaut.

Eigentlich fehlt da sogar noch eine Vorstufe.
Ehe man eine PWM auf die Pins loslässt, schaltet man sie ganz einfach 
erst mal von Hand auf 'Motor soll VOllgas drehen' - einfach Pin auf 
Ausgang und nach Bedarf eine 1 bzw. 0 an die jeweiligen Pins.

von Karl H. (kbuchegg)


Lesenswert?

> Kann ich Theoretisch auch mit einem Multimeter das PWM testen?
> Einfach um zu schauen ob der Ausgang schaltet.

Niemand hindert dich daran, da ein VOltmeter drann zu halten.
Aber eine LED ist deutlich einfacher. Ist der Pin auf 0, dann leuchtet 
die LED (oder umgekehrt, je nachdem wie sie angeschlossen ist), bzw. sie 
leuchtet nicht. Toggelt der Pin mit unterschiedlichen Tastgraden, dann 
leuchtet die LED eben nicht mehr voll, sondern in unterschiedlichen 
Dimmgraden. Da genügt 1 Blick von der Ferne um das zu erfassen und man 
muss keine Zahlenwerte ablesen und interpretieren.

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.