Forum: Compiler & IDEs PWM von hand?


von der_hier (Gast)


Lesenswert?

Hallo,
ich nutze zurzeit einen ATMega644 mit einem AVR Dragon board und 
programmiere in AVR Stodio 4. Muss gleich dazu sagen ich nutze den 
mega644 nur zum testen da ich dort nen jtag zugriff habe, das Programm 
soll später mal auf nem ATtiny45 laufen und da ist auch schon das 
Problem. Ich möchte eine PWM machen über einen externen Takt und einen 
Sensor an die I²C Schnittstelle anschließen, allerdings teilen sich 
Timer0 (einzigster Timer mit pwm beim tiny45) und I²C einen Pin.

Nun hatte ich die Idee über einen Externen interrupt (beim mega nutze 
ich int0, beim tiny wollte ich dann pcintX nehmen) den Timer/Counter 
direkt hochzuzählen.
Tja, hochzählen funktioniert, aber keine PWM und kein TIMER1 overflow. 
ist es nicht möglich diese funktionen über interne zugriffe zu steuern? 
Brauche ich unbedingt dafür einen direkten Takt am Timer?

hier Programmauszug:
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
4
#ifndef F_CPU
5
#define F_CPU 1000000UL // interner 8MHz oszi mit CKDIV8
6
#endif
7
8
9
ISR (TIMER1_OVF_vect){  //Interrupt bei Timer1 overflow
10
    //hier wird Programm ausgeführt  
11
}
12
13
ISR (INT0_vect){
14
  TCNT1++;  
15
}
16
17
void INT0init(){
18
  EICRA |= (1<<ISC01); //int0 interrupt falling edge
19
  EIMSK |= (1<<INT0);  //int0 interrupt enable
20
}
21
22
void PWMinit(){
23
  OCR1A = 63; //bis dahin soll Timer1 laufen
24
  OCR1B = 32; // 50% PWM durch OCR1B
25
26
  // PWM auf mode 15(Fast PWM, Top OCR1A) und externe clock aus
27
  TCCR1A |= (1<<COM1B1) | (1<<WGM10) |(1<<WGM11);
28
  TCCR1B |= (1<<WGM12) | (1<<WGM13) ;
29
  
30
  TIMSK1 |= (1<<TOIE1); //Timer1 overflow interrupt enable
31
32
}
33
34
int main (){
35
  
36
  // Port Deklaration
37
  DDRD = (1<<PD4); //PD4 als Ausgang
38
39
  //Initialisierungen
40
  INT0init();
41
  PWMinit();
42
43
  sei();
44
45
  //Programmschleife
46
  while(1){}
47
48
return 0;
49
}

von Karl H. (kbuchegg)


Lesenswert?

der_hier schrieb:

> Nun hatte ich die Idee über einen Externen interrupt (beim mega nutze
> ich int0, beim tiny wollte ich dann pcintX nehmen) den Timer/Counter
> direkt hochzuzählen.

Warum muss die PWM eigentlich extern getaktet werden.

> Tja, hochzählen funktioniert, aber keine PWM und kein TIMER1 overflow.
> ist es nicht möglich diese funktionen über interne zugriffe zu steuern?
> Brauche ich unbedingt dafür einen direkten Takt am Timer?

Hab ich ehrlich gesagt nie ausprobiert. Aber IMHO kriegst du gar keinen 
Overflow Interrupt sondern einen Compare Match Interrupt

Aber im Ernst: Wenn du sowieso schon extern taktest, kannst du auch 
gleich noch die PWM selber auch noch mit dazusimulieren. Die 2 
C-Anweisungen sind in der ISR auch schon egal und dann kannst du als 
Bonus auch noch die PWM auf den Pin legen, der dir genehm ist.

