Midi auf dem Atmega8... unzuverlässige Werte... What do?

matze f.

Neues Mitglied
29. Mai 2014
22
0
0
Sprachen
Hallo liebes Forum,

Ich bin neu hier. bin seit ein paar wochen / wenigen monaten am atmega programmieren. ich hatte schon ein paar fragen in ein anderes forum geschrieben, wurde dort aber nicht sonderlich freundlich begrüßt, weshalb ich es mal hier versuche...

ich bastele an einem kleinen projekt. hauptziel ist dabei midi zu verstehen. der Atmega8 soll also mit Uart midi auslesen und dann entsprechende LEDs anschalten.
ich leg mal los, mit dem schon erledigten:
ich habe mir ein kleines board mit optokopler aufgebaut. das gewandelte signal schicke ich dann direkt an mein stk500 an den RX pin für das UART. Getacktet wird mein Atmega8 mit einem 4 Mhz Quarz direkt auf dem eingebauten sockel des stk500. also quasi mit Quarzoszillator. midi läuft ja mit 31250 baud. laut verschiedener rechner im internet, sollte ein 4 Mhz takt genau geeinget sein. also ohne relative fehler. den UBRR-Wert, den ich dazu errechnet habe ist 7 (kommt weiter unten zum einsatz).

gefused habe ich den atmega 8 mit 0xD9 (high) und 0xE0 (low). ich habe auch den quarz an einem oszi überprüft. der scheint korrekt zu schwingen. das müsste alles soweit passen denke ich. jumper habe ich auch entsprechend gesetzt. assembliert wird alles mit gcc-avr.

unten stehendes programm ist nur zum testen der übertragung gedacht. das erste übertragene byte (in dem das midi-event und der kanal stehen) soll am port B ausgegeben werden und ich habe diesen port dann mit den LEDs vom stk500 verbunden. so sehe ich, ob und was als erstes übertragenes byte (Statusbyte bei normalen midi-messages) ankam.

...und, naja... es gibt schwankungen. fast alle messages werden korrekt übertragen. nur die (sehr wichtigen) note-on messages haben oft bit-kipper. tatsächlich funktioniert es etwa in 50 prozent der fälle (ich bekomme dann 1001 xxxx angezeigt). wenns nicht funktioniert dann erhalte ich meistens 1000 xxxx (was ja einer note-off message entspräche. obwohl ich die taste noch nicht mal unbedingt losgelassen habe). ich teste die ganze sache mit nem kleinen midi-keyboard, an dem ich tasten drücke und vergleiche mit den ausgegebenen werten an den LEDs. hab jetzt echt viel gedrückt. kann keine systematik der fehler erkennen. es scheinen vermehrt die unteren midi-kanäle (0 - ca. 5) betroffen zu sein. aber auch bei den oberen funktioniert es nicht zuverlässig. kann sich jemand nen reim drauf machen? das würde mir echt viel helfen. ich bin mit meinem latein am ende... vielleicht habe ich auch nen echt banalen fehler beim programmieren gemacht...



jetzt mal zum code: hier ist mein folständiges programm:

Code:
#define __SFR_OFFSET 0 
#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdlib.h>

#define temp0  r17
#define temp1  r18

.section .text
.global main


main:

//*********************Stackpointer initialisierung*********************\\

ldi temp1, hi8(RAMEND-0x20)
out SPL, temp1
ldi temp1, lo8(RAMEND-0x20)
out SPH, temp1

//*********************Port B konfigurieren*********************\\

ldi temp1, 0xFF				
out DDRB, temp1
ldi temp1, 0x00

//*********************Uart konfigurieren*********************\\


ldi temp0, 0x80				// laut diesem thread muss man erst UBRRH so setzten, bevor man UCSRC beschreiben kann: 
                                           // http://www.avr-praxis.de/forum/showthread.php?178-Erste-Erfahrungen-mit-der-seriellen-Daten%FCbertragung
out UBRRH, temp0

ldi temp0, (1<<URSEL)|(1<<UCSZ1)|(1<<UCSZ0)
out UCSRC, temp0

sbi UCSRB, RXCIE
sbi UCSRB, RXEN

ldi temp1, 0b00000000
out UBRRH, temp1

ldi temp1, 0b00000111
out UBRRL, temp1

sei                   			//Interrupts allgemein aktivieren



ldi temp0, 0b00000001			//Eine LED leuchtet, wenn startbereit (bzw. leuchtet nicht. die leds sind beim stk500 ja negiert)
out PORTB, temp0

//*********************loop*********************\\

loop:

rjmp loop

//*********************Uart interrupt*********************\\

.global USART_RXC_vect
USART_RXC_vect:
cli                                          //schaltet die interrupts gleich wieder aus. so wird (hoffentlich) nur das erste übertragene byte angezeigt

in temp0, UDR

ror temp0
ror temp0                             // ich muss hier zweimal rotieren, da an den beiden höchsten bits vom port B ja der externe oszi dran 
                                              //hängt.  deswegen verschiebe ich das ganze um zwei bits nach rechts, so sehe ich zumindest die obersten 6 
                                             //bits vom übertragenen byte. die untersten 2 sind jetzt auch erstmal nicht so wichtig. da steht ja nur der midi-kanal 
                                              //drin

out PORTB, temp0                 //hier wird das byte am port B ausgegeben

ret

ich danke jeden, der bis hier durchgehalten hat...
 
