Hallo zusammen,
gibt's hier noch mehr Fans vom Schneider (Amstrad) CPC 464?
Als Fan der Hardware-Sprachsynthese habe ich einmal einen Emic 2
Sprachsynthesizer für den CPC aufgebaut. Warum poste ich das hier?
ATMega 328p wird für parallel-nach-seriell Wandlung verwendet, und als
Buffer / Driver. Dazu habe ich auch eine Frage (s.u.).
Doch zunächst einen zum YouTube Video, damit klar ist, um was es geht:
https://youtu.be/C5BnD1NPwGw
Das ist die Prototype-Breadboard-Version. Ich habe auch schon eine
Platine in der Produktion (erstellt mit KiCad + OSHPark). Mehr
demnächst.
Meine Frage: die ursprüngliche Idee war, direkt mit dem 328 am Datenbus
zu lauschen, und direkt Bytes vom Datenbus des CPCs zu lesen und zu
puffern. Die Addressdekodierung mache ich mit 2 PALs (Lattice / Atmel
G18V8), die ein "Adresses erkannt"-Signal erzeugen sobald die
IO-Addresse &F9E0 auf dem Addressbus erscheint. Ursprünglich sollte
dieses Signal dann im 328 einen Interrupt mittels INT0 auslösen, der
sich dann schnell das Datenbyte vom Bus holen und merken sollte. Da
Timing natürlich alles ist, habe ich den 328 sogar mit 16 Mhz getaktet
(der CPC ist mit 4 MHz unterwegs). Soweit der Plan.
Hat aber nicht geklapp - wohl aus mehreren Gründen:
Zum einen dauert die Interrupt-Behandlung wohl einfach zu lange im 328,
und das Byte ist "schon weg vom Datenbus" wenn der 328 es denn lesen
will. So benötigte ich dann sogar ein 74LS343 Flip Flop, um das Byte vom
Datenbus "zu latchen", damit es dann für den 328 zum Auslesen längere
Zeit anliegt. So weit, so gut.
Zum anderen, und das ist das größere Mysterium, scheint es nicht so
einfach zu sein, zuverlässig mit den IO-Ports des 328 ein ganzes Byte
parallel zu lesen, zumindest nicht schnell. Als Lösung musste ich auf
einen Protokoll-Trick zurückgreifen: um Bytes "AB" (65 66 ASCII) an den
328 zu senden, alterniere ich die Nutzbytes (65 66= mit 0-Bytes, und
setze das 8. Bit in den Nutzbytes (Sprachsynthesizer verwendet nur ASCII
7 Bit)... also sende ich 193 (= 65+128) 0 194 (= 66 + 128) 0 statt 65
66, und der 328 kann dann die 0-Bytes und das 8. Bit zur "Taktung /
Synchronisation" verwende. Das wird einfach in einem Array gepuffert.
Mit etwas Wartezeit für das 0-Byte zwischendrin kann der 328 dann die
Bytes vom CPC-Datebus (bzw. dem Flop Flop) sehr zuverlässig lesen und
puffern, und dann, wenn "Enter" kommt, per seriell an den Emic 2 senden.
Wenn ich diesen Trick nicht anwende, bekomme ich beim Auslesen der
Bytes mit dem 328 komische Effekt, z.B. Bitflips (Bits die 0 sein
sollten, sind noch 1 wenn sie es vorher waren), und andere komische
Effekte. Anscheinend scheint eine 0 zwischen 2 Nutzbytes erforderlich zu
sein, damit die Input des 328 "wieder runter kommen". Und das scheint
eine Zeit zu dauern? Hmm.
Meine Frage wäre - kommt jemanden dieses Problem bekannt vor, und/oder
hat eine Idee, wie es zu lösen ist? Ich meine, die Lösung oben
funktioniert, aber es wäre doch schöner, wenn ich einfach 65 66 +
"Addresse erkannt"-Signal an INT0 per Interrupt senden könnte. Das
einzige, was für mich momentan funktioniertt, ist eine "tight loop" und
polling der Art "loop-until-set(bit8)" und so weiter.
Ich weiß, man kann den Z80 im CPC auch "drosseln" mit Waitstates, aber
das wollte ich nicht. Wenn ich schon einen uP als intelligenten Treiber
(Buffer + Seriellen Konverter) verwende. UART Chip will ich auch nicht.
Bustreibe macht keinen Unterschied denke ich - das FlopFlop (373) oben
ist gut genug.
Gruß,
Michael
Wenn ich das richtig verstehe, geht es um einen parallel zu seriell
Wandler der mit &F9E0 Adressiert wird und sofort nach Erhalt wandelt.
Passt das Ganze nicht auch in ein CPLD?
Das dürfte schnell genug sein.
Danke für den Tipp - klingt gut! Allerdings habe ich keinerlei Erfahrung
mit CPLDs... außerdem ist Timing kritisch - der Emic 2 benötigt 8N1
seriell mit 9600 Bauds. Ich habe ein paar FPGA-Experimentierboards, das
wäre natürlich auch noch mal einen Versuch wert.
Mit dem 328 und SUART.h ist das serielle Interface zum Emic 2 ein
No-Brainer - ich war immer der Meinung, dass der 328 doch irgendwie
schnell genug sein müsste, um ein paar Bytes, die mit ein paar KHz
Frequenz kommen, zuverlässig zu puffern - vielleicht habe ich ihn ja
überschätzt :-) Oder ich muss mal die nächst größere Version vom AVR
probieren.
Michael W. schrieb:> Ich habe ein paar FPGA-Experimentierboards
Und auch schon etwas damit gemacht, oder?
Da es nur um Register, Adressvergleicher und evtl. Baudteiler geht,
dürfte der Unterschied zum CPLD Design nur minimal sein.
pegel schrieb:> Michael W. schrieb:>> Ich habe ein paar FPGA-Experimentierboards>> Und auch schon etwas damit gemacht, oder?
Nein, ich bin Anfänger... habe 2 Bücher über FPGA-Programmierung
gelesen, aber noch nichts praktisches gemacht.
Ich habe einen DEO-Nano, einen Elbert 2, und einen Papilio DUO.
Es erschien mir etwas "Overkill", die guten Teile für soetwas "profanes"
einzusetzen - aber sicherlich ein gutes Lernprojekt für mich!
Mein Favorit für kleine Sachen ist die XC9500 Serie.
Ohne XL dahinter da die für 5V Logik sind.
Bei deinen FPGA Boards würden die Pegelwandler vermutlich den größten
Teil der Schaltung ausmachen, oder?
pegel schrieb:> Bei deinen FPGA Boards würden die Pegelwandler vermutlich den größten> Teil der Schaltung ausmachen, oder?
Gut möglich - in meiner Ignoranz / Unwissenheit hatte ich ehrlich gesagt
gehofft, dass ich keine brauchen würde :-)
Das sieht interessant aus, ich schalt mal das Abo on :)
Btw sehr geiles Projekt und die Sing-Funktion ist der Hammer^^
Ansonsten ... Es gibt 5V nach 3,3V Transceiver (bidirktional) 74LVX4245
... Die sind recht klein und verhalten sich wie normale 245er und haben
das nette Feature, dass eine Seite mit 5V und die andere Seite mit 3,3V
bestromt wird.
http://www.st.com/content/ccc/resource/technical/document/datasheet/6e/85/52/2a/cb/3d/4c/47/CD00001693.pdf/files/CD00001693.pdf/jcr:content/translations/en.CD00001693.pdf
Jetzt noch 5V CPLDs zu verwenden wäre zwar schön vintage, aber die sind
mittlerweile schwer erhältlich ... Lieber dann gleich XC9572XL oder
sowas verwenden und 1-2 Transceiver vorne dran bauen. Die sind an den
Eingängen 5V-Tolerant, aber für eine Rückantwort an die Z80-CPU könnte
man dann die Transceiver verwenden.
Gute Ideen, dank Euch!
Am Liebsten wäre mir ja ein
"parallel-nach-8N1-seriell-ich-mach-alles-für-Dich" Chip - vielleicht
gucke ich mir ja doch noch mal an, wie ein "retro" 16C550 zu beschalten
wäre. Ich habe immer davor zurückgeschreckt, weil ich dachte, die
erfordern komplizierte Konfigurationseinstellungen. Aber schnell genug
für diese Anwendung sollten die wohl sein, nicht wahr? (Schneller als
der AVR 328).
Jetzt nochmal einen Schritt zurück von zusätzlicher Hardware zu einer
Softwarelösung.
Was wäre, wenn Du den Port so auslesen würdest?
1
.org INT0
2
in r15,PINB ;1 clock cycle
3
rjmp handle_exint
Was hat der 328 für eine Interrupt-Latency, 4 Takte?
Dann hättest Du 5 Takte nach dem Interrupt den Wert im Register, bei 16
MHz also nach knapp über 300 ns (bei 20 MHz nur 250 ns, das sollte beim
einem 4 MHz-Z80 WIMRE eigentlich schnell genug sein). Alles weitere ist
dann nicht mehr so zeitkritisch und kann danach in Ruhe erledigt werden
(inkl. Sichern des Statusregisters usw.).
Voraussetzung wäre, daß R15 (oder irgendein anderes Register) exklusiv
für den Interrupt reserviert wird und nirgendwo anders im Programmcode
verwendet wird.
Günstig wäre auch, wenn der Controller im Moment des Interrupts im
Idle-Modus wäre, damit ist die Interrupt-Latency konstant.
Falls erst ein Puffer im Controller gefüllt und dann der String zum
Sprachsynthesizer gesendet wird, sollte das doch möglich sein.
pegel schrieb:> Für die kleine Logik vor dem MK3801 kannst du deine PAL weiter benutzen.> Der Aufwand hält sich also sehr in Grenzen.
In der Tat, den MK3801 kannte ich nicht, das sieht ja wirklich einfach
aus!! Ich werde mir mal 2 davon bestellen! Super Tipp, Danke!
Horst M. schrieb:> Jetzt nochmal einen Schritt zurück von zusätzlicher Hardware zu einer> Softwarelösung.>> Was wäre, wenn Du den Port so auslesen würdest?> .org INT0> in r15,PINB ;1 clock cycle> rjmp handle_exint>> Was hat der 328 für eine Interrupt-Latency, 4 Takte?> Dann hättest Du 5 Takte nach dem Interrupt den Wert im Register, bei 16> MHz also nach knapp über 300 ns (bei 20 MHz nur 250 ns, das sollte beim> einem 4 MHz-Z80 WIMRE eigentlich schnell genug sein). Alles weitere ist> dann nicht mehr so zeitkritisch und kann danach in Ruhe erledigt werden> (inkl. Sichern des Statusregisters usw.).
Hallo Horst,
ich hatte C verwendet, und habe den Sourcecode jetzt gerade nicht
vorliegen, aber das Programm mit INT0-Interrupt sah im Wesentlich so aus
(aus dem Gedaechtnis, ist nur ne Skizze!):
volatile uint8_t index = 0;
... buffer array etc.
dann:
ISR(INT0_vect) {
unit8_t byte = (( PINB & 0x00011110) >> 1) | ( PINC & 0x00001111) <<
4)
buffer[index] = byte;
index++;
}
void initInterrupt0(void) {
EIMSK |= (1 << INT0);
EICRA |= (1 << ISC00);
sei();
}
int main(void) {
initInterrupt0();
while (1) {
if (index > 0 && buffer[index-1]=10) {
sendToEmic2();
index = 0;
}
}
return 0;
}
Ich hatte keine 8bit "am Stueck" frei an den Ports, deswegen musste ich
ein Nibble vom PORTB und eines vom PORTC lesen und verknusen. Evtl.
kostet selbst das zu viel Zeit... evtl. sollte ich die Daten lieber
"Raw" in 2 Arrays speichern (eines fur PORTB, eines fuer PORTC), und vor
dem Abspielen / Senden an den Emic 2 konvertieren. Mag sein, dass das
ein paar Takte Zeit spart.
Interessant ist jedoch, dass selbst wenn ich die Bytes langsam sende,
ich am Ende immer Murks mit dieser Loesung im buffer habe. Weshalb ich
davon ausgehe, das die Eingaenge vom 328 irgendwie nicht geeignet sind
fuer diese Aufgabe (parasitaere Kapazitaeten o.ae.?)
Dabei hatte ich fuer die INT0-Konfiguration sowohl fallende als auch
steigende Flanke und "Pin CHange" etc. auspropiert... am Ende hatte ich
stets Murks im buffer. Mit oder ohne Flipflop. Evtl. genuegt das
INT0-Signal, dass meine PLDs erzeugen, nicht den Qualitaetsanforderungen
des 328... das 74LS343 hat allerdings keine Probleme mit dem Signal.
Vielleicht ist es zu kurz oder was auch immer?
Gepuffert und so weiter hatte ich also...
PS Ich hatte auch beobachtet, dass der buffer gefuellt wird. Aber eben
mit Murks - Bitflips etc. Wenn ich z.B. Bytes 0, 1, 2, 3, 4, ...
sendete, konnte ich beobachten, dass Bits die vorher 1 waren und nun 0
sind, noch als 1 registriert wurden - im Array hatte ich dann oft sowas
wie 0, 1, 3, 3, 7, ...
... und die momentane "Loesung" fuer dieses Phaenomen ist fuer mich,
immer ein 0-Byte zwischen 2 Nutzbytes zu senden. Damit klappt es dann...
allerdings auch nur, wenn ich auf die INT0-Routine verzichte. Ich mache
momentan also sowas wie
while(1) {
loop_until_bit_set(bit8_pinc);
byte = (( PINB & 0x00011110) >> 1) | ( PINC & 0x00001111) << 4);
buffer[index] = byte;
index++;
if (byte = 10) {
sendToEmic2();
index = 0;
}
}
Das ist die einzige Loesung, die fuer mich funktioniert. Mit 0-Bytes
zwischendrin und Bit8-gesetzt-Protokoll, wie oben beschrieben.
> Verrätst du uns woher du den Emic 2 hast?
Ich lebe in Palo Alto, CA, da gibt es den bei Fry's Electronic zu
kaufen. Ca. 55 $. Es gibt ihn aber auch bei Amazon.com (.de weiss ich
nicht).
Bei Licht betrachtet ist der 328p mit seiner Portbelegung dafür wirklich
nicht ideal, Quarz an Port B, USART an Port D und bei Port C fehlt ein
Pin.
Da ist kein kompletter Port mit 8 Bits übrig.
Mit einem 40-Pinner ginge's einfacher, mit 20MHz könntest Du's am 328
evtl. nochmal so probieren:
1
.org INT0addr
2
in r14,PINB
3
in r15,PINC
4
rjmp handle_exint
Ist zwar nur ein Takt mehr, könnte aber evtl. knapp werden (der Z80 hat
für den OUT-Befehl doch nur 3 Taktzyklen, oder? *).
Das Ausmaskieren, Zusammenschieben usw. machst Du dann in der restlichen
Interruptroutine.
Wenn Du zwei Register aus dem Upper-Block ab R16 spendierst, gehen dann
auch die Immediate-Befehle, spart noch ein paar Zyklen.
* Hab's eben nochmal im Kieser/Meder gecheckt - IORQ ist 1 1/2
Prozessortakte Low, das sind bei 4 MHz also 375 ns, könnte also reichen.
Horst M. schrieb:> Was wäre, wenn Du den Port so auslesen würdest?>>
1
> .org INT0
2
> in r15,PINB ;1 clock cycle
3
> rjmp handle_exint
4
>
Nur nebenbei: INT0 sollte wohl heißen: INT0addr...
> Was hat der 328 für eine Interrupt-Latency, 4 Takte?
Wie jeder AVR8 ohne externen Speicher und 128k oder weniger Flash.
Leider ist das weniger als die halbe Wahrheit, denn das ist nur die
konstante Interruptlatenz. Dazu kommt die variable, die oft viel größer
ist. Selbst unter allergünstigstenen Umständen (keine cli/sei-Blocks im
Code und keine konkurrierenden Interrupts, keine Unterprogramme in main
und keine Datenzugriffe auf den Flash) hast du immer noch eine variable
Latenz von einem Takt, die du zur konstanten hinzurechnen musst.
Und wann hat man schonmal so extrem günstige Umstände...
> Dann hättest Du 5 Takte nach dem Interrupt den Wert im Register
Es sind bei günstigsten Umständen (s.o.) im worst case immer noch 6
Takte.
> Günstig wäre auch, wenn der Controller im Moment des Interrupts im> Idle-Modus wäre, damit ist die Interrupt-Latency konstant.
Günstig? Ich würde es eher eine notwendige (aber nicht hinreichende)
Voraussetzung dafür nennen, dass deine Rechnung überhaupt jemals
aufgeht...
Dann dann (und nur dann) sind die meisten obigen Einschränkungen
hinfällig. Bleiben beim Mega328 "nur" noch: konkurrierende Interrupts...
c-hater schrieb:>> Dann hättest Du 5 Takte nach dem Interrupt den Wert im Register>> Es sind bei günstigsten Umständen (s.o.) im worst case immer noch 6> Takte.
Ja, timing etc. ist wie gesagt nur ein Problem.
Mit dem FlipFlop in der Schaltung liegt das Datenbyte ja solange an, wie
ich es will, also kein Problem. Selbst wenn ich Bytes langsam mit dem
CPC sende, und vom FlipFlop lese, hatte ich wie gesagt das Problem, dass
eine Sequenz wie 0, 1, 2, 3, 4 als 0, 1, 3, 3, 7 im buffer erschien.
Dabei hat das FlopFlop stets die richtigen Werte (kontrolliert). Da ist
also was schraeg mit den Eingaengen des 328. Was ich noch mal probieren
kann ist, nachdem ich den INT0 gekriegt habe, ein paar ms zu warten,
bevor ich die beiden Nibbles lese, in der Hoffnung, dass die
parasitaeren Kapazitaeten oder was auch immer verantwortlich ist fuer
den Effekt, dass 0 BIts die vorher 1 waren noch als 1 erscheinen, damit
kompensiert werden. Komisch ist das schon, muss ich sagen.
Weiss jemand, mit welcher Baud-Rate man maximal von den Eingaengen lesen
kann, zuverlaessig? Ich meine, um Zuverlaessig 0,1,0,1,... als
alternierende Bitfolge and einem einzelnen PIN zu empfangen. (Senden
scheint einfacher zu sein - ich weiss, da geht es bis in die MHz ohne
Probleme, aber die Eingaenge im 328 scheinen ja eine Art eierlegende
Wollmilchsau zu sein, mit ADC und was auch immer da vor sich geht...)
S. Landolt schrieb:>> byte = (( PINB & 0x00011110) >> 1) | ( PINC & 0x00001111) << 4);>> Das ist aber nicht das Originalprogramm, oder?
Nein, eine Skizze aus dem Gedaechtnis. Aber schon so ungefaehr - hast Du
bessere Vorschlaege / Ideen, wie man 2 Nibbles von PINB, PINC in ein
Byte verwandelt?
S. Landolt schrieb:> Es geht um 0x... <-> 0b...
Aehh, ja ist klar, danke ;-) Nein nein, das echte Programm verwendet
schon 0b... ;-)
C ist nicht meine Haupt-Sprache, daher spielt mir mein Gedaechtnis
oefters mal einen Streich -
if (byte = 10 ) ...
sollte wohl auch eher
if (byte == 10) ...
heissen, ich weiss ;-)
c-hater schrieb:> Michael W. schrieb:>>> Da ist>> also was schraeg mit den Eingaengen des 328.>> Nein, die funktionieren, darauf kannst du einen lassen.
Na ja, schon, aber irgendwie sind sie "traege"... kennt keiner das
Phaenomen? Vielleicht liegt es ja wirklich an zu langen Leitungen. Aber
15 cm sind doch nicht lang... und die Frequenzen sind laecherlich gering
(im kHz-Bereich!)
Michael W. schrieb:> Selbst wenn ich Bytes langsam mit dem> CPC sende, und vom FlipFlop lese, hatte ich wie gesagt das Problem, dass> eine Sequenz wie 0, 1, 2, 3, 4 als 0, 1, 3, 3, 7 im buffer erschien.> Dabei hat das FlopFlop stets die richtigen Werte (kontrolliert)
Was ich also nicht verstehe, ist - wie kann es sein, dass ein Retro
FLipFlop von 1968 das Datenbyte mit dem generierten Signal korrekt
latchen kann, und der 328 kriegt das nicht hin.
Michael W. schrieb:> Na ja, schon, aber irgendwie sind sie "traege"... kennt keiner das> Phaenomen?
Naja, sie hängen bis zu einem Takt dem realen Geschehen hinterher. Das
ist dokumentiert, ist der Synchronisierung mit dem MCU-Kern geschuldet.
Alles darüber hinaus kommt entweder von außen oder von mieser
Software...
Dazu gehört natürlich auch Software, die dieser dokumentierten
Verzögerung nicht Rechnung trägt...
S. Landolt schrieb:> Ich betreibe hier einen ATmega644P mit 25 MHz und> lese mit 'in r0,PINA' ... 'in r29,PINA' 30 Bytes in 1.2 us ein.
OK, guter Datenpunkt! Das ist allerdings auch ein dickerer Hammer als
der 328, stimmt's?
pegel schrieb:> Vielleicht ist auch nur dein Breadboard von ähnlicher Qualität wie> meins.> Habe auch schon ein paar mal an mir gezweifelt dadurch.
Ich habe ja ein PCB in Produktion,,, bin gespannt, ob es besser geht
damit! Zumindest habe ich mein PCB so entworfen, dass das Signal auch an
INT0 anliegt, damit kann ich das dann einfach ausprobieren. Ansonsten
habe ich ja noch die andere Loesung.
> Das ist allerdings auch ein dickerer Hammer als> der 328, stimmt's?
Schon, ich habe ja nicht die Zeit, um ein Byte aus zwei Stücken
zusammenzusetzen. Aber die Technologie ist die gleiche.
c-hater schrieb:> Alles darüber hinaus kommt entweder von außen oder von mieser> Software...
Verstehe. Na ja, in meinem Fall ist die Software so minimal, dass da
eigentlich kaum was zu optimieren ist auf der C-Seite, oder?
(Und wenn, wuerde ich hoffen, dass GCC das fuer mich macht ;-) )
S. Landolt schrieb:> Schon, ich habe ja nicht die Zeit, um ein Byte aus zwei Stücken> zusammenzusetzen.
... das mache ich ja auch nur unfreiwilig, weil ich den Quarz habe und
SIN / SOUT...
S. Landolt schrieb:
> Ich betreibe hier einen ATmega644P mit 25 MHz und> lese mit 'in r0,PINA' ... 'in r29,PINA' 30 Bytes in 1.2 us ein.
Verwendest Du eine spezielle Beschaltung, um diese Raten zu erreichen?
Bist Du sicher, dass keine Artefakte auftreten?
Ich ware mir z.B. nicht sicher, ob ich AVCC and VREF und diese Sachen
beschalten sollte, da ich ja eigentlich keine AD-Wandlung benoetigt.
Evtl. hat das aber Einfluss auf "Pin-Traegheit" oder was auch immer?
Oder alle nicht benutzten PINs auf GND ziehen, ...
Michael W. schrieb:> Na ja, in meinem Fall ist die Software so minimal, dass da> eigentlich kaum was zu optimieren ist auf der C-Seite
C? Bei timingkritischen Anwendungen im Bereich weniger Takte? Und oder
sogar noch bei solcherart Anwendungen mit Interruptnutzung?
Das ist, als wenn du mit einer Flex vom Bau einen Brillianten zu
schleifen versuchst...
Dafür ist C einfach weder gedacht noch geeignet.
c-hater schrieb:> Michael W. schrieb:>>> Na ja, in meinem Fall ist die Software so minimal, dass da>> eigentlich kaum was zu optimieren ist auf der C-Seite>> C? Bei timingkritischen Anwendungen im Bereich weniger Takte? Und oder> sogar noch bei solcherart Anwendungen mit Interruptnutzung?>> Das ist, als wenn du mit einer Flex vom Bau einen Brillianten zu> schleifen versuchst...>> Dafür ist C einfach weder gedacht noch geeignet.
Ich sehe schon, Dein Name ist Programm... ;-)
Ja evtl. beisse ich mal in den sauren Apfel und versuche es direkt!
Vorher gucke ich mir aber mal an, was gcc da erzeugt hat. Ich glaube
aber nicht, dass das wirklich die Erklaerung fuer die beobachtete "Pin
Traegheit" ist.
> Verwendest Du eine spezielle Beschaltung
Nein. Vor den Eingängen liegen (hier unnötige) Schutzwiderstände von 750
Ohm und die Leitungen sind unter 15 cm lang.
> Bist Du sicher, dass keine Artefakte auftreten?
Ja.
S. Landolt schrieb:>> Verwendest Du eine spezielle Beschaltung> Nein. Vor den Eingängen liegen (hier unnötige) Schutzwiderstände von 750> Ohm und die Leitungen sind unter 15 cm lang.>>> Bist Du sicher, dass keine Artefakte auftreten?> Ja.
OK, danke.. das zeigt mir zumindest, dass es prinzipiell moeglich sein
muesste, mit Technologie von 2017 auf den Stand von 1968 zu kommen ;-)
Ich werde das mal mit einem anderen AVR probieren.
Michael W. schrieb:> Ich ware mir z.B. nicht sicher, ob ich AVCC and VREF und diese Sachen> beschalten sollte, da ich ja eigentlich keine AD-Wandlung benoetigt.> Evtl. hat das aber Einfluss auf "Pin-Traegheit" oder was auch immer?> Oder alle nicht benutzten PINs auf GND ziehen, ...
IMHO muß AVCC immer angeschlossen werden, sonst funktioniert Port C
nicht.
MfG Klaus
Klaus schrieb:> IMHO muß AVCC immer angeschlossen werden, sonst funktioniert Port C> nicht.
Hallo Klaus,
habe ich jetzt ausprobiert, konnte keinen Unterschied in meiner
Anwendung beoabachten. Habe nach wie vor Murks im Buffer, nun
insbesondere "doppelte" Bytes, also als ob der Interrupt "prellen" würde
(bin mir aber ziemlich sicher, dass er das nicht tut - bei PIN Change
kann ich das ja verstehen, ein Byte für steigende, eines für fallende
Flanke, aber sonst nicht, oder?) Muss also was elektrisches sein,
irgendwie.
Letzte Programm-Version war (diesesmal keine Skizze, das ist das echte
Programm):
#include <avr/io.h>
#include <util/delay.h>
#include "pinDefines.h"
#include <avr/interrupt.h>
#define BAUD 9600
#include "USART.h"
#define SIZE 1024
#define BV(bit) (1 << (bit))
#define toggleBit(byte, bit) (byte ^= BV(bit))
#define setBit(byte, bit) (byte |= BV(bit))
#define clearBit(byte, bit) (byte &= ~BV(bit))
volatile uint8_t buffer_low[SIZE];
volatile uint8_t buffer_high[SIZE];
volatile uint8_t buffer_index = 0;
volatile uint8_t last_low = 0;
volatile uint8_t last_high = 0;
ISR(INT0_vect) {
cli();
// _delay_ms(10);
last_low = PINB;
last_high = PINC;
last_low &= 0b00011110;
last_high &= 0b00001111;
buffer_low[buffer_index] = last_low;
buffer_high[buffer_index] = last_high;
buffer_index++;
sei();
}
void initInterrupt(void) {
EICRA |= (1 << ISC01);
EICRA |= (0 << ISC00);
EIMSK |= (1 << INT0);
sei();
}
int main(void) {
DDRB = 0;
DDRC = 0b00110000;
initUSART();
clearBit(PORTC, 5);
clearBit(PORTC, 4);
while (receiveByte() != ':');
buffer_index = 0;
last_low = 0;
last_high = 0;
initInterrupt();
while ( 1 ) {
setBit(PORTC, 5);
if ( buffer_index > 0 && ( last_low == 20 || last_low == 26)) {
clearBit(PORTC, 5);
setBit(PORTC, 4);
cli();
for (int i = 0; i < buffer_index; i++) {
uint8_t byte = ( buffer_low[i] >> 1 ) | (buffer_high[i] <<
4);
transmitByte( byte);
}
sei();
buffer_index = 0;
last_low = 0;
last_high = 0;
}
}
return 0;
}
(PORTC 5 und 4 sind Status-LEDs)
Habe erneute fallende und steigende Flanke probiert, und sowohl INT0 als
auch INT1, mit und ohne AVCC, mit verschiedenen 328ern - macht alles
keinen Unterschied :-( Und, wie gesagt, der Effekt tritt auch auf, wenn
ich seeeehr langsam die Bytes sende. Es scheint so, dass der 328 mein
Interrupt-Signal nicht mag. Vielleicht ist es zu kurz o.ä, wie gesagt,
das Flip Flop hat kein Problem mit dem Signal. Ich weiß ehrlich gesagt
nicht, wo da was "Prellen" könnte... (elektrische Reflektionen??)
Gruß.
Michael
Ich habe keine Ahnung von C, aber wie funktioniert das:
> #define SIZE 1024> volatile uint8_t buffer_low[SIZE];> volatile uint8_t buffer_high[SIZE];
der ATmega328 hat doch nur 2 KiB SRAM?
> volatile uint8_t buffer_low[SIZE];> volatile uint8_t buffer_high[SIZE];
der ATmega328 hat doch nur 2 KiB SRAM?
Danke... guter Hinweis... sollte mich der Compiler da nicht warnen?
Ich werde es mal auf 128 bytes oder so runter setzen und gucken, ob es
einen Unterschied macht. Das Program macht ja was... und es sendet auch
Bytes an den Emic... kann es dennoch sein, dass es "intern korumpiert"
ist durch zu großen buffer?
> Ich habe keine Ahnung von C
kann die Fragen also nicht beantworten. Der avrasm2 brächte für '.dseg'
eine Warnung "OVER", eventuelle Laufzeitfehler hingen von der
Variablenanordnung ab.
Nur am Rande, auch wenn der eine Takt zu Beginn der ISR wohl keine
Rolle spielt: cli/sei zu Beginn/Ende einer ISR sind überflüssig, das
macht die Hardware (schneller und besser).
Michael W. schrieb:> Danke... guter Hinweis... sollte mich der Compiler da nicht warnen?
... habe den SIZE buffer auf 16 gesetzt... leider kein Unterschied.
Immer noch BitFlips und Doppelte :-(
S. Landolt schrieb:>> Ich habe keine Ahnung von C> kann die Fragen also nicht beantworten. Der avrasm2 brächte für '.dseg'> eine Warnung "OVER", eventuelle Laufzeitfehler hingen von der> Variablenanordnung ab.> Nur am Rande, auch wenn der eine Takt zu Beginn der ISR wohl keine> Rolle spielt: cli/sei zu Beginn/Ende einer ISR sind überflüssig, das> macht die Hardware (schneller und besser).
Danke, ja, gelesen hatte ich das, aber in meiner "Verzweiflung" greife
ich halt nach jedem Strohhalm ;-)
Zusammengefasst:
- INT0 scheint zu "prellen" -> Doppelte
- PORTB, PORTC sind traege - bits, die vorher 1 waren und nun 0 sein
sollten, sind immer noch 1. Das einzige was hilft, ist ein 0 byte zu
senden, und etwas zu warten...
- AVCC macht keinen Unterschied hier
- INT0, INT1, ... macht keinen Unterschied hier
- steigende und fallende Flanke erzeugt logischerweise mehr Murks und
Doppelte als nur steigende oder nur fallende Flanke, aber auch damit ist
Murks im Buffer
- wenn ich zu schnell sende, reicht die
Interrupt-Bearbeitungsgeschwindkeit nicht aus, und Bytes gehen verloren.
Das ist klar, aber nicht das eigentlich Problem hier.
- mein 74LS373 Flip Flop hat diese Probleme nicht, und das Datenbyte ist
stets korrekt am Ausgang. Dabei kriegt das Flip Flop exact das gleiche
Select-Signal, wie INT0.
Vielleicht sind meine PINB, PINC Eingaenge irgendwie falsch
konfiguriert?? Es ist raetselhaft.
S. Landolt schrieb:> Das 74LS373 verarbeitet ja auch nicht, fragt z.B. nicht auf 20 oder 26> ab - was hat es eigentlich damit auf sich?
20 = 10 (LF) << 1
26 = 13 (CR) << 1
Das ist die Aufforderung, den Buffer an den Emic 2 zu senden.
(PINB-Eingang ist um 1 Bit nach link verschoben - um Zeit zu sparen in
der Interrupt-Routine, speichere ich die ausmaskierten Ports "raw", also
ohne Verschiebung, das mache ich erst beim Senden (das Ausmaskieren kann
ich mir wohl auch sparen). Das alles macht zwar die
Interrupt-Behandlungsroutine schneller, behebt aber nicht die beiden
Grundprobleme:
- INT0 prellt (Doppelte)
- pins sind traege (Bitflips)
S. Landolt schrieb:> Das heißt, die 'Nutzbytes' haben im unteren Teil nie 0x1a bzw. 0x14?
Richtig, ich sollte wohl auch auf "last_high == 0" testen im "if", danke
;-)
> Und warum wird überhaupt ein Zeilenende abgewartet?
... in erster Linie, um die Interrupt-Behandlung "so schnell wie
möglich" zu machen. Ich hatte auch versuche, das empfangene Byte direkt
(ungepuffert) rauszusenden, kostet aber natuerlich Zeit. Ob das
Abspeichern in 2 Arrays nicht letztlich mehr Zeit kostet, als das
konvertierte Byte direkt rauszusenden, weiß ich nicht, ich denke aber
nicht (das serielle Senden wird viel länger dauern IMHO). Daher will
puffern und das nicht in der INT0-Routine machen.
Da Geschwindigkeit wie gesagt nicht das Hauptproblem ist - selbst wenn
ich direkt sende, sende ich Murks - Bitflips.
Nun weiß ich erstmal nicht weiter.
Das mit den "Bitflips" sieht nach Hardware aus, eingangs- oder auch
ausgangsseitig.
Wie lang ist eigentlich so eine 'Datenzeile', wie oft kommt eine und
wie schnell kommen die einzelnen Bytes?
Nun, der kuerzeste Test fuer "Bitflips" und Doppelte ist sowas wie
"s123<CR>" oder "s123<LF>" - ("s" = "speech" ist das "Start-Token" fuer
den Emic 2 - der puffert das selbst, und spricht erst wenn er CR oder LF
kriegt). Wohlgemerkt, diese ASCII Ziffern haben alle kein & 10 oder & 13
gesetzt. Und dann sagt der Emic dann ab und zu korrekt
"onehundredtwentythree", oftmals aber eben auch sowas wie "1223", also
"onethousandtwohundredandtwentythree" (-> Doppelte), oder manchmal eben
was voellig verqueres (Bitflips) Die Bytes koennen beliebig langsam
gesendet werden - ich verwende ein BASIC-Programm, also nicht mehr als
10 Zeichen pro Sekunde oder so.
< 10 char/s? 9600 Bd sind rund 1000 - da würde ich zum Testen die ganze
Buffer-Mimik weglassen; man könnte sogar alles in der INT0-ISR machen,
das Byte zusammensetzen und gleich auf den UART (sts UDR0,rn braucht 2
Takte) geben, es muss ja nicht einmal auf UDRE0 abgefragt werden.
Die serielle Verbindung zum EMIC ist in Ordnung?
S. Landolt schrieb:> Nun weiß ich erstmal nicht weiter.
In jedem Fall - vielen Dank für Deine Hilfe!! Ich denke, ich werde
einmal die nächst-größere Variante des AVRs probieren. Dann habe ich 8
Bit am Stück frei. Vielleicht habe ich ja auch eine "Billig-Raubkopie"
des AVRs erworben, der irgendwie elektrisch minderwertig ist (gefälschte
Chips soll es ja geben). Und nun habe ich davon 5 Stück ;-)
S. Landolt schrieb:> Die serielle Verbindung zum EMIC ist in Ordnung?
Oh ja - ich habe ja auch eine funktionierende (wenn auch unschöne)
Lösung, s. YouTube-Video oben.
Wie gesagt - diese Lösung kommt ohne Interrupts aus, benötigt 0-bytes im
Datenstrom, das 8. Bit in den Nutzbytes gesetzt "für Selbsttaktung"
(habe ja keinen Interrupt dann), und etwas Wartezeit. Gepuffert wird in
der while (1) Hauptschleife. Das funktioniert dann. Ist aber
unbefriedigend.
S. Landolt schrieb:
> < 10 char/s? 9600 Bd sind rund 1000 - da würde ich zum Testen die ganze> Buffer-Mimik weglassen; man könnte sogar alles in der INT0-ISR machen,> das Byte zusammensetzen und gleich auf den UART (sts UDR0,rn braucht 2> Takte) geben, es muss ja nicht einmal auf UDRE0 abgefragt werden.
... und hier noch mal ein Versuch ohne Buffering -
#include <avr/io.h>
#include <util/delay.h>
#include "pinDefines.h"
#include <avr/interrupt.h>
#define BAUD 9600
#include "USART.h"
#define BV(bit) (1 << (bit))
#define toggleBit(byte, bit) (byte ^= BV(bit))
#define setBit(byte, bit) (byte |= BV(bit))
#define clearBit(byte, bit) (byte &= ~BV(bit))
ISR(INT0_vect) {
uint8_t lo_nib = PINB;
uint8_t hi_nib = PINC;
//_delay_us(100);
uint8_t byte = ( ( lo_nib & 0b00011110 ) >> 1 ) | ( ( hi_nib &
0b00001111 ) << 4);
transmitByte( byte );
// _delay_us(100);
}
void initInterrupt(void) {
// DDRD &= ~(1 << DDD2);
// PORTD |= (1 << PORTD2);
EICRA |= (1 << ISC01);
EICRA |= (1 << ISC00);
EIMSK |= (1 << INT0);
sei();
}
int main(void) {
DDRB = 0;
DDRC = 0;
_delay_ms(1000);
initUSART();
while (receiveByte() != ':');
printString("SSpeech synthesizer ready.\n");
while (receiveByte() != ':');
initInterrupt();
while ( 1 ) {
}
return 0;
}
... was soll ich sagen - klappt auch nicht besser.
Die Eingänge im 328 sind für diese Anwendung nicht geeignet,
die Signalqualität ist nicht ausreichend, oder die Stromversorgung
oder was auch immer.
Gruß,
Michael
Im Prinzip bräuchtest du einen Controller mit Slave-Buseingang. Meines
Wissens gibt es keinen AVR, der soetwas hat - aber durchaus ein paar
PICs.
Ohne /WAIT-Signal wirst du vermutlich das Timing nicht einhalten können,
und Dummy-Bytes schicken müssen ist auch keine ordentliche Lösung.
S. R. schrieb:> Im Prinzip bräuchtest du einen Controller mit Slave-Buseingang. Meines> Wissens gibt es keinen AVR, der soetwas hat - aber durchaus ein paar> PICs.>> Ohne /WAIT-Signal wirst du vermutlich das Timing nicht einhalten können,> und Dummy-Bytes schicken müssen ist auch keine ordentliche Lösung.
Timing ist ja eigentlich variabel einstellbar... zumindest liegt das
Nutzbyte ja solange am FlipFlop, wie ich es will. Ich muss nur sehen,
dass ich das Select-Signal erwische. Anders als der 328 hat das FlipFlop
keine Probleme damit, das byte korrekt vom Datenbus zu lesen und zu
latchen, wenn mein Selec-Signal kommt. Das Select-Signal brauche ich
also nur, um zu wissen, wann ein "neues" Byte fuer mich anliegt. Und
anscheinend mag der 328 mein Select-Signal aus unerfindlichen Gruenden
nicht.
Ich werde jetzt noch mal folgende 2 Sachen probieren, bevor ich den AVR
gegen was besseres austausche:
- beim Einsprung in die ISR etwas warten, bevor ich die beiden Nibbles
von PINB und PINC lese, in der Hoffnung, dass sich die Pegel der PINs
dann "stabilisieren" und "einschwingen". Natuerlich setzt das die
BAUD-Rate, mit der ich Bytes vom CPC senden kann, etwas herab. Das
Select Signal ist kurz - es wird erzeugt von den beiden PLDs, wenn
Addresse &FE90 auf dem Addressbus erscheint, und ~IORQ und ~WR runter
gehen (~M1 verwende ich nicht). Dann hat der CPC mittles out &fe90,<x>
das Byte auf den Datenbus gelegt. Wenn ich das richtig sehe, liegt das
Select Signal und das Datenbyte fuer 1 Takt an, das waeren also bei 4
Mhz unter 1 Mikrosekunde. Klar, dass die urspruenglich Idee, das Byte
direkt vom Datenbus zu laden mit dem 328 bei Select, illusorisch war.
Das FlipFlop kann's aber.
- anstatt mein Select-Signal fuer INT0 zu nehmen, werde ich einmal Bit8
vom Datenbyte vom FlipFlop dafuer nehmen. Dann muss ich zwar wieder 0,
"Nutzbyte + 8. Bit gesetzt" senden (kein Problem bei 7Bit ASCII), um
einen "Takt" auf das 8. Bit fuer den INT0 zu kriegen, aber damit kann
ich dann sehen, ob es irgendwie an der QUalitaet des Select-Signals
liegt. Das ist dann im Prinzip mein "altes" Protokol, nur mit ISR
anstatt busy loop; diese "Loesung" funktioniert wie gesagt wunderbar, s.
Video:
while (1) {
loop_until_bit_is_set(bit8);
byte = PINB, PINC lesen
buffer[index]=byte
if (byte = End of line)
... send to Emic
loop_until_bit_is_clear(bit8);
}
Wenn ich das Einlesen mit dem 8. Bit vom FLipFlop zuverlaessig
hinkriege, aber nicht mit dem eigentlichen Select-Signal, muss ich mir
das Signal mal genauer mit dem Scope anschauen. Vielleicht kann es ja
"elektrisch aufgewertet" werden.
Schoene waere es ja schon, wenn ich volle Bytes senden koennte, und
keine 0-Bytes braeuchte :-)
Ohne das Problem genauer eingegrenzt zu haben (hast du dir eigentlich
mal deine Signale mit dem Oszi angesehen?) ist es nie verkehrt sich
immer die folgenden Fragen zu stellen:
Warum sollte ausgerechnet ich unter Millionen Anwendern der Erste sein,
der dieses Hardwareproblem mit dem AVR hat? Warum berichtet niemand
sonst über dieses Problem? Mit der Konsequenz aus diesen Überlegungen
macht man sich dann auf die Fehlersuche…
Michael W. schrieb:> gibt's hier noch mehr Fans vom Schneider (Amstrad) CPC 464?
Hatte ich auch einmal, kam nach dem ZX Spectrum. Sich heute damit zu
befassen ist so, als ob man mit einer Dampfmaschine spielt.
mfg klaus
Joe G. schrieb:> Ohne das Problem genauer eingegrenzt zu haben (hast du dir eigentlich> mal deine Signale mit dem Oszi angesehen?) ist es nie verkehrt sich> immer die folgenden Fragen zu stellen:> Warum sollte ausgerechnet ich unter Millionen Anwendern der Erste sein,> der dieses Hardwareproblem mit dem AVR hat? Warum berichtet niemand> sonst über dieses Problem? Mit der Konsequenz aus diesen Überlegungen> macht man sich dann auf die Fehlersuche…
Da stimme ich zu - in der Hoffnung hatte ich ja hier gepostet, dass
einer sagt - "mensch, bevor mit der ISR neu von PINB, PINC lesen willst,
musst Du doch die Eingänge reseten", oder "wo ist denn Dein RC-Glied am
Pin?". oder was auch immer ;-) Ich bin kein Elektroniker.
Wie gesagt - ich halte das, was ich aufgebaut habe, für ein
Minimal-Standard-Design und hatte eigentlich solche Probleme nicht
erwartet. Etliche Hardware-Erweiterungen in den 80er für den CPC wurden
genau so aufgebaut. OK, Select-Signal hat man in den 80er mit 74x...
gemacht, das Signal sollte / dürfte nicht wesentlich anders aussehen,
mit PLDs. Ich gebe zu bedenken, dass das Flip Flop kein Problem mit der
Schaltung hat - nur der 328. Ich will den 328 nicht schlecht machen, ich
finde es halt rätselhaft, was da mit den Eingängen vor sich geht.
Ja, ich habe mir das Select-Signal mal angesehen mit dem Scope - es ist
halt sehr kurz! Aber ich kann nichts abwegiges daran feststellen. Ich
kann ja mal einen Screenshot posten demnächst.
Danke für den Input,
Gruß
Michael
Klaus R. schrieb:> Hatte ich auch einmal, kam nach dem ZX Spectrum. Sich heute damit zu> befassen ist so, als ob man mit einer Dampfmaschine spielt.> mfg klaus
Warum auch nicht, wenn's Spaß macht - man muss ja auch ein Hobby haben
:-)
Michael W. schrieb:> Ja, ich habe mir das Select-Signal mal angesehen mit dem Scope - es ist> halt sehr kurz! Aber ich kann nichts abwegiges daran feststellen. Ich> kann ja mal einen Screenshot posten demnächst.
Stichwort Puls-Breite - weiß hier einer, wie lang der Puls sein muss,
damit 328 überhaupt zwischen fallender und steigender Flanke sauber
unterscheiden kann? Ist garantiert, dass steigende vor fallender kommt,
wenn der Puls sehr kurz ist? Wie gesagt - das INT0 scheint mir "zu
prellen".
Ich wollte noch einmal ein Update in dieser Angelegenheit senden -
- die Pulsbreite vom Select-Signal ("IO-Adresse erkannt") beträgt 1,8 uS
- die maximale Geschwindigkeit, mit der Bytes gesendet werden per BASIC,
beträgt 840 uS (mit Z80 Assembler ginge es schneller)
Das sollte eigentlich locker reichen, um auf den Interrupt zu reagieren.
Am Wochende habe ich folgendes ausprobiert:
- statt des FlipFlops und parallelem Auslesens des Bytes habe ich einmal
einen 74HC165 parallel-2-seriell Schieberegister verwendet. Load wie
gehabt mit dem Select-Signal ("Adresse erkannt"), lesen über PB1, takten
zum Auslesen über PB2 und PB3. Interessanterweise erzeugt auch das
Bitfehler!! Es liegt also nicht am parallelen Auslesen mit dem 328.
Das serielle Auslesen ist ebenfalls fehleranfällig :-(
- dann dachte ich, so, vielleicht hatte ich ja den Datenbus und das
FlipFlop zu früh "frei gesprochen", und die Daten sind wirklich nicht
astrein auf dem Bus... also spendierte ich dem Aufbau noch einen 74HC244
- zu meiner Frustration hat auch das nichts verbessert.. ich kriege
immer noch eigenartigen Murks im Buffer.
- ich habe auch noch ~M1 vom Z80 in das Select-Signal einfließen lassen,
es machte aber keinen Unterschied (ich dachte mir, vielleicht wir das
Signal "zu oft" ausgelöst - so ist die Bedingung nun &F9Ex & ~WRT &
~IORQ & M1) - M1 macht keinen Unterschied.
- ich habe mehrere Sieb-Kondensatoren eingebaut - kein Unterschied.
- dann habe ich noch am Program was geändert - u.a. hatte ich gelesen,
dass das INT0 Flag gesetzt werden kann, auch innerhalb der ISR, und das
nach Rückkehr aus der ISR sofort wieder die ISR - wenn Flag
zwischenzeitlich gesetzt wurde - angesprungen werden kann, wenn man
nicht das Flag-Register mittels EIFR = (1 << INTF0); löscht. Das hätte
natürlich "doppelte Bytes" erkären können. Har allerdings keinen
wesentlichen Unterschied gemacht.
- auch habe ich natürlich erneut mit dem Timing gespielt. Selbst mit
langsamsten Einstellungen (Senden von einem Zeichen per Byte und
entsprechende großzügigen Wartezeiten im AVR-Code) habe ich die
fehlerhaften Bytes nicht vollständig eliminieren können.
- ich habe inzwischen auf mehren Web-Seiten gelesen, dass die
AVR-Eingänge eigentlich keine echten "Digital-Eingänge" sind, sondern
analoge Eingänge sind. Und dass die TTL- bzw CMOS-Kompatibilität nur
bedingt gegeben ist. Kann das hier der Fall sein? Wie kommt es dann,
dass meine busy-Loop-Polling Lösung perfekt funktioniert?
Ich muss sagen, dass ich mit meinen Ideen inwzischen einigermaßen am
Ende angelangt bin :-)
Ich habe mit mal einen PIC bestellt. Ich bin gespannt, und ich damit
ähnliche Probleme habe.
Ich habe die Signale mit dem Oszi angeschaut, und es sieht eigentlich
alles gut gut.
pegel schrieb:> So viel Aufwand um einen MK3801 nach zu bauen ;)
Richtig.. aber für den MK3801 benötigt ich Treiber-Software auf dem
CPC... während ich mit meiner Lösung einfach "out &F9Ex, <byte>" in
BASIC senden kann ;-)
Michael W. schrieb:> Treiber-Software
Na ja, ich habe mir den Treiber jetzt nicht genau angesehen, aber ich
denke das der darin besteht einige Register zu setzen bevor die
Nutzdaten ausgegeben werden.
Das ist sicher auch in Basic möglich.
Gerade noch mal rein gesehen.
Ist schon ein richtiger Treiber der 3 Basic Befehle erzeugt.
Scheint recht übersichtlich zu sein und wirklich hauptsächlich E/A
Befehle abzusetzen.
Meine Z80 Zeiten sind aber zu lange her für ein schnelles Verständnis
der Befehle :(
Ja, es ist sicherlich nicht super schwierig das hinzubekommen,
allerdings hätte die uC-Lösung den Charme, dass ich damit ein
wiederverwendbares Schema hätte, um eigentlich beliebige Hardware am CPC
anzuschließen. Insofern "hänge" ich an der Lösung.
Joe G. schrieb:> Warum sollte ausgerechnet ich unter Millionen Anwendern der Erste sein,> der dieses Hardwareproblem mit dem AVR hat? Warum berichtet niemand> sonst über dieses Problem? Mit der Konsequenz aus diesen Überlegungen> macht man sich dann auf die Fehlersuche…
Zu dieser Aussage mal eine Nachfrage - bisher habe ich ein Projekt
gefunden, wo einer mit dem atmega 328 was vom Z80 Bus liest bzw. Flip
Flop:
https://hackaday.io/projects/hacker/623
In der Tat habe ich mich jetzt einmal mit dem Autor ausgetauscht - und
zu meiner Ueberraschung stellte ich fest, dass er ebenfalls mit Polling
in der Busy-Loop arbeitet, da er ebenfalls Probleme hatte mit Lesen in
der ISR... Coincidence?
Ueber weitere Pointer auf vergleichbare Projekte (immerhin muss es doch
schon einige hundertausende AVR-Nutzer geben, die genau das gemacht
haben) wuerde ich mich super freuen :-)
Das Problem ist, dass ein Z80 @ 4 MHz den AVR ohne Probleme überrennen
kann, weil der AVR kein natives Businterface hat. Mit Polling kannst du
kürzere Reaktionszeiten erreichen als mit Interrupts, was aus meiner
Sicht hier das Problem ist.
In meinem System hatte der AVR ein "AUTOWAIT"-Signal, welches - wenn
aktiv und der AVR angesprochen wird - automatisch /WAIT zieht. Das war
auch notwendig, da ich den Z80-Bus per I2C angebunden hatte.
Michael W. schrieb:> ... was soll ich sagen - klappt auch nicht besser.> Die Eingänge im 328 sind für diese Anwendung nicht geeignet,> die Signalqualität ist nicht ausreichend, oder die Stromversorgung> oder was auch immer.
Dafür gibt es ja exakte Spezifikationen für die Chips, besonders
Bus/IO/Pegel/Timing etc. So ganz verstehe ich das Problem nicht.
Bei den alten Prozessoren muß/te man immer die sehr schwachen Bustreiber
berücksichtigen. Um einen fetten AVR-Port umzuladen, brauchen die
vielleicht etwas mehr Zeit als man denkt.
Ich habe inzwischen meine Busy-Polling-Loop mal so umprogrammiert, dass
PD2 nicht als INT0 verwendet wird, sondern einfach als "naechstes Byte
ist verfuegbar" Flag; Skizze:
while (1) {
do {
loop_until_bit_is_set(PORTD, 2);
byte = read PINB, PINC
buffer[index++] = byte;
} while ( byte != 13);
send buffer to Emic
}
Das scheint zu 95% zu funktionieren. Damit brauche ich also keine
0-Bytes mehr im Datenstrom, und auch kein 8. Bit gesetzt in den
Nutzdaten; ich kann also einfach das Select-Signal ("IO-Addresse
erkannt") verwenden. Insofern ist das Protokoll auf CPC-Seite also so
einfach wie moeglich - ein "out &f9ex, <byte>" Befehlen reicht, um
<byte> zu senden.
Danke fuer die Ideen... READY / WAIT Signal wollte ich ja immer
vermeiden. Tatsaechlich ist es moeglich, den CPC in den Wartezustand zu
versetzten, wenn ich READY / WAIT auf GND ziehe. Allerdings - ich
brauche dafuer ja tristate bzw. einen hochohmigen 328 Ausgang. Wenn ich
READY auf VCC ziehe, schmiert der CPC natuerlich sofort ab... ich
brauche also die beiden Zustaende "nicht verbunden (hochohmig) und 0
(GND)".
Dafuer habe ich nur diese Loesung gefunden;
hochohmig: PIND = 0; (INPUT)
0 (GND): DDRD = 0b00001000; (ich verwende PD3)
clearBit(PORTD, 3);
m.a.W., um Tristate hinzukriegen mit den 328, muss ich den Pin zwischen
Input und Output-Konfiguration toggeln. Wenn ich das in der Busy Loop
oben mache, kriege ich wieder Murks in den Eingaengen...!! Evtl. dauert
diese Operation zu lange, oder die Eingaenge werden sonstwie elektrisch
korrumpiert.
Gibt es eine bessere Loesung, um einen Pin in den hochohmigen
Ausgangs-Zustand zu versetzen?
Joe G. schrieb:> Ich glaube, der folgene Artikel ist genau richtig für dein Problem:> https://www.mikrocontroller.net/articles/AVR-Tutorial:_IO-Grundlagen
Danke für den Hinweis, allerdings erschließt sich mir die Relevanz nicht
unmittelbar. Willst Du mir damit sagen, dass:
a) ich Assembler verwenden soll,
b) sicherstellen soll, dass ich ein NOP oder eine sonstige
Warte-Operation zwischen Wechsel von Eingang auf Ausgangs-Modus für PD3
benötige (das ist in der Tat neu für mich!)
c) ... ?
Ich weiß über den PullUp-Widerstand Bescheid für Eingänge; im Artikel
kann ich keinen Hinweis darauf entdecken, wie man einen Ausgang zwischen
Hochohmig (nicht verbunden) und Low (GND) toggelt, ohne den Modus
zwischen Input / Output zu toggeln?
Danke und mfG
Michael
Ich wollte eigentlich auf diese Stelle hinweisen.
Vorsicht! In bestimmten Situationen kann es passieren, dass scheinbar
Pins nicht richtig gelesen werden.
Speziell bei der Abfrage von Matrixtastaturen kann der Effekt auftreten,
dass Tasten scheinbar nicht reagieren. Typische Sequenzen sehen dann so
aus:
ldi r16,0x0F
out DDRD,r16 ; oberes Nibble Eingang, unteres Ausgang
ldi r16,0xFE
out PORTD,r16 ; PD0 auf 0 ziehen, PD4..7 Pull ups aktiv
in r17,PIND ; Pins lesen schlägt hier fehl!
Warum ist das problematisch? Nun, der AVR ist ein RISC-Microcontroller,
welcher die meisten Befehle in einem Takt ausführt. Gleichzeitig werden
aber alle Eingangssignale über FlipFlops abgetastet (synchronisiert),
damit sie sauber im AVR zur Verfügung stehen. Dadurch ergibt sich eine
Verzögerung (Latenz) von bis zu 1,5 Takten, mit der auf externe Signale
reagiert werden kann. Die Erklärung dazu findet man im Datenblatt unter
der Überschrift "I/O Ports - Reading the Pin Value".
Michael W. schrieb:> b) sicherstellen soll, dass ich ein NOP oder eine sonstige> Warte-Operation zwischen Wechsel von Eingang auf Ausgangs-Modus für PD3> benötige (das ist in der Tat neu für mich!)
Joe G. schrieb:
> Dadurch ergibt sich eine> Verzögerung (Latenz) von bis zu 1,5 Takten, mit der auf externe Signale> reagiert werden kann. Die Erklärung dazu findet man im Datenblatt unter> der Überschrift "I/O Ports - Reading the Pin Value".
Ja, danke, das habe ich gelesen, dass war mein Punkt b) oben - wie
gesagt, war mir nicht bewusst! Ich werde sehen, dass ich das
berücksichtige. Wahrscheinlich ist der C-Code ohnehin suboptimal genug
um das automatisch zu gewährleisten ;-)
Um es noch mal deutlicher zu sagen - nach dem hilfreichen Hinweis von
Joe
auf mögliche Lesefehler beim Wechseln zwischen Input und Output-Modus
eines
Pins / Ports, ist meine aktuelle Frage also:
gibt es was besseres, um einen Ausgang zwischen hochohmig und 0 (GND) zu
toggeln, als
hochohmig: PIND = 0; (INPUT)
0 (GND): DDRD = 0b00001000; (ich verwende PD3)
clearBit(PORTD, 3);
Gruß und danke,
Michael
Ich möchte mich bei allen für die tatkräftige Unterstützung bedanken!
Auch wenn das Rätsel um die nicht funktionierende ISR-Lösung nie ganz
aufgeklärt worden konnte (ich habe doch ein FlipFlop, verdammt!!), haben
doch eine Reihe von Änderungen dazu geführt, dass ich nun eine 100%
funktionierende Polling-Busy-Loop habe.
Ich möchte die Lösung nicht vorenthalten:
1
intmain(void){
2
3
DDRB=0;
4
DDRC=0b00110000;
5
6
while(1){
7
8
length=0;
9
10
// Status LEDs
11
clearBit(PORTC,4);
12
setBit(PORTC,5);
13
14
do{
15
16
DDRB=0;// enable Z80 READY pin (Tristate not connected)
17
18
do{
19
pinb=PINB;
20
pinc=PINC;
21
while(bit_is_clear(PIND,2));
22
23
do{
24
pinb=PINB;
25
pinc=PINC;
26
}while(bit_is_set(PIND,2));
27
28
pinb&=0b00011110;
29
pinc&=0b00001111;
30
31
//pull Z80 pin ready low
32
DDRB|=1;
33
clearBit(PORTB,0);
34
35
buffer_lo[length]=pinb;
36
buffer_hi[length]=pinc;
37
length++;
38
39
}while(pinb!=26||pinc!=0);
40
41
// Status LEDs
42
43
clearBit(PORTC,5);
44
setBit(PORTC,4);
45
46
// send to Emic-2
47
48
if(length>0){
49
for(inti=0;i<length;i++){
50
buffer_lo[i]=(buffer_lo[i]>>1)|(buffer_hi[i]<<4);
51
}
52
buffer_lo[length]=0;
53
printString(buffer_lo);
54
}
55
56
clearBit(PORTC,4);
57
58
}
59
60
return0;
61
62
}
PB0 ist nun mit dem ~READY-Signal der Z80-Cpu verbunden.
Das Timing scheint selbst mit Flip Flop so delikat zu sein, dass selbst
Code-Fragmente wie
1
loop_until_bit_is_set(PIND,2);
2
//pull ready READY immediately
3
DDRB|=1;
4
clearBit(PORTB,0);
5
6
pinb=PINB;
7
pinc=PINC;
8
9
....
dazu führen, dass pinb, pinc NICHT das korrekte Byte haben, obwohl das
FlipFlop dieses Byte auf unbestimmte Zeit latched. Das mag zu tun haben
mit dem im AVR-Tutorial beschriebenen Effekt, dass die Eingänge intern
FlipFlops sind, und da der Select-Puls (PIND, 2) extrem kurz ist (1,8
uS), dass dann evtl. durch Clock-Versatz das Byte in den PINB, PINC
Ports entweder der Input / das Byte vor oder nach dem Clock-Signal
anliegt, mit einer evtl. Differenz von 1.5 Takten. Dabei ist es
unerheblich, ob das externe FlipFlop den Wert auf unbestimmte Zeit
speichert... der Versatz tritt ja trotdem auf bzgl. des Select-Signals
(PIND, D). Das macht die Synchronisation mit C fast unmöglich, selbst
wenn man ein FlipFlop hat - man weiß nie, ob man nun das Byte 1,5 Takte
zu früh oder zu spät gelesen hat. Zumindest ist das meine Erkärung,
warum diese Lösung zu 100 % funktioniert:
1
2
do{
3
pinb=PINB;
4
pinc=PINC;
5
}while(bit_is_clear(PIND,2));
6
7
do{
8
pinb=PINB;
9
pinc=PINC;
10
}while(bit_is_set(PIND,2));
11
12
pinb&=0b00011110;
13
pinc&=0b00001111;
14
15
//pull ready low immediately
16
DDRB|=1;
17
clearBit(PORTB,0);
Vielen Dank an Alle - ich muss sagen, dass ich noch nie so viel "Trial
and Error" für ein Projekt gebraucht habe, wie für dieses....
... ich wollte mich in dieser Sache noch einmal abschliessend melden...
so wie es aussieht, ist all die Diskusion um Timing- und
Port-Lese-Probleme etc. Makulatur. So wie es jetzt aussieht, war all
dies durch ein Problem in der Adress-Dekodierungs-Logik verursacht - ein
falsch beschaltetes UND-Gatter; das C-Programm oben war also ein
Workaround (bzw. Red Herring).
Inzwischen ist dieses Problem behoben, und der DIY CPC-Sprachsynthesizer
hat seine endgueltige Form erreicht ("LambdaSpeak").
https://youtu.be/ckCTHi5_2f8
Falls jemand den Amstrad / Schneider SSA-1 Sprachsynthesizer kennt -
dieser kann ebenfalls emuliert werden durch LambdaSpeak:
https://youtu.be/PJCfPbCQvmohttps://youtu.be/WbvsnRWKF0shttps://youtu.be/aTxufTKfrYk
An dieser Stelle vielen Dank an alle fuer die Hilfe!!
Gruss,
Michael
Michael W. schrieb:> ... ich wollte mich in dieser Sache noch einmal abschliessend melden...> so wie es aussieht, ist all die Diskusion um Timing- und> Port-Lese-Probleme etc. Makulatur.
Kannst du mal testweise einen String schnellstmöglich an deinen
Synthesizer schicken (also mehrfaches LDI), und schauen, ob dein AVR
hinterherkommt?
Deine Schaltung präsentiert dem AVR ja das zuletzt gesendete Byte, bis
ein neues Byte reinkommt. Wenn sich die Z80-Software also genug Zeit
nimmt, passt das - interessant ist es aber, ob der AVR die Daten auch im
worst case wegspeichern kann.
S. R. schrieb:> Deine Schaltung präsentiert dem AVR ja das zuletzt gesendete Byte, bis> ein neues Byte reinkommt. Wenn sich die Z80-Software also genug Zeit> nimmt, passt das - interessant ist es aber, ob der AVR die Daten auch im> worst case wegspeichern kann.
Ja, gutes Experiment! Kann ich mal machen. In BASIC braucht man keine
Warteschleife einbauen, da kommt der AVR locker mit. Zudem ist der AVR
momentan gerade so programmiert, dass er den Z80 anhaelt, bis das Byte
weggepuffert wurde, und er wieder empfangsbereit ist. So kann also
nichts verloren gehen. Buffer-Ueberlauf wird so ebenfalls verhindert
(Buffer wird gesprochen, wenn er voll ist). Das funktioniert natuerlich
ebenfalls in Maschinensprache.
Fuer einen Bandbreiten-Test kann ich diese Warte-Logik mal rausnehmen
und dann schauen.
Im SSA-1-Kompatibilitaetsmodus gibt es eine etwas andere Loesung, ich
wollte ja (weitgehend) kompatibel zum SSA-1 Sprachsynthesizer sein -
hier fragt die Z80-Software den IO-Port per Polling ab, der SPO256 hat
dazu 2 Signale / Pins (BUSY and READY o.ae.) Also ohne dass der Z80
angehalten wird.
Michael W. schrieb:> Zudem ist der AVR> momentan gerade so programmiert, dass er den Z80 anhaelt, bis das Byte> weggepuffert wurde, und er wieder empfangsbereit ist.
Wie machst du das? Ziehst du /WAIT, wie wir das im Thread vorher
diskutiert hatten?
Michael W. schrieb:> hier fragt die Z80-Software den IO-Port per Polling ab,
Okay, wenn du ein langsames Device nachbildest, muss deine Nachbildung
auch nicht unbedingt schneller sein. :-)
S. R. schrieb:> Michael W. schrieb:>> Zudem ist der AVR>> momentan gerade so programmiert, dass er den Z80 anhaelt, bis das Byte>> weggepuffert wurde, und er wieder empfangsbereit ist.>> Wie machst du das? Ziehst du /WAIT, wie wir das im Thread vorher> diskutiert hatten?
Genau :-)