Source file: /~heha/ewa/Reluktanzmotor/firmware.zip/main.cpp

/* Firmware für den ATmega32U4 eines eHaJo Display-Adapters
   Henrik Haftmann, 180711
Aufgaben:
1. Frequenzmessung an 10 Eingängen mit Pullup (max. 1 kHz der Flanken)
2. Report über serielle Schnittstelle

Hardware (nach Ports sortiert; 44 Pins):
SS  8	PB0 i0	SS			E0 (Buchse links oben)
SC  9	PB1 i1	SCL			E1
MO 10	PB2 i2	MOSI			E2
MI 11	PB3 i3	MISO			E3
 8 28	PB4 i4			ADC11	E4
 9 29	PB5 i5	OC1A	!OC4B	ADC12	E5 (untere Reihe)
10 30	PB6 i6	OC1B	 OC4B	ADC13	E6
11 12	PB7 i7	OC1C	 OC0A	!RTS	E7

 5 31	PC6	OC3A	!OC4A		E8
13 32	PC7	ICP3	 OC4A	CLK0	E9

 3 18	PD0 I0	SCL	 OC0B		frei
 2 19	PD1 I1	SDA			frei
RX 20	PD2 I2	RxD			frei
TX 21	PD3 I3	TxD			frei
 4 25	PD4	ICP1		ADC8	frei
-- 22	PD5	XCK		!CTS	frei
12 26	PD6	T1	!OC4D	ADC9	frei
 6 27	PD7	T0	 OC4D	ADC10	frei

-- 33	PE2	!HWB			frei
 7  1	PE6 I6			AIN0	Kontroll-LED

A5 41	PF0			ADC0	frei
A4 40	PF1			ADC1	frei
A3 39	PF4	TCK		ADC4	frei
A2 38	PF5	TMS		ADC5	frei
A1 37	PF6	TDO		ADC6	frei
A0 36	PF7	TDI		ADC7	frei

    3	-	D-			USB Data-
    4	-	D+			USB Data+
    7	-	Ubus			USB-Busspannung
RS 13	-	!Reset			Reset-Eingang
   16	-	XTAL2			16-MHz-Quarz
   17	-	XTAL1			16-MHz-Quarz
   42	-			AREF	Kondensator
    6	-	UCAP			Kondensator
2,34,14,44,24	UUcc,Ucc,AUcc		5P
5,15,23,35,43	UGND,GND		GND

Verwendung der Timer:
0	1-ms-Abfragetakt
1	frei
3	frei
4	frei
*/

#include <avr/io.h>
#include <avr/wdt.h>		// wdt_reset()
#include <avr/eeprom.h>
#include <avr/sleep.h>
#include <avr/interrupt.h>	// sei()
#include <stdio.h>		// sprintf_P
#include <stdlib.h>		// strtoul
#include <avr/pgmspace.h>	// PSTR
#include "usb.h"

#define N 10

struct{
 unsigned long summa[N];	// Wasseruhren-Stände
 word freqinterval;		// Frequenzmess-Intervall (Standard: 1000 = 1 s)
 word saveinterval;		// Wasseruhrenstand-Speicherintervall (Standard: 60000 = 60 s)
}eedata;
word freq[N];
word prev[N];
word frtime,eetime,prevstate;
bool ee_update;

static word getstate() {return (PINC>>6&0x03)<<8|PINB;}	// Alle 10 Eingänge

//Interruptroutine mit 1 kHz, die die 10 Eingänge abfragt
//und die Frequenz für 1 Sekunde ermittelt
ISR(TIMER0_COMPA_vect) {
 word state=getstate();	// Eingänge abfragen
 word edges=prevstate^state;	// Flanken erkennen
 prevstate=state;		// Zustand merken
 for (byte i=0; i<N; i++,edges>>=1) {
  if (edges&1) ++eedata.summa[i];	// Flanken zählen (wie bei einer Wasseruhr)
 }
 if (++frtime>=eedata.freqinterval) {	// 1 Sekunde vergangen: Frequenz ermitteln
  frtime=0;
  PINE|=0x40;		// LED toggeln
  for (byte i=0; i<N; i++) {
   word s=word(eedata.summa[i]);	// LoWord der Wasseruhr
   freq[i]=s-prev[i];	// Subtraktionsüberläufe ignorieren
   prev[i]=s;		// Wasseruhrenstand merken für nächste Sekunde
  }
 }
 if (++eetime>=eedata.saveinterval) {
  eetime=0;
  ee_update=true;	// minutenweise Zählerstände speichern
 }
}