Hallo Matze,

willkommen im AVR-PRAXiS Forum!


Für dein Problem habe ich direkt keine Lösung, konnte auch wegen Zeitmangel deinen Beitrag nur kurz überfliegen. Vielleicht helfen dir aber folgende Hinweise weiter, dei Fehlerursache einzugrenzen oder später einmal Folgefehler zu vermeiden.


UBRRH und UCSRC
nutzen beide die selbe IO Adresse. Wenn man in das UCSRC Register schreiben möchte, muss das Bit 7 gesetzt sein (1<<URSEL, bzw die Maske davon ist 0x80). Ist das Bit nicht gesetzt schreibt man in UBRRH. UCSRC musst du eigentlich gar nicht ändern, da nach Reset bereits UCSZ1 und UCSZ0 gesetzt sind.

Bei der Initialisierung nutze folgende Reihenfolge:
1. Baudrate setzen
2. Frameformat einstellen
3. Receiver aktivieren
4. Interrupt Anforderungsflag RXC ISR sicherheitshalber löschen (dummyread von UDR), danach erst global Interrupts freigeben.


Initialisierung des Stack Pointer
hat es einen Grund warum du 0x20 von RAMEND abziehst? (hat es was mit dem eingesetzten Assembler zu tun, hier bin ich nicht mehr so informiert, da ich inzwischen nicht mehr Assembler programmiere?)
Achtung: du verwechselst High- und Lowbyte!

USART ISR
hier deaktivierst du global die Interrupts (cli). Beim Einsprung in die ISR Vektortabelle, bzw in die ISR macht das der AVR sowieso. Aktiviert werden die Interrupts global immer beim Beenden der ISR durch reti. Du springst hier mit ret zurück. Die Interrupts bleiben also deaktiviert, was allerdings in deinem Fall ja gewollt ist.


Sonst fällt mir im Moment nichts weiter auf oder ein ;-)

Dirk :ciao:
 
Hallo, und willkommen hier...

Das ist doch kein reines ASM-Programm - eher C mit eingebettetem ASM, oder? Warum?

Jetzt zu einigen Vermutungen (?) von Dir...

Beim Mega8 (und pinidentischen µCs) liegen die Quarz-Beinchen auf B6 und B7, ist eine entsprechende Taktquelle gefused, nehmen diese Pins ihre entsprechende Alternativfunktion ein, und sind als konventionelle I/Os nicht verfügbar.
Das STK500 hat diese Beinchen (bei diesem Sockel) konsequenterweise nicht auf den PORTB-Header geleitet, sondern auf PORTE (glaub ich - dort auf XTAL1 und XTAL2). Außerdem wird der Quarzsockel des STK nicht auf den µC gelegt, sondern damit sowas wie ein Oszillator betrieben - für den Controller ist jeder Takt, der vom STK kommt 'ne externe Clock (hattest Du ja selbst angedeutet).
XTAL2 (B7) könntest Du also auf dem anderen Header sehen).

