Source file: /~heha/ewa/Reluktanzmotor/maweig-Motor-200831.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.
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&regs=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 (&regs==&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