PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Atmega48 läuft auf eigener Platine nicht wie er soll!?



DanielSan
02.08.2011, 17:11
Hi,

ich habe mir eine eigene Platine in absoluter minimalkonfiguration gefräst. Ich habe nur einen 4,7kOhm Pullup am Resetpin und einen 104 Kondensator an VCC und GND. Auf meinem Steckbrett hat alles super funktioniert. Nur auf meiner Platine läuft der Controller nicht sauber durch.
Es handelt sich um den selben Controller wie auf dem Steckbrett.

Ich habe den Eindruck, das der Controller zwischendurch neustartet. Er soll ein RC-Empfängersignal auslesen und wenn es größer als 1,5ms ist die Ledblinken lassen. Aber die Led blinkt nicht immer und wenn sich das Empfängersignal nicht ändert kann es trotzdem sein das er ein paar Sekunden nicht blinkt wie er soll. An der Software habe ich nachdem es auf dem Steckbrett lief nichts mehr geändert. Den µC hab ich auf dem Steckbrett geflasht und dann eingelötet.

Habt ihr eine Idee woran das liegen kann?

Danke
Gruß Daniel

Kampi
02.08.2011, 17:41
Ein Schaltplan von der Platine wäre gut......weil so eine Fehleranalyse an hand von Fotos ist doof.

oberallgeier
02.08.2011, 18:07
... Ich habe den Eindruck, das der Controller zwischendurch neustartet ... Habt ihr eine Idee woran das liegen kann? ...Das Problem mit ungewollten Resets kenne ich. Seit langem verwende ich daher unmittelbar nach der Portdefinition, noch vor dem Erlauben der Interrupts, eine markante Blinkroutine, siehe Code (ALLE programmrelevanten Zeilen aber viel Kommentar/Dokumentation gelöscht)


// ================================================== ===============================
// === HAUPTProgramm ================================================== ============
// Initialisierungen, LED1 kurzblinken als Signal für Programmstart,
// Ausgabe des Identifizierungsstrings per USART
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
int main(void)
{
uint8_t i;
DDRB = 0b00011111; // siehe aktuell oben ...
PORTB = 0b00100000; // und Port/Pull Ups (1) aktivieren
DDRC = 0b01110000; // PC3 ist ADC3, PC0 .. 6 , kein PC7-Pin bei m168
PORTC = 0b00000111; // Beachte für ADC: PC3 ist ADC-Eingang ##>> OHNE Pullup !!
DDRD = 0b11110000; // -> siehe unter DDRB, sowie PD2,3 extInt
PORTD = 0b00001111; // Pull Ups aktivieren
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
for(i=0; i<10; i++) // gLED auf PC5 i-fach blinken lassen
// ###>>> bevor Interrupts erlaubt sind, um ungewollte
// Resets u.ä. besser erkennen zu können
{
SetBit(PORTC, 5); // LED auf PC5 schalten EIN, HELL
waitms(3); // ... damit man kurze resets besser erkennt
ClrBit(PORTC, 5); // LED auf PC5 schalten AUS, Dunkel
waitms(97);
}
.................................
} // Ende main
// ================================================== ==============================
/* ================================================== ============================ */
/* ================================================== ============================ */
/*### Programm pausieren lassen !! Der Pausenwert ist nur experimentell !*/
void waitms(uint16_t ms)
{
for(; ms>0; ms--)
{
uint16_t __c = 4000;
__asm__ volatile (
"1: sbiw %0,1" "\n\t"
"brne 1b"
: "=w" (__c)
: "0" (__c)
);
}
}
/* ================================================== ============================ */
// Auszug aus headerdatei:

#define SetBit(ADDR,BIT) ((ADDR) |= (1<<(BIT))) // Setzt Bit
#define ClrBit(ADDR,BIT) ((ADDR) &= ~(1<<(BIT))) // Löscht Bit


Das eindeutig identifizierbare Blinken nach einem Reset/Kaltstart hilft erstmal nix - aber man weiß dann sicher, dass es kam - und manchmal fällt einem etwas Kluges ein, wenn man sieht WANN es kommt . . . .

