#include #include // --------------------------------------------------------- // PIN‑MAPPING (ELECTRONICFOX 2026(c), PORT‑BASIERT, ATmega32) // --------------------------------------------------------- // IR‑Empfänger: PB2 // Tasten: PB0, PD3, PD4, PD5 // Display‑Bus: CLK=PD2, DLEN=PB6, DATA=PB1 // Test/Power: TEST=PB5 // SC‑Out: SC=PD6 // Statusports: LED1=PC3=SUCHLAUF, LED2=PB4=MEMORY // PWM-DAC: PB3 (Timer0), PD7 (Timer2) // ADC: ADC2 (PA2), ADC3 (PA3) // --------------------------------------------------------- #define IR_RECEIVE_PIN PIN_PB2 // PB2 IR-Eingang // Display / Steuer‑Signale #define CLK_PORT PORTD #define CLK_DDR DDRD #define CLK_BIT PD2 // DISPLAY CLOCK #define DLEN_PORT PORTB #define DLEN_DDR DDRB #define DLEN_BIT PB6 // DISPLAY Übernahme #define DATA_PORT PORTB #define DATA_DDR DDRB #define DATA_BIT PB1 // DISPLAY-DATA #define TEST_PORT PORTB #define TEST_DDR DDRB #define TEST_BIT PB5 // STBY LOW-Aktiv #define SC_PORT PORTD #define SC_DDR DDRD #define SC_BIT PD6 // EXTERN LOW-Aktiv #define LED1_PORT PORTC #define LED1_DDR DDRC #define LED1_BIT PC3 // SUCHLAUF LOW-Aktiv #define LED2_PORT PORTB #define LED2_DDR DDRB #define LED2_BIT PB4 // MEMORY LOW-Aktiv // PWM-DAC Pins #define PWM1_PORT PORTB #define PWM1_DDR DDRB #define PWM1_BIT PB3 // Timer0 OC0 #define PWM2_PORT PORTD #define PWM2_DDR DDRD #define PWM2_BIT PD7 // Timer2 OC2 // Tasten #define KEY0_PIN PB0 // Fronttaste 0 #define KEY1_PIN PD3 // Fronttaste 1 #define KEY2_PIN PD4 // Fronttaste 2 #define KEY3_PIN PD5 // Fronttaste 3 bool minusWasActive = false; #define MSP_ADDR 0x40 #define PCF_ADDR 0x20 #define PCF_STEREO 0x30 #define PCF_SONORAMA 0xA0 #define PCF_PSEUDO 0x90 #define PCF_SPATIAL 0xF0 // --- Funktionsprototypen --- void MSP_SetVolumeBoth(uint16_t vol); void applySoundProfile(uint8_t mode); void scanI2C(); void MSP_SetInput(bool av); uint16_t volumeSpeaker = 0x5800; uint16_t volumeStep = 0x0400; uint8_t digit[16] = { 0x03, 0x9F, 0x25, 0x0D, 0x99, 0x49, 0x41, 0x1F, // LED-Daten für SAA1061 0x01, 0x09, 0x11, 0xC1, 0x63, 0x85, 0x61, 0x71 // LED-Daten für SAA1061 }; uint16_t AV_symbol = 0x1083; uint16_t ST_symbol = 0x48E1; uint16_t MINUS_symbol = 0xFFFE; uint16_t SC_symbol = 0x4863; uint16_t SC2_symbol = 0x4963; uint8_t program = 1; #define MODE_NORMAL 0 #define MODE_BLINK 1 #define MODE_SEARCH 2 #define MODE_MEMORY_BLINK 3 #define MODE_MINUS 99 uint8_t mode = MODE_NORMAL; uint8_t i2cList[16]; uint8_t i2cCount = 0; bool scanRequested = false; bool i2cMode = false; unsigned long lastBlink = 0; bool blinkState = true; uint8_t memoryBlinkCount = 0; bool irSearchActive = false; unsigned long lastIRTime = 0; uint8_t lastStable = 0x0F; uint8_t lastRaw = 0x0F; unsigned long lastChange = 0; const unsigned long debounceTime = 10; unsigned long lastDisp = 0; uint16_t currentSymbol = 0; uint8_t pcf_last = 0xFF; bool lastAV = false; unsigned long memoryPulseUntil = 0; // PWM-DAC Regelung uint8_t pwm1Value = 128; // PB3 uint8_t pwm2Value = 128; // PD7 const uint16_t targetADC = 511; const uint8_t pwmStep = 2; bool isAV() { return (program == 17); } // --------------------------------------------------------- // Hilfs‑Makros für Ports // --------------------------------------------------------- #define SET_HIGH(port, bit) ((port) |= (1 << (bit))) #define SET_LOW(port, bit) ((port) &= ~(1 << (bit))) // Spezielle Makros #define CLK_HIGH() SET_HIGH(CLK_PORT, CLK_BIT) #define CLK_LOW() SET_LOW(CLK_PORT, CLK_BIT) #define DLEN_HIGH() SET_HIGH(DLEN_PORT, DLEN_BIT) #define DLEN_LOW() SET_LOW(DLEN_PORT, DLEN_BIT) #define DATA_HIGH() SET_HIGH(DATA_PORT, DATA_BIT) #define DATA_LOW() SET_LOW(DATA_PORT, DATA_BIT) #define TEST_HIGH() SET_HIGH(TEST_PORT, TEST_BIT) #define TEST_LOW() SET_LOW(TEST_PORT, TEST_BIT) #define SC_HIGH() SET_HIGH(SC_PORT, SC_BIT) #define SC_LOW() SET_LOW(SC_PORT, SC_BIT) #define LED1_HIGH() SET_HIGH(LED1_PORT, LED1_BIT) #define LED1_LOW() SET_LOW(LED1_PORT, LED1_BIT) #define LED2_HIGH() SET_HIGH(LED2_PORT, LED2_BIT) #define LED2_LOW() SET_LOW(LED2_PORT, LED2_BIT) // --------------------------------------------------------- // CLOCK // --------------------------------------------------------- void pulseCLK() { CLK_HIGH(); delayMicroseconds(5); CLK_LOW(); delayMicroseconds(5); } // --------------------------------------------------------- // FRAME SENDEN // --------------------------------------------------------- void sendFrame(uint8_t A0, uint16_t data, uint8_t A1) { DLEN_HIGH(); delayMicroseconds(3); if (A0) DATA_HIGH(); else DATA_LOW(); pulseCLK(); for (int i = 15; i >= 0; i--) { if ((data >> i) & 1) DATA_HIGH(); else DATA_LOW(); pulseCLK(); } if (A1) DATA_HIGH(); else DATA_LOW(); pulseCLK(); DLEN_LOW(); delayMicroseconds(5); pulseCLK(); } // --------------------------------------------------------- // TASTEN (PORT‑basiert) // --------------------------------------------------------- uint8_t readKeys() { uint8_t b0 = (PINB & (1 << KEY0_PIN)) ? 1 : 0; // PB0 uint8_t b1 = (PIND & (1 << KEY1_PIN)) ? 1 : 0; // PD3 uint8_t b2 = (PIND & (1 << KEY2_PIN)) ? 1 : 0; // PD4 uint8_t b3 = (PIND & (1 << KEY3_PIN)) ? 1 : 0; // PD5 uint8_t raw = (b3 << 3) | (b2 << 2) | (b1 << 1) | b0; if (raw != lastRaw) { lastRaw = raw; lastChange = millis(); } if (millis() - lastChange > debounceTime) { if (raw != lastStable) { lastStable = raw; return raw; } } return 0xFF; } // --------------------------------------------------------- // SYMBOLERZEUGUNG // --------------------------------------------------------- uint16_t makeSymbol(uint8_t p) { if (p == 17) return AV_symbol; return (digit[p / 10] << 8) | digit[p % 10]; } uint16_t makeHexSymbol(uint8_t hex) { return (digit[(hex >> 4) & 0x0F] << 8) | digit[hex & 0x0F]; } // --------------------------------------------------------- // DIMMING // --------------------------------------------------------- uint8_t getBrightnessFromADC() { int adc = analogRead(A0); const int ADC_MIN = 0x0A0; const int ADC_MAX = 0x15E; if (adc < ADC_MIN) adc = ADC_MIN; if (adc > ADC_MAX) adc = ADC_MAX; return map(adc, ADC_MIN, ADC_MAX, 2, 14); } void showSymbol(uint16_t sym) { sendFrame(0, sym, 1); } void showSymbolDimmed(uint16_t sym) { uint8_t brightness = getBrightnessFromADC(); uint8_t onFrames = brightness; uint8_t offFrames = 15 - brightness; for (uint8_t i = 0; i < onFrames; i++) showSymbol(sym); for (uint8_t i = 0; i < offFrames; i++) showSymbol(0xFFFF); } // --------------------------------------------------------- // PWM-DAC Regelung (für MightyCore / ATmega32 angepasst) // --------------------------------------------------------- void regulatePWM_DACs() { // PB3 (Timer0) regelt ADC2 int adc2 = analogRead(A2); if (adc2 < targetADC - 8) { if (pwm1Value < 255 - pwmStep) pwm1Value += pwmStep; } else if (adc2 > targetADC + 8) { if (pwm1Value > pwmStep) pwm1Value -= pwmStep; } OCR0 = pwm1Value; // PD7 (Timer2) regelt ADC3 int adc3 = analogRead(A3); if (adc3 < targetADC - 8) { if (pwm2Value < 255 - pwmStep) pwm2Value += pwmStep; } else if (adc3 > targetADC + 8) { if (pwm2Value > pwmStep) pwm2Value -= pwmStep; } OCR2 = pwm2Value; } // --------------------------------------------------------- // MSP3400 / PCF8574 // --------------------------------------------------------- void TWI_MSP(uint8_t sub, uint8_t sh, uint8_t sl, uint8_t dh, uint8_t dl) { Wire.beginTransmission(MSP_ADDR); Wire.write(sub); Wire.write(sh); Wire.write(sl); Wire.write(dh); Wire.write(dl); Wire.endTransmission(); delay(10); } void TWI_PCF(uint8_t value) { if (value == pcf_last) return; pcf_last = value; Wire.beginTransmission(PCF_ADDR); Wire.write(value); Wire.endTransmission(); delayMicroseconds(500); } // --------------------------------------------------------- // MSP INIT // --------------------------------------------------------- void MSP_Init() { TEST_LOW(); delay(800); TEST_HIGH(); delay(1000); Wire.beginTransmission(MSP_ADDR); Wire.write(0x00); Wire.write(0x80); Wire.write(0x00); Wire.endTransmission(); delay(10); Wire.beginTransmission(MSP_ADDR); Wire.write(0x00); Wire.write(0x00); Wire.write(0x00); Wire.endTransmission(); delay(700); // Registerblock 0x10 Grundeinstellung, ZF, DSP TWI_MSP(0x10, 0, 0xBB, 0, 0xC6); TWI_MSP(0x10, 0, 0x83, 0, 0x08); TWI_MSP(0x10, 0, 0x01, 0, 0x00); TWI_MSP(0x10, 0, 0x05, 0, 0x00); TWI_MSP(0x10, 0, 0x93, 0x06, 0x68); TWI_MSP(0x10, 0, 0x9B, 0, 0); TWI_MSP(0x10, 0, 0xA3, 0x06, 0x90); TWI_MSP(0x10, 0, 0xAB, 0, 0); delay(500); // Registerblock 0x12 SOUND-Profile, Quellen und Lautstärke TWI_MSP(0x12, 0, 0x01, 0, 0); TWI_MSP(0x12, 0, 0x02, 0x40, 0); TWI_MSP(0x12, 0, 0x03, 0x50, 0); TWI_MSP(0x12, 0, 0x07, 0x73, 0x21); TWI_MSP(0x12, 0, 0x40, 0x73, 0x21); TWI_MSP(0x12, 0, 0x08, 0x02, 0x20); TWI_MSP(0x12, 0, 0x09, 0x02, 0x20); TWI_MSP(0x12, 0, 0x0A, 0x02, 0x20); TWI_MSP(0x12, 0, 0x41, 0x02, 0x20); TWI_MSP(0x12, 0, 0x0B, 0x02, 0x20); TWI_MSP(0x12, 0, 0x0D, 0x80, 0); TWI_MSP(0x12, 0, 0x0E, 0x30, 0); TWI_MSP(0x12, 0, 0x0F, 0, 0x3F); TWI_MSP(0x12, 0, 0x12, 0x10, 0); TWI_MSP(0x12, 0, 0x16, 0x10, 0); uint8_t h = volumeSpeaker >> 8; uint8_t l = volumeSpeaker & 0xFF; TWI_MSP(0x12, 0, 0x07, h, l); TWI_MSP(0x12, 0, 0x00, h, l); TWI_MSP(0x12, 0, 0x13, 0x14, 0); // BEEP TWI_MSP(0x12, 0x00, 0x14, 0x60, 0x40); delay(100); TWI_MSP(0x12, 0x00, 0x14, 0x00, 0x00); } // --------------------------------------------------------- // MSP WAKE‑INIT // --------------------------------------------------------- void MSP_WakeInit() { Wire.beginTransmission(MSP_ADDR); Wire.write(0x00); Wire.write(0x80); Wire.write(0x00); Wire.endTransmission(); delay(10); Wire.beginTransmission(MSP_ADDR); Wire.write(0x00); Wire.write(0x00); Wire.write(0x00); Wire.endTransmission(); delay(700); // Registerblock 0x10 TWI_MSP(0x10, 0, 0xBB, 0, 0xC6); TWI_MSP(0x10, 0, 0x83, 0, 0x08); TWI_MSP(0x10, 0, 0x01, 0, 0x00); TWI_MSP(0x10, 0, 0x05, 0, 0x00); TWI_MSP(0x10, 0, 0x93, 0x06, 0x68); TWI_MSP(0x10, 0, 0x9B, 0, 0); TWI_MSP(0x10, 0, 0xA3, 0x06, 0x90); TWI_MSP(0x10, 0, 0xAB, 0, 0); delay(300); // Registerblock 0x12 TWI_MSP(0x12, 0, 0x01, 0, 0); TWI_MSP(0x12, 0, 0x02, 0x40, 0); TWI_MSP(0x12, 0, 0x03, 0x50, 0); TWI_MSP(0x12, 0, 0x07, 0x73, 0x21); TWI_MSP(0x12, 0, 0x40, 0x73, 0x21); TWI_MSP(0x12, 0, 0x08, 0x02, 0x20); TWI_MSP(0x12, 0, 0x09, 0x02, 0x20); TWI_MSP(0x12, 0, 0x0A, 0x02, 0x20); TWI_MSP(0x12, 0, 0x41, 0x02, 0x20); TWI_MSP(0x12, 0, 0x0B, 0x02, 0x20); TWI_MSP(0x12, 0, 0x0D, 0x80, 0); TWI_MSP(0x12, 0, 0x0E, 0x30, 0); TWI_MSP(0x12, 0, 0x0F, 0, 0x3F); TWI_MSP(0x12, 0, 0x12, 0x10, 0); TWI_MSP(0x12, 0, 0x16, 0x10, 0); uint8_t h = volumeSpeaker >> 8; uint8_t l = volumeSpeaker & 0xFF; TWI_MSP(0x12, 0, 0x07, h, l); TWI_MSP(0x12, 0, 0x00, h, l); // Input defaults TWI_MSP(0x12, 0, 0x04, 0x08, 0x04); TWI_MSP(0x12, 0, 0x02, 0x18, 0x00); TWI_MSP(0x12, 0, 0x03, 0x04, 0x00); TWI_MSP(0x12, 0, 0x05, 0x00, 0x00); TWI_MSP(0x12, 0, 0x13, 0x14, 0x00); // PCF Reset TWI_PCF(0x00); delay(400); TWI_PCF(0x30); // BEEP TWI_MSP(0x12, 0x00, 0x14, 0x60, 0x40); delay(100); TWI_MSP(0x12, 0x00, 0x14, 0x00, 0x00); } void MSP_SetVolumeBoth(uint16_t vol) { uint8_t h = vol >> 8; uint8_t l = vol & 0xFF; TWI_MSP(0x12, 0, 0x07, h, l); TWI_MSP(0x12, 0, 0x00, h, l); } void applySoundProfile(uint8_t mode) { switch (mode) { case 0: // STEREO TWI_MSP(0x12, 0, 0x07, 0x78, 0x04); TWI_MSP(0x12, 0, 0x04, 0x08, 0x04); TWI_MSP(0x12, 0, 0x02, 0x18, 0x00); TWI_MSP(0x12, 0, 0x03, 0x04, 0x00); TWI_MSP(0x12, 0, 0x05, 0x00, 0x00); TWI_MSP(0x12, 0, 0x13, 0x14, 0x00); TWI_PCF(PCF_STEREO); break; case 1: // SONORAMA TWI_MSP(0x12, 0, 0x07, 0x78, 0x04); TWI_MSP(0x12, 0, 0x05, 0x7F, 0x20); TWI_MSP(0x12, 0, 0x04, 0x08, 0x04); TWI_MSP(0x12, 0, 0x02, 0x15, 0x00); TWI_MSP(0x12, 0, 0x03, 0x02, 0x00); TWI_MSP(0x12, 0, 0x13, 0x94, 0x10); TWI_PCF(PCF_SONORAMA); break; case 2: // PSEUDO / SONORAMA-HQ TWI_MSP(0x12, 0, 0x07, 0x00, 0x00); TWI_MSP(0x12, 0, 0x05, 0x6F, 0x24); TWI_MSP(0x12, 0, 0x04, 0x08, 0x04); TWI_MSP(0x12, 0, 0x02, 0xC8, 0x00); TWI_MSP(0x12, 0, 0x03, 0x02, 0x00); TWI_MSP(0x12, 0, 0x13, 0x96, 0x10); TWI_PCF(PCF_PSEUDO); break; case 3: // SPATIAL TWI_MSP(0x12, 0, 0x07, 0x00, 0x00); TWI_MSP(0x12, 0, 0x04, 0x08, 0x04); TWI_MSP(0x12, 0, 0x02, 0xC8, 0x00); TWI_MSP(0x12, 0, 0x03, 0x06, 0x00); TWI_MSP(0x12, 0, 0x05, 0x00, 0x04); TWI_MSP(0x12, 0, 0x13, 0xD6, 0x00); TWI_PCF(PCF_SPATIAL); break; } } void scanI2C() { i2cCount = 0; for (uint8_t addr = 1; addr < 127; addr++) { Wire.beginTransmission(addr); if (Wire.endTransmission() == 0) { if (i2cCount < 16) { i2cList[i2cCount++] = addr; } } } // Doppel‑Beep TWI_MSP(0x12, 0x00, 0x14, 0x60, 0x40); delay(100); TWI_MSP(0x12, 0x00, 0x14, 0x00, 0x00); delay(100); TWI_MSP(0x12, 0x00, 0x14, 0x60, 0x40); delay(100); TWI_MSP(0x12, 0x00, 0x14, 0x00, 0x00); } void MSP_SetInput(bool av) { if (av == lastAV) return; lastAV = av; TWI_MSP(0x12, 0, 0x13, 0x14, 0x00); TWI_MSP(0x12, 0, 0x04, 0x04, 0x04); TWI_MSP(0x12, 0, 0x05, 0x00, 0x00); TWI_MSP(0x12, 0, 0x05, 0x00, 0x00); TWI_PCF(PCF_STEREO); } // --------------------------------------------------------- // SETUP // --------------------------------------------------------- void setup() { analogReference(INTERNAL); delay(5); // === JTAG deaktivieren === MCUCSR |= (1 << JTD); MCUCSR |= (1 << JTD); Wire.begin(); IrReceiver.begin(IR_RECEIVE_PIN); // Display‑ und Steuer‑Pins als Output CLK_DDR |= (1 << CLK_BIT); DLEN_DDR |= (1 << DLEN_BIT); DATA_DDR |= (1 << DATA_BIT); TEST_DDR |= (1 << TEST_BIT); SC_DDR |= (1 << SC_BIT); LED1_DDR |= (1 << LED1_BIT); LED2_DDR |= (1 << LED2_BIT); PWM1_DDR |= (1 << PWM1_BIT); PWM2_DDR |= (1 << PWM2_BIT); // Tasten als Input mit Pullup DDRB &= ~(1 << KEY0_PIN); PORTB |= (1 << KEY0_PIN); DDRD &= ~((1 << KEY1_PIN) | (1 << KEY2_PIN) | (1 << KEY3_PIN)); PORTD |= (1 << KEY1_PIN) | (1 << KEY2_PIN) | (1 << KEY3_PIN); // Initialzustände CLK_LOW(); DLEN_LOW(); DATA_LOW(); TEST_HIGH(); LED1_HIGH(); LED2_HIGH(); SC_HIGH(); // Timer0 für PB3 TCCR0 = (1 << WGM01) | (1 << WGM00) | (1 << COM01); // Fast PWM TCCR0 |= (1 << CS01) | (1 << CS00); // Prescaler 64 OCR0 = pwm1Value; // Timer2 für PD7 TCCR2 = (1 << WGM21) | (1 << WGM20) | (1 << COM21); TCCR2 |= (1 << CS22); // Prescaler 64 OCR2 = pwm2Value; MSP_Init(); // --- DATA initial LOW --- DATA_LOW(); } // ----------------------------- // LOOP // ----------------------------- void loop() { // MINUS‑Mode Flankenerkennung + WakeInit beim Verlassen static uint8_t oldMode = mode; if (mode == MODE_MINUS) { minusWasActive = true; } else { if (minusWasActive) { MSP_WakeInit(); minusWasActive = false; } } oldMode = mode; // ------------------------------------------------------- // IR‑FERNBEDIENUNG // ------------------------------------------------------- if (IrReceiver.decode()) { uint32_t code = IrReceiver.decodedIRData.decodedRawData; lastIRTime = millis(); // MEMORY + AV → I2C‑Scan if (code == 0xF20DFF00) { if (isAV()) { i2cMode = true; scanRequested = true; } else if (mode == MODE_BLINK) { mode = MODE_MEMORY_BLINK; memoryBlinkCount = 6; blinkState = true; memoryPulseUntil = millis() + 1000; } } switch (code) { // AV case 0xFF00FF00: mode = MODE_NORMAL; program = 17; irSearchActive = false; break; // Programme 1–9 case 0xFE01FF00: if (mode == MODE_MINUS) mode = MODE_NORMAL; program = 1; break; case 0xFD02FF00: if (mode == MODE_MINUS) mode = MODE_NORMAL; program = 2; break; case 0xFC03FF00: if (mode == MODE_MINUS) mode = MODE_NORMAL; program = 3; break; case 0xFB04FF00: if (mode == MODE_MINUS) mode = MODE_NORMAL; program = 4; break; case 0xFA05FF00: if (mode == MODE_MINUS) mode = MODE_NORMAL; program = 5; break; case 0xF906FF00: if (mode == MODE_MINUS) mode = MODE_NORMAL; program = 6; break; case 0xF807FF00: if (mode == MODE_MINUS) mode = MODE_NORMAL; program = 7; break; case 0xF708FF00: if (mode == MODE_MINUS) mode = MODE_NORMAL; program = 8; break; case 0xF609FF00: if (mode == MODE_MINUS) mode = MODE_NORMAL; program = 9; break; // MINUS‑MODE (mit MUTE VOR dem Umschalten) case 0xF50AFF00: MSP_SetVolumeBoth(0x0000); // SOFORT MUTE TWI_MSP(0x12, 0x00, 0x14, 0x60, 0x40); // BEEP ON delay(200); TWI_MSP(0x12, 0x00, 0x14, 0x00, 0x00); // BEEP OFF mode = MODE_MINUS; break; // Programm +/- case 0xEA15FF00: if (mode == MODE_MINUS) mode = MODE_NORMAL; if (mode == MODE_NORMAL) { program++; if (program > 16) program = 1; } break; case 0xE718FF00: if (mode == MODE_MINUS) mode = MODE_NORMAL; if (mode == MODE_NORMAL) { if (program == 1) program = 16; else program--; } break; // Blink case 0xE41BFF00: if (!isAV() && mode != MODE_MINUS) { mode = (mode == MODE_BLINK) ? MODE_NORMAL : MODE_BLINK; } break; // Suchlauf case 0xF40BFF00: if (mode == MODE_BLINK) { mode = MODE_SEARCH; irSearchActive = true; } break; // Lautstärke +/- case 0xBE41FF00: if (volumeSpeaker < 0x7000) volumeSpeaker += volumeStep; MSP_SetVolumeBoth(volumeSpeaker); break; case 0xBD42FF00: if (volumeSpeaker > volumeStep) volumeSpeaker -= volumeStep; MSP_SetVolumeBoth(volumeSpeaker); break; // Soundprofile case 0xEB14FF00: applySoundProfile(0); break; case 0xE817FF00: applySoundProfile(1); break; case 0xE21DFF00: applySoundProfile(2); break; case 0xBC43FF00: applySoundProfile(3); break; } IrReceiver.resume(); pulseCLK(); } // ------------------------------------------------------- // I2C‑MODE: Adressen anzeigen // ------------------------------------------------------- if (scanRequested && i2cMode) { scanRequested = false; scanI2C(); for (uint8_t i = 0; i < i2cCount; i++) { unsigned long t0 = millis(); while (millis() - t0 < 1000) { showSymbolDimmed(makeHexSymbol(i2cList[i])); } } i2cMode = false; } // ------------------------------------------------------- // IR‑Suchlauf Timeout // ------------------------------------------------------- if (irSearchActive && mode == MODE_SEARCH) { if (millis() - lastIRTime > 300) { irSearchActive = false; mode = MODE_BLINK; } } // ------------------------------------------------------- // FRONT‑TASTEN // ------------------------------------------------------- uint8_t k = readKeys(); if (k != 0xFF) { if (k == 0b1110) { // Prog + if (mode == MODE_MINUS) mode = MODE_NORMAL; else if (mode == MODE_NORMAL) { program++; if (program > 16) program = 1; } } else if (k == 0b1101) { // Prog - if (mode == MODE_MINUS) mode = MODE_NORMAL; else if (mode == MODE_NORMAL) { if (program == 1) program = 16; else program--; } } else if (k == 0b0101 && !isAV() && mode != MODE_MINUS) { // Blink mode = (mode == MODE_BLINK) ? MODE_NORMAL : MODE_BLINK; } else if (k == 0b1000 && mode == MODE_BLINK) { // Search mode = MODE_SEARCH; } else if (k == 0b0110 && mode == MODE_BLINK) { // Memory mode = MODE_MEMORY_BLINK; memoryBlinkCount = 6; blinkState = true; memoryPulseUntil = millis() + 1000; } else if (k == 0b0111) { // Minus MSP_SetVolumeBoth(0x0000); // MUTE vor Minus mode = MODE_MINUS; } else if (k == 0b1111 && mode == MODE_SEARCH) { // Exit Search mode = MODE_BLINK; } else if (k == 0b1100) { // Volume Up if (volumeSpeaker < 0x7000) volumeSpeaker += volumeStep; MSP_SetVolumeBoth(volumeSpeaker); } else if (k == 0b1011) { // Volume Down if (volumeSpeaker > volumeStep) volumeSpeaker -= volumeStep; MSP_SetVolumeBoth(volumeSpeaker); } } // ------------------------------------------------------- // MEMORY‑Blinken // ------------------------------------------------------- if (mode == MODE_MEMORY_BLINK) { if (millis() - lastBlink > 250) { lastBlink = millis(); blinkState = !blinkState; memoryBlinkCount--; if (memoryBlinkCount == 0) mode = MODE_NORMAL; } } // ------------------------------------------------------- // AV‑Logik (MINUS überschreibt AV) // ------------------------------------------------------- int adcA1 = analogRead(A1); bool avByADC = (adcA1 < 800); bool avByProgram = (program == 17); bool AV_ACTIVE = (mode == MODE_MINUS) ? false : (avByADC || avByProgram); if (AV_ACTIVE) SC_LOW(); else SC_HIGH(); if (mode != MODE_MINUS) MSP_SetInput(AV_ACTIVE); // ------------------------------------------------------- // Anzeige auswählen // ------------------------------------------------------- uint16_t out; if (mode == MODE_MINUS) { out = MINUS_symbol; // MINUS hat höchste Priorität } else if (AV_ACTIVE) { out = AV_symbol; } else if (mode == MODE_NORMAL) { out = makeSymbol(program); } else if (mode == MODE_SEARCH) { if (millis() - lastBlink > 300) { lastBlink = millis(); blinkState = !blinkState; } out = blinkState ? SC_symbol : SC2_symbol; } else if (mode == MODE_BLINK) { if (millis() - lastBlink > 300) { lastBlink = millis(); blinkState = !blinkState; } out = blinkState ? makeSymbol(program) : 0xFFFF; } else if (mode == MODE_MEMORY_BLINK) { out = blinkState ? ST_symbol : 0xFFFF; } // ------------------------------------------------------- // TEST‑Pin – Gerät EIN/AUS // ------------------------------------------------------- if (mode == MODE_MINUS) TEST_LOW(); else TEST_HIGH(); // ------------------------------------------------------- // LED1: LOW wenn SUCHLAUF aktiv ist // ------------------------------------------------------- if (mode == MODE_SEARCH) { if (blinkState) LED1_HIGH(); else LED1_LOW(); } else { LED1_HIGH(); } // ------------------------------------------------------- // LED2: 1 Sekunde LOW nach MEMORY (mit Doppel‑Beep) // ------------------------------------------------------- if (millis() < memoryPulseUntil) { TWI_MSP(0x12, 0x00, 0x14, 0x60, 0x40); // BEEP ON delay(200); TWI_MSP(0x12, 0x00, 0x14, 0x00, 0x00); // BEEP OFF delay(200); TWI_MSP(0x12, 0x00, 0x14, 0x60, 0x40); // BEEP ON delay(200); TWI_MSP(0x12, 0x00, 0x14, 0x00, 0x00); // BEEP OFF LED2_LOW(); } else { LED2_HIGH(); } // ------------------------------------------------------- // PCF8574: Programmplatz binär auf P0–P3 // ------------------------------------------------------- uint8_t pcfProg = pcf_last; // Oberes Nibble (Soundprofil) erhalten pcfProg &= 0xF0; // Unteres Nibble = Programmplatz 0–15 uint8_t progIndex = (program - 1) & 0x0F; pcfProg |= progIndex; if (mode == MODE_MINUS) { TWI_PCF(0x00); // Alles aus im STBY } else { TWI_PCF(pcfProg); } // PWM-DAC Regelung static unsigned long lastRegulate = 0; if (millis() - lastRegulate >= 15) { lastRegulate = millis(); regulatePWM_DACs(); } // ------------------------------------------------------- // Display‑Refresh entkoppelt (200 Hz) // ------------------------------------------------------- currentSymbol = out; if (millis() - lastDisp >= 5) { lastDisp = millis(); showSymbolDimmed(currentSymbol); } }