Laut Kommentar willst Du das empfangene Byte zweimal rechtsschieben - Du rollst aber (der Unterschied ist der, daß dabei das Carry in's MSBit gerollt wird, beim Schieben (LSR) eine "0". Effektiv hast Du dann also hinterher im Bit6 irgendein Carry von vor dem IRQ (wahrscheinlich Bit1 des letzten Bytes), in Bit7 das ursprüngliche Bit0 und der Rest ist wie gewünscht verschoben.

Ich habe jetzt den Baudratenfehler nicht nachgerechnet, aber wäre der interne 4MHz-Takt keine Option für Dich (also zum testen)? Dann hättest Du PORTB frei...
Alternativ einfach ohne schiebe ausgeben, Bit7 aus dem E-Header auf den LED-Header verbinden, Bit6 auf irgendein anderes Bein kopieren, und dieses mit der LED verbinden.
Die Kopie kannst Du zB so erstellen:
-Bein mit CBI (Clear Bit Immediate) erstmal löschen
-Mit SBRC (Skip if Bit in Register is Clear) auf BIT6 im empfangenen Byte prüfen (also im temp0) - wenn es 0 ist wird die folgende Instruktion automatisch übersprungen
-Bein mit SBI (Set Bit Immediate) setzen

Daß beim Eintritt in eine ISR generell die IRQs global deaktiviert werden, hatte Dirk bereits gesagt, daß sie nach einem RET ohne I nicht wieder freigegeben werden auch (wird das eventuell durch Deinen Compiler ersetzt?)

Abgesehen davon verstehe ich den Sinn mit dem Kommentar (2tes Byte ignorieren) nicht ganz. Dein Programm verarbeitet so genau ein empfangenes Byte, und bleibt dann in 'ner Endlosschleife?

Noch was kurzes zum UART und dem UDR:
das UDR gibt es als I/O-Register eigentlich gar nicht - eine Schreiboperation in das UDR beschreibt das Transmit Data Buffer Register(TXB). Sobald das Tx-Schieberegister leer ist, wird das TXB dorthin kopiert und gelöscht. Schreiben ins UDR (also ins TXB) kann man nur, wenn das TXB leer ist (dafür gibts das UDRE-Flag). Wenn ein Byte also in's UDR geschrieben, ins Schieberigister übertragen wurde, und der Transmitter sendet, ist das TXB leer - es kann also während des Sendens das nächste Byte in den Sendepuffer.
Dich interessiert aber eher das lesen des UDR. Das ist nämlich sogar doppelt gepuffert.

(*Akku leer - schreibe später mehr*)
 
hallo,

das ist ja echt super, das ihr mir so ausführlich antwortet. danke dafür.

ich habe in der zwischen zeit auch weitere erkenntnisse gewonnen. aber dazu weiter unten. ich beantworte erstmal eure fragen. kann ja für andere nutzer mal ganz interessant sein oder werden...

UBRRH und UCSRC
nutzen beide die selbe IO Adresse. Wenn man in das UCSRC Register schreiben möchte, muss das Bit 7 gesetzt sein (1<<URSEL, bzw die Maske davon ist 0x80). Ist das Bit nicht gesetzt schreibt man in UBRRH.
jup, das habe ich ja beachtet. ist das richtig so gemacht?

UCSRC musst du eigentlich gar nicht ändern, da nach Reset bereits UCSZ1 und UCSZ0 gesetzt sind.
ach. dann sind automatisch die frames auf 8 bit gesetzt? aber schadet ja auch nicht, wenn ich das nochmal einstelle. ist das richtig so?


Bei der Initialisierung nutze folgende Reihenfolge:
1. Baudrate setzen
2. Frameformat einstellen
3. Receiver aktivieren
4. Interrupt Anforderungsflag RXC ISR sicherheitshalber löschen (dummyread von UDR), danach erst global Interrupts freigeben.
aber wenn ich erst die baudrate setzte, und dann wider das msb vom UBRRH auf high setzten muss um die frames einzustellen (ja, ok, das könnte man offenbar auch weglassen), dann muss ich ja danach wieder die UBRRH korrekt beschreiben...

Initialisierung des Stack Pointer
hat es einen Grund warum du 0x20 von RAMEND abziehst? (hat es was mit dem eingesetzten Assembler zu tun, hier min ich nicht mehr so informiert, da ich inzwischen nicht mehr Assembler programmiere?)
So ganz genau habe ich das gestern auch nicht nachvollziehen können. ich bin erst vor kurzem von gavrasm auf avr-gcc umgestiegen. und ein freund, der den schön länger benutzt hat mir gesagt, was man alles anders machen muss, dass alles gut funktioniert. jetzt haben wir nochmal nachgelesen, warum das so ist. hat was damit zu tun, dass der gcc offenbar intern Port-adressen auf das ende des Rams mapped (oder so ähnlich??). kann man hier nachlesen: http://www.nongnu.org/avr-libc/user-manual/group__avr__sfr.html

Achtung: du verwechselst High- und Lowbyte!
right. danke.

USART ISR
hier deaktivierst du global die Interrupts (cli). Beim Einsprung in die ISR Vektortabelle, bzw in die ISR macht das der AVR sowieso.
ja, stimmt. das kann ich mir an der stelle sparen. mein gedanke war der: da ich ja nur das erste byte anschauen wollte, verbiete ich weitere interrupts, sobald der erste kommt. aber das ist blödsinn.

Aktiviert werden die Interrupts global immer beim Beenden der ISR durch reti. Du springst hier mit ret zurück. Die Interrupts bleiben also deaktiviert, was allerdings in deinem Fall ja gewollt ist.
ganz recht.

Das ist doch kein reines ASM-Programm - eher C mit eingebettetem ASM, oder? Warum?
hm... ich hab zwar noch nicht viel ahnung von C, aber so manche dinge (wie die schiebebefehle) sind in vielen tutorials immer so dargestellt. der gavrasm hat die auch so entgegengenommen. auch wenns kein lupenreiner assambler-code ist. reiner pragmatismus

Beim Mega8 (und pinidentischen µCs) liegen die Quarz-Beinchen auf B6 und B7, ist eine entsprechende Taktquelle gefused, nehmen diese Pins ihre entsprechende Alternativfunktion ein, und sind als konventionelle I/Os nicht verfügbar.
soweit klar

Das STK500 hat diese Beinchen (bei diesem Sockel) konsequenterweise nicht auf den PORTB-Header geleitet, sondern auf PORTE (glaub ich - dort auf XTAL1 und XTAL2).
geschieht das, durch den jumpter "OSCSEL"?

Außerdem wird der Quarzsockel des STK nicht auf den µC gelegt, sondern damit sowas wie ein Oszillator betrieben - für den Controller ist jeder Takt, der vom STK kommt 'ne externe Clock (hattest Du ja selbst angedeutet).
jup

XTAL2 (B7) könntest Du also auf dem anderen Header sehen).
das habe ich nicht verstanden. mit Header meinst du die steckverbindungen, oder?




