So verwenden Sie ATmega328 SPI mit einem 31-Bit-Schieberegister

Ich verwende einen Allegro 6280 PWM LED-Treiber für ein Projekt. Dies ist ein cooler kleiner IC, abgesehen von der Tatsache, dass es sich um ein 31 Bit langes Eingangsregister handelt. Dies ist kein Problem, wenn ich einfach meine 31 Bit durchschleife und die Pin-Zustände entsprechend einstelle, aber ich möchte den On-Board-SPI des Atmega verwenden, um die Dinge etwas zu beschleunigen (der 6280 benötigt eine externe Taktquelle für seine 10 Bit). langer PWM-Zyklus).

Das 31 Bit lange Register sieht in meiner Anwendung so aus, wobei R,G & B die 10-Bit-PWM-Werte für die angeschlossenen LEDs angeben.

0BBBBBBBBBBGGGGGGGGGGRRRRRRRRRR

Diese Schieberegister sind verkettbar, daher habe ich eine verknüpfte Liste erstellt, um sie darzustellen:

struct Pixel {
  uint16_t red;
  uint16_t green;
  uint16_t blue;
  Pixel *next;
};

Die Idee ist also, den Latch-Pin auf Low zu setzen, alle Pixel zu durchlaufen und dann nach dem Senden des letzten den Latch auf High zu setzen, um die Daten in jedem Register zu speichern. Dies scheint einfach zu sein, außer dass ich aufgrund der ungleichmäßigen Länge des Registers einen hässlich aussehenden Code konstruieren muss, um wieder zum Anfang zu schleifen, damit er niemals leere Bits sendet. Außerdem muss der Latch möglicherweise erfolgen, nachdem eine beliebige Anzahl von Bits übertragen wurde, nicht nur das letzte.

Hat jemand eine Idee, wie man das hinbekommt, oder belle ich einfach den falschen Baum und stecke mit dem manuellen Drehen der Pin-Zustände fest?

Danke.

Antworten (2)

Ich habe diese Frage beobachtet, und da noch niemand geantwortet hat, werde ich es versuchen. Ich entschuldige mich im Voraus, wenn meine Argumentation fehlerhaft ist, aber ich werde mein Bestes tun.

Ich denke, die Möglichkeit, Ihr Projekt zum Laufen zu bringen, hängt davon ab, wie viele RGB-LEDs Sie verwenden möchten. Ich habe das Allegro-Datenblatt durchgesehen, und es sieht so aus, als müssten Sie einen IC mit jeweils einer roten, grünen und blauen LED (dh einem RGB-Pixel) koppeln. Alle Daten werden über die Kette der 6280er übertragen.

Wenn Sie nur ein RGB-Pixel oder genauer gesagt N Pixel haben möchten, die dieselben zeitlichen Informationen anzeigen, können Sie meiner Meinung nach mit der Verwendung von SPI davonkommen. Ich denke, Ihre Idee mit verknüpften Listen ist der richtige Weg, aber offensichtlich liegt der Schlüssel darin, LI zur richtigen Zeit (alle 31 Bits) zu verriegeln, und es wird nicht an 8-Bit-Grenzen liegen. Die einzigen SPI-Bibliotheken, die ich je auf Mikros verwendet habe, nehmen ein Byte, takten die Daten aus und lesen das Antwortbyte. In Ihrem Fall müssen Sie herausfinden, wie Sie eine SPI-Funktion erstellen, die LI während einer Byteübertragung auslösen kann.

Da SPI die Übertragung von jeweils einem Byte erfordert, müssen Sie 32 Bit senden, wenn Sie nur 31 möchten. Das 32. Bit ist also tatsächlich das erste Bit Ihres zweiten Satzes von RGB-Daten. Sie setzen LI, bevor Sie das 32. Bit austakten. Für die nächste Datenrunde triggern Sie LI, bevor Sie das 31. Bit austakten. Möglicherweise können Sie dies tun, ohne eine eigene SPI-Bibliothek zu schreiben, aber ich bin mir nicht sicher.

Wenn Sie das Scrollen von Daten unterstützen möchten, sieht es so aus, als wären die Dinge etwas kniffliger. Es fällt mir schwer, die Erklärung zu formulieren, aber das Timing von LI wird interessant sein. LI bestimmt, wie schnell Ihr Display scrollt, da es vorgibt, wann ein 6280 die Daten an den nächsten ausgibt.

