Forum: Compiler & IDEs Ports/Pins übergeben


von Eric (Gast)


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:
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
4
5
  typedef struct {
6
    char ledPin;
7
    uint8_t pwm;
8
  } pwmLed;
9
10
  volatile pwmLed leds[15] = { //PWM config für die Ports (15 Stück)
11
    {0,0},
12
    {1,16},
13
    {2,32},
14
    {3,48},
15
    {4,64},
16
    {5,80},
17
    {6,96},
18
    {7,112},
19
    {10,128},
20
    {11,144},
21
    {12,160},
22
    {13,176},
23
    {14,192},
24
    {15,208},
25
    {16,224}
26
  };
27
28
volatile uint8_t pwm_pulse_counter = 0;
29
30
31
int main (void) {
32
  uint8_t i = 0; 
33
  int tempo = 1;
34
35
  //led ausgang
36
    DDRA |= 0xff;
37
    DDRD |= 0xff;
38
39
  //timer -> 16bit timmer
40
     TCCR1B |= (1 << WGM12); // timer aus CTC mode setzen -> wert in OCR1A
41
    TIMSK |= (1 << OCIE1A); // CTC interrupt funktion aktivieren -> TIMER1_COMPA_vect
42
43
    OCR1A = 64; //-> 25600hz bei 16mhz und prescale 1 = 1 pwm zyklus 100hz 
44
    TCCR1B |= (1 << CS10); //timer auf prescale 1 setzen...
45
46
  //interrupt handling starten
47
    sei(); //  Enable global interrupts
48
49
50
  //main loop
51
    for (;;) {
52
      for(i = 0;i<=14;i++) {
53
        leds[i].pwm-=2;
54
      }
55
    }
56
}
57
58
ISR(TIMER1_COMPA_vect) { //Interupt funktion
59
  uint8_t i = 0;     
60
61
  pwm_pulse_counter++; //überlauf gewollt (8bit)
62
     
63
  for(i = 0;i<=14;i++) {
64
    if(pwm_pulse_counter < leds[i].pwm) {
65
      if(leds[i].ledPin == 0) PORTA |= (1 << PA0);
66
      else if(leds[i].ledPin == 1) PORTA |= (1 << PA1);
67
      else if(leds[i].ledPin == 2) PORTA |= (1 << PA2);
68
      else if(leds[i].ledPin == 3) PORTA |= (1 << PA3);
69
      else if(leds[i].ledPin == 4) PORTA |= (1 << PA4);
70
      else if(leds[i].ledPin == 5) PORTA |= (1 << PA5);
71
      else if(leds[i].ledPin == 6) PORTA |= (1 << PA6);
72
      else if(leds[i].ledPin == 7) PORTA |= (1 << PA7);
73
74
      else if(leds[i].ledPin == 10) PORTD |= (1 << PD0);
75
      else if(leds[i].ledPin == 11) PORTD |= (1 << PD1);
76
      else if(leds[i].ledPin == 12) PORTD |= (1 << PD2);
77
      else if(leds[i].ledPin == 13) PORTD |= (1 << PD3);
78
      else if(leds[i].ledPin == 14) PORTD |= (1 << PD4);
79
      else if(leds[i].ledPin == 15) PORTD |= (1 << PD5);
80
      else if(leds[i].ledPin == 16) PORTD |= (1 << PD6);
81
    } else {
82
      if(leds[i].ledPin == 0) PORTA &= ~(1 << PA0);
83
      else if(leds[i].ledPin == 1) PORTA &= ~(1 << PA1);
84
      else if(leds[i].ledPin == 2) PORTA &= ~(1 << PA2);
85
      else if(leds[i].ledPin == 3) PORTA &= ~(1 << PA3);
86
      else if(leds[i].ledPin == 4) PORTA &= ~(1 << PA4);
87
      else if(leds[i].ledPin == 5) PORTA &= ~(1 << PA5);
88
      else if(leds[i].ledPin == 6) PORTA &= ~(1 << PA6);
89
      else if(leds[i].ledPin == 7) PORTA &= ~(1 << PA7);
90
91
      else if(leds[i].ledPin == 10) PORTD &= ~(1 << PD0);
92
      else if(leds[i].ledPin == 11) PORTD &= ~(1 << PD1);
93
      else if(leds[i].ledPin == 12) PORTD &= ~(1 << PD2);
94
      else if(leds[i].ledPin == 13) PORTD &= ~(1 << PD3);
95
      else if(leds[i].ledPin == 14) PORTD &= ~(1 << PD4);
96
      else if(leds[i].ledPin == 15) PORTD &= ~(1 << PD5);
97
      else if(leds[i].ledPin == 16) PORTD &= ~(1 << PD6);
98
    }
99
  }
100
}

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:
1
ISR(TIMER1_COMPA_vect) { //Interupt funktion
2
  uint8_t i = 0;     
3
4
  pwm_pulse_counter++; //überlauf mit 255 beabsichtigt
5
     
6
  for(i = 0;i<=14;i++) {
7
    leds[i].pin = pwm_pulse_counter < leds[i].pwm;
8
  }
9
}

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
1
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.

