Ich habe ein 8-Bit Parallel in Serial Out (PISO) Schieberegister in VHDL auf meinem Max V CPLD implementiert. Ich verwende SPI als Schnittstelle zum CPLD mit meinem AVR. Die Schaltung funktioniert aber nur teilweise. Angenommen, ich habe einen Eingang, der nur ein gesetztes Bit enthält, dh 00010000 oder 10000000 usw. In diesem Fall liest der AVR die Daten korrekt ein.
Wenn die Eingabe in das PISO-Register jedoch 10100000 ist, liest der AVR die Daten als 11100000 ein. Dies spiegelt sich auch in meinem Oszilloskop wider. Zur Veranschaulichung habe ich das Bild angehängt.
Die obere Wellenform ist die serielle Ausgabe von meinem Schieberegister und die untere Wellenform ist die Uhr. Sollte der serielle Ausgang nicht im 2. Taktzyklus 0 werden?
Wenn die Daten 10000001 sind, werden sie als 10000011 eingelesen. Was könnte dies verursachen? Da ich dachte, es könnte mein VHDL-Code sein, habe ich sogar die Codes anderer Leute aus dem Internet kopiert, um zu sehen, ob es funktioniert, aber leider bekomme ich die gleichen Ergebnisse.
Das Folgende ist der Code für meinen AVR
int main(void)
{
DDRB = (1 << DD_MOSI) | (1 << DD_SCK) | (1 << 2) | (1 << 0);
SPCR = (1 << SPE) | (1 << MSTR) | (1 << SPR0) | (1 << SPR1);
DDRD = 0xFF;
PORTD = 0x00;
char datain;
while(1)
{
PORTB = 1; // This loads the shift register.
_delay_ms(10);
PORTB = 0x00;
_delay_ms(10);
SPDR = 0b01011101;
while(!(SPSR & (1 << SPIF)));
datain = SPDR;
_delay_ms(10);
PORTD = datain;
}
return 1;
}
Und das ist der VHDL-Code, den ich verwende. Beachten Sie, dass ich die Eingabe absichtlich weggelassen habe. Ich codiere den Eingang fest, da ich dann nicht mit zusätzlichen Drähten herumspielen muss. Was ich gehofft hatte, würde weniger Raum für Fehler bieten.
library ieee;
use ieee.std_logic_1164.all;
entity PISO is
port(C, CS_N : in std_logic;
--D : in std_logic_vector(7 downto 0);
SO : out std_logic);
end PISO;
architecture archi of PISO is
signal tmp: std_logic_vector(7 downto 0);
begin
process (C, CS_N)
begin
if CS_N = '1' then
tmp <= "10000001";
elsif rising_edge(C) then
tmp <= tmp(tmp'high - 1 downto tmp'low) & '0';
end if;
end process;
SO <= tmp(7);
end archi;
Beachten Sie, dass VHDL/CPLDs für mich ziemlich neu sind und ich immer wieder auf etwas in Bezug auf Timings stoße und wie kritisch sie sind. Ich habe versucht, sie zu lesen, aber ich scheine es nicht ganz hinbekommen zu haben. Ich verstehe Gate-Verzögerungen usw., aber wie soll ich das CPLD so einrichten, dass dies kein Problem darstellt? Meine Uhr geht nicht sehr schnell. Ich betreibe es mit der niedrigstmöglichen Frequenz, dh 62,5 kHz. Könnte dieser Fehler etwas mit Steckbrettern zu tun haben? Mir sind die Photoresist-Kupferkaschierungen ausgegangen, daher kann ich noch keine Leiterplatte herstellen. Der Max V befindet sich auf einem Entwicklungsboard.
Ich freue mich über jede Art von Hinweisen zur Lösung dieses Problems. Ich habe es in den letzten 8 Stunden oder so vergeblich versucht.
HINZUGEFÜGTE INFO: Als ich das CPLD programmierte, testete ich es zunächst über die Tasten auf meiner Entwicklungsplatine. Das zeigte überhaupt keine Probleme und das Register spuckte Bits seriell gut aus. Nicht nur das, ich habe es sogar über den AVR getaktet. Das hat auch super funktioniert. Erst nachdem ich bestätigt hatte, dass es in Ordnung war, begann ich mit der Arbeit an der SPI-Schnittstelle, bei der die Dinge schief liefen.
Neue Wellenformen:
Ok, los geht's. Bei allen Screenshots war die Seriennummer gleich (10000001). Ich habe eine leichte Verbesserung vorgenommen, sodass 10000001 nicht mehr als 10000011 ausgegeben wird, sondern 10000010 ist. Als ich vorher debuggte, habe ich versehentlich das CPHA-Bit auf 1 gesetzt. Als ich es auf 0 zurücksetzte, waren die Daten 100000010 anstelle von 10000011 Natürlich immer noch nicht richtig. Hier sind die Wellenformen aus dem Oszilloskop.
Obere Wellenform: Serial Out
Untere Wellenform: Uhr. Ausgelöst bei steigender Flanke.
Obere Wellenform: Chip Select (Active Low)
Untere Wellenform: Uhr. Ausgelöst bei steigender Flanke.
Obere Wellenform: Chip Select
Untere Wellenform: Uhr. Ausgelöst bei fallender Flanke.
Obere Wellenform: Serial Out.
Untere Wellenform: Uhr. Ausgelöst bei fallender Flanke.
EDIT: Vergessen hinzuzufügen, das ist jetzt mein bereinigter AVR-Code.
while(1)
{
bit_clear(PORTB,BIT(CS)); // Low Chip Select
SPDR = 0xFF;
while(!(SPSR & (1 << SPIF)));
datain = SPDR;
PORTD = datain;
bit_set(PORTB,BIT(CS)); // High Chip select
_delay_us(100); // Keep high for 100us. This made it easier to see it on o-scope.
}
Schauen wir uns Ihren VHDL-Code an. Bitte denken Sie daran, dass ich nicht versuche, kritisch oder schroff zu sein – ich versuche nur, Sie schnell auf den neuesten Stand zu bringen. Ihr Code ist derzeit dieser:
process (C, ALOAD)
begin
if (ALOAD='1') then
tmp <= "10000001";
elsif (C'event and C='1') then
tmp <= tmp(6 downto 0) & '0';
SO <= tmp(7);
end if;
end process;
Sollte aber so aussehen:
process (C, ALOAD)
begin
if ALOAD='1' then
tmp <= "10000001";
elsif rising_edge(C) then
tmp <= tmp(tmp'high-1 downto tmp'low) & "0"; -- '0' and "0" are equivalent in this context
end if;
end process;
SO <= tmp(tmp'high);
Die Änderungen, die ich vorgenommen habe, sind sicherlich geringfügig, aber wichtig.
Das erste ist meine Verwendung von "rising_edge()". Dies ist etwas Neues für VHDL und wird in einigen VHDL-Büchern nicht behandelt. Dies wird als besser angesehen als die Verwendung von „event. Ebenso gibt es eine fallende_Kante().
Die nächste Sache ist meine Verwendung von „hoch“ und „niedrig“, anstatt die Werte fest zu codieren. Dies spielt für diesen Code wirklich keine Rolle, aber wenn Sie anfangen, größere Dinge zu tun, werden Ihnen diese sehr helfen. Zum Beispiel könnten Sie einfach die Definition von tmp größer machen und der Rest des Codes passt sich einfach automatisch an (mit Ausnahme der Initialisierung auf "10000001").
Ich sollte auch darauf hinweisen, dass von einem asynchronen Zurücksetzen oder Laden in FPGAs abgeraten wird, aber in CPLDs in Ordnung ist.
Beachten Sie auch, dass ich die Zuweisung von SO nach außerhalb des Prozesses verschoben habe. Dies kann oder kann nicht das sein, was Sie beabsichtigt haben. So, wie Sie es haben, gibt es ein zusätzliches Flip-Flop, das von tmp(7) nach SO geht. Normalerweise ist dies bei SPI nicht erwünscht, da die SPI-Uhr am Ende der Übertragung verschwinden könnte und Sie das letzte Bit nie herausbekommen. Auf der anderen Seite bekommen Sie mit der Art und Weise, wie ich es gemacht habe, das erste Bit heraus, wenn ALOAD='1', nicht an der steigenden Flanke von C.
Leider beantwortet dies nicht wirklich Ihre Frage, warum Sie schlechte Daten in den AVR bekommen. Ihre Frage enthält einfach nicht genügend Informationen. Hier ist die Art von Dingen, die ich mir ansehen oder über die ich mir Sorgen machen würde:
Ihr O-Scope-Bild zeigt keine anderen Spi-Signale wie CS. CS ist entscheidend für das Verständnis, wohin die Bits gehen sollen. Auch zu wissen, was der SPI-Modus ist, wird dabei helfen.
Auf dem O-Scope-Bild ist der erste Taktzyklus, bei dem SO = '1' ist, NICHT das erste Bit der SPI-Übertragung. Sie haben das Schieberegister 10-20 ms davor geladen, und Ihre Taktperiode beträgt ungefähr 20 uS. Sie hatten also mindestens 1 Taktzyklus vor SO='1' und wahrscheinlich mehr. Hier gehen also einige seltsame Dinge vor – wir haben nicht genügend Informationen, um das Verhalten zu verstehen.
Sie verwenden ALOAD, um das Schieberegister zu laden, aber normalerweise würden Sie CS_N (aktiv niedrig) verwenden. Während CS_N = '1' führen Sie ein asynchrones Laden des Schieberegisters durch, während CS_N = '0' Sie es herausschieben. Die Verwendung von ALOAD wie hier ist in Ordnung, aber wahrscheinlich nicht das, was Sie wollten, und funktioniert nicht mit scheinbar seltsamen SPI-Uhren (vom vorherigen Punkt).
Also, hier ist, was Sie tun sollten ...
Bereinigen Sie das VHDL ein wenig. Reposte die aktualisierte Version. Da Ihr Oszilloskop nur 2 Kanäle hat, schließen Sie einen Kanal an CS_N und den anderen Kanal an CLK an. Trigger auf die fallende Flanke von CLK. Erfassen Sie eine Wellenform, die 2 Takte vor der fallenden Flanke von CLK und 5 Takte danach zeigt. Entfernen Sie CLK, ohne die Einstellungen am Oszilloskop zu ändern , und setzen Sie die Sonde auf SO. Nehmen Sie ein weiteres Bild auf. Wiederholen Sie dies für die ansteigende Flanke von CLK. Also insgesamt 4 Wellenformbilder.
Wenn Sie das tun, können wir neu bewerten, was falsch sein könnte.
Bearbeiten: Aktualisiert, um die aktualisierte Frage widerzuspiegeln.
Ich sehe zwei Probleme: Erstens, wenn der AVR an der steigenden clk-Flanke abtastet, sollten Sie Ihr Schieberegister von der fallenden Flanke abtakten. Wie Supercat erwähnt, erhalten Sie dadurch +/- 0,5 Taktperioden Setup & Hold, die in Ihren AVR gehen.
Und zweitens: Wie Sie bereits erwähnt haben, erhalten Sie 10000010 anstelle von 10000001. Ich glaube nicht, dass Ihr VHDL-Code in diesem Fall fehlerhaft ist, aber er kommt offensichtlich falsch aus dem CPLD. Wenn ich raten müsste, würde ich vermuten, dass das Problem mit einigen Signalintegritätsproblemen bei Ihrem CLK zusammenhängt. Mit Ihrem Oszilloskop ist das schwer zu sagen, aber es sieht so aus, als hätten Sie bei diesem Signal mehr als ein Volt Über- und Unterschwingen (und damit würde viel Klingeln einhergehen). Dieses Klingeln könnte, wenn es schlimm genug ist, dazu führen, dass das CPLD "doppelt taktet" - was bedeutet, dass das Schieberegister zweimal für eine einzelne Taktflanke ausgeführt wird. Und wenn es _wirklich_schlecht_ ist, könnte es dazu führen, dass das CPLD hängen bleibt und buchstäblich explodiert (ich habe es gesehen).
Hier sind einige Experimente zum Ausprobieren:
Verwenden Sie anstelle von 10000001 10101010 oder 01010101. Dadurch können Sie sehen, welches Bit doppelt getaktet wird und ob es immer dasselbe Bit ist.
Zoomen Sie mit dem Zielfernrohr auf die Ränder der Uhr. Stellen Sie sicher, dass sich Ihre Oszilloskopsonden dabei am CPLD und nicht am AVR befinden. Ja, es macht einen RIESIGEN Unterschied.
Unter der Annahme, dass die Uhr über-/unterschritten wird und klingelt, besteht die Lösung darin, einen ordnungsgemäßen Signalabschluss auf der Leitung hinzuzufügen. Ich würde mit einem 50 Ohm Vorwiderstand am AVR anfangen. Hinweis: Dadurch werden die Taktflanken verlangsamt, aber da Sie das CPLD auf der fallenden Flanke takten, haben Sie viel Zeit zur Verfügung.
Wie läuft die Uhr von AVR zu CPLD? Ein langer Draht zwischen Leiterplatten? Das wäre meine Vermutung.
darron
Olin Lathrop