-
Also deinen Code habe ich fast zur gänze verstanden, ich weiß nur nicht wofür ich solche Sachen wie JTAG oder .CSEG brauche. Ich habe meinen Code jetzt so verändert das man mithilfe zweier Taster die LED heller oder dünkler machen kann, zumindest mit dem Simmulator funktionierts schon mal. Das STK 500 bekomm ich hald erst in ein paar tagen.
Hier mal mein Code, wenn der funktioniert, dann is das ein richtig einfach verständlicher Code zum erlernen von PWM ^^
Code:
.include "m16def.inc"
.def temp = r16
.def PWMstatus = r17
.def Helligkeit = r18
.org 0x000
jmp main ; Reset Interupt Handler
jmp EXT_INT0 ; INT 0 Interupt Handler
jmp EXT_INT1 ; INT 1 Interupt Handler
jmp Kein_Interrupt ;TIM2_COMP ; Timer 2 Compare Interupt Handler
jmp Kein_Interrupt ;TIM2_OVF ; Timer 2 Overflow Interupt Handler
jmp Kein_Interrupt ;TIM1_CAPT ; Timer 1 Capture Interupt Handler
jmp Kein_Interrupt ;TIM1_COMPA ; Timer 1 Compare A Interupt Handler
jmp Kein_Interrupt ;TIM1_COMPB ; Timer 1 Compare B Interupt Handler
jmp Kein_Interrupt ;TIM1_OVF ; Timer 1 Overflow Interupt Handler
jmp Kein_Interrupt ;TIM0_OVF ; Timer 0 Overflow Interupt Handler
jmp Kein_Interrupt ;SPI_STC ; SPI Transfer Complete Interupt Handler
jmp Kein_Interrupt ;USART_RXC ; USART RX Complete Interupt Handler
jmp Kein_Interrupt ;USART_DRE ; USART UDR empty Interupt Handler
jmp Kein_Interrupt ;USART_TXC ; USART TX Complete Interupt Handler
jmp Kein_Interrupt ;ADC ; ADC Conversion Complete Interupt Handler
jmp Kein_Interrupt ;EE_RDY ; EEPROM Ready Interupt Handler
jmp Kein_Interrupt ;ANA_COMP ; Analog Comperator Interupt Handler
jmp Kein_Interrupt ;TWSI ; TWI Interupt Handler
jmp Kein_Interrupt ;EXT_INT2 ; INT 2 Interupt Handler
jmp TIM0_COMP ; Timer 0 Compare Interupt Handler
jmp Kein_Interrupt ;SPM_RDY ; Store Program Memory Ready Interupt Handler
main:
;Stackpointer
ldi temp, LOW(RAMEND)
out SPL, temp
ldi temp, HIGH(RAMEND)
out SPH, temp
;Aus/Eingänge
ldi temp, 0xFF
out DDRB, temp ; Ausgang
;PWM
ldi temp, (1<<WGM00) | (1<<CS00) ; Einstellungen siehe Seite 81-83
out TCCR0, temp
ldi Helligkeit, 0b00000000
out OCR0, Helligkeit ; Stellt die Einschaltzeit ein (Alles gesetzte --> immer ein, alles aus --> immer aus)
ldi temp, (1<<OCIE0)
out TIMSK, temp
;External Interrupt
ldi temp, 0b00001111
out MCUCR, temp ; Steigende Flanke löst Ext. Int. 0 und 1 aus
ldi temp, 0b11000000
out GICR, temp ; Ext. Int 0 und 1 aktivieren
;Interrupts freigeben
sei ; Interrupts allgemein aktivieren
Ende:
jmp ende
Kein_Interrupt:
reti
TIM0_COMP:
inc PWMstatus
ldi temp, 0b00000001
andi PWMstatus, 0b00000001
cp PWMstatus, temp
breq PC+3
cbi PortB, 0
reti
sbi PortB, 0
reti
EXT_INT0:
ldi temp, 0b11111111
cp Helligkeit, temp
breq PC+4
ldi temp, 0b00001111
add Helligkeit, temp
out OCR0, Helligkeit
reti
EXT_INT1:
ldi temp, 0b00000000
cp Helligkeit, temp
breq PC+4
ldi temp, 0b00001111
sub Helligkeit, temp
out OCR0, Helligkeit
reti
Ich habe jetzt noch vor den Ausgangsport auch per Taster verändern zu können, das Problem ist nur, jetzt habe ich nur noch einen EXT_int.
Sehr hilfreich war der Hinweis das meine Interruptvektortabelle nicht korrekt ist, die hab ich einfach aus dem tuturial von mikrocontroller.net abgeschrieben ^^, wusste nicht das das beim mega16 anders ist.
Für Verbesserungsvorschläge bin ich natürlich immer offen.
Grüße,
Gerko
-
Okay, ich hab den Code einmal überflogen.
Was mir hierbei auffällt sind die folgenden Dinge:
- Die folgende Sequenz hatte durchaus seinen Sinn.
Damit wird das JTAG Interface des Mikrocontrollers deaktivert. Welches, sollte man dieses bei den FUSE Bit Einstellungen vergessen mal eben den kompletten PORT C blockiert.
Code:
in temp, MCUCSR ; Disable JTAG Interface
ori temp, (1<<JTD)
out MCUCSR, temp
out MCUCSR, temp
- Die Assemblerdirektive .CSEG heisst das die nachfolgenden Zeilen im Codesegment abgelegt werden (FLASH).
Weitere Möglichkeiten sind .DSEG und .ESEG
Genauere Infos findest du in der AVR Studio Hilfe.
- Beim Einsprung in eine Interupt Routine sollte immer das folgende auf den Stack wandern[list:833f66c656]
- Alle Register die sonst noch im Programm verwendet werden,
- das Statusregister SREG
Sicherlich erscheint es in diesem Fall unnötig, allerdings sollte man es sich schon angewöhnen diese zu sichern, da es sonst bei Programmerweiterungen schnell zu unerwünschten Nebeneffekten kommen kann.
[*]Nicht belegte Pins sollte man auf einen definierten Pegel ziehen. Ich habe mir angewöhnt, diese als Eingang mit internem Pullup zu schalten.
Der Grund ist unter anderem der jene, das man damit unerwünschte Pinwackeleien vermeidet.
[*]Du initialisierst mit deinem Code eine Hardware PWM, also das genaue Gegenteil von dem, was ich dir in diesem Beispiel aufgezeigt habe.
Dieses ist an und für sich nicht schlimm, es ist sogar der zu bevorzugende Weg, wenn man nur 1-4 PWM's benötigt und die entsprechenden Pins am ATmega noch frei sind (OC0, OC1A, OC1B, OC2).
Das Problem bei dir ist allerdings darin, das du diese Hardwaregenerierte Pulsweitenmodulation im µC behältst und nicht ausgibst.
Mit deinem Code generierst du sicherlich eine PWM an jedem beliebigem PIN, allerdings erscheint mir der Aufwand dafür etwas übertrieben.
Auch denke ich, das du damit bei gewissen Werten saubere Rechteckimpulse mit 50% Puls Pausenverhältniss generierst.[/list:o:833f66c656]
So, das war ein kleiner Auszug aus 'Hannis aktueller Meckerstunde' ich hoffe du nimmst es mir nicht übel.
Grüße,
Hanni.
-
Hallo,
Ich nehme deine "Meckerstunde" ja nicht böse, ist ja sehr hilfreich für mich.
Den Befehl .CSEG kannte ich bisher noch nicht. Bis jetzt hat es auch immer ohne dem funktioniert, aber ich lasse mir gerne einreden das dieser Befehl nicht unwichitg ist.
Das mit dem SREG habe ich gerade nachgelesen, bisher dachte ich das die sicherung davon automatisch geschieht.
Ich wollte ja von Anfang an eine Hardware PWM, und ich finde nicht das ich das so umständlich gemacht habe. Ich glaube sogar das meine Methode schneller läuft als deine, denn bei dir muss jedesmal ein ganzes Register durchlaufen werden. Dadurch wird dieFrequenz erheblich gesenkt denke ich. Außerdem ist deine Aussage das ich max 4 Ausgänge verwenden kann nicht richtig, ich kann ja alle ansteuern wenn ich will.
Was meinst du mit 50% Pausenverhältniss?
Grüße,
Gerko
EDIT: Mir ist gerade aufgefallen das du die Register r4, r5,... und so verwendest. Mir hat mal jemand erzählt das diese Register für andere sachen vorbehalten sind und man nur die Register R16-R32 als Arbeitsregister verwenden kann. Fals das stimmt, für was kann man die register r1-r15 dann verwenden?, ich hab in dem saulangen AVR datenblatt nichts darüber gefunden :(
-
Okay:
1. Die Register 0-15 haben eigentlich nur die Einschränkung, das gewisse Befehle mit diesen nicht funktionieren.
also z.B. ldi cpi andi
ansonsten bieten sich diese in diesem Fall für mich regelrecht an, da der Inhalt der von diesen Verarbeitet wird in meinem Fall aus dem RAM kommen würde.
2. 50% Pausenverhältniss:
bei gewissen Werten im OCR (um genau zu sein ist es 0 & der maximalwert) erhältst du exakt einen Compare Interupt je 0-max-0 Zyklus. Daraus folgt, das du nur einen Sauberen Rechteck mit einem Puls - Pausenverhältniss von 50% / 50% erzeugst.
3. "Außerdem ist deine Aussage das ich max 4 Ausgänge verwenden kann nicht richtig, ich kann ja alle ansteuern wenn ich will."
Nuja, mit meiner Routine kann ich aber 4 verschiedene PWM's erzeugen (also 4 verschiedene Puls - Pausenverhältnisse).
Das dürfte mit deiner Routine eben nicht funktionieren.
Grüße,
da Hanni.
-
Hi!
Ich habe gestern mein STK 500 bekommen :) und jetzt verstehte ich endlich was du damit meinst, das es nicht perfekt ist ^^. Ich habe mein Programm daher jetzt komplett umgekrempelt und verwende nun den OC0 Ausgang. Anschließend überprüfe ich einfach in einer Routine ob dieser Ausgang gesetzt oder nicht gesetzt ist, und wende dies auf andere Ausgänge an, diese kann ich natürlich auch aus, bzw. einschalten, je nach belieben.
Wenn es allerdings so weit ist das ich eine Platine baue, und dort auch einen Motortreiber includiere, dann werde ich diesen über den Timer1 laufen lassen, geht einfach schneller, und im Hintergrund.
Das ich bei deiner Routine 4 verschiedene PWMs erzeugen kann weiß ich, wir vielleicht von Vorteil sein, wenn man auf den Rädern unterschiedliche Drehzahlen haben will, ich hatte eigentlich vor den Roboter nur schneller oder langsamer fahren zu lassen, und die Kurfen mit Drehungen zu erledigen. An so "schöne Kurfen" wagte ich bis jetzt noch garnicht zu träumen ^^.
Danke jedenfalls das du mir soweit geholfen hast. Jetzt verstehe ich endlich wie Timer funktionieren, und vorallem wie Interrupts ausgelöstet werden.
Grüße,
Gerko
-
Nichts zu danken, jeder hat mal mit dem kleinem Einmaleins angefangen. Auch wenn dieses so der eine oder andere gelegentlich vergisst.
-
Hallo Leute,
Ich hab neulich angefangen, mit Mikrocontrollern zu experimentieren und hab mir nen Atmega 16 gekauft... Zusätzlich benutze ich das Buch von Elektor "AVR Hardware und C-Programmierung in der Praxis". Ich habe jetzt angefangen mit PWM zu arbeiten. Allerdings schaffe ich es nicht, dass sich der OCR0 Wert verändert. Ich habe folgendes Programm:
Code:
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
uint8_t wert;
void delay_ms(uint16_t ms)
{
for(uint16_t t=0; t<=ms; t++)
_delay_ms(1);
}
// Timer/Counter0 Overflow
ISR (TIMER0_OVF_vect)
{
PORTD |= (1 << PD0); // PD0 ein
}
// Timer/Counter0 Compare Match
ISR (TIMER0_COMP_vect) // Wenn OCR0 Wert erreicht wird
{
PORTD &= ~(1 << PD0); // PD0 aus
}
void main(void)
{
DDRD |= (1 << DDD0) | (1 << DDD1) ; // PORTB als Ausgang
wert = 10;
TIMSK |= (1 << TOIE0) | (1 << OCIE0); // IRQ bei Überlauf + Compare Match
OCR0 = wert; // Vergleichswert 128 einstellen
sei(); // IRQs enable
PORTD |= (1 << PD0); // PB0
TCCR0 |= (1 << CS02); // Prescaler: 1/1024 (CS1=0 per default) (1 << CS00) |
while (1); // endlos
{
delay_ms(100);
if(wert >= 240)
wert = 10;
else
wert++;
OCR0 = wert;
}
return 0;
}
Der Sinn ist, eine LED zu faden... also von dunkel bis hell. Aber es klappt nicht. Der Wert von OCR0 verändert sich nicht.
Vielen Dank schonmal im voraus.
MfG
RP6fahrer
-
Was passiert mit dem Ausgang? Immer ein, Immer aus, immer mit der gleichen Helligkeit gedimmt?
MfG Hannes
-
Hi,
Danke für die schnelle Antwort.... also die LED leuchtet immer so, als würde OCR0 = 10 sein.
-
Ist das ein Beispiel aus dem Buch? Wenn nicht würde ich das Programm so umschreiben das du den Timer/Counter direkt zur PWM erzeugung nutzt (Phase Correct PWM oder Fast PWM).
MfG Hannes