// Asynchrones Update: Wartet nicht sondern stößt EEPROM-Schreibvorgang nur an
bool eeprom_update(const void*ram, void*eeprom, size_t len) {
 if (EECR&0x02) return true;
 const byte*src=(const byte*)ram;
 byte*dst=(byte*)eeprom;
 for (;len;src++,dst++,len--) {
  EEAR=(word)dst;
  EECR|=1;
  if (EEDR!=*src) {
   EEDR=*src;	// Adresse steht noch in EEAR
   cli();
   EECR|=0x04;
   sei();
   EECR|=0x02;	// immer noch mit gesperrten Interrupts (wird zu SBI compiliert)
   return true;
  }
 }
 return false;
}

void bootstart() __attribute__((naked,noreturn));
void bootstart() {
 cli();
 WDTCSR=0x18;
 WDTCSR=0;	// Watchdog aus
 PRR0=0;
 PRR1=0;
 SMCR=0;
 asm("jmp 0x7000");	// Adresse des Urladers
}

/***********************************************
 * Generelle Initialisierung und Hauptprogramm *
 ***********************************************/

static void setupHardware(void) {
 MCUCR = 0x80;	// JTAG deaktivieren (sonst gehen PF4..PF7 = ADC4..ADC7 nicht)
 MCUSR = 0;	// WDRF löschen, damit das nachfolgende Deaktivieren des Watchdogs klappt
 WDTCSR|=0x18;	// Watchdog aktivieren
 WDTCSR= 0x0D;	// auf 500 ms, dann Reset
 wdt_reset();
 CLKPR = 0x80;
 CLKPR = 0x00;	// CLKDIV8-Fuse ist ab Werk gesetzt, beim Arduino Micro gelöscht
 SMCR  = 0x01;	// Sleep aktivieren
 PRR0  = 0x8D;	// I²C, Timer1, SPI, ADC aus
 PRR1  = 0x19;	// Timer4, Timer3, USART aus
 PORTB = 0xFF;	// Pullups an den Eingängen
 PORTC = 0xFF;
 PORTD = 0xFF;
 PORTF = 0xFF;
 DDRE  = 0x44;	// PE2 / !HWB = low
 ACSR  = 0x80;	// Analogvergleicher deaktivieren
 OCR0A = 249;	// Zählumfang 0..249
 TCCR0A= 0x02;	// CTC-Modus
 TIMSK0= 0x02;	// mit Interrupt
 TCCR0B= 0x03;	// Vorteiler 64, ergibt 1 kHz
 prevstate=getstate();
 for (byte i=0; i<N; i++) prev[i]=word(eedata.summa[i]);
 sei();
}

int main() {
 eeprom_read_block(&eedata,0,sizeof eedata);	// alles in einem Rutsch
 if (eedata.freqinterval==0xFFFF) eedata.freqinterval=1000;
 if (eedata.saveinterval==0xFFFF) eedata.saveinterval=60000;
 setupHardware();
 usbInit();
 for(;;) {
  ee_update=ee_update && eeprom_update(&eedata,0,sizeof eedata);
  sleep_cpu();
  wdt_reset();
  usbPoll();
 }
}

void handleQuery() {
 switch (query[0]) {
  case '*': {	// "*IDN?"
   answerlen=sprintf_P(answer,PSTR("Frequenzmesser FZ 180716 Haftmann\r\n"));
   usbSend();
  }return;
  case '?': {	// alle 10 Frequenzen abfragen (passt geradeso in ein 64 Byte-USB-Paket)
   answerlen=0;
   for (byte c=0; c<N; c++) {
    answerlen+=sprintf_P(answer+answerlen,PSTR("%6u"),freq[c]);
   }
   answerlen+=sprintf_P(answer+answerlen,PSTR("\r\n"));
   usbSend();
  }return;
  case 'B': {		// "BL" = Urlader anspringen (für Firmware-Update)
   if (query[1]=='L') bootstart();
  }break;
  case 'T': {			// Integrationszeit für Frequenzmessung setzen/abfragen
   if (query[1]=='?') {
    answerlen=sprintf_P(answer,PSTR("%u\r\n"),eedata.freqinterval);
    usbSend();
    return;
   }
   word t=strtoul(query+1,NULL,10);
   if (t>=50 && t<=60000) {	// min. 50 ms, max. 1 Minute
    eedata.freqinterval=t;
    ee_update=true;		// umgehend im EEPROM aktualisieren
    return;
   }
  }break;		// Fehler
  default: {
   char*p=query;
   for (byte i=0; i<querylen; i++,p++) if ('0'<=*p && *p<='9') {
    bool sumflag = p!=query && *(p-1)=='S';
    byte channel=byte(strtoul(p,&p,10));
    if (channel<N && *p=='?') {
     answerlen=sumflag
     ?sprintf_P(answer,PSTR("%lu\r\n"),eedata.summa[channel])
     :sprintf_P(answer,PSTR("%u\r\n"),freq[channel]);
     usbSend();
     return;
    }
   }
  }
 }
 answerlen=sprintf_P(answer,PSTR("Error\r\n"));
 usbSend();
}
Detected encoding: UTF-80