Laut Kommentar willst Du das empfangene Byte zweimal rechtsschieben - Du rollst aber (der Unterschied ist der, daß dabei das Carry in's MSBit gerollt wird, beim Schieben (LSR) eine "0". Effektiv hast Du dann also hinterher im Bit6 irgendein Carry von vor dem IRQ (wahrscheinlich Bit1 des letzten Bytes), in Bit7 das ursprüngliche Bit0 und der Rest ist wie gewünscht verschoben.
OK, ja danke. ich habe das ganze durch lsr ersetztn. hat aber zunächst keinen unterschied gemacht.

Ich habe jetzt den Baudratenfehler nicht nachgerechnet, aber wäre der interne 4MHz-Takt keine Option für Dich (also zum testen)? Dann hättest Du PORTB frei...
naja, man hört ständig, das UART mit dem internen takt des µC zwar funktionieren kann, aber nicht muss. diese fehlerquelle hätte ich gerne ausgeschaltet. ist zwar etwas doof, das ich PORTB, nicht voll nutzen kann, aber im moment gehts ja nur um den test der funktion. später will ich da andere dinge machen, wo ich (wahrscheinlich) nicht den ganzen PORTB brauche.


Alternativ einfach ohne schiebe ausgeben, Bit7 aus dem E-Header auf den LED-Header verbinden, Bit6 auf irgendein anderes Bein kopieren, und dieses mit der LED verbinden.
Die Kopie kannst Du zB so erstellen:
-Bein mit CBI (Clear Bit Immediate) erstmal löschen
-Mit SBRC (Skip if Bit in Register is Clear) auf BIT6 im empfangenen Byte prüfen (also im temp0) - wenn es 0 ist wird die folgende Instruktion automatisch übersprungen
-Bein mit SBI (Set Bit Immediate) setzen
ja, dachte mir schon, dass ich das irgendwie hätte hinfummeln können. aber wollte nicht nochmal nachts um 1 das stecker-löten anfangen. zumal mich ja die untersten zwei bits echt nicht so interessier haben.

Daß beim Eintritt in eine ISR generell die IRQs global deaktiviert werden, hatte Dirk bereits gesagt, daß sie nach einem RET ohne I nicht wieder freigegeben werden auch (wird das eventuell durch Deinen Compiler ersetzt?)
ne. aber war ja so gedacht. wollte ja nur immer ein byte anschauen. für das nächste muss ich halt immer auf reset drücken. ist ja auch nicht schlimm..

Abgesehen davon verstehe ich den Sinn mit dem Kommentar (2tes Byte ignorieren) nicht ganz. Dein Programm verarbeitet so genau ein empfangenes Byte, und bleibt dann in 'ner Endlosschleife?
genau das soll es tun. es geht nur um die überprüfung der funktion
Noch was kurzes zum UART und dem UDR:
das UDR gibt es als I/O-Register eigentlich gar nicht - eine Schreiboperation in das UDR beschreibt das Transmit Data Buffer Register(TXB). Sobald das Tx-Schieberegister leer ist, wird das TXB dorthin kopiert und gelöscht. Schreiben ins UDR (also ins TXB) kann man nur, wenn das TXB leer ist (dafür gibts das UDRE-Flag). Wenn ein Byte also in's UDR geschrieben, ins Schieberigister übertragen wurde, und der Transmitter sendet, ist das TXB leer - es kann also während des Sendens das nächste Byte in den Sendepuffer.
Dich interessiert aber eher das lesen des UDR. Das ist nämlich sogar doppelt gepuffert.
was bedeutet in diesem falle "doppoelt gepuffert"? muss ich da auf was achten? "nop"s einfügen?

So, jetzt zu der Lageänderung: Ich hatte vergessen zu erwähnen, dass ich den Optokoppler mit ca 3,5 V vom stk500 mit versorgt habe. hatte zwar auch nen anschluss für ein externes netzteil ran gelötet, aber aus bequemlichkeit nicht mehr benutzt (weil ich so nur einen pfostenstecker verbinden muss, midi kabel rein, und schön läuft der hase). allerdings war das wohl ein bisschen wenig. jetzt (mit externer 5 V versorgung für den optokoppler-kreis) gehts VIEEEEEEEL besser. fast keine bit-kipper mehr. also tatsächlich vielleicht noch einer in 50 oder 100 versuchern. damit kann ich erstmal leben. falls sich das noch als problem erweisen wird (wirds es wahrscheinlich) versuche ich zum einen einen schmitt trigger hinter den ausgang des Optokopplers zu schalten, zum anderen vielleicht einen ganz anderen Optokopper (einen schnelleren) zu verwenden.

beim bau des optokoppler-boards habe ich mich an diesem schaltbild orientiert:
http://www.wiesolator.de/index.php?...Einstieg&stopic=MIDI und AVR&komm=show#notice
allerdings habe ich nicht genau den angegebenen Optokoppler verwendet, und auch irgend eine Diode, die gerade noch hier rumflog...

aber jetzt läuft ja alles fast wie gedacht. ich bedanke mich schonmal für euren einsatz. das war echt super.
 
Hallo Matze,

noch einmal kurz zum USART ...

Deine Initialisierung wird schon funktionieren, ich hatte nur den Eindruck, es ist noch nicht ganz verstanden.

Code:
ldi temp0, 0x80            
out UBRRH, temp0

Hier schreibst du tatsächlich in das Register UCSRC und nicht in UBRRH, da das Bit 7 ( 0x80 = 0b10000000= 1<<URSEL ) gesetzt ist.

Hier einfach mal ein Auszug aus dem Datenblatt, dann wirds vielleicht klarer ...

Code:
USART_Init:
; Set baud rate
out UBRRH, r17
out UBRRL, r16

; Enable receiver and transmitter
ldi r16, (1<<RXEN)|(1<<TXEN)
out UCSRB,r16

; Set frame format: 8data, 2stop bit
ldi r16, (1<<URSEL)|(1<<USBS)|(3<<UCSZ0)
out UCSRC,r16
ret

Diese Reihenfolge bei der Initialisierung ...
1. Baudrate setzen
2. Frameformat einstellen
3. Receiver aktivieren
4. Interrupt Anforderungsflag RXC ISR sicherheitshalber löschen (dummyread von UDR), danach erst global Interrupts freigeben.
hat den Vorteil, dass der Receiver nicht zunächst mit falscher Baudrate aktiviert ist. In deinem Fall wird es vielleicht nichts ausmachen, wenn du eine andere Reihenfolge wählst. Die obere ist allerdings sicherer.


Die Sache mit dem Offset 0x20 was in sfr_defs.h von AVR LibC beschrieben ist, bezieht sich eigentlich nur auf die ersten 32 Byte MCU Register r0 bis r31, danach (ab 0x20) kommen die mapped IO Register im Data Memory, danach das frei verfügbare SRAM bis RAMEND. Warum man den Stack dann auf RAMEND-0x20 initialisieren muss, ist mir nicht klar. Das ist zunächst erst mal kein Fehler, wenn der Stackpointer nicht in den IO Speicherbereich gerät. Der effektive Platz für den Stack ist eben somit kleiner. MSB/LSB Vertauschen war allerdings ein Problem.


Anscheinend hat das Problem ja etwas mit dem Optokoppler zu tun, nicht mit der Software.

EDIT:
Ich habe mir gerade mal die Schaltung angesehen ...
Der Optokoppler hat einen OpenCollector Ausgang, mit einem zu hohen Pullupwiderstand und zu viel C, könnte das Signal beim Ausschalten zu sehr verzögert werden.

Dirk :ciao:
 
Falls es Dich interessiert, hole ich mal ein wenig aus, auch was die Hintergründe betrifft...
UBRRH & UCSRC
Es handelt sich dabei nicht um dasselbe I/O-Register sondern es werden 2 verschiedene Register unter derselben Adresse angesprochen. Welches der beiden es konkret ist entscheidet dabei das MSB. Beschrieben/Gelesen wird also das tatsächliche Register immer unabhängig von dem anderen. Folglich sind in den beiden tatsächlichen Registern nur 7 Bits verwendbar.
Hintergrund:
Der klassische I/O-Space umfaßt lediglich 64 Register (0x00..0x3F) bzw die Instruktionen die für den Zugriff auf die Register vorgesehen sind bieten nur eine 6bit breite binäre Zahl für die Adresse. Mit der steigenden Anzahl der Sonderfunktionen reichten die 64 Register so nicht mehr aus. Beim Mega8 wurden deswegen 2 Register die nur 7 relevante Bits benötigen auf derselben Adresse untergebracht.
Inzwischen wird dieser Weg bei neueren Controllern nicht mehr genutzt, würde so auch nicht mehr reichen. Stattdessen wurde einfach ein extendet I/O-Space definiert, der wie SRAM angebunden ist (und zwar vor dem SRAM). Einhergehend damit wurde versucht, die Register zwischen den Controllern etwas zu vereinheitlichen - Tinies sind im konventionelle I/O ziemlich leer.

UCSZ1 und UCSZ0 bereits gesetzt...ja, schau mal ins Datenblatt (S.150) da stehen die Initial Values, aber wenn Du die nochmal mit denselben Werten überschreibst, ändert sich ja nichts...

XTAL1 und XTAL2 beim Mega8-Sockel des STK500
Andere Controller in anderen Sockeln können natürlich auch B6 bzw B7 Beinchen haben, und seperate XTAL-Beinchen. da die aber alle auf dieselben PORT-Header des STK geroutet sind, würdest Du beim großen Sockel XTAL und PORTB-Pin verbinden (kurzschließen). Die beiden XTAL-Beinchen des Mega8-Sockels sind auf den PORTE (AUX) Header des STK geroutet, da sie auf dem STK auf den XTAL-Netzen liegen um eben den Taktgenerator des STK nutzen zu können. Und deswegen sind sie nicht auf den PORTB-Header geroutet.
Neben dem Oszillator, der mit dem Steckbaren Quarz betrieben wird, kannst Du den auf dem Board verlöteten (Master(?)) Controller auch einen Takt ausgeben lassen, der OSCSEL-Jumper verbindet nun entweder den "Quarz-Oszillator" oder den generierten Takt-OUT des Master-µC mit dem XTAL1-Netz. Dazwischen befindet sich allerdings noch ein Voltage Converter, und der XTAL1-Jumper, mit dem Du die ganze Takterei des STK überhaupt erst mit dem XTAL1-Netz verbinden kannst (bzw davon trennen) (Siehe Seite 3-22 aus der Bedienungsanleitung des STK)

Ja, mit den Headern meine ich die entsprechenden Pinleisten des STK. Die werden auch in der Anleitung so genannt (Port Connectors, 3-3 ff.)

schieben & rollen
der einzige Unterschied liegt in den beiden obersten Bits (nach dem schieben/rollen), und die interessieren Dich hier nicht/siehst Du nicht. Hier macht das also effektiv keinen Unterschied - aber der Kommentar paßt nicht zum Code. Wenn man das also irgendwann mal übernimmt, wo's auf die oberen Bits auch ankommt, wundert man sich vielleicht...

hinfummeln
Zum verbinden (LEDS<->PORTB) nutzt Du die beiligenden 10poligen Pfostenbuchsen? Es liegen ja noch ein paar 2adrige bei. Ich habe mir inzwischen noch ein paar mehr 2adrige und einadrige gecrimpt;)

