mikrocontroller.net

Forum: Compiler & IDEs Ports/Pins übergeben


Autor: Eric (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

ich experimentiere grade ein wenig mit Soft PWM. Jedoch bin ich in C 
nicht wirklich fit, daher hab ich jetzt ein kleines schönheits Problem.

Hier erstmal der Code zum besseren verständnis:
#include <avr/io.h>
#include <avr/interrupt.h>


  typedef struct {
    char ledPin;
    uint8_t pwm;
  } pwmLed;

  volatile pwmLed leds[15] = { //PWM config für die Ports (15 Stück)
    {0,0},
    {1,16},
    {2,32},
    {3,48},
    {4,64},
    {5,80},
    {6,96},
    {7,112},
    {10,128},
    {11,144},
    {12,160},
    {13,176},
    {14,192},
    {15,208},
    {16,224}
  };

volatile uint8_t pwm_pulse_counter = 0;


int main (void) {
  uint8_t i = 0; 
  int tempo = 1;

  //led ausgang
    DDRA |= 0xff;
    DDRD |= 0xff;

  //timer -> 16bit timmer
     TCCR1B |= (1 << WGM12); // timer aus CTC mode setzen -> wert in OCR1A
    TIMSK |= (1 << OCIE1A); // CTC interrupt funktion aktivieren -> TIMER1_COMPA_vect

    OCR1A = 64; //-> 25600hz bei 16mhz und prescale 1 = 1 pwm zyklus 100hz 
    TCCR1B |= (1 << CS10); //timer auf prescale 1 setzen...

  //interrupt handling starten
    sei(); //  Enable global interrupts


  //main loop
    for (;;) {
      for(i = 0;i<=14;i++) {
        leds[i].pwm-=2;
      }
    }
}

ISR(TIMER1_COMPA_vect) { //Interupt funktion
  uint8_t i = 0;     

  pwm_pulse_counter++; //überlauf gewollt (8bit)
     
  for(i = 0;i<=14;i++) {
    if(pwm_pulse_counter < leds[i].pwm) {
      if(leds[i].ledPin == 0) PORTA |= (1 << PA0);
      else if(leds[i].ledPin == 1) PORTA |= (1 << PA1);
      else if(leds[i].ledPin == 2) PORTA |= (1 << PA2);
      else if(leds[i].ledPin == 3) PORTA |= (1 << PA3);
      else if(leds[i].ledPin == 4) PORTA |= (1 << PA4);
      else if(leds[i].ledPin == 5) PORTA |= (1 << PA5);
      else if(leds[i].ledPin == 6) PORTA |= (1 << PA6);
      else if(leds[i].ledPin == 7) PORTA |= (1 << PA7);

      else if(leds[i].ledPin == 10) PORTD |= (1 << PD0);
      else if(leds[i].ledPin == 11) PORTD |= (1 << PD1);
      else if(leds[i].ledPin == 12) PORTD |= (1 << PD2);
      else if(leds[i].ledPin == 13) PORTD |= (1 << PD3);
      else if(leds[i].ledPin == 14) PORTD |= (1 << PD4);
      else if(leds[i].ledPin == 15) PORTD |= (1 << PD5);
      else if(leds[i].ledPin == 16) PORTD |= (1 << PD6);
    } else {
      if(leds[i].ledPin == 0) PORTA &= ~(1 << PA0);
      else if(leds[i].ledPin == 1) PORTA &= ~(1 << PA1);
      else if(leds[i].ledPin == 2) PORTA &= ~(1 << PA2);
      else if(leds[i].ledPin == 3) PORTA &= ~(1 << PA3);
      else if(leds[i].ledPin == 4) PORTA &= ~(1 << PA4);
      else if(leds[i].ledPin == 5) PORTA &= ~(1 << PA5);
      else if(leds[i].ledPin == 6) PORTA &= ~(1 << PA6);
      else if(leds[i].ledPin == 7) PORTA &= ~(1 << PA7);

      else if(leds[i].ledPin == 10) PORTD &= ~(1 << PD0);
      else if(leds[i].ledPin == 11) PORTD &= ~(1 << PD1);
      else if(leds[i].ledPin == 12) PORTD &= ~(1 << PD2);
      else if(leds[i].ledPin == 13) PORTD &= ~(1 << PD3);
      else if(leds[i].ledPin == 14) PORTD &= ~(1 << PD4);
      else if(leds[i].ledPin == 15) PORTD &= ~(1 << PD5);
      else if(leds[i].ledPin == 16) PORTD &= ~(1 << PD6);
    }
  }
}

Ich möchte gerne den Pin der vom Timer Interrupt geschalten werden soll 
mit in der pwmLed Struktur speichern. Zurzeit hab ich das, wie man 
sieht, recht unschön gelöst, indem ich einfach eine Id hinterlege, diese 
im Interrupt auswerte und den entsprechenden Pin schalte.

Mein Ziel ist eigentlich das die Interrupt-Routine am ende so aussieht:
ISR(TIMER1_COMPA_vect) { //Interupt funktion
  uint8_t i = 0;     

  pwm_pulse_counter++; //überlauf mit 255 beabsichtigt
     
  for(i = 0;i<=14;i++) {
    leds[i].pin = pwm_pulse_counter < leds[i].pwm;
  }
}

wie stell ich das am dümmsten an? Ich habs vorns erstmal mit nem Zeiger 
auf den PortX probiert, aber das hat den Compiler schon nicht gepasst. 
Über ein paar tipps würde ich mich freuen. Danke...

Achja... wo ich hier grade nochmal drüberschaue. Wie kann ich die Anzahl 
der gePWMten Ports zentrall festlegen? Ich jetzt einmal oben beim 
Initialisieren die Anzahl angegeben, im Interrupt in der fot Schleife 
und in der main loop auch nochmal. Gibts da auch ne elegantere Lösung? 
Habs vorns mit #define LED_COUNT 15 versucht, das mag der Compiler aber 
nicht und bricht schon bei
volatile pwmLed leds[LED_COUNT] = {
 ab. Wie gesagt bin nicht wirklich Fit in C. Vor 4 Jahren hatte ich mal 
20 Stunden C/C++ und dann höhrts mit meinen C Kenntnissen auch schon 
wieder auf.

Autor: Christian (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Eric,

floge doch mal bei Deiner Suche nach "PORTA" dem avr/io.h Include, dann 
wirst Du nach ein paar Defines hierauf stoßen:
#define _MMIO_BYTE(mem_addr) (*(volatile uint8_t *)(mem_addr))
Ein Port ist nichts anderes als eine Variable vom Typ "volatile 
uint8_t", die an einer besonderen Adresse liegt. Dies wird eben so 
realisiert, in dem eine Zahl auf einen Pointer gecastet wird, und gleich 
danach wird dieser Pointer dereferenziert.

Daher ist ein Pointer auf einen Port vom Typ "volatile uint8_t *".

Viel einfacher sind "Pins", deren defines sehen nämlich so aus:
#define PA1     1
In diesem Fall bietet sich ein "uint8_t" als Datentyp an.

Das würde dann so aussehen:
typedef struct {
  volatile uint8_t *port;
  uint8_t pin;
  uint8_t pwm;
} pwmLed;

volatile pwmLed leds[3] = { //PWM config für die Ports (15 Stück)
  {&PORTA, PA1, 0},
  {&PORTA, PA2, 16},
  {&PORTA, PA3, 32}
};

Was hat denn Dein Compiler an der Arraydefinition auszusetzen? Das muss 
funktionieren, vorausgesetzt das Define steht davor!

Wie die Interrupt Routine aussieht, darft Du Dir mal selbst überlegen - 
oder mich am Montag fragen! ;-)

Grüße und viel Erfolg,
 Christian

Autor: Eric B. (erc)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Danke für deine Hilfe. Habs hinbekommen... hatte ja gestern schon 
versucht das ganze mit Zeigern zu lösen. Aber wenn man keine ahnung hat 
wie Zeiger in C richtig angewendet werden kann das ja nix werden ;)

Und das problem mit #define lag an einem ;. Ich hab "#define PWM_COUNT 
15;" geschreiben. Nachdem ich mir heute nochmal die Fehlermeldung genau 
angeschaut hatte war das Problem sofort klar.

So sieht der Code jetzt aus:
#include <avr/io.h>
#include <avr/interrupt.h>

#define PWM_COUNT 15

//Led config
  typedef struct {
    volatile uint8_t *port;
    uint8_t pin;
    uint8_t pwm;
  } pwmLed;

  volatile pwmLed leds[PWM_COUNT] = {
    {&PORTA, PA0 ,0},
    {&PORTA, PA1 ,0},
    {&PORTA, PA2 ,0},
    {&PORTA, PA3 ,0},
    {&PORTA, PA4 ,0},
    {&PORTA, PA5 ,0},
    {&PORTA, PA6 ,0},
    {&PORTA, PA7 ,0},
    {&PORTD, PD0 ,0},
    {&PORTD, PD1 ,0},
    {&PORTD, PD2 ,0},
    {&PORTD, PD3 ,0},
    {&PORTD, PD4 ,0},
    {&PORTD, PD5 ,0},
    {&PORTD, PD6 ,0}
  };


volatile uint8_t pwm_pulse_counter = 0;


int main (void) {
  uint8_t i = 0,temp = 0;

  //led ausgang
    DDRA |= 0xff;
    DDRD |= 0xff;

  //timer -> 16bit timmer
     TCCR1B |= (1 << WGM12); // timer aus CTC mode setzen -> wert in OCR1A
    TIMSK |= (1 << OCIE1A); // CTC interrupt funktion aktivieren -> TIMER1_COMPA_vect

    OCR1A = (uint16_t)(F_CPU / (100 * 256)); ; //-> (F_CPU / (PWM_Zyklen pro Sekunde) * 256)
    TCCR1B |= (1 << CS10); //timer auf prescale 1 setzen...

  //interrupt handling starten
    sei(); //  Enable global interrupts
  
  //pwm initiallisieren
    for(i = 0;i<=PWM_COUNT-1;i++) {
      temp += (int)(256/PWM_COUNT);
      leds[i].pwm = temp;
    }

  //main loop
      for (;;) {
        for(i = 0;i<=PWM_COUNT-1;i++) {
          leds[i].pwm-=1;
        }
      }
}

ISR(TIMER1_COMPA_vect) { //Interupt funktion
  uint8_t i = 0;     

  pwm_pulse_counter++; //überlauf gewollt (8bit)

  for(i = 0;i<=PWM_COUNT-1;i++) {
    if(pwm_pulse_counter < leds[i].pwm) {
      *leds[i].port |= (1 << leds[i].pin);
    } else {
      *leds[i].port &= ~(1 << leds[i].pin);
    }
  }
}

Vielleicht habt ihr ja noch ein paar Tipps was man noch besser machen 
könnte oder ob ich typische Anfängerfehler gemacht habe.

Danke...

Autor: Werner B. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Sieht doch gut aus.

Nur eine (kleine) Optimierung.
Lass den Compiler zur Übersetzungzeit schieben.
 volatile pwmLed leds[PWM_COUNT] = {
    {&PORTA, 1<<PA0 ,0},
    {&PORTA, 1<<PA1 ,0},
    {&PORTA, 1<<PA2 ,0},
    {&PORTA, 1<<PA3 ,0},
    {&PORTA, 1<<PA4 ,0},
    {&PORTA, 1<<PA5 ,0},
    {&PORTA, 1<<PA6 ,0},
    {&PORTA, 1<<PA7 ,0},
    {&PORTD, 1<<PD0 ,0},
    {&PORTD, 1<<PD1 ,0},
    {&PORTD, 1<<PD2 ,0},
    {&PORTD, 1<<PD3 ,0},
    {&PORTD, 1<<PD4 ,0},
    {&PORTD, 1<<PD5 ,0},
    {&PORTD, 1<<PD6 ,0}
  };

  ...

ISR(TIMER1_COMPA_vect) { //Interupt Funktion
  uint8_t i = 0;     

  pwm_pulse_counter++; //überlauf gewollt (8bit)

  for(i = 0;i<=PWM_COUNT-1;i++) {
    if(pwm_pulse_counter < leds[i].pwm) {
      *(leds[i].port) |= (leds[i].pin);
    } else {
      *(leds[i].port) &= ~(leds[i].pin);
    }
  }
}

Das spart die auf dem AVR aufwändige (sowohl Zeit als auch Codegröße), 
mehrfache Schiebeoperation zur Laufzeit.

Werner

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.