von Christian (Gast)


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:
1
#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:
1
#define PA1     1
In diesem Fall bietet sich ein "uint8_t" als Datentyp an.

Das würde dann so aussehen:
1
typedef struct {
2
  volatile uint8_t *port;
3
  uint8_t pin;
4
  uint8_t pwm;
5
} pwmLed;
6
7
volatile pwmLed leds[3] = { //PWM config für die Ports (15 Stück)
8
  {&PORTA, PA1, 0},
9
  {&PORTA, PA2, 16},
10
  {&PORTA, PA3, 32}
11
};

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

von Eric B. (erc)


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:
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
4
#define PWM_COUNT 15
5
6
//Led config
7
  typedef struct {
8
    volatile uint8_t *port;
9
    uint8_t pin;
10
    uint8_t pwm;
11
  } pwmLed;
12
13
  volatile pwmLed leds[PWM_COUNT] = {
14
    {&PORTA, PA0 ,0},
15
    {&PORTA, PA1 ,0},
16
    {&PORTA, PA2 ,0},
17
    {&PORTA, PA3 ,0},
18
    {&PORTA, PA4 ,0},
19
    {&PORTA, PA5 ,0},
20
    {&PORTA, PA6 ,0},
21
    {&PORTA, PA7 ,0},
22
    {&PORTD, PD0 ,0},
23
    {&PORTD, PD1 ,0},
24
    {&PORTD, PD2 ,0},
25
    {&PORTD, PD3 ,0},
26
    {&PORTD, PD4 ,0},
27
    {&PORTD, PD5 ,0},
28
    {&PORTD, PD6 ,0}
29
  };
30
31
32
volatile uint8_t pwm_pulse_counter = 0;
33
34
35
int main (void) {
36
  uint8_t i = 0,temp = 0;
37
38
  //led ausgang
39
    DDRA |= 0xff;
40
    DDRD |= 0xff;
41
42
  //timer -> 16bit timmer
43
     TCCR1B |= (1 << WGM12); // timer aus CTC mode setzen -> wert in OCR1A
44
    TIMSK |= (1 << OCIE1A); // CTC interrupt funktion aktivieren -> TIMER1_COMPA_vect
45
46
    OCR1A = (uint16_t)(F_CPU / (100 * 256)); ; //-> (F_CPU / (PWM_Zyklen pro Sekunde) * 256)
47
    TCCR1B |= (1 << CS10); //timer auf prescale 1 setzen...
48
49
  //interrupt handling starten
50
    sei(); //  Enable global interrupts
51
  
52
  //pwm initiallisieren
53
    for(i = 0;i<=PWM_COUNT-1;i++) {
54
      temp += (int)(256/PWM_COUNT);
55
      leds[i].pwm = temp;
56
    }
57
58
  //main loop
59
      for (;;) {
60
        for(i = 0;i<=PWM_COUNT-1;i++) {
61
          leds[i].pwm-=1;
62
        }
63
      }
64
}
65
66
ISR(TIMER1_COMPA_vect) { //Interupt funktion
67
  uint8_t i = 0;     
68
69
  pwm_pulse_counter++; //überlauf gewollt (8bit)
70
71
  for(i = 0;i<=PWM_COUNT-1;i++) {
72
    if(pwm_pulse_counter < leds[i].pwm) {
73
      *leds[i].port |= (1 << leds[i].pin);
74
    } else {
75
      *leds[i].port &= ~(1 << leds[i].pin);
76
    }
77
  }
78
}

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

Danke...

von Werner B. (Gast)


Lesenswert?

Sieht doch gut aus.

Nur eine (kleine) Optimierung.
Lass den Compiler zur Übersetzungzeit schieben.
1
 volatile pwmLed leds[PWM_COUNT] = {
2
    {&PORTA, 1<<PA0 ,0},
3
    {&PORTA, 1<<PA1 ,0},
4
    {&PORTA, 1<<PA2 ,0},
5
    {&PORTA, 1<<PA3 ,0},
6
    {&PORTA, 1<<PA4 ,0},
7
    {&PORTA, 1<<PA5 ,0},
8
    {&PORTA, 1<<PA6 ,0},
9
    {&PORTA, 1<<PA7 ,0},
10
    {&PORTD, 1<<PD0 ,0},
11
    {&PORTD, 1<<PD1 ,0},
12
    {&PORTD, 1<<PD2 ,0},
13
    {&PORTD, 1<<PD3 ,0},
14
    {&PORTD, 1<<PD4 ,0},
15
    {&PORTD, 1<<PD5 ,0},
16
    {&PORTD, 1<<PD6 ,0}
17
  };
18
19
  ...
20
21
ISR(TIMER1_COMPA_vect) { //Interupt Funktion
22
  uint8_t i = 0;     
23
24
  pwm_pulse_counter++; //überlauf gewollt (8bit)
25
26
  for(i = 0;i<=PWM_COUNT-1;i++) {
27
    if(pwm_pulse_counter < leds[i].pwm) {
28
      *(leds[i].port) |= (leds[i].pin);
29
    } else {
30
      *(leds[i].port) &= ~(leds[i].pin);
31
    }
32
  }
33
}

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

Werner

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.