Forum: Mikrocontroller und Digitale Elektronik Assembler PWM Problem mit atmega88


von Michael H. (ripper)


Lesenswert?

Hallo Leute,

bitte nicht steinigen falls das schonmal aufgetaucht sein sollte, aber 
ich hab ein Problem mit meinem Atmega88. Gesucht hab ich hier im Forum 
schon, nur nix brauchbares zutage befördert. Ich will mit dem Timer1 
eine PWM erzeugen. Meine letzten Versuche mit AVR-Assembler sind schon 
länger her, daher hab ich echt keinen plan was hier nicht funktioniert.
Den AD-Wandler Teil bitte ignorieren, der ist (noch) nicht von 
bedeutung.

Das Problem ist folgendes:
An PB1 hängt eine LED mit vorwiderstand gegen Versorgungsspannung, die 
sollte mit der PWM helligkeitsgeregelt werden. Nur wenn ich mich mit dem 
Oszi an den Pin höng hab ich nur ca. 2,7V gleichspannung dort. Der 
Controller wird mit 5V versorgt und hat einen externen Quarz mit 16Mhz. 
Die Fuses für den Quarz hab ich im Atmel STudio schon richtig gesetzt. 
Programmiert wird übrigens mit einem AVRISP Mk2.

Ich hab dann auch schon versucht den Pin in der Hauptschleife einfach 
nur mit sbi und cbi zu toggeln, aber selbst da hatte ich kein Signal, 
obwohl der Simulator wunderbar funktioniert hat.

Könnte es sein, dass der ganze Port was hat? Oder hab ich einfach nur 
vergessen irgendein Bit zu setzen?
1
.cseg        //program is stored in the flash memory
2
3
reset:        
4
.org  0      //compiling starts at adress 00
5
  rjmp  setup  //
6
7
.org  0x00D
8
  rjmp  t1_overflow    //Interrupt routine for the timer1 overflow
9
10
.org  0x015
11
  rjmp  adc_int      //jump to the interrupt routine for the ADC
12
13
.org  0x01A        //Start after the interrupt vectors
14
15
setup:
16
  ldi    r16,high(RAMEND)  //initializing the stack pointers
17
  out    SPH,r16        
18
  ldi    r16,low(RAMEND)     
19
  out    SPL,r16      
20
  
21
  //First, we need to activate interrupts for the ADC:
22
  
23
  sei            //global interrupt enable
24
25
  //then we need to configure the ADC module:
26
27
  ldi    r16,0b01100000  //AVCC as reference voltage; result is left orientated; channel0 as input
28
  sts    ADMUX,r16
29
  ldi    r16,0b11101111  //ADC is enabled; first conversion started; auto trigger and interrupt is enabled; 128 prescaler => 125kHz ADC clock
30
  sts    ADCSRA,r16
31
  ldi    r16,0b00000001  //disabling the digital input buffer on PC0
32
  sts    DIDR0,r16
33
              //we don't need to modify ADCSRB because it's default set to free running mode for the ADC
34
35
  //we also need to set the pwm pin OC1A (PB1) as an output
36
37
  ldi    r16,0b00000010  //only PB1 needs to be an output
38
  sts    DDRB,r16
39
  clr    r16
40
  sts    PORTB,r16
41
42
  //Next we're going to set the timer1 module to fast pwm mode:
43
  
44
  ldi    r16,0b10000000  //Initial 50% duty cycle
45
  sts    OCR1AL,r16
46
  ldi    r16,0b11000010  //inverting mode, WGM11 = 1 and WGM10 = 0 (fast pwm mode with ICR1 as TOP value)
47
  sts    TCCR1A,r16    
48
  ldi    r16,0b00011001  //no input, so the first 2 bits are 0, WGM13 and 12 are both one for fast pwm and no clock prescaler
49
  sts    TCCR1B,r16
50
  ser    r16        //set top value for pwm to 255
51
  sts    ICR1L,r16
52
53
  
54
loop:        //At this moment the main program is just a loop of doing nothing
55
  rjmp  loop
56
57
adc_int:      //the ADC interrupt routine
58
  lds    r16,ADCH    //reading the upper 8 bits of the conversion result
59
  sts    OCR1AL,r16    //and set them as compare value for the pwm
60
  reti
61
  
62
t1_overflow:
63
  reti      //just return without doing anything

mfg Michael

von Karl H. (kbuchegg)


Lesenswert?

Michael H. schrieb:


> Das Problem ist folgendes:
> An PB1 hängt eine LED mit vorwiderstand gegen Versorgungsspannung, die
> sollte mit der PWM helligkeitsgeregelt werden. Nur wenn ich mich mit dem
> Oszi an den Pin höng hab ich nur ca. 2,7V gleichspannung dort.

Das ist bedenklich.
Du kannst da 0V haben oder 5V. Aber 2.7V kannst du da normalerweise 
überhaupt nicht haben - wir sind schliesslich in der Digitaltechnik,

Ich würde jetzt erst mal dieses Problem angehen und den ganzen Teil PWM 
erst mal weglassen.
Einfach nur ein Programm schreiben

   Portpin auf Ausgang konfigurieren
   Portpin auf 1 setzen

loop: rjmp loop

wenn du dann immer noch 2.7V am Pin misst, dann muss man sich die 
Hardware näher ansehen. Aber solange das nicht geklärt ist, ist es 
sinnlos da noch 2 Konzepte (PWM und ADC) draufzupacken.



Zu deinem Programm.
Du hast natürlich das schlimmste gemacht, was du hättest machen können, 
in dem du überall Bitkonstanten benutzt hast.
1
 ldi    r16,0b11101111  //ADC is enabled; first conversion started; auto trigger and interrupt is enabled; 128 prescaler => 125kHz ADC clock

