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