UART und interner Takt
Die Sache ist die, daß einerseits die Serienstreuung der internen Oszillatoren dazukommt (ok, den könnte man auch kalibrieren), andererseits auch eine Temperaturdrift besteht (obwohl es inzwischen auch Controller mit Temperaturkompensation zu geben scheint).
Dazu kommt dann noch der konkrete (und berechenbare) Baudratenfehler. Deswegen meine diesbezügliche Frage, die Hardware synchronisiert ja auf das Startbit, wenn also der Baudratenfehler klein ist, hast Du mehr Toleranzen bei den anderen Fehlerquellen.
(In diesem Beitrag hatten TommyB und ich mal einem Tiny (in meinem Fall dem Tiny10) 'nen Software-RX beigebogen, mit 8fach Oversampling. Ich habe dabei nichtmal den Oversamplingtimer mit dem Stopbit synchronisiert (da dort Hard-PWM lief), sondern nur die Oversampling-Takte gezählt. Mit 4800baud gings, trotzdem ist eine deutliche Drift erkennbar.)
Der Hardware-UART synchronisiert aber auf das Startbit.

Statt dem Reset hätte man natürlich auch was mit'nem Taster zum freigeben der IRQs machen können, aber egal...

Das mit dem doppelt gepufferten Empfang setze ich in 'ner neuen Antwort fort - Dich betrifft das aber so erstmal nicht, da Du nur das erste komplett empfangene Byte betrachtest, und den Controller danach in'ne interruptfreie Endlosschleife schickst. Nach dem neuen Reset ist alles wieder Neu.
Einziges Problem könnte sein, wenn Du den grad während einer Transmission Resettest, also der Reciever mitten im Byte aktiv wird. Rein theoretisch könnte die Möglichkeit bestehen, daß ein Restfragment des ersten Bits und ein Fragment des nächsten zu einem gültigen (Start- Stopbits usw) zusammeninterpretiert werden...
 
Code:
ldi temp0, 0x80            
out UBRRH, temp0


Hier schreibst du tatsächlich in das Register UCSRC und nicht in UBRRH, da das Bit 7 ( 0x80 = 0b10000000= 1<<URSEL ) gesetzt ist.
ok, wenn ich das jetzt richtig verstanden habe, dann kann ich mir diese zeilen sparen?:

ldi temp0, 0x80
out UBRRH, temp0


EDIT:
Ich habe mir gerade mal die Schaltung angesehen ...
Der Optokoppler hat einen OpenCollector Ausgang, mit einem zu hohen Pullupwiderstand und zu viel C, könnte das Signal beim Ausschalten zu sehr verzögert werden.
Moment, das habe ich nicht ganz verstanden, aber das interessiert mich wirklich. mit OpenCollector Ausgang meinst du die art, wie er verschaltet ist?
und du meinst, dass der 1k widerstand zu viel sein könnte? mit "C" meinst du kapazitäten in den leitungen oder ähnliches? wie krieg ich das in den griff?
 
Moment, das habe ich nicht ganz verstanden, aber das interessiert mich wirklich. mit OpenCollector Ausgang meinst du die art, wie er verschaltet ist?
und du meinst, dass der 1k widerstand zu viel sein könnte? mit "C" meinst du kapazitäten in den leitungen oder ähnliches? wie krieg ich das in den griff?
Der Collector des sekundären Phototransistors ist offen (open), kann aber auf Gnd durrchgeschaltet werden (in Deiner Schaltung). Mit C sind hier neben eventuellen Letungskapazitäten (zwischen dem Koppler und dem Mega8 wohl eher vernachlässigbar wenig) auch die Pineingangskapazität des Mega8 gemeint. Wenn der Koppler "öffnet", muß die vorher entladene Kapazität über den externen Pullup geladen werden - Du hast da sozusagen 'n Tiefpaß drin, was die steigende Flanke betrifft. Bei der Fallenden entlädst Du ja schnell (mit viel Strom) durch den Koppler selbst...
 
ok, wenn ich das jetzt richtig verstanden habe, dann kann ich mir diese zeilen sparen?:

ldi temp0, 0x80
out UBRRH, temp0
Ja genau.
Moment, das habe ich nicht ganz verstanden, aber das interessiert mich wirklich. mit OpenCollector Ausgang meinst du die art, wie er verschaltet ist?
und du meinst, dass der 1k widerstand zu viel sein könnte? mit "C" meinst du kapazitäten in den leitungen oder ähnliches? wie krieg ich das in den griff?
Ja. Es muss nicht an RC liegen (1k ist nicht besonders gross), es wäre aber eine mögliche Ursache. Je höher die Übertragungsrate, desto mehr Einfluss hat dies. Es kann auch am nicht passenden Pegel liegen, du hättest hier ja etwas geändert so dass es besser funktioniert hat ... Hängt vielleicht alles ein bisschen zusammen. Leider kann ich mir im Moment das nicht genauer ansehen.
 
danke für eure antworten. im moment funktioniert die sache sehr zuverlässig. falls es da nochmal probleme gibt, werd ich mich nochmal mit dem optokoppler auseinander setzten.

jetzt nochmal kurz ne andere sache, die ich nicht verstehe: wenn ihr euch nochmal das schaltbild anseht, dann ist da ja auf der eingangs seite des opto-kopplers ein widerstand in der midi-stromschleife. kann mir jemand erklären, wieso? denn in ner stromschleife ist der strom ja immer gleich, egal wie hoch (theoretisch) der widerstand ist...
 
Stromschleife?
Du hast dort eigentlich nur die 5V und den UART des Senders, welcher zwischen knapp 5V und gut 0V hin und her schalten kann.
Diese knapp 5V bringen die LED im Koppler zum leuchten, wobei der Strom über die 3 Vorwiderstände (und den Widerstand des Kabels) begrenzt wird.
Hast Du ein Datenblatt zum konkret verwendeten Koppler zur Hand? dieweil 660Ohm schon ein heftiger Widerstand sind...
 
ok, ich muss folgendes klarstellen: ich benutze als master ein midi-keyboard. weiß also nicht was da wirklich rauskommt... (bzw. in den optokoppler rein geht).

aber hier mal ein zitat aus wikipedia: "Im Unterschied zu pegelgesteuerten Schnittstellen wird bei MIDI eine 5-mA-Stromschleife verwendet."

also ist doch mein midi auf der leitung erstmal ne stromschleife, die ich dann mit dem optokoppler wieder in spannung umwandle... und dann versteh ich einfach nicht, was dieser 220 ohm widerstand auf der rechten seite (vor dem optokoppler) tut...

MIDI-Kabel.png


Hast Du ein Datenblatt zum konkret verwendeten Koppler zur Hand?

ne, weiß gerade auch nicht aus dem hut, welchen optokoppler ich verwendet habe. muss ich heute abend mal schauen... meinst du etwa, die 660 ohm sind zu viel?
 
Meine Glaskugel zaubert mir auch kein Datenblatt zum von Dir nicht angegebenen OK her...;)
Mit den 3R in Reihe hast Du quasi einen Vorwiderstand für die LED im OK. Das scheint laut Wiki so vorgeschrieben zu sein. Mit den geforderten 5mA und den 5V ergibt sich für den OK die Anforderung:
  • IF=5mA bei UF=1,7V für die LED
  • Das sekundäre Schaltglied (Transistor etc) muß bei diesem "Strom" (Licht) durchschalten (Current Transfer ratio? Verstärkung?).