Für den ersten Satz RGB-Daten triggern Sie LI, sobald Sie 31 Bit ausgetaktet haben. Sie haben auch 1 Bit für den nächsten Datensatz getaktet. Sie werden dann weitere 3 Datenbytes plus 6 weitere austakten, bevor Sie LI auslösen. Wenn Ihre Bildlaufzeit jedoch langsam ist, können Sie das Byte mit den letzten 6 Bits nicht senden, bis Sie bereit sind, LI auszulösen , da Sie den Schieberegisterpuffer nicht überlaufen lassen möchten! Aber sobald Sie bereit sind, LI auszulösen, senden Sie dieses Byte. Jetzt sind bereits 2 Bits für den nächsten Datensatz im seriellen Puffer. Senden Sie weitere 3 Bytes (insgesamt 26 Bit) und warten Sie, bis Sie LI auslösen müssen. Wenn es soweit ist, sende das nächste Byte (das die verbleibenden 5 Bits enthält). Wiederholen Sie dies immer wieder.

Ich hoffe das macht Sinn. Wie gesagt, ich habe keine Erfahrung mit diesem Chip, aber was ich hier geschrieben habe, ist zumindest der erste Schritt, den ich unternehmen würde, um zu versuchen, dieses Problem mit SPI zu lösen.

Viel Glück und halte uns auf dem Laufenden!

Hallo Dave. Dank dafür. Sie haben ziemlich genau zusammengefasst, wo ich mit dem Problem bin. Der Kern des Problems scheint für mich zu sein, dass Sie Ihr Byte in SPDR ablegen und dann darauf warten, dass SPSR und SPIF anzeigen, dass die Übertragung abgeschlossen ist. Soweit ich weiß, ist dies die einzige Auflösung, die Sie erhalten.
@jamesotron: Ich verstehe. Mein einziger anderer Vorschlag wäre gewesen, Daten an einen Eingangspin zu binden und dann jederzeit einen ISR-Umschalter LI zu haben counter % 31 == 0. Aber wenn Sie diesen Ärger durchmachen, macht das den Sinn der Verwendung von SPI zunichte - Sie könnten genauso gut einen Ausgangspin bitbangen. Ich verstehe eigentlich nicht, warum SPI praktischer ist. Sie sind gezwungen, durch Reifen zu springen, die am Ende zu weniger überschaubarem Code führen ... und Sie haben durch die Verwendung von SPI keine I/O gewonnen.
Hauptsächlich versuche ich, durch die Verwendung von SPI einen Geschwindigkeitsgewinn zu erzielen, da die MCU die Takterzeugung und andere Dinge schneller handhabt, als ich es in der Software tun kann.
@jamesotron, aber es ist nicht so, als müssten die Dinge genau getaktet sein. Das Schöne an so etwas wie SPI ist, dass die Daten durch Flankenübergänge getaktet werden, aber es ist nicht wie bei RS232, wo das Timing präzise sein muss. Sie sollten in der Lage sein, die 31 Bits so schnell wie möglich auszutakten. Setze LI zurück, schleife jedes Bit durch und strobe CI jedes Mal, setze LI. Vielleicht übersehe ich etwas, aber es scheint, dass ein solcher Ansatz gut funktionieren wird.
Okay. Ich hatte eine Idee. Da es sich um ein Schieberegister handelt, kann ich im Wesentlichen nur weiter in Nullen verschieben, um die Uhr anzutreiben, solange ich nur verriegele, wenn ich tatsächlich Daten sende. Wenn ich einen Puffer erstelle, der lang genug ist, um so viele Register wie möglich in der Kette aufzunehmen, und sicherstelle, dass die Daten an der rechten Bytegrenze des letzten Bytes ausgerichtet sind, kann ich diese Bytes herausschieben und am Ende zwischenspeichern und dann einfach weitermachen Nullen verschieben - das ist zumindest meine Vermutung. Ich denke, ich werde es heute Abend testen.

Wie sich herausstellt, besteht die Möglichkeit, dies zu tun, einfach darin, das Schieberegister mit mehr Bits als nötig zu überfluten und sicherzustellen, dass die 31 wichtigen die LETZTEN sind. Ich erstelle einen Puffer, der lang genug ist, um 32 Bit aufzunehmen:

static uint8_t[BUFFER_SIZE] spiBuffer;

Ich fülle es immer dann auf, wenn es eine Änderung gibt, und verwende das Interrupt-gesteuerte SPI, um es zu übertragen:

void transmitSpiBuffer() {
  static uint8_t c = 0;
  SPDR=spiBuffer[c++];
  if (c == (BUFFER_SIZE)) {
    c = 0;
    digitalWrite(LATCH_PIN, HIGH);
    // should I sleep? nah.
    digitalWrite(LATCH_PIN, LOW);
  }
}

void setup() {
   // Enable MOSI and SCK as output, all others input.
   PORTB = (1<<PINB3)|(1<<PINB5);

   ISR(SPI_STC_vect) {
     transmitSpiBuffer();
   }

   // Enable SPI
   SPCR = (1<<SPIE)|(1<<SPE)|(1<<MSTR)|(1<<SPR1)|(1<<SPR0);
}

Das funktioniert bei mir hervorragend. Problem gelöst.