/* Kode für mfv2c: 425-Hz-Messung mittels A/D-Wandler an PB2 = ADC1 = Pin7
und Nulldurchgangs-Abstandsmessung.
Genauso wie beim CLIP-Empfang, nur mit freigegebenen Interrupts
und (damit) ohne ADC-ISR.
Einzige exportierte Funktion ist „bool mess425()“,
die bis zum Watchdog-Interrupt (Flags&0x80) = 16 ms arbeiten darf.
220410 erstellt
*/
#include <avr/io.h>
typedef unsigned char byte;
typedef unsigned short word;
constexpr char LOG2(unsigned v) {return 15-__builtin_clz(v);}
constexpr char CLOG2 = LOG2(F_CPU/1000000);
constexpr char BLOG2 = LOG2(1000/125); // 3 = ÷8 → 125 kHz → 9,6 kSa/s
#define Flags GPIOR0 // Bits für NSK() u.a.
extern void eechange(byte); // Debug
static_assert(CLOG2>=1,"F_CPU zu gering, mindestens 2 MHz!");
extern byte SinTab[];
extern void clock_set(byte div);
static void adcwait() { // Warte bis ADC-Wert fertig (Ohne ADC-Interrupt!)
while (!(ADCSRA&0x10));
ADCSRA|=0x10;
}
// R19, R21:R20, XH:XL bleiben unverändert, aber das kann avr-gcc nicht optimieren
static int mulSin(char adcval,word pha) {
int ret; // ret = adcval*sin(pha)*0.57 (73/128)
asm(
" .macro mulbit b \n"
" sbrc %A2,\\b \n"
" add %A0,%1 \n" // niemals Carry
" lsr %A0 \n"
" .endm \n"
" mov ZL,%B2 \n"
" cbr ZL,1<<7 \n" // Halbsinus: Nur positiv
" ldi ZH,hi8(SinTab) \n"
" lpm %A2,Z \n"
" subi %A2,73 \n" // TODO? Kein Offset
" eor %B2,%1 \n"
" sbrc %1,7 \n"
" neg %1 \n"
" clr %A0 \n"
" mulbit 0 \n"
" mulbit 1 \n"
" mulbit 2 \n"
" mulbit 3 \n"
" mulbit 4 \n"
" mulbit 5 \n"
" mulbit 6 \n"
" sbrc %B2,7 \n"
" neg %A0 \n"
" lsl %B2 \n"
" sbc %B0,%B0 \n" // 36 Takte
:"=&d"(ret):"d"(adcval),"r"(pha));
return ret;
}
// Aufteilung: Nicht-statisches Unterprogramm,
// damit dieses (während Tonausgabe nicht läuft)
// R8..R15 benutzen kann. Leider unsinnges PUSH/POP dafür.
// Stack ist beim ATtiny45 knapp!
word internMess() {
if (PB5DEBUG&3) {
DDRB |= 0x20;
PORTB&=~0x20;
}
constexpr word ADD = 425.0*65536*(1<<CLOG2+BLOG2)*13/F_CPU;
int a=0, b=0;
word pha=0;
byte period=6;
do{
adcwait(); // Der Gleichspannungspegel liegt bei über 75%
if (PB5DEBUG&1) PINB|=0x20; // Sample anzeigen
int adcval = ADC-0x380;
if (adcval<-127) {
adcval=-127;
if (PB5DEBUG&1) PINB|=0x20; // Abschneiden anzeigen
}
a+=mulSin(adcval,pha+0x4000); // Kosinus (Realteil)
b+=mulSin(adcval,pha); // Sinus (Imaginärteil)
asm( // if (overflows(pha+=ADD)) --period
" add %A0,%A2 \n"
" adc %B0,%B2 \n"
" sbc %1,r1 \n"
:"+r"(pha),"+r"(period):"r"(ADD));
}while (period); // 6 Perioden = 14 ms erwarten
if (PB5DEBUG&3) {
DDRB &=~0x20;
PORTB|= 0x20;
}
if (a<0) a=-a;
if (b<0) b=-b;
return a>=b ? a+(b>>1) : (a>>1)+b; // Betrag der komplexen Zahl a+ib ohne Quadrat und Wurzel
// spektrale Spannung bei 425 Hz im Rechteckfenster von 6 Perioden
}
bool mess425() { // Messung 425 Hz mit A/D-Wandler
if (DDRB&2) return false; // Tonausgabe läuft: Messung sinnlos
if (ADMUX&0x20) return false; // A/D-Wandler mit Messung der Betriebsspannung beschäftigt
// Bei geteiltem CPU-Takt (1 MHz) und ADC-Taktteiler 8 stehen
// 104 Takte pro A/D-Wert zur Verfügung: Zu wenig!
// 208 Takte reichen hier schon.
ADCSRA=0xA0|CLOG2+BLOG2; // Passender Taktteiler
clock_set(0); // Maximale Taktfrequenz
word c=internMess();
clock_set(CLOG2);
ADCSRA=0xA0|BLOG2; // Taktteiler zurückstellen
return c>=2000;
}
Vorgefundene Kodierung: UTF-8 | 0
|