Forum: Mikrocontroller und Digitale Elektronik PWM mit ATMEGA168


von Tobias x. (dertobias)


Lesenswert?

Hallo Commmunity,

ich habe mal wieder eine Frage, udn da mir bisher immer gut geholfen 
werden konnte, hoffe ich auch diesmal auf eure Hilfe:

Wie funktioniert das mit der PWM, damit ich eine LED Dimmen kann bei 
einem ATMEGA168.

Ich habe mir schon Applicationsnotes und andere Quellen über Software 
PWM mit einem ATMEGA durchgelesen, konnte aber kein ansatzweise 
laufendes Programm erstellen. um das mal zu testen.

Nun meine Frage, kann mir wer ein kleines Beispiel für eine Softwarepwm 
für den ATMEGA 168 zeigen und erklären? Es reicht völlig, wenn in diesem 
Beispiel nur 1 LED z.B. die led an PORTD PIN3 auf die hälfte gedimmt 
wird, wenn ich PORTB PIN0 auf 0V ziehe.

Vielen Dank

von ... (Gast)


Lesenswert?

Tobias xxx schrieb:
> Nun meine Frage, kann mir wer ein kleines Beispiel für eine Softwarepwm
> für den ATMEGA 168

Du kannst ein Programm für den ATmega8 hier aus dem Forum nehmen und 
mußt nur ein paar Registerbezeichnungen anpassen. Funktionieren tut der 
ATmega168 sonst sehr ähnlich.

von Tobias x. (dertobias)


Lesenswert?

wo finde ich den so einen beispiel-code? finde mich noch nicht so recht 
zurecht. das Programm sollte auch in c sein, da ich assembler noch 
fremder bin als c.

Danke

von ... (Gast)


Lesenswert?

Tobias xxx schrieb:
> wo finde ich den so einen beispiel-code?

Über die Suchfunktion des Forums. Zu PWM finden sich ungefähr 13744 
Threads. Hast du da schon mal geguckt?

von Tobias x. (dertobias)


Lesenswert?

JA aber irgendwie finde ich nur forenbeiträge. und da ist kein definitiv 
funktionierender code für mich gerade zu finden aber ich versuche es 
weiter

von ... (Gast)


Lesenswert?

Du mußt dir im Datenblatt nur den passenden PWM-Mode aus dem Datenblatt 
raussuchen und die Register entsprechend setzen. Der Rest läuft dann per 
Timer und PWM-Hardware des Prozessors selbständig. Zur Änderung des 
Tastverhältnis muß dann nur z.B. eines der Output Compare Register 
geändert werden.
z.B.
Beitrag "Ansteuerung einer RGB LED (PWM)"

Es geht natürlich auch per Software:
Beitrag "8-fach 12-Bit Soft-PWM LED-Dimmer für mega48"

von Tobias x. (dertobias)


Lesenswert?

Hallo,

habe jetzt einen Code gefunden und umgeschrieben, womit ich alle meine 
LED'S Dimmen kann.
Der Code war für einen ATMEGA 8 und ich habe ihn schon soweit 
umgeschrieben, das er auf einem MEGA168 läuft.
Meine Frage ist jetzt, wo muss ich eingreifen, bzw. was ändern/löschen, 
damit die nicht wie es ja zu demo zwecken ist, die ganze zeit dimmen. 
sondern das ich die LEDS mit einem POTI am Analogeingang PC5 dimmen 
kann. Der Poti soll dann für Später einen Fototransiostor simulieren. 
Also die LEDS sollen dauerhaft an sein, udn durch den POTI gedimmt 
werden können.

Wäre klasse, wenn mir wer da helfen könnte.

von Tobias x. (dertobias)


Lesenswert?

... schrieb:
> Es geht natürlich auch per Software:
> Beitrag "8-fach 12-Bit Soft-PWM LED-Dimmer für mega48"

Hier geht es doch wenn ich das richtig sehe um eine HArdware PWM 
anstelle iner Software pwm.

von Tobias x. (dertobias)


Lesenswert?

Tobias xxx schrieb:
> Hallo,
>
> habe jetzt einen Code gefunden und umgeschrieben, womit ich alle meine
> LED'S Dimmen kann.
> Der Code war für einen ATMEGA 8 und ich habe ihn schon soweit
> umgeschrieben, das er auf einem MEGA168 läuft.
> Meine Frage ist jetzt, wo muss ich eingreifen, bzw. was ändern/löschen,
> damit die nicht wie es ja zu demo zwecken ist, die ganze zeit dimmen.
> sondern das ich die LEDS mit einem POTI am Analogeingang PC5 dimmen
> kann. Der Poti soll dann für Später einen Fototransiostor simulieren.
> Also die LEDS sollen dauerhaft an sein, udn durch den POTI gedimmt
> werden können.
>
> Wäre klasse, wenn mir wer da helfen könnte.

