Source file: /~heha/ewa/Reluktanzmotor/maweig-Motor-190927.zip/amc1210spi.cpp

/* 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-80