Als Grund für ungewollte Resets hatte ich schon falsche Portinitialisierungen, falsche Interruptinitialisierung, mikroskopische Kurzschlüsse etc. Aber ich glaube, das ist noch nicht die Liste aller Möglichkeiten.

Besserwessi
02.08.2011, 18:08
An sich sollte der Mega48 2 Abblock-kondensatoren bekommen und man sollte auch AVCC anschließen. Ist der Widerstand wirklich nach VCC, nicht nach GND ?. Den Widerstand am Reset Pin könnte man noch eher weglassen als den 2. Kondensator.

021aet04
03.08.2011, 08:46
Ich könnte mir vorstellen das entweder die Spannung einbricht oder das du Störungen auf der RST Leitung hast. Du könntest einen Elko an parallel zum Abblockkondensator schalten (VCC und GND) bzw einen Abblockkondensator zusätzlich zwischen RST und GND schalten.

MfG Hannes

DanielSan
03.08.2011, 12:01
Hi,

danke für eure Tips!

Ich habe jetzt noch zusätzlich einen 100nF von Reset nach GND, einen 100nF von AVCC nach GND, 5V an AVCC und einen 100nF von AREF nach GND gelötet. Leider funktioniert das immer noch nicht.
Ich habe auch schon eine andere Spannungsversorgung ausprobiert weil ich dachte, das es evtl. daran liegt aber das ändert auch nichts.
Die resets scheinen so schnell zu kommen, das er es nicht mal bis zum ersten Timer überlauf schafft und der ist jede 1ms.
Wenn ich die Platine anfasse dann funktioniert es manchmal für 1-2 Sekunden so wie es soll. Aber dann bricht es wieder zusammen.

Einen Schaltplan habe ich aktuell garnicht. Ich habe die Platine in einem 2D-Cad gezeichnet.

Ich glaub ich entwickel das nochmal komplett neu und zeige euch dann erst hier den Schaltplan. Ich versteh das zwar nicht, weil ich schon einige deutlich komplexere Projekte umgesetzt habe. Vielleicht ist das zu einfach für mich :cool:.

Oder habt ihr noch eine Idee?