Dino, kannst Du uns mal erleuchten?
 
Hallo zusammen,

ich hab mir mal eben das Datenblatt von einem SFH601 angesehen (hatte ich gerade zur Hand). Also 5mA sollten grunsätzlich kein Problem sein, ABER! es gibt diese (und andere) Optokoppler mit unterschiedlichem "Übersetzungsverhältnis". Wenn du einen mit geringem Übersetzungsverhältnis verwendest und dann nur mit 5mA betreibst, dann können die Signalflanken schon arg deformiert werden - was die "unzuverlässigen" Werte erklären würde. Im Zweifelsfall mal einen Oszi dranhängen.

zu #12
und dann versteh ich einfach nicht, was dieser 220 ohm widerstand auf der rechten seite (vor dem optokoppler) tut...
ganz einfach:
Der Widerstand hat eine Schutzfunktion, genau wie die Diode - man kann ja nie wissen, was über die Buchse reinkommt.
Leider sind die Steckverbinder nie eindeutig einer Funktion zugeordnet - da könnte auch mal eine höhere Spannung, oder falsche Polarität anliegen.

Gruß
- gp177 -
 
hallo,

so, jetzt mal das datenblatt vom verwendeten OK :)
http://www.produktinfo.conrad.com/d...038-da-01-en-OPTOKOPPLER_PC817X2NSZ0F_SHP.pdf

