/* SPI-Kommunikation zum AMC1210 (nur eine CPU, da nur 1 SPI-Port verfügbar
Komischer IC: Erfordert zusätzliche Taktflanke bei f > 25 MHz.
Maximaler SPI-Takt: 40 MHz.
Da sich die dann verschiedenen Taktflanken zum Schreiben und Lesen per SPI-
Hardware nicht realisieren lassen, bleibt die Taktfrequenz auf 25 MHz begrenzt.
Der sog. Low-Speed-Takt ist in main.cpp auf 200 MHz festgelegt.
(Idee: Wenn man schon die Bitzahl umschaltet, dann auch — bei Empfang — die
Empfangsflanke umschalten, 1 Dummy-Bit senden und Daten mit 40 MHz reinholen.)
SPI ist immer bidirektional; prinzipbedingt kommen immer genauso viele Bits
zurück wie man sendet.
Will man nur senden, verwirft man das Leseergebnis (mit nullptr),
will man nur empfangen, schickt man irgendetwas; nullptr schickt Nullen.
*/
#include "Settings.h"
#define regs SpibRegs
inline uint8_t sendadr(uint8_t adr) {
regs.SPITXBUF = adr<<8;
while (!regs.SPIFFRX.bit.RXFFST); // warten bis fertig (40 CPU-Takte)
return regs.SPIRXBUF; // auslesen
}
static uint16_t*rx_ptr; // nullptr = Aktion war Schreiben
// Der Umgang mit der 8-Bit-Adresse und den 16-Bit-Daten erfolgt derart,
// dass die Transferbitzahl „mittendrin“ umgeschaltet wird.
// Das erfordert Software-Eingriff, nutzt aber die FIFO maximal aus.
void Amc1210::send(uint8_t adr, const uint16_t*tx, size_t count) {
rx_ptr=0;
regs.SPICCR.all = bit(7) | bit(5) | 7;// 8 Bit, High-Speed
GpioDataRegs.GPCCLEAR.all = bit(2); // GPIO66 = Low
sendadr(adr); // Adresse senden, Ergebnis verwerfen
regs.SPICCR.all = bit(7) | bit(5) |15;// 16 Bit, High-Speed
regs.SPIFFRX.all = bit(13) | count; // Länge merken
do regs.SPITXBUF = *tx++; // alles raus, maximal 16 WORDs
while(--count);
}
// Dasselbe zum Lesen, nur dass hier 1 Taktbit mehr gesendet wird,
// um mit verzögerten Taktflanken effektiv an der /steigenden/ Flanke
// die Datenbits einzulesen. Erforderlich für AM1210 und SPI-Takt > 25 MHz.
void Amc1210::recv(uint8_t adr, uint16_t*rx, size_t count) {
rx_ptr=rx;
regs.SPICCR.all = bit(7) | bit(5) | 8;// 9 Bit, High-Speed
GpioDataRegs.GPCCLEAR.all = bit(2); // GPIO66 = Low
sendadr(adr|0x80); // Adresse senden, Ergebnis verwerfen
regs.SPICCR.all = bit(7) | bit(5) |15;// 16 Bit, High-Speed
regs.SPICTL.all =bit(3)|bit(2)|bit(1);// CLK_PHASE: Taktausgabe-Phase verschieben
regs.SPIFFRX.all = bit(13) | count; // Länge merken
do regs.SPITXBUF = 0; // TxFIFO mit Nullen füllen
while(--count);
}
// „Bottom Half“ des Transfers: Wartet bis Empfangs-FIFO genügend aufgefüllt
// und (raub)kopiert dann die Daten nach *rx
void Amc1210::wait() {
while (!regs.SPIFFRX.bit.RXFFINT); // warten (80 × Datenworte CPU-Takte)
GpioDataRegs.GPCSET.all = bit(2); // GPIO66 = High
if (rx_ptr) {
regs.SPICTL.all = bit(2) | bit(1); // CLK_PHASE: Taktverschiebung rückgängig
uint16_t*rx=rx_ptr;
do *rx++=regs.SPIRXBUF; // RxFIFO ausräumen („Raubkopie“)
while(regs.SPIFFRX.bit.RXFFST); // bis RxFIFO leer
}else{
regs.SPIFFRX.bit.RXFIFORESET = 0; // RxFIFO leeren
regs.SPIFFRX.bit.RXFIFORESET = 1;
}
}
// Für diesen Fall (1 Datenwort) ginge es auch mit 2 12-Bit-Transfers.
void Amc1210::send(uint8_t adr, uint16_t tx) {
send(adr,&tx);
wait();
}
uint16_t Amc1210::recv(uint8_t adr) {
uint16_t rx;
recv(adr,&rx);
wait();
return rx;
}
void Amc1210::init() {
// GPIO-Multiplexer einstellen (Aufrufer macht EALLOW)
GpioCtrlRegs.GPBGMUX2.all |= 0b11UL<<30;
GpioCtrlRegs.GPBMUX2 .all |= 0b11UL<<30; // GPIO63 -> SPISIMOB
GpioCtrlRegs.GPCGMUX1.all |= 0b11UL<<0 | 0b11UL<<2; // GPIO64 -> SPISOMIB,
GpioCtrlRegs.GPCMUX1 .all |= 0b11UL<<0 | 0b11UL<<2; // GPIO65 -> SPICLKB
GpioCtrlRegs.GPCQSEL1.all |= 0b11UL<<0; // GPIO64 = SOMI -> asynchron
GpioCtrlRegs.GPCDIR.bit.GPIO66 |= 1; // GPIO66 -> !CS
// SPI konfigurieren
regs.SPICTL.all = bit(2) | bit(1); // Master, niemals Tristate
regs.SPIBRR.all = 4; // schnellstmöglich: 200 MHz ÷ 5 = 40 MHz
regs.SPIFFTX.all = bit(15)|bit(14)|bit(13); // mit FIFO, kein TxFIFOreset
regs.SPIPRI.all = bit(4); // auch bei DebugBreak
// Initialisierungswerte aus Excel-Datei „Filter_Einstellungen_AMC1210.xlsx“
for (int i=0; i<4; i++) {
static const uint16_t inits[]={
0x0010, // Pin CLKx ist Output
0x0FFF, // Sinc³ mit Oversampling OSR = 256
0x4800, // 16 Bit shr 9 mit Oversampling 1
0x7FFF, // Oberer Schwellwert (= Vorgabe)
0x0000, // Unterer Schwellwert (= Vorgabe)
0x03FF, // Komparator: Int-Flags enabled, Sinc³, COSR = 32
};
send(1+i*6,inits,elemof(inits));
wait(); // für alle 4 Kanäle dasselbe
}
send(0x1B,0x0800); // Master Filter Enable, kein Taktteiler
send(0x19,0x2000); // Master Interrupt Enable
}
Detected encoding: UTF-8 | 0
|