main: add IR NEC decoding
This commit is contained in:
parent
0e7914ad9d
commit
d54b5f1d26
132
main.c
132
main.c
|
@ -45,6 +45,35 @@ static volatile uint32_t time_count = 0;
|
|||
// time after last shake to go to sleep, in seconds
|
||||
#define REST_TIME (5 * 60U)
|
||||
|
||||
// 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;
|
||||
|
||||
// blocking wait (in 10 us steps, up to UINT32_MAX / 10)
|
||||
static void wait_10us(uint32_t us10)
|
||||
{
|
||||
|
@ -205,11 +234,12 @@ void main(void)
|
|||
// 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 interrupt
|
||||
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 modulate IR LEDs at 38 kHz
|
||||
TIM1->PSCRH.reg = 0; // set prescaler to get most precise 38 kHz
|
||||
TIM1->PSCRL.reg = 0; // 16E6/(0+1)/65536 = down to 244 Hz
|
||||
|
@ -224,6 +254,32 @@ void main(void)
|
|||
TIM1->BKR.fields.MOE = 1; // enable outputs
|
||||
TIM1->EGR.fields.UG = 1; // transfer all registers
|
||||
TIM1->CR1.fields.CEN = 1; // enable counter to start PWM
|
||||
*/
|
||||
|
||||
// configure timer to receive IR message
|
||||
TIM1->CR1.fields.CEN = 0; // disable counter before reconfiguring it
|
||||
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->CCER2.fields.CC3E = 0; // ensure other channel is not used
|
||||
TIM1->CCER2.fields.CC4E = 0; // ensure other channel is not used
|
||||
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->EGR.fields.UG = 1; // transfer all registers
|
||||
TIM1->CR1.fields.CEN = 1; // enable counter to start capture
|
||||
|
||||
/* 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
|
||||
|
@ -257,6 +313,36 @@ void main(void)
|
|||
puts("\r\n");
|
||||
shake_count = 0; // reset count
|
||||
}
|
||||
if (nec_flag) {
|
||||
puth(nec_msg[0]);
|
||||
puth(nec_msg[1]);
|
||||
puth(nec_msg[2]);
|
||||
puth(nec_msg[3]);
|
||||
puts("\r\n");
|
||||
if (0x00 == nec_msg[0] && 0xff == nec_msg[1]) { // radio remote
|
||||
if (0x08 == nec_msg[2] && 0xf6 == nec_msg[3]) { // 1
|
||||
led_red(0x8000);
|
||||
led_green(0);
|
||||
led_blue(0);
|
||||
} else if (0x0a == nec_msg[2] && 0xf4 == nec_msg[3]) { // 2
|
||||
led_red(0);
|
||||
led_green(0x8000);
|
||||
led_blue(0);
|
||||
} else if (0x0c == nec_msg[2] && 0xf2 == nec_msg[3]) { // 3
|
||||
led_red(0);
|
||||
led_green(0);
|
||||
led_blue(0x8000);
|
||||
} else if (0x02 == nec_msg[2] && 0xfc == nec_msg[3]) { // mute
|
||||
LED_UV_PORT->ODR.reg |= LED_UV_PIN; // switch UV LED on
|
||||
} else if (0x24 == nec_msg[2] && 0xda == nec_msg[3]) { // power
|
||||
LED_UV_PORT->ODR.reg &= ~LED_UV_PIN; // switch UV LED off
|
||||
led_red(0);
|
||||
led_green(0);
|
||||
led_blue(0);
|
||||
}
|
||||
}
|
||||
nec_flag = false; // clear flag
|
||||
}
|
||||
if (time_count > 488UL * REST_TIME) {
|
||||
puts("rest\r\n");
|
||||
time_count = 0; // reset counter
|
||||
|
@ -293,3 +379,47 @@ 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) { // slot longer than NEC slot
|
||||
nec_bit = -2; // set to invalid message
|
||||
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;
|
||||
nec_bit++; // start the bit
|
||||
if (0 == 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 (32 == nec_bit) { // trailing pulse received
|
||||
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) {
|
||||
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
|
||||
}
|
||||
}
|
||||
TIM1->SR1.fields.CC1IF = 0; // clear flag
|
||||
}
|
||||
if (TIM1->SR1.fields.CC2IF) {
|
||||
TIM1->SR1.fields.CC2IF = 0; // clear flag
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue