Source file: /~heha/mb-iwp/Inkrementalgeber/IAI/Inkr.zip/fw/main.cpp

/*
Protokolladapter IAI <=> BiSS/EnDat
Aus Sicht von BiSS/EnDat ist der Mikrocontroller ein Slave!
Daher ist hier MOSI ein Eingang und MISO ein Ausgang, anders als gewohnt.

Hardware: ATtiny2313
Pin	Port	Funktion
1	(PA2)	!RESET	Reset; ISP
2	PD0	RxD	IAI-Geber via MAX485
3	PD1	TxD	IAI-Geber via MAX485
4	(PA1)	XTAL2	Quarz 20 MHz
5	(PA0)	XTAL1	Quarz 20 MHz
6	PD2	(!CS)	Debugport
7	PD3	(SDI)	Debugport
8	PD4	(SDO)	Debugport
9	PD5	(SCK)	Debugport
10	GND
11	PD6		Taste low-aktiv ohne ext. Pullup
12	PB0		Gelbe LED, high-aktiv
13	PB1		Grüne LED, high-aktiv
14	PB2	(SDE)	BiSS/EnDat Sendefreigabe high-aktiv
15	PB3	(!RE)	BiSS/EnDat Empfängerfreigabe low-aktiv
16	PB4	(TE)	IAI-Geber Sendefreigabe high-aktiv
17	PB5	DI	BiSS/EnDat Datenempfang; ISP:MOSI
18	PB6	DO	BiSS/EnDat Datensenden; ISP:MISO
19	PB7	USCK	BiSS/EnDat Takteingang; ISP:SCK
20	Ucc

RS485-Protokoll:
Der IAI-Controller schickt "0x1A" alle 100 µs (10 kHz), Dauer 4 µs
Nach 4 µs Pause schickt die Achse 11 Byte Antwort:
"0x1A,0x00,posLo,posMi,posHi,k1,k2,k3,k4,k5,chkxor"

Weitere Kommunikation beim Hochlauf wird ignoriert
*/
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <avr/eeprom.h>
#include <avr/fuse.h>
#include <avr/signature.h>


FUSES={
  0b11111111,	// Quarz 20 MHz, ganz langsam hochfahren, kein Taktteiler
  0b11011001,	// SPI-Programmiermöglichkeit bleibt erhalten, BrownOut bei 4,3 V(!)
  0b11111111,	// unverändert
};

typedef unsigned char byte;
#define NOINIT __attribute__((section(".noinit")))

//EMPTY_INTERRUPT(TIMER0_OVF_vect)

//static volatile byte bissbuf[32];
//volatile register byte bbi asm("r2");
//volatile register byte bbc asm("r3");

static volatile byte iaibuf[32] NOINIT;
static volatile byte iairecvidx;
//volatile register byte iairecvcnt asm("r7");
const byte iairecvcnt=11;
//volatile register byte msk asm("r6");
//volatile register byte iaisendcnt asm("r8");	// Sende-Byte-Abwärtszähler für IAI
//volatile register byte iaisendidx asm("r9");

#if 0
static void iai_query() {
  iairecvidx=0;
//  iairecvcnt=11;
  iaibuf[0]=0x1A;
  iaisendidx=0;
  iaisendcnt=1;
  PORTB|= 0b00010000;	// MAX485-Leitungstreiber aktivieren
  UCSRB = 0b00101000;	// Senden mit Interrupts
}			// der Rest passiert interruptgesteuert

ISR(USART_UDRE_vect) {
  UDR=iaibuf[iaisendidx];
  if (++iaisendidx==iaisendcnt) UCSRB = 0b01001000;	// Ab jetzt Interrupt wenn Sendeschieberegister leer
}


ISR(USART_TX_vect) {	// Ohren auf Empfang stellen
  PORTB&=~0b00010000;	// MAX485 Sendefreigabe wegnehmen
  UCSRB = 0b10010000;	// Empfänger aktivieren mit Interrupts und Sender aus
}
#endif

ISR(USART_RX_vect,ISR_NAKED) {
  PORTD|=0x08;
  if (UCSRA&0x10) PORTB&=~0x02;	// Falsche Baudrate
  byte b=UDR;
  byte r=iairecvidx;
  if (r<2 && b!=0x1A) {
    r=0;	// von vorn
  }else{
    iaibuf[r]=b;
    if (r==0 && b==0x1A) PORTB|=0x02;	// Grüne LED ein
    if (r==1 && b==0x1A) PORTB|=0x01;	// Gelbe LED ein
    if (++r==12) {
//    UCSRB = 0;	// alles aus, Puffer voll
      PORTB&=~0x01;		// Gelbe LED aus
      GPIOR0|=0x01;	// Softwareinterrupt: P
      r=0;
    }
  }
  iairecvidx=r;
  reti();
}
#if 0
#define USIBR _SFR_IO8(0)

ISR(USI_OVERFLOW_vect) {
  USIDR=bissbuf[bbi];	// Hm, erst schreiben, dann lesen, oder andersherum??
  bissbuf[bbi]=USIBR;
  USISR|= 0b01000000;	// Interrupt löschen
  if (++bbi==bbc) {
 // * Anfrage dekodieren
    PORTB|= 0b00000100;	// antworten
 // * Antwort bereitstellen
  }
}
#endif

static int debugSPI(int v) {
 PORTD&=~0x04;		// Debug-!CS Low
 byte i=16;
 do{
  if (v<0) PORTD|=0x10; else PORTD&=~0x10;	// Debug-SDO
  PORTD|=0x20;		// Debug-SCK High
  v+=v;
  if (PIND&0x08) v|=1;	// Debug-SDI
  PORTD&=~0x20;		// Debug-SCK Low
 }while(--i);
 PORTD|=0x04;		// Debug-!CS High
 return v;
}

void debugPosOut() {
#if 1
 unsigned pos;
 asm(
"	ldd	%B0,%a1+5\n"
"	ror	%B0\n"	// Bit 0 ausschieben
"	ldd	%B0,%a1+4\n"
"	ror	%B0\n"
"	ldd	%A0,%a1+3\n"
"	ror	%A0\n"
:"=&r"(pos):"b"(iaibuf));
 debugSPI(pos);
#else
 debugSPI(iaibuf[4]<<8|iaibuf[3]);
#endif
}

int main() {
  MCUCR = 0b00100000;	// Sleep aktivieren
  DDRB  = 0b01011111;	// PWM als Ausgänge
  PORTB = 0b00000010;	// keinerlei Pullups, alles Low; grüne LED ein
  DDRD  = 0b00111110;
  PORTD = 0b01001101;
// IAI-Seite: Baudratenregister ist bereits 0, UCSRC stimmt schon (8 bit)
  UCSRA = 0b00100010;	// Double-Speed = 2,5 MBaud
//  UCSRB = 0b00011000;	// Sender und Empfänger sowie Interrupts freigeben

// Testweise: 2× 0x1A empfangen und Telegramm aufzeichnen
  iairecvidx=0;
//  iairecvcnt=12;
  UCSRB = 0b10010000;	// UART-Empfänger aktivieren mit Interrupts

// BiSS/EnDat-Seite
//  USICR = 0b01011000;	// Daten einlesen bei steigender Taktflanke, Wechsel der Ausgabe bei fallender Flanke, Interrupts
  sei();
  for(;;) {
    PORTD&=~0x08;	// an Debug-SDI Schlaf/Wachzustand anzeigen
    sleep_cpu();
    PORTD|= 0x08;
    if (GPIOR0&0x01) {
      GPIOR0&=~0x01;
      debugPosOut();
    }
  }
}
Detected encoding: UTF-80