Edit: @021aet04: Den Elko hab ich noch nicht ausprobiert. Welchen Wert sollte der denn haben?
(https://www.roboternetz.de/community/member.php?7714-021aet04)
Danke
Gruß Daniel

Kampi
03.08.2011, 12:15
Da reichen auch 100nF

021aet04
03.08.2011, 12:19
Das ist eigentlich relativ egal. Die Versorgungsspannung muss nur konstant sein und nicht einbrechen (z.B. beim Schalten eines Relais bzw andere Verbraucher mit hohem Stromverbrauch). Wenn nur der µC auf der Versorgung hängt würden schon ca. 10µF reichen. Je mehr desto besser ist es natürlich. Wenn du Verbraucher mit hohem Stromverbrauch hast könntest du auch den µC mit einer Diode entkoppeln und mit einem Elko den µC puffern (z.B. bei einem Servo). Bei einer Led sollte das aber egal sein.

Wenn du schreibst das wenn du die Platine berührst die Schaltung länger funktioniert kommt mir die Idee mit den Lötstellen. Kontrolliere einmal die Lötstellen und löte gegebenenfalls nach.
Du könntest noch kontrollieren ob der Watchdog aktiv ist. Der resettet den µC auch wenn du ihn nicht immer zurücksetzt.

MfG Hannes

DanielSan
03.08.2011, 14:04
Ok hilft alles nix.

Der einzige Verbraucher ist bis jetzt die Led. Als spannungsversorgung hab ichs mit dem 7805 vom Steckbrett und einem UBEC versucht. Bei beiden das gleiche Ergebniss.

Ich habe mir überlegt, das ich das ganze nochmal neu mache. Evtl. hab ich ja irgendwo einen Kurzschluss oder der Controller ist beim einlöten gestorben (glaub ich zwar nicht) oder sonst irgend etwas ist schief gelaufen.

Im Anhang hab ich mal den Schaltplan als PDF eingefügt.
Int0 und Int1 sind die beiden Pins mit denen ich das Empfängersignal (also 2 Kanäle) einlesen möchte. Wie gesagt auf dem Steckbrett hat alles bestens geklappt.

Meint ihr das der Schaltplan so ok ist?
Ich weiss das ginge sicherlich noch schöner, aber ich kenne das Programm (KiCad) noch nicht sooo gut.

Danke
Gruß Daniel * der eigentlich schon viel komplexere Sachen durchgezogen hat :-( *

Hubert.G
03.08.2011, 16:12
Die Schaltung sieht schon OK aus. Du hast nur keinen Programmierstecker vorgesehen, da würde ich einen Sockel verwenden.
Wenn es auf dem Steckbrett funktioniert hat, dann dürfte doch mit der Hardware etwas nicht stimmen. Vielleicht doch ein Kurzschluss, beim fräsen legt sich schnell mal wo ein Span hinein.

DanielSan
03.08.2011, 16:27
Du hast nur keinen Programmierstecker vorgesehen, da würde ich einen Sockel verwenden.
Doch hab ich. Ich möchte die Platine per RS232 Programmieren können. Also mit einem Bootloader. Das hat auch alles auf dem Steckbrett funktioniert. Einen ISP möchte ich aus verschiedenen Gründen nicht. Den hat mein Kumpel -für den die Platine ist- nicht. Ausserdem ist der größer als die 3Pins für RS232.
Diesesmal wird der Atmega gesockelt so kann ich ihn ggf. austauschen oder auf dem Steckbrett programmieren und testen.

Ja mit dem Span könntest du recht haben. Ich denke ich werde den aktuellen Schaltplan nochmal als Layout machen und dann etwas tiefer Fräsen. Dadurch werden die abstände der Leiterbahnen größer und die gefahr eines Kurzschluss geringer. Zusätzlich werde ich vor dem Löten nochmal genau hinsehen ob nicht doch ein Span vorhanden ist.

Ich melde mich dann wie es geklappt hat.

Gruß Daniel

DanielSan
03.08.2011, 20:47
Soooo

hab die Platine jetzt nochmal neu aufgebaut. Sie läuft jetzt fast, der Rest scheint jetzt Software zu sein. Der "restliche" Fehler lag auf dem Steckbrett auch schon vor ist mir aber nicht aufgefallen.

Mein Programm hat jetzt ganz am Anfang eine kurze Blink routine, damit ich sehe wann der µC neu startet. Ich poste erstmal den Quellcode:

$regfile = "m48def.dat"
$crystal = 8000000

Config Pinb.1 = Output
Config Pinb.2 = Output
Config Pinb.0 = Output
Config Pinb.4 = Output

Mg1 Alias Portb.1
Mg2 Alias Portb.2
Mg3 Alias Portb.3
Mg4 Alias Portb.4

Dim I As Byte

For I = 0 To 2
Portb.1 = 0
Waitms 150
Portb.1 = 1
Waitms 150
Next


Dim Mg1flag As Byte
Dim Mg2flag As Byte
Dim Mg3flag As Byte
Dim Mg4flag As Byte
Dim Mg1time As Integer
Dim Mg2time As Integer
Dim Mg3time As Integer
Dim Mg4time As Integer
'Dim I As Byte
Dim State As Byte
Dim Signal As Word


Config Timer0 = Timer , Prescale = 64
Const Timervorgabe = 131
On Timer0 Timer_irq
Enable Timer0

Config Timer1 = Timer , Prescale = 8
Enable Timer1
Timer1 = 0
On Int1 Messen
Enable Int1
Config Int1 = Rising
Enable Interrupts

Mg1flag = 0
Mg2flag = 0
Mg3flag = 0
Mg4flag = 0
Mg1time = 0
Mg2time = 0
Mg3time = 0
Mg4time = 0

Do
Print Signal
If Signal < 1500 Then
State = 0
Mg1 = 0
Mg2 = 0
Mg3 = 0
Mg4 = 0
Else
State = 1
End If
Loop

End


Messen:
If Timer1 = 0 Then
Start Timer1
Disable int1
Config Int1 = Falling
Enable int1
Else
Signal = Timer1
Stop Timer1
Disable int1
Config int1 = Rising
Enable int1
Timer1 = 0
End If

Return

Timer_irq:
Timer0 = Timervorgabe
Disable Interrupts
Disable Timer1
If State = 1 Then
If Mg1time = 0 Then
If Mg1flag = 1 Then
Do ' Auszeit
I = Rnd()
Loop Until I < 10
Mg1time = I + 46 ' Auszeit
Mg1 = 0
Mg1flag = 0
Print I
Else
Mg1time = 10 ' Anzeit
Mg1 = 1
Mg1flag = 1
End If
End If

If Mg2time = 0 Then
If Mg2flag = 1 Then
Do ' Auszeit
I = Rnd()
Loop Until I < 10
Mg2time = I + 91 ' Auszeit
Mg2 = 0
Mg2flag = 0
Print I
Else
Mg2time = 10 ' Anzeit
Mg2 = 1
Mg2flag = 1
End If
End If

If Mg3time = 0 Then
If Mg3flag = 1 Then
Do ' Auszeit
I = Rnd()
Loop Until I < 10
Mg3time = I + 71 ' Auszeit
Mg3 = 0
Mg3flag = 0
Print I
Else
Mg3time = 20 ' Anzeit
Mg3 = 1
Mg3flag = 1
End If
End If

If Mg4time = 0 Then
If Mg4flag = 1 Then
Do ' Auszeit
I = Rnd()
Loop Until I < 10
Mg4time = I + 71 ' Auszeit
Mg4 = 0
Mg4flag = 0
Print I
Else
Mg4time = 10 ' Anzeit
Mg4 = 1
Mg4flag = 1
End If
End If


Decr Mg1time
Decr Mg2time
Decr Mg3time
Decr Mg4time
Enable Interrupts
Enable Timer1
End If
Return

Sorry das ichs nicht kommentiert hab, da bin ich noch nicht zu gekommen.

Nun wie ihr seht springt das Programm jede 1ms in die Interrupt routine. Je nach Empfängersignal blinken die Led´s. Mein Problem ist jetzt, das der µC wenn das Empfängersignal unter 1,5ms ist sauber durchläuft und wenn es über 1,5ms ist die Led´s nur eine kurze Zeit ca. 2sekunden blinken. Danach startet der µC neu. Wenn die Led´s nicht blinken, bleibt er an und läuft sauber weiter.

Ich hab leider im Moment keine Idee woran das liegen kann, ausser das irgendwie ein Timer überläuft und was unvorhergesehenes passiert. Oder das die beiden Interrupts sich behindern.

Der Watchdog ist aus. Jedenfalls wenn damit das Fuse "Watchdog timer always on" = unchecked im Burnomat gemeint ist.

Danke
Gruß Daniel

021aet04
04.08.2011, 10:52
Es gibt 2 Möglichkeiten um den Watchdog zu aktivieren. Entweder so wie du schon geschrieben hast über Fuses oder im Programm mit dem Register WDTCSR. Aber der scheint nicht aktiv zu sein, da der Reset nur bei aktiver Led ausgeführt wird.

Bei Bascom kenne ich mich aber nicht aus, da ich mit C programmiere.

MfG Hannes

DanielSan
05.08.2011, 12:09
Ich komme nicht weiter.
Mir fehlt ein Ansatz woran der Reset liegen könnte. Den Watchdog schliesse ich mal aus.
Spannungsversorgung ist auch stabil genug.

Gruß Daniel

Besserwessi
05.08.2011, 18:15
Ein Problem könnte ein zu knapper Stack sein. Wenn man Interrupts nutzt braucht man da mehr Platz und wenn man gar wie hier (vermutlich unbeabsichtigt) auch noch verschachtelte Interrupts zuläßt, brauch man noch mehr Platz auf dem Stack. In der ISR (hier aber Timer_irq: ) braucht man kein "Disable Interrupts" - das macher der µC schon von sich aus. Entsprechend ist das "Enable Interrupts" dann zu viel, und gibt ungewollt einen verschachtelten Interrupt für den kurzen Rest frei.

DanielSan
09.08.2011, 11:17
Hi,

wenn du mit den verschachtelten Interrupts meine Messroutine und die Blinkroutine meinst, das ist schon so gewollt das die sich überlagern können. Weil ich so nicht erst warten muss bis er irgendwas erledigt hat. Ich kann also den momentanen Auftrag abbrechen und mit neuen bedingungen Versorgen.
Wenn du das "enable" am ende der Routine meinst dann hast du sicher recht das ist so nicht gewollt!
Das "disable Interrupts" und "disable timer" hab ich rausgenommen. Das "enable" dementsprechend natürlich auch.

Ich hab mal als Stack

$hwstack = 50
$swstack = 50
$framesize = 50
angegeben. Dann müsste ich ja noch 106 bytes für meine Variabeln über haben. Ist das so genug Stack?

Getestet hab ichs noch nicht, da komme ich evtl. nachher noch zu.

Danke
Gruß Daniel

Rone
09.08.2011, 15:58
Hallo!

Vieleicht liegts ja daran:

Auszug aus der Hilfe
var = RND( limit )


Für was soll diese Random Aktion gut sein?

MfG
Rone

DanielSan
09.08.2011, 16:59
Die RND() soll etwas variation in die Blinkerei bringen.

Die RND-Funktion ist echt nicht das Gelbe vom Ei. Wie man sieht musste ich ja schon pfuschen um überhaupt einen Wert zwischen 0 und 10 zu bekommen:

Do
I = Rnd()
Loop Until I < 10
weil das mit dem Limit scheinbar in Verbindung mit Interrupts nicht funktioniert. Dazu hab ich auch schon in einem anderen Thread Hilfe bekommen. Da sind wir eben auf diese Pfuschlösung gekommen.

Danke
Gruß Daniel

DanielSan
09.08.2011, 18:13
So habs gerade getestet. Jetzt läuft er fast wie er soll. Danke nochmal an alle die mir geholfen haben!!!

Das einzige was mir jetzt nicht gefällt, ist meine pfusch RND() Lösung. Denn manchmal dauert es sehr lange -ein paar Millisekunden- bis er eine RND-Zahl unter 10 errechnet hat. Ich glaube das sich die abfolge der RND-Zahlen nach ca. 2-3 Sekunden wiederholt.

Gibt es da eine andere Möglichkeit ohne äußere Beschaltung?
Ich hätte da eine Idee, weiss aber nicht ob die so praktikabel ist. Was wäre wenn ich einfach am PC eine Liste von RND-Zahlen erstelle und diese ins EEPROM schreibe. Diese könnte mein Programm ja dann einfach nacheinander lesen und den Timer passend setzen. Das sich das ganze dann Irgendwann wiederholt ist mir klar aber das ist ja mit der RND Funktion auch nicht anders. Das EEPROM kann man doch unendlich oft lesen oder?
Wie lange dauert es ca. bei 8Mhz ein Byte zu lesen?

Edit: Sonst könnte ich ja...


DIM I as Byte 'am Anfang definieren
'-------------------------------------------
I = RND()
I = I/25

... das wäre zeitlich konstanter und schneller. Denn RND() mit limit funktioniert nicht und wenn ichs so wie im Beispiel mache, ist es eine RND-Zahl ziwschen 0 und 255.
Was meint ihr?

Danke
Gruß Daniel

Searcher
09.08.2011, 18:54
Hallo,
ich habe gerade die RND Funktion mit Limit ausprobiert.

DIM I as BYTE
I = RND(10)

funktioniert und erzeugt Zahlen von 0 bis 9. (BASCOM Vers 2.0.5.0)

Wenn das bei Dir in der ISR nicht geht, könntest Du in der Hauptschleife eine Zufallszahl erzeugen und in der ISR benutzen?


Gruß
Searcher