Hatte leider vergessen, den CODE anzuhängen:
1
// Software-PWM by PoWl - 25.10.08
2
// webmaster@the-powl.de
3
4
// ATmega168 @ 8Mhz
5
6
#include <avr/io.h>
7
#include <avr/interrupt.h>
8
#include <util/delay.h>
9
#include <stdbool.h>
10
11
12
// Sinustabelle für Demo
13
const uint8_t sinetable[] = {
14
128, 131, 134, 137, 140, 143, 146, 149, 152, 155, 158, 162, 165, 167,
15
170, 173, 176, 179, 182, 185, 188, 190, 193, 196, 198, 201, 203, 206,
16
208, 211, 213, 215, 218, 220, 222, 224, 226, 228, 230, 232, 234, 235,
17
237, 238, 240, 241, 243, 244, 245, 246, 248, 249, 250, 250, 251, 252,
18
253, 253, 254, 254, 254, 255, 255, 255, 255, 255, 255, 255, 254, 254,
19
254, 253, 253, 252, 251, 250, 250, 249, 248, 246, 245, 244, 243, 241,
20
240, 238, 237, 235, 234, 232, 230, 228, 226, 224, 222, 220, 218, 215,
21
213, 211, 208, 206, 203, 201, 198, 196, 193, 190, 188, 185, 182, 179,
22
176, 173, 170, 167, 165, 162, 158, 155, 152, 149, 146, 143, 140, 137,
23
134, 131, 128, 124, 121, 118, 115, 112, 109, 106, 103, 100, 97, 93,
24
90, 88, 85, 82, 79, 76, 73, 70, 67, 65, 62, 59, 57, 54, 52, 49, 47,
25
44, 42, 40, 37, 35, 33, 31, 29, 27, 25, 23, 21, 20, 18, 17, 15, 14,
26
12, 11, 10, 9, 7, 6, 5, 5, 4, 3, 2, 2, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
27
1, 1, 1, 2, 2, 3, 4, 5, 5, 6, 7, 9, 10, 11, 12, 14, 15, 17, 18, 20,
28
21, 23, 25, 27, 29, 31, 33, 35, 37, 40, 42, 44, 47, 49, 52, 54, 57,
29
59, 62, 65, 67, 70, 73, 76, 79, 82, 85, 88, 90, 93, 97, 100, 103,
30
106, 109, 112, 115, 118, 121, 124 };
31
32
33
34
// -------------------------- 8-Bit PWM --------------------------- //
35
// An - 254 Graustufen - Aus
36
37
#define CHANNELS 12
38
39
uint8_t pwm_mask_PORTC_A[CHANNELS + 1];
40
uint8_t pwm_mask_PORTD_A[CHANNELS + 1];
41
uint8_t pwm_timing_A[CHANNELS + 1];
42
43
uint8_t pwm_mask_PORTC_B[CHANNELS + 1];
44
uint8_t pwm_mask_PORTD_B[CHANNELS + 1];
45
uint8_t pwm_timing_B[CHANNELS + 1];
46
47
uint8_t *ptr_PORTC_main = &pwm_mask_PORTC_A;
48
uint8_t *ptr_PORTD_main = &pwm_mask_PORTD_A;
49
uint8_t *ptr_timing_main = &pwm_timing_A;
50
51
uint8_t *ptr_PORTC_isr = &pwm_mask_PORTC_B;
52
uint8_t *ptr_PORTD_isr = &pwm_mask_PORTD_B;
53
uint8_t *ptr_timing_isr = &pwm_timing_B;
54
55
uint8_t pwm_value[CHANNELS];
56
uint8_t pwm_queue[CHANNELS];
57
58
volatile uint8_t pwm_cycle;
59
volatile bool pwm_change;
60
61
uint8_t pwm_pin[CHANNELS] = { PC0, PC1, PC2, PC3, PC4, PD0, PD1, PD2, PD3, PD4, PD6, PD7 };
62
63
uint8_t **pwm_port[CHANNELS] = {
64
  &ptr_PORTC_main,
65
  &ptr_PORTC_main,
66
  &ptr_PORTC_main,
67
  &ptr_PORTC_main,
68
  &ptr_PORTC_main,
69
  &ptr_PORTD_main,
70
  &ptr_PORTD_main,
71
  &ptr_PORTD_main,
72
  &ptr_PORTD_main,
73
  &ptr_PORTD_main,
74
  &ptr_PORTD_main,
75
  &ptr_PORTD_main
76
};
77
78
79
void pwm_update(void)
80
{
81
  pwm_change = 0;
82
83
  // Sortieren mit InsertSort
84
85
  for(uint8_t i=1; i<CHANNELS; i++)
86
  {
87
    uint8_t j = i;
88
89
    while(j && pwm_value[pwm_queue[j - 1]] > pwm_value[pwm_queue[j]])
90
    {
91
      uint8_t temp = pwm_queue[j - 1];
92
      pwm_queue[j - 1] = pwm_queue[j];
93
      pwm_queue[j] = temp;
94
95
      j--;
96
    }
97
  }
98
99
  // Maske generieren
100
101
  for(uint8_t i=0; i<(CHANNELS + 1); i++)
102
  {
103
    ptr_PORTC_main[i] = 0;
104
    ptr_PORTD_main[i] = 0;
105
    ptr_timing_main[i] = 0;
106
  }
107
108
  uint8_t j = 0;
109
110
  for(uint8_t i=0; i<CHANNELS; i++)
111
  {
112
    // Sonderfall "ganz AUS" ausschließen:
113
    //  - Pins garnicht erst anschalten
114
    //  - Pins die vorher "ganz AN" waren müssen trotzdem noch deaktiviert werden, deshalb nur folgendes in der if
115
    if(pwm_value[pwm_queue[i]] != 0)
116
    {
117
      (*pwm_port[pwm_queue[i]])[0] |= (1 << pwm_pin[pwm_queue[i]]);
118
    }
119
120
    // Sonderfall "ganz AN" ausschließen:
121
    //  - Nicht in Timing-Tabelle übernehmen da sonst die Pointer niemals getauscht werden
122
    //  - Pins die "ganz AN" sind müssen nicht abgeschaltet werden
123
    if(pwm_value[pwm_queue[i]] != 255)
124
    {
125
      (*pwm_port[pwm_queue[i]])[j + 1] |= (1 << pwm_pin[pwm_queue[i]]);
126
127
      ptr_timing_main[j] = pwm_value[pwm_queue[i]];
128
    }
129
130
    if(pwm_value[pwm_queue[i]] != pwm_value[pwm_queue[i + 1]])
131
    {
132
      j++;
133
    }
134
  }
135
136
  /*
137
  // Der ISR etwas Arbeit abnehmen
138
  for(uint8_t i=1; i<(CHANNELS + 1); i++)
139
  {
140
    ptr_PORTC_main[i] = ~ptr_PORTC_main[i];
141
    ptr_PORTD_main[i] = ~ptr_PORTD_main[i];
142
  }
143
  */
144
145
  pwm_change = 1;
146
}
147
148
149
int main(void)
150
{
151
  // PWM initialisieren
152
  for(uint8_t i=0; i<CHANNELS; i++)
153
  {
154
    pwm_queue[i] = i;
155
  }
156
157
  DDRC = 0b00011111;
158
  DDRD = 0b11011111;
159
  DDRB = 0x00;
160
161
162
  // Timer für PWM
163
  OCR1A = 255;
164
  TIMSK1 = (1 << OCIE1A) | (1 << OCIE1B);
165
  TCCR1B = (1 << WGM12) | 4;
166
167
  sei();
168
169
170
  uint8_t pos = 0;
171
172
  while(1)
173
  {
174
    for(uint8_t i=0; i<CHANNELS; i++)
175
    {
176
      pwm_value[i] = sinetable[(uint8_t) (pos + i * 20)];
177
    }
178
179
    pwm_update();
180
    pos++;
181
182
    _delay_ms(5);
183
  }
184
}
185
186
187
188
ISR(TIMER1_COMPA_vect)
189
{
190
  pwm_cycle = 0;
191
192
  PORTC |= ptr_PORTC_isr[0];
193
  PORTD |= ptr_PORTD_isr[0];
194
195
  OCR1B = ptr_timing_isr[0];
196
}
197
198
199
200
ISR(TIMER1_COMPB_vect)
201
{
202
  pwm_cycle++;
203
204
  PORTC &= ~ptr_PORTC_isr[pwm_cycle];
205
  PORTD &= ~ptr_PORTD_isr[pwm_cycle];
206
207
  if(ptr_timing_isr[pwm_cycle] != 0)
208
  {
209
    OCR1B = ptr_timing_isr[pwm_cycle];
210
  }
211
  else if(pwm_change)
212
  {
213
    pwm_change = 0;
214
215
    uint8_t *temp_ptr;
216
217
    temp_ptr = ptr_PORTC_main;
218
    ptr_PORTC_main = ptr_PORTC_isr;
219
    ptr_PORTC_isr = temp_ptr;
220
221
    temp_ptr = ptr_PORTD_main;
222
    ptr_PORTD_main = ptr_PORTD_isr;
223
    ptr_PORTD_isr = temp_ptr;
224
225
    temp_ptr = ptr_timing_main;
226
    ptr_timing_main = ptr_timing_isr;
227
    ptr_timing_isr = temp_ptr;
228
  }
229
}

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.