/* 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.
Der sog. Low-Speed-Takt ist in main.cpp auf 200 MHz festgelegt.
Das Umschalten der Bitzahl von 8 auf 16 erfolgt „inmitten des Transfers“
nach dem Senden des Adressbytes.
Beim Empfang wird mittels 9-Bit-Senden der Adresse ein Dummy-Bit eingeschoben
und der Ausgabetakt zum Lesen 1/2 Takt verzögert,
sodass an der steigenden Takt(vorder)flanke eingelesen wird.
SPI ist immer bidirektional; prinzipbedingt kommen immer genauso viele Bits
zurück wie man sendet. Der AMC1210 macht davon aber keinen Gebrauch.
*/
#include "Settings.h"
#include "regdef2.h"
static volatile SPI_REGS®s=SpiaRegs; // vorzugsweise SpibRegs
static const uint8_t CS_PIN = 61; // vorzugsweise 66
static const float F_SPI = 40E6; // SPI-Takt in Hz
// 191014: Mit 30 oder 40 MHz funktioniert die Datenübertragung
// nicht mehr zuverlässig; irgendetwas verschluckt Bits.
// Die Schuldfrage muss mit Oszi geklärt werden.
// 191017: Schuldfrage geklärt: Bei Verlegung der 20-MHz-Quarzleitung auf dem
// Daughterboard über einen DIL-Schalter verschlechterte sich das Gesamtbild rapide:
// SPI-Lesefehler auch bei 20 MHz SPI-Takt, dadurch „falsche“ Messwerte.
// Hingegen der mühselig programmierte 20-MHz-Ausgang am ePWM-Anschluss GPIO16
// lässt alles auch bei 40 MHz fehlerfrei funktionieren,
// über den gleichen DIL-Schalter.
// Maximilian hatte immer schon den 20-MHz-Quarzoszillator (im Blechkasten)
// im Verdacht, dass dieser heftig Störungen verursacht.
inline void sendadr(uint8_t adr) {
regs.SPITXBUF = adr<<8;
while (!regs.SPIFFRX.bit.RXFFST); // warten bis fertig (40 CPU-Takte)
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 void*tx, unsigned count) {
rx_ptr=0;
regs.SPICCR.all = bit(7) | bit(5) | 7;// 8 Bit, High-Speed
GpioDataRegsA.doCLEAR(CS_PIN); // GPIO66 = Low
sendadr(adr); // Adresse senden
regs.SPICCR.all = bit(7) | bit(5) |15;// 16 Bit, High-Speed
regs.SPIFFRX.all=bit(13)|bit(6)|count;// Länge merken, Interruptflag löschen
const uint16_t*txp=reinterpret_cast<const uint16_t*>(tx);
do regs.SPITXBUF = *txp++; // 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, void*rx, unsigned count) {
rx_ptr=reinterpret_cast<uint16_t*>(rx);
regs.SPICCR.all = F_SPI>25E6 ? bit(7)|bit(5)|8 : bit(7)|bit(5)|7;// 8 oder 9 Bit, High-Speed
GpioDataRegsA.doCLEAR(CS_PIN); // GPIO66 = Low
sendadr(adr|0x80); // Adresse senden mit gesetztem Lese-Bit
regs.SPICCR.all = bit(7) | bit(5) |15;// 16 Bit, High-Speed
if (F_SPI>25E6)
regs.SPICTL.all=bit(3)|bit(2)|bit(1);// CLK_PHASE: Taktausgabe-Phase verschieben
regs.SPIFFRX.all=bit(13)|bit(6)|count;// Länge merken, Interruptflag löschen
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)
GpioDataRegsA.doSET(CS_PIN); // GPIO66 = High
if (rx_ptr) {
if (F_SPI>25E6)
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;
}
}
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::InitAll() {
// GPIO-Multiplexer einstellen (Aufrufer macht EALLOW)
if (®s==&SpibRegs) {
configGpio(63,15,3); // SPISIMOB
configGpio(64,15,3); // SPISOMIB
configGpio(65,15,3); // SPICLKB
}else{
configGpio(58,15,3); // SPISIMOA Master Out
configGpio(59,15,3); // SPISOMIA Master In
configGpio(60,15,3); // SPICLKA Clock Out
}
configGpioOut(CS_PIN); // !CS
// SPI konfigurieren
regs.SPICTL.all = bit(2) | bit(1); // Master, niemals Tristate
regs.SPIBRR.all = F_LSP/F_SPI-0.01; // 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: ASCII (7 bit) | 8
|