Du brauchst dann im Grunde noch nicht einmal den Timer, da seine Aufgabe 
eine normale 8-Bit Variable genausogut übernehmen kann.
1
volatile uint8_t nextPwmCompare;  // PWM Wert
2
3
ISR (INT0_vect)
4
{
5
  static uint8_t pwmTimer;
6
  static uint8_t pwmCompare;
7
8
  pwmTimer++;  
9
10
  if( pwmTimer == 63 ) {
11
    pwmTimer = 0;
12
    PWM_PORT &= ~( 1 << PWM_PIN );
13
    pwmCompare = nextPwmCompare
14
  }
15
  else if( pwmTimer == pwmCompare ) {
16
    PWM_PORT |= ~( 1 << PWM_PIN );
17
  }
18
}

von der_hier (Gast)


Lesenswert?

Karl heinz Buchegger schrieb:
> Warum muss die PWM eigentlich extern getaktet werden.

da ich auf ein externes 125KHz Signal eine manchestercodierung 
aufbringen möchte (halbbit 32 takte lang).

Karl heinz Buchegger schrieb:
> Wenn du sowieso schon extern taktest, kannst du auch
> gleich noch die PWM selber auch noch mit dazusimulieren

klingt gut, so hab ich da noch nicht drüber nachgedacht. werds gleich 
mal ausprobieren und dann rückmeldung geben.

von der_hier (Gast)


Lesenswert?

so ich versuchs nun mit:
1
volatile nachr[38];
2
3
ISR (INT0_vect){
4
    
5
    static uint8_t timer;
6
    static uint8_t j;
7
8
    timer++;
9
10
    if (timer == 64) { 
11
        timer = 0 ;
12
        j++;
13
        if (j == 39) {j = 0;} 
14
    }                            //rücksetzen und j hochzählen
15
    
16
    if (timer == 0){                    
17
        if(nachr[j]){ PORTD &= ~(1<<PD4) ; } //erste Halbbit 0 wenn nachr[j] = 1
18
        else { PORTD |= (1<<PD4) ; }         //erste Halbbit 1 wenn nachr[j] = 0
19
    }
20
    else if (timer == 32){
21
        if(nachr[j]){ PORTD |= (1<<PD4) ; } //zweite Halbbit 1 wenn nachr[j] = 1
22
        else { PORTD &= ~(1<<PD4) ; }        //zweite Halbbit 0 wenn nachr[j] = 0
23
    }
24
    
25
}


zum wirklichen testen werd ich erst morgen zeit haben, muss erstmal 
anderweitig schaffen. ich lass wissen obs funktioniert

von Stefan W. (swessels)


Lesenswert?

der_hier schrieb:
> .....Timer0 (einzigster Timer mit pwm beim tiny45).....

Der Timer 1 des Tiny45 kann sehr wohl PWM. Auszug aus dem Datenblatt:

Peripheral Features
– 8-bit Timer/Counter with Prescaler and Two PWM Channels
– 8-bit High Speed Timer/Counter with Separate Prescaler
  • 2 High Frequency PWM Outputs with Separate Output Compare Registers
  • Programmable Dead Time Generator

Timer 1 soll für PWM sogar besonders geeignet sein.

Gruß,
Stefan

von der_hier (Gast)


Lesenswert?

Ah, entschuldigung hab ich verwechselt, timer0 ist der einzige mit 
externer Clock (T0), deswegen könnte ich wenn nur den verwenden (Problem 
bleibt leider). konnte aufgrund anderer arbeiten den neuen code auch 
noch nicht ausprobieren.(bleibt dabei, ich sag bescheid obs 
funktioniert)

von Simon K. (simon) Benutzerseite


Lesenswert?

Ich werd aus deinen Ausführungen nicht schlau. Warum muss der PWM Takt 
von extern kommen?

von der_hier (Gast)


Lesenswert?

der_hier schrieb:
> Karl heinz Buchegger schrieb:
>> Warum muss die PWM eigentlich extern getaktet werden.
>
> da ich auf ein externes 125KHz Signal eine manchestercodierung
> aufbringen möchte (halbbit 32 takte lang).

das Signal wird von einer externen Quelle vorgegeben und ist nicht exakt 
125 KHz und variable (sagen wir mal 120-130 KHz, je nachdem). 32 Takte 
soll heißen 32 Schwingungen des 125KHz signals

von der_hier (Gast)


Lesenswert?