deine Kommentare in allen Ehren, nichts desto trotz muss ich sie bei der 
Fehlersuche ignorieren. Denn ob das was du da beschreibst mit dem 
zusammenstimmt, welche Bits du gesetzt hast, muss mit dem Datenblatt 
abgeklärt werden. Da halst du mir allerdings mit dieser Bitschreibweise 
zusätzliche unnötige Arbeit auf, weil ich erst mal im Datenblatt 
identifizieren muss, welches Bit an welcher Bitposition (sind es 
überhaupt 8 Bit?) wie heißt, ehe ich dann mit diesem so ermitteltem 
Namen in die Beschreibung geben kann und kontrollieren kann, ob es 
gesetzt sein sollte oder nicht.


Weiters fällt mir auf, dass du alle Register mittels STS beschreibst. 
Und das kommt mir komisch vor. Dass einige Register nicht mit IN/OUT zu 
erreichen sind mag ja sein. Aber ALLE. Das wäre in der Tat ungewöhnlich.

von Ich (Gast)


Lesenswert?

Nicht am Pin, sondern an der LED gemessen?
Pin->R->LED->GND?

von spess53 (Gast)


Lesenswert?

Hi

> ldi    r16,0b00000010  //only PB1 needs to be an output
>  sts    DDRB,r16
>  clr    r16
>  sts    PORTB,r16

DDRB und PORTB liegen im normalen IO-Adressraum und dafür gilt 'out'. 
Mit z.B. 'sts PortB,..' beschreibst du eine falsche Adresse.

MfG Spess

von Michael H. (ripper)


Lesenswert?

Danke spess53, das wars, jetzt gehts wunderbar. Hatte gedacht sts geht 
überall. :)

@Karl:
WIe kann man das denn übersichtlicher machen? Das letzte mal, dass ich 
AVRs programmiert habe ist einige Zeit her, daher kenn ich mich da nicht 
mehr so aus...

Mfg Michael

von Karl H. (kbuchegg)


Lesenswert?

Ich nehm nur mal 2 Beispiele raus (weil ich den Rest jetzt nicht im 
Datenblatt nachschlagen will)

  ldi    r16,0b00000010  //only PB1 needs to be an output
->
  ldi    r16, ( 1 << PB1 )

da steht direkt in der Anweisung PB1. Ich muss nicht Bitzählen, ich muss 
auch nicht deinen Kommentar lesen.

  ldi    r16,0b11000010  //inverting mode, WGM11 = 1 and WGM10 = 0 (fast 
pwm mode with ICR1 as TOP value)
->
  ldi    r16, (1<<COM1A1) | (1<<COM1A0 ) | (1<<WGM11)

selbiges. Dier Bits haben Namen. Benutze sie!


  ldi    r16,0b10000000  //Initial 50% duty cycle
  sts    OCR1AL,r16

du willst also einen 50% Duty Cycle. Da der maximale PWM Wert 255 ist, 
wären 50% 128 (gerundet). Gut. Dann schreib auch 128

  ldi    r16, 128
  out    OCR1AL,r16

Man benutzt die Schreibweise, die am besten der Verwendung entspricht. 
Wenn du die Verwendung am besten mit Dezimalzahlen ausdrücken kannst, 
dann benutz Dezimalschreibweise. Wenn es sich um Bitgeschichten handelt, 
dann benutz Binärschreibweise. Wenn die Bitgeschichten so sind, dass man 
sie als Hex-Zahlen übersichtlicher schreiben kann, dann benutz 
Hex-Schreibweise.

Willst du in einem Wecker (Uhr), die Minuten der Weckzeit auf 20 Minuten 
stellen, dann schreib
    ldi   r_irgendwas, 20

und nicht

    ldi   r_irgendwas, 0b00010100
oder
    ldi   r_irgendwas, 0x14

Alle 3 Schreibweisen sind technisch gesehen vollständig gleichwertig. Es 
sind nur verschiedene Schreibweisen für ein und dasselbe. Und trotzdem 
sollte auf Anhieb klar sein, warum

    ldi   r_irgendwas, 20

in diesem konkreten Fall, die beste Schreibweise für den Job ist, die 
Minutenvorgabe auf 20 zu setzen.

von Ich (Gast)


Lesenswert?

Michael H. schrieb:
> WIe kann man das denn übersichtlicher machen?

Ja, in C -> duck weg... :-)

von Michael H. (ripper)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Ich nehm nur mal 2 Beispiele raus (weil ich den Rest jetzt nicht im
> Datenblatt nachschlagen will)
>
>   ldi    r16,0b00000010  //only PB1 needs to be an output
> ->
>   ldi    r16, ( 1 << PB1 )
>
> da steht direkt in der Anweisung PB1. Ich muss nicht Bitzählen, ich muss
> auch nicht deinen Kommentar lesen.
>
>   ldi    r16,0b11000010  //inverting mode, WGM11 = 1 and WGM10 = 0 (fast
> pwm mode with ICR1 as TOP value)
> ->
>   ldi    r16, (1<<COM1A1) | (1<<COM1A0 ) | (1<<WGM11)
>
> selbiges. Dier Bits haben Namen. Benutze sie!

Danke, werd ich gleich bei mir ändern. Ich dachte nämlich das geht nur 
in C so.

mfg Michael

von Karl H. (kbuchegg)


Lesenswert?

Michael H. schrieb:

> Danke, werd ich gleich bei mir ändern. Ich dachte nämlich das geht nur
> in C so.

Ich sag zwar nicht, dass das Tutorial alles 100% richtig macht, aber so 
manches kann man sich dann doch dort abschauen
AVR-Tutorial

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.