hat etwas gedauert... aus irgend einem grund mehren sich im moment wieder die ausfälle. obwohl ich an der hardware (und eigentlich auch der software) nichts nennenswertes verändert habe...

der verwendete OK hat eine rise- bzw. falltime von 4 bzw 3 µs... typischer weise.. maximal 18. ist das vielleicht zu langsam? soll ich mal den nehmen? der hat wesentlich bessere reaktionszeiten...
http://www.produktinfo.conrad.com/d.../184098-da-01-en-SPX7911_entspricht_PC900.pdf


wie sieht das mit der diode aus? ich hab da einfach mal irgendeine genommen... so wie ich das sehe ist die aber nur ein verpol-schutz für den OK, oder?
 
Hi Matze,

also mal was zu MIDI ... wikipedia - MIDI

Die Übertragungsgeschwindigkeit beträgt 31250 Bit/s (exakt 32 µs pro Bit).
Also sind deine "maximal 18µs" schonmal ganz schlecht.
Wenn man nun rechnet das ein UART etwa 8-16 Samplewerte bei einem Bit setzt (für die Biterkennung) und normalerweise die "Mehrheit" gewinnt dann solltest du wirklich auf nen schnelleren Optokoppler setzen.

Ich hab mal etwas gesucht ... duckduckgo - "MIDI signal rise time fall time bit"

http://www.electro-tech-online.com/threads/help-for-midi-decoder.114211/
... MIDI specification calls for a rise/fall time of 2uS or less.

The 6N138 along with the Sharp brand PC900 are the two most popular for this application. The PC900s are becoming increasingly harder to find while the 6N138s are plentifully available. ...

http://mitat.tuu.fi/?p=22
... It requires less than 5 mA to turn on. The Sharp PC-900 and HP 6N138 optoisolators are satisfactory devices. Rise and fall time for the optoisolator should be less than 2 microseconds. ...