So sieht mein Programm nun aus. leider funktioniert es nicht wirklich. 
Seltsamste Sache: an PORTD und PORTC (außer JTAG) liegen nun dauerhaft 
+2V (5V versorgungsspannung bzw. high signal)

1
/* Programminterner Header ATmega644
2
--------------------------------------------------------------------------------*/
3
#include <avr/io.h>
4
#include <avr/interrupt.h>
5
6
#ifndef F_CPU
7
#define F_CPU 1000000UL
8
#endif
9
10
11
/*------------------------------------------------------------------------------*/
12
volatile uint8_t nachr[38];
13
14
15
/* Interrupts
16
--------------------------------------------------------------------------------*/
17
18
ISR (INT0_vect){
19
  
20
  static uint8_t timer;
21
  static uint8_t j;
22
23
  if (timer == 63) { 
24
    timer = 0 ;
25
    j++;
26
    if (j == 39) {j = 0;} 
27
  }              //rücksetzen und j hochzählen
28
  
29
  if (timer == 0){          
30
    if(nachr[j]){ PORTD &= ~(1<<PD4) ; } //erste Halbbit 0 wenn nachr[j] = 1
31
    else { PORTD |= (1<<PD4) ; }     //erste Halbbit 1 wenn nachr[j] = 0
32
  }
33
  else if (timer == 32){
34
    if(nachr[j]){ PORTD |= (1<<PD4) ; } //zweite Halbbit 1 wenn nachr[j] = 1
35
    else { PORTD &= ~(1<<PD4) ; }    //zweite Halbbit 0 wenn nachr[j] = 0
36
  }
37
  
38
}
39
/*------------------------------------------------------------------------------*/
40
41
42
43
44
/* int0 und Nachrichten 
45
--------------------------------------------------------------------------------*/
46
47
48
void nachrinit(){
49
  uint8_t i;
50
  for (i=0; i<39; i++){
51
    switch (i){
52
      case (0):
53
      case (1):
54
      case (2):
55
      case (3):
56
      case (4):
57
      case (5):
58
      case (6):
59
      case (7):
60
      case (8):
61
      case (9):
62
      case (10):
63
      case (14):
64
      case (16):
65
      case (19):
66
      case (20):
67
      case (21):
68
      case (22):
69
      case (24):
70
      case (25):
71
      case (26):
72
      case (33):
73
        nachr[i]=1;
74
      break;
75
      default:
76
        nachr[i]=0;
77
            
78
    }
79
  }
80
}
81
82
83
84
85
void int0init(){
86
  EICRA |= (1<<ISC01);
87
  EIMSK |= (1<<INT0); //int0 interrupt falling edge
88
}
89
90
91
/*------------------------------------------------------------------------------*/
92
93
94
95
/* Hauptprogramm
96
--------------------------------------------------------------------------------*/
97
int main (){
98
  
99
  // Port Deklaration
100
  DDRD = (1<<PD4); //PD4 als Ausgang
101
  PORTD = (1<<PD4);
102
103
  //Variablen Deklaration
104
105
  //Initialisierungen
106
  nachrinit();
107
  int0init();
108
109
  sei();
110
111
  //Programmschleife
112
  while(1){
113
    
114
    
115
116
  }
117
118
return 0;
119
}
120
/*------------------------------------------------------------------------------*/

von der_hier (Gast)


Lesenswert?

kann das bitte wer löschen da fehlt doch glatt timer++; allerdings 
bleiben die seltsamen 2V an den PORTS da jemand ne idee?

von julian (Gast)


Lesenswert?

Nach einem Reset sind alle Pins (mehr oder weniger hochohmige) Eingänge. 
Also misst du mit dem Voltmeter irgendwelchen zufälligen Mist der durch 
die Bude fliegt

von der_hier (Gast)


Lesenswert?

Ok, das nehm ich mal so hin.
problem ist auch gelöst. Clock auf 1 MHZ und interrupt 125KHz => 8 Takte 
um den Interrupt auszuführen (zu wenig). auf 8 MHz getaktet und die 
"Bude" rennt.

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.