/* firmware for STM8S003-based dachboden badge * Copyright (C) 2019-2022 King Kévin * SPDX-License-Identifier: GPL-3.0-or-later */ #include #include #include #include "stm8s.h" #include "main.h" // enable UART debug #define DEBUG 1 // EEPROM start address #define EEPROM_ADDR 0x4000 // pinout // pin to power IR demodulator (source on) #define IRM_ON_PIN PA3 #define IRM_ON_PORT GPIO_PA // IR demodulator output pin #define IRM_OUT_PIN PC7 // TIM1_CH2 #define IRM_OUT_PORT GPIO_PC // RGB LED pins (sink controlled by nMOS) #define LED_RED_PIN PD3 // TIM2_CH2 #define LED_RED_PORT GPIO_PD #define LED_GREEN_PIN PD2 // TIM2_CH3 #define LED_GREEN_PORT GPIO_PD #define LED_BLUE_PIN PC5 // TIM2_CH1 #define LED_BLUE_PORT GPIO_PC // IR LED pin (source on) #define LED_IR_PIN PC4 // TIM1_CH4 #define LED_IR_PORT GPIO_PC // UV LED pin (source on) #define LED_UV_PIN PC3 // TIM1_CH3 #define LED_UV_PORT GPIO_PC // vibration sensor input (high on vibration) #define SHAKE_PIN PA2 #define SHAKE_PORT GPIO_PA #define SHAKE_IRQ IRQ_EXTI0 // port A // number of vibrations registered static volatile uint16_t shake_count = 0; // number of time counts (+1 @ 488 Hz) static volatile uint32_t time_count = 0; // last time the badge was shook static volatile uint32_t time_shake = 0; // time after last shake to go to sleep, in seconds #define REST_TIME (5 * 60U) // period to share our color code, in seconds #define SHARE_TIME (1U) // period to enforce our color code, in seconds #define MASTER_TIME (1U) // time counts per us (1/(16E6/(3+1)) * 1000*1000 = 0.25 us) #define NEC_TICKS_PER_US 4UL // burst error margin in % #define NEC_ERROR 15 // AGC burst length (9 ms) #define NEC_AGC_BURST (9000 * NEC_TICKS_PER_US) // AGC slot length (9 + 4.5 ms) #define NEC_AGC_SLOT (NEC_AGC_BURST + (4500 * NEC_TICKS_PER_US)) // bit burst length (560 us) #define NEC_BIT_BURST (560 * NEC_TICKS_PER_US) // logical 0 slot length #define NEC_0_SLOT (1125 * NEC_TICKS_PER_US) // logical 1 slot length #define NEC_1_SLOT (2250 * NEC_TICKS_PER_US) #define NEC_AGC_SLOT_MIN (NEC_AGC_SLOT * (100 - NEC_ERROR) / 100U) #define NEC_AGC_SLOT_MAX (NEC_AGC_SLOT * (100 + NEC_ERROR) / 100U) #define NEC_0_SLOT_MIN (NEC_0_SLOT * (100 - NEC_ERROR) / 100U) #define NEC_0_SLOT_MAX (NEC_0_SLOT * (100 + NEC_ERROR) / 100U) #define NEC_1_SLOT_MIN (NEC_1_SLOT * (100 - NEC_ERROR) / 100U) #define NEC_1_SLOT_MAX (NEC_1_SLOT * (100 + NEC_ERROR) / 100U) // bit position in the NEC message (-2 = invalid, -1 = AGC) static volatile int8_t nec_bit = -2; // complete NEC message static volatile uint8_t nec_msg[4] = {0}; // flag set if NEC message has been received static volatile bool nec_flag = false; // set when data is received over UART static volatile char uart_c = 0; // blocking wait (in 10 us steps, up to UINT32_MAX / 10) static void wait_10us(uint32_t us10) { us10 = ((us10 / (1 << CLK->CKDIVR.fields.HSIDIV)) * 1000) / 206; // calibrated for 1 ms while (us10--); // burn energy } void putc(char c) { (void)c; IWDG_KR = IWDG_KR_KEY_REFRESH; // reset watchdog #if DEBUG while (!UART1->SR.fields.TXE); // wait until TX buffer is empty UART1->DR.reg = c; // put character in buffer to be transmitted // don't wait until the transmission is complete #endif } void puts(const char* s) { if (NULL == s) { return; } while (*s) { putc(*s++); } } void putn(uint8_t n) { n &= 0x0f; // ensure it's a nibble if (n < 0xa) { n += '0'; } else { n = 'a' + (n - 0x0a); } putc(n); } void puth(uint8_t h) { putn(h >> 4); putn(h & 0x0f); } // ASCII to nibble (0xff if invalid) static uint8_t a2n(char c) { if (c >= '0' && c <= '9') { return c - '0'; } else if (c >= 'a' && c <= 'f') { return c - 'a' + 0xa; } else if (c >= 'A' && c <= 'F') { return c - 'A' + 0xa; } else { return 0xff; } } // set duty cycle of red LED void led_red(uint16_t bightness) { TIM2->CCR2H.reg = (bightness >> 8); // set duty cycle TIM2->CCR2L.reg = (bightness >> 0); // set duty cycle } // set duty cycle of green LED void led_green(uint16_t bightness) { TIM2->CCR3H.reg = (bightness >> 8); // set duty cycle TIM2->CCR3L.reg = (bightness >> 0); // set duty cycle } // set duty cycle of blue LED void led_blue(uint16_t bightness) { TIM2->CCR1H.reg = (bightness >> 8); // set duty cycle TIM2->CCR1L.reg = (bightness >> 0); // set duty cycle } void led_rgb(uint8_t* rgb) { if (NULL == rgb) { return; } led_red(rgb[0] << 8); led_red(rgb[0] << 8); led_green(rgb[1] << 8); led_green(rgb[1] << 8); led_blue(rgb[2] << 8); // no idea why, but if I don't do it a second time the blue is a bit on when switched off // it is not about the register order or preload led_blue(rgb[2] << 8); } // configure timer to capture IR NEC codes static void timer_ir_in(void) { TIM1->CR1.reg = 0; // disable counter before reconfiguring it TIM1->IER.reg = 0; // reset interrupts TIM1->BKR.reg = 0; // reset register TIM1->CCER1.reg = 0; // reset register TIM1->CCER2.reg = 0; // reset register TIM1->PSCRH.reg = 0; // set prescaler to get most precise 9+4.5 ms TIM1->PSCRL.reg = 3; // 16E6/(3+1)/65536 = up to 16 ms TIM1->ARRH.reg = 0xff; // let it count to the end TIM1->ARRL.reg = 0xff; // an overflow means the signal is corrupted TIM1->CCMR1.input_fields.CC1S = 1; // configure channel as input and map CH1 to TI1FP1 TIM1->CCER1.fields.CC1P = 1; // trigger on a low level or falling edge of TI1F TIM1->CCMR2.input_fields.CC2S = 2; // configure channel as input and map CH2 to TI1FP2 TIM1->CCER1.fields.CC2P = 0; // trigger on a high level or rising edge of TI1F TIM1->SMCR.fields.TS = 5; // set trigger to filtered timer input 1 (TI1FP1) // don't filter the external trigger TIM1->SMCR.fields.SMS = 4; // reset on trigger TIM1->CCER1.fields.CC1E = 1; // enable channel 1 for input capture TIM1->CCER1.fields.CC2E = 1; // enable channel 2 for input capture TIM1->IER.fields.CC1IE = 1; // enable interrupt for channel TIM1->IER.fields.CC2IE = 1; // enable interrupt for channel TIM1->IER.fields.UIE = 1; // enable update interrupt TIM1->CR1.fields.URS = 1; // only update on overflow TIM1->SR1.reg = 0; // clear all flags TIM1->CNTRL.reg = 0; // reset counter TIM1->CNTRH.reg = 0; // reset counter TIM1->EGR.fields.UG = 1; // transfer all registers TIM1->CR1.fields.CEN = 1; // enable counter to start capture nec_bit = -2; // invalidate current packet IRM_ON_PORT->ODR.reg |= IRM_ON_PIN; // switch IR demodulator on } #define TIM1_PERIOD 421U // 16E6/(0+1)/38000 // configure timer to transmit IR burst at 38 kHz static void timer_ir_out(void) { IRM_ON_PORT->ODR.reg &= ~IRM_ON_PIN; // switch IR demodulator off TIM1->CR1.reg = 0; // disable counter before reconfiguring it TIM1->CCER1.reg = 0; // reset register TIM1->CCER2.reg = 0; // reset register TIM1->IER.reg = 0; // reset interrupts TIM1->PSCRH.reg = 0; // set prescaler to get most precise 38 kHz TIM1->PSCRL.reg = 0; // 16E6/(0+1)/65536 = down to 244 Hz TIM1->ARRH.reg = (TIM1_PERIOD >> 8); // set auto-reload register for 38 kHz period TIM1->ARRL.reg = (TIM1_PERIOD & 0xff); // 16E6/(0+1)/38000 TIM1->CCR4H.reg = (TIM1_PERIOD / 3) >> 8; // set duty cycle to 33% TIM1->CCR4L.reg = (TIM1_PERIOD / 3 ) & 0xff; // set duty cycle to 33% TIM1->CCMR4.output_fields.OC4M = 6; // set PWM1 mode TIM1->CCMR4.output_fields.CC4S = 0; // use channel as output TIM1->CCER2.fields.CC4E = 1; // enable channel output TIM1->BKR.fields.MOE = 1; // enable outputs TIM1->SR1.reg = 0; // clear all flags TIM1->CNTRL.reg = 0; // reset counter TIM1->CNTRH.reg = 0; // reset counter TIM1->CR1.fields.OPM = 1; // send one pulse at a time TIM1->EGR.fields.UG = 1; // transfer all registers // don't enable timer yet } // transmit IR pulses static void nec_pulse(uint16_t pulses, bool mark) { if (mark) { TIM1->CCR4H.reg = (TIM1_PERIOD / 3) >> 8; // set duty cycle to 33% TIM1->CCR4L.reg = (TIM1_PERIOD / 3 ) & 0xff; // set duty cycle to 33% } else { TIM1->CCR4H.reg = 0; // set duty cycle to 0% TIM1->CCR4L.reg = 0; // set duty cycle to 0% } while (pulses--) { TIM1->CR1.fields.CEN = 1; // enable counter to start PWM for 1 pulse while (TIM1->CR1.fields.CEN); // wait until pulse completes } // ensure we are off at the end TIM1->CCR4H.reg = 0; // set duty cycle to 0% TIM1->CCR4L.reg = 0; // set duty cycle to 0% } // transmit 4 byte NEC code over IR (LSb first) static void nec_transmit(uint32_t code) { if (NULL == code) { return; } timer_ir_out(); // configure to transmit pulses sim(); // disable interrupts to keep timings tight // all time are hand tuned nec_pulse(333, true); // send AGC burst, 9 ms nec_pulse(166, false); // AGC space, 4.5 ms // transmit bits for (uint8_t i = 0; i < 32; i++) { nec_pulse(21, true); // bit burst, 560 us if (code & 0x1) { nec_pulse(61, false); // bit space, 2.25 ms - 560 us } else { nec_pulse(21, false); // bit space, 1.12 ms - 560 us } code >>= 1; // go to next bit } nec_pulse(22, true); // end pulse, 560 us rim(); // re-enable interrupts } void main(void) { bool master = false; // if we are not a slave badge, but master controller sim(); // disable interrupts (while we reconfigure them) CLK->CKDIVR.fields.HSIDIV = CLK_CKDIVR_HSIDIV_DIV0; // don't divide internal 16 MHz clock CLK->CKDIVR.fields.CPUDIV = CLK_CKDIVR_CPUDIV_DIV0; // don't divide CPU frequency to 16 MHz while (!CLK->ICKR.fields.HSIRDY); // wait for internal oscillator to be ready // only power used peripherals CLK_PCKENR1 = CLK_PCKENR1_UART1234 | CLK_PCKENR1_TIM1 | CLK_PCKENR1_TIM25 | CLK_PCKENR1_TIM46; CLK_PCKENR2 = 0; // configure option bytes // disable DATA (e.g. option byte) write protection if (0 == (FLASH_IAPSR & FLASH_IAPSR_DUL)) { FLASH_DUKR = FLASH_DUKR_KEY1; FLASH_DUKR = FLASH_DUKR_KEY2; } FLASH_CR2 |= FLASH_CR2_OPT; // set option bytes programming FLASH_NCR2 &= ~FLASH_NCR2_NOPT; // set option bytes programming OPT->OPT2.fields.AFR0 = 1; // remap TIM2_CH1 to PC5, TIM1_CH1 to C6, and TIM1_CH2 to C7 OPT->NOPT2.fields.NAFR0 = 0; // set complementary option byte OPT->OPT2.fields.AFR1 = 1; // remap TIM2_CH3 to PD2 OPT->NOPT2.fields.NAFR1 = 0; // set complementary option byte while (!(FLASH_IAPSR & FLASH_IAPSR_EOP)); // wait for write to complete FLASH_IAPSR &= ~FLASH_IAPSR_DUL; // re-enable write protection // configure UART for debug output UART1->CR1.fields.M = 0; // 8 data bits UART1->CR3.fields.STOP = 0; // 1 stop bit UART1->BRR2.reg = 0x0B; // set baud rate to 115200 (at 16 MHz) UART1->BRR1.reg = 0x08; // set baud rate to 115200 (at 16 MHz) UART1->CR2.fields.TEN = 1; // enable TX UART1->CR2.fields.REN = 1; // enable RX UART1->CR2.fields.RIEN = 1; // enable RX interrupt char uart_cmd[10]; // buffer for received data uint8_t uart_used = 0; // how much of the buffer is used // configure IR demodulator pin IRM_ON_PORT->ODR.reg &= ~IRM_ON_PIN; // switch IR demodulator off IRM_ON_PORT->CR1.reg |= IRM_ON_PIN; // use as push-pull IRM_ON_PORT->DDR.reg |= IRM_ON_PIN; // switch pin to output /* use PWM instead of GPIO for controlling RGB LED LED_RED_PORT->ODR.reg &= ~LED_RED_PIN; // switch LED off LED_RED_PORT->CR1.reg |= LED_RED_PIN; // use as push-pull LED_RED_PORT->DDR.reg |= LED_RED_PIN; // use pin to output LED_GREEN_PORT->ODR.reg &= ~LED_GREEN_PIN; // switch LED off LED_GREEN_PORT->CR1.reg |= LED_GREEN_PIN; // use as push-pull LED_GREEN_PORT->DDR.reg |= LED_GREEN_PIN; // use pin to output LED_BLUE_PORT->ODR.reg &= ~LED_BLUE_PIN; // switch LED off LED_BLUE_PORT->CR1.reg |= LED_BLUE_PIN; // use as push-pull LED_BLUE_PORT->DDR.reg |= LED_BLUE_PIN; // use pin to output */ // configure timer 2 for PWM-controlling RGB LED TIM2->PSCR.fields.PSC = 0; // set prescaler to to 244 Hz, 16E6/(2**0)/65536 = 244 Hz TIM2->ARRH.reg = 0xff; // set period to max for most precisions TIM2->ARRL.reg = 0xff; // set period to max for most precisions TIM2->CCMR1.output_fields.OC1M = 6; // set PWM1 mode TIM2->CCMR1.output_fields.CC1S = 0; // use channel as output TIM2->CCER1.fields.CC1E = 1; // enable channel output led_blue(0); // switch off blue LED TIM2->CCMR2.output_fields.OC2M = 6; // set PWM1 mode TIM2->CCMR2.output_fields.CC2S = 0; // use channel as output TIM2->CCER1.fields.CC2E = 1; // enable channel output led_red(0); // switch off red LED TIM2->CCMR3.output_fields.OC3M = 6; // set PWM1 mode TIM2->CCMR3.output_fields.CC3S = 0; // use channel as output TIM2->CCER2.fields.CC3E = 1; // enable channel output led_green(0); // switch off green LED TIM2->EGR.fields.UG = 1; // transfer all registers TIM2->CR1.fields.CEN = 1; // enable counter to start PWM // load color uint8_t rgb[3]; // eyes color rgb[0] = *(uint8_t*)(EEPROM_ADDR + 0); // load red color rgb[1] = *(uint8_t*)(EEPROM_ADDR + 1); // load green color rgb[2] = *(uint8_t*)(EEPROM_ADDR + 2); // load blue color led_rgb(rgb); // set color // configure UV LED LED_UV_PORT->ODR.reg &= ~LED_UV_PIN; // switch LED off LED_UV_PORT->CR1.reg |= LED_UV_PIN; // use as push-pull LED_UV_PORT->DDR.reg |= LED_UV_PIN; // use pin to output // configure vibration sensor input SHAKE_PORT->CR1.reg &= ~SHAKE_PIN; // leave floating (pulled down externally) SHAKE_PORT->DDR.reg &= ~SHAKE_PIN; // set as input SHAKE_PORT->CR2.reg |= SHAKE_PIN; // enable external input EXTI->CR1.fields.PAIS = EXTI_RISING_EDGE; // interrupt when vibration is detected shake_count = 0; // reset counter // use timer 4 (8-bit) as timeout counter TIM4->PSCR.fields.PSC = 7; // make it as slow as possible 16E6 / 2**7 = 125 kHz, / 256 = 488 Hz TIM4->CNTR.fields.CNT = 0; // reset counter TIM4->IER.fields.UIE = 1; // enable update interrupt time_count = 0; // reset time counter TIM4->CR1.fields.URS = 1; // only update on overflow TIM4->CR1.fields.CEN = 1; // enable counter // configure timer to receive IR message timer_ir_in(); /* don't use the AWU, else it will cause an active-halt instead of halt, using more power // configure auto-wakeup (AWU) to be able to refresh the watchdog // 128 kHz LSI used by default in option bytes CKAWUSEL // we skip measuring the LS clock frequency since there is no need to be precise AWU->TBR.fields.AWUTB = 10; // interval range: 128-256 ms AWU->APR.fields.APR = 0x3e; // set time to 256 ms AWU->CSR.fields.AWUEN = 1; // enable AWU (start only when entering wait or active halt mode) */ /* don't use IWDG since it wakes up from HALT mode and uses (a little) power use WWDG instead // configure independent watchdog (very loose, just it case the firmware hangs) IWDG->KR.fields.KEY = IWDG_KR_KEY_REFRESH; // reset watchdog IWDG->KR.fields.KEY = IWDG_KR_KEY_ENABLE; // start watchdog IWDG->KR.fields.KEY = IWDG_KR_KEY_ACCESS; // allows changing the prescale IWDG->PR.fields.PR = IWDG_PR_DIV256; // set prescale to longest time (1.02s) IWDG->KR.fields.KEY = IWDG_KR_KEY_REFRESH; // reset watchdog */ rim(); // re-enable interrupts bool action = false; // if an action has been performed puts("\r\nready\r\n"); while (true) { IWDG_KR = IWDG_KR_KEY_REFRESH; // reset watchdog if (shake_count) { puts("vibrations: "); puth(shake_count); puts("\r\n"); time_shake = time_count; // remember time to stay awake shake_count = 0; // reset count } if (nec_flag) { puts("\r\nr"); puth(nec_msg[0]); puth(nec_msg[1]); puth(nec_msg[2]); puth(nec_msg[3]); puts("\r\n"); if (0x80 == nec_msg[0] && 0x7f == nec_msg[1]) { // radio remote if (0x04 == nec_msg[2] && 0xfb == nec_msg[3]) { // 1 rgb[0] = 0x80; rgb[1] = 0; rgb[2] = 0; } else if (0x05 == nec_msg[2] && 0xfa == nec_msg[3]) { // 2 rgb[0] = 0; rgb[1] = 0x80; rgb[2] = 0; } else if (0x06 == nec_msg[2] && 0xf9 == nec_msg[3]) { // 3 rgb[0] = 0; rgb[1] = 0; rgb[2] = 0x80; } else if (0x01 == nec_msg[2] && 0xfe == nec_msg[3]) { // mute LED_UV_PORT->ODR.reg |= LED_UV_PIN; // switch UV LED on } else if (0x12 == nec_msg[2] && 0xed == nec_msg[3]) { // power LED_UV_PORT->ODR.reg &= ~LED_UV_PIN; // switch UV LED off rgb[0] = 0; rgb[1] = 0; rgb[2] = 0; } led_rgb(rgb); // ensure [new] color is set } else if (0x01 == nec_msg[0]) { // badge tries to influence us for (uint8_t i = 0; i < 3; i++) { if (nec_msg[1 + i] > rgb[i]) { rgb[i]++; } else if (nec_msg[1 + i] < rgb[i]) { rgb[i]--; } } led_rgb(rgb); // set new color } else if (0x02 == nec_msg[0]) { // master sets our color rgb[0] = nec_msg[1]; rgb[1] = nec_msg[2]; rgb[2] = nec_msg[3]; led_rgb(rgb); // set new color } nec_flag = false; // clear flag action = true; // redo loop } if (!master && 0 == time_count % (488UL * SHARE_TIME)) { uint32_t code = 0x01; // code to send code |= ((uint32_t)rgb[0] << 8); code |= ((uint32_t)rgb[1] << 16); code |= ((uint32_t)rgb[2] << 24); nec_transmit(code); timer_ir_in(); // go back to IR capture putc('t'); action = true; // redo main loop } if (master && 0 == time_count % (488UL * MASTER_TIME)) { uint32_t code = 0x02; // code to send code |= ((uint32_t)rgb[0] << 8); code |= ((uint32_t)rgb[1] << 16); code |= ((uint32_t)rgb[2] << 24); nec_transmit(code); putc('m'); action = true; // redo main loop } if (time_count > time_shake + 488UL * REST_TIME && !master) { LED_UV_PORT->ODR.reg &= ~LED_UV_PIN; // switch UV LED off IRM_ON_PORT->ODR.reg &= ~IRM_ON_PIN; // switch IR demodulator off led_red(0); // ensure LED is off led_green(0); // ensure LED is off led_blue(0); // ensure LED is off // save color if (rgb[0] != *(uint8_t*)(EEPROM_ADDR + 0) || rgb[1] != *(uint8_t*)(EEPROM_ADDR + 1) || rgb[2] != *(uint8_t*)(EEPROM_ADDR + 2)) { // disable DATA (e.g. EEPROM) write protection if (0 == (FLASH_IAPSR & FLASH_IAPSR_DUL)) { FLASH_DUKR = FLASH_DUKR_KEY1; FLASH_DUKR = FLASH_DUKR_KEY2; } for (uint8_t i = 0; i < 3; i++) { *(uint8_t*)(EEPROM_ADDR + i) = rgb[i]; while (!(FLASH_IAPSR & FLASH_IAPSR_EOP)); // wait until programming is complete } FLASH_IAPSR &= ~FLASH_IAPSR_DUL; // re-enable write protection } puts("rest\r\n\n"); halt(); IRM_ON_PORT->ODR.reg |= IRM_ON_PIN; // switch IR demodulator on led_rgb(rgb); // set color time_shake = 0; // reset stay awake time time_count = 0; // reset counter } if (uart_c) { // data received over UART putc(uart_c); // echo back if ('\r' == uart_c || '\n' == uart_c) { // end of line received if (uart_used >= 9 && 'T' == uart_cmd[0]) { // transmit command bool code_valid = true; // verify it it's really a hex string uint32_t code = 0; // parsed code for (uint8_t i = 0; i < 8; i++) { uint32_t n = a2n(uart_cmd[1 + i]); if (n > 0xf) { code_valid = false; break; } else { code |= (n << (i * 4)); } } if (code_valid) { nec_transmit(code); // transmit code timer_ir_in(); // go back to IR capture puts("\r\ncode transmitted\r\n"); } } else if (uart_used >= 7 && 'M' == uart_cmd[0]) { // switch to master bool code_valid = true; // verify it it's really a hex string uint32_t code = 0x02000000; // parsed master code for (uint8_t i = 0; i < 6; i++) { uint32_t n = a2n(uart_cmd[1 + i]); if (n > 0xf) { code_valid = false; break; } else { code |= (n << ((i + 1) * 4)); } } if (code_valid) { rgb[0] = code >> 8; // save color rgb[1] = code >> 16; // save color rgb[2] = code >> 24; // save color led_rgb(rgb); // set color nec_transmit(code); // transmit code if (!master) { master = true; // switch to master mode IRM_ON_PORT->ODR.reg &= ~IRM_ON_PIN; // switch IR demodulator off puts("\r\nmaster set\r\n"); } } } uart_used = 0; // reset buffer } else if (uart_used < ARRAY_LENGTH(uart_cmd)) { uart_cmd[uart_used++] = uart_c; } uart_c = 0; // clear flag action = true; // redo main loop } if (action) { // something has been performed, check if other flags have been set meanwhile action = false; // clear flag } else { // nothing down wfi(); // go to sleep (wait for any interrupt, including periodic AWU) } } } // auto wakeup void awu(void) __interrupt(IRQ_AWU) { volatile uint8_t awuf = AWU_CSR; // clear interrupt flag by reading it (reading is required, and volatile prevents compiler optimization) // let the main loop kick the dog } // vibration sensor input void shake_isr(void) __interrupt(SHAKE_IRQ) { shake_count++; // register vibration } // system time counter void time_isr(void) __interrupt(IRQ_TIM4) { TIM4->SR.fields.UIF = 0; // clear flag time_count++; // remember overflow to count time } // IR capture timer counter void nec_uo_isr(void) __interrupt(IRQ_TIM1_UO) { if (TIM1->SR1.fields.UIF) { // timer overflow nec_bit = -2; // set to invalid message since it took longer than NEC slot TIM1->SR1.fields.UIF = 0; // clear flag } } // IR capture timer counter void nec_cc_isr(void) __interrupt(IRQ_TIM1_CC) { static uint32_t nec_bits = 0; // temporary buffer to construct message if (TIM1->SR1.fields.CC1IF) { // start of burst const uint16_t slot = (TIM1->CCR1H.reg << 8) + TIM1->CCR1L.reg; if (-1 == nec_bit) { // AGC received nec_bits = 0; // reset message if (slot < NEC_AGC_SLOT_MIN || slot > NEC_AGC_SLOT_MAX) { nec_bit = -2; // slot invalid } } else if (nec_bit >= 0 && nec_bit < 32) { if (slot >= NEC_0_SLOT_MIN && slot <= NEC_0_SLOT_MAX) { // bit should already be 0 } else if (slot >= NEC_1_SLOT_MIN && slot <= NEC_1_SLOT_MAX) { nec_bits |= (1UL << nec_bit); } else { nec_bit = -2; // slot invalid } } nec_bit++; // start the bit TIM1->SR1.fields.CC1IF = 0; // clear flag } if (TIM1->SR1.fields.CC2IF) { // start of pause if (32 == nec_bit) { nec_msg[0] = (nec_bits >> 0); // save message nec_msg[1] = (nec_bits >> 8); // save message nec_msg[2] = (nec_bits >> 16); // save message nec_msg[3] = (nec_bits >> 24); // save message nec_flag = true; // notify we received a message nec_bit = -2; // restart message } else if (nec_bit >= 0) { // here we could verify the burst length } TIM1->SR1.fields.CC2IF = 0; // clear flag } } // UART RX interrupt void rx_isr(void) __interrupt(IRQ_UART1_RX) { if (UART1->SR.fields.RXNE) { // there is data uart_c = UART1->DR.reg; // read received data, also clears flag } }