Gruß
Dino
 
ja danke dino. ich hab mir jetzt mal nen schnelleren OK besorgt. mal sehen, obs damit besser geht..

mal kurz ne andere sache: wenn ein interrupt eintritt, dann werden ja für diese zeit alle interrupts unterbrochen, bis die schleife wieder verlassen wird. was passiert als, wenn ich zum beispiel mit einem taster auf Int0 einen interrupt triggere und in dieser zeit käme ein u-art interrupt? wird dieser interrupt dann nach dem verlassen der int0-schleife ausgelöst, oder garnicht? und kann ich theoretisch mit "sei" auch IN der Int0-schleife alle interrupts wieder an machen?
 
mal kurz ne andere sache: wenn ein interrupt eintritt, dann werden ja für diese zeit alle interrupts unterbrochen, bis die schleife wieder verlassen wird. was passiert als, wenn ich zum beispiel mit einem taster auf Int0 einen interrupt triggere und in dieser zeit käme ein u-art interrupt? wird dieser interrupt dann nach dem verlassen der int0-schleife ausgelöst, oder garnicht? und kann ich theoretisch mit "sei" auch IN der Int0-schleife alle interrupts wieder an machen?

Der AVR "merkt" sich den aufgetretenen Interrupt (Interruptanforderungsflag ist gesetzt). Sobald eine Interrupt-Serviceroutine (ISR) beendet ist, werden alle zwischenzeitlich aufgetretenen Interrupts bzw. deren ISRs abgearbeitet und zwar nach der Reihenfolge in der ISR Vektor Tabelle (Priorität).

Wenn du in einer ISR selber die Interrupts global frei gibst (setzen des I Flags mit SEI), kann die ISR durch eine andere ISR unterbrochen werden. Dies würde ich nur dann machen, wenn es zeitlich zwingend notwendig ist und dann auch nur für die zeitkritische Interruptquelle (d.h. zwischenzeitlich alle anderen Interruptquellen deaktivieren).

Dirk :ciao:
 
Achtung:
Bei den meisten IRQs gibt es wie angedeutet ein entsprechendes Flag, welches sich den "wartenden" (pending) Interrupt "merkt", bis es entweder durch die Ausführung der ISR (strenggenommen der automatische Sprung in die IVT) bei aktiviertem Interrupt, oder das beschreiben des Flags mit einer 1 gelöscht wird. (Das Flag wird auch bei deaktiviertem IRQ gesetzt, wenn das entsprechende Ereignis eintritt - dann halt ohne die ISR auszuführen (genauer: ohne die IVT anzuspringen)

ABER der INT0 kann auch als pegelbasierter IRQ verwendet werden, dann IST der Zustand des Beinchens quasi das Interrupt-Flag. Wenn also der INT0 kurz low geht während die IRQs global deaktiviert sind, und rechtzeitig wieder high, wird KEIN INT0-IRQ ausgelöst (da der Pin wieder High ist...), es wird sich hier nichts "gemerkt". Wird INT0 hingegen flankengetriggert verwendet, wird ein Flag mit "Merkfunktion";) verwendet.
Wenn Du innerhalb einer ISR die IRQs global wieder frei gibst, kann die ISR logischerweise durch andere IRQs unterbrochen werden. Insbesondere durch dieselbe Interruptquelle. Prinzipiell ist das erstmal kein Problem, wenn man mit den dort verwendeten Variablen ordentlich umgeht, ABER:
Beim Aufruf der ISR landet erstmal die Rücksprungadresse (wo's im Programm weitergehen soll) auf dem Stack, danach werden verwendete Rechenregister dort zwischengespeichert. Beim Austritt aus der ISR wird das ganze dann rückwärts wieder runtergenommen/wiederhergestellt.
Wenn der IRQ sich nun (einmal) selbst unterbricht, ist die benötigte Stack-Höhe doppelt so groß.
Machbar ist das zwar (rekursiv), aber DU mußt sicherstellen, daß der Stack nicht überläuft (bzw irgendwo in anderweitig verwendete SRAM-Bereiche rein). Und so'n AVR hat im allgemeinen nicht sehr viel SRAM...
 

Über uns

  • Makerconnect ist ein Forum, welches wir ausschließlich für einen Gedankenaustausch und als Diskussionsplattform für Interessierte bereitstellen, welche sich privat, durch das Studium oder beruflich mit Mikrocontroller- und Kleinstrechnersystemen beschäftigen wollen oder müssen ;-)
  • Dirk
  • Du bist noch kein Mitglied in unserer freundlichen Community? Werde Teil von uns und registriere dich in unserem Forum.
  •  Registriere dich

User Menu

 Kaffeezeit

  • Wir arbeiten hart daran sicherzustellen, dass unser Forum permanent online und schnell erreichbar ist, unsere Forensoftware auf dem aktuellsten Stand ist und der Server regelmäßig gewartet wird. Auch die Themen Datensicherheit und Datenschutz sind uns wichtig und hier sind wir auch ständig aktiv. Alles in allem, sorgen wir uns darum, dass alles Drumherum stimmt :-)

    Dir gefällt das Forum und unsere Arbeit und du möchtest uns unterstützen? Unterstütze uns durch deine Premium-Mitgliedschaft!
    Wir freuen uns auch über eine Spende für unsere Kaffeekasse :-)
    Vielen Dank! :ciao:


     Spende uns! (Paypal)