add library to decode IR NEC codes
parent
c5fc45b5a1
commit
0bfae07c30
|
@ -0,0 +1,189 @@
|
|||
/* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
/** library to decode InfraRed NEC code
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @date 2018
|
||||
* @note peripherals used: timer channel @ref ir_nec_timer
|
||||
*/
|
||||
|
||||
/* standard libraries */
|
||||
#include <stdint.h> // standard integer types
|
||||
#include <stdbool.h> // standard boolean type
|
||||
|
||||
/* STM32 (including CM3) libraries */
|
||||
#include <libopencmsis/core_cm3.h> // Cortex M3 utilities
|
||||
#include <libopencm3/cm3/nvic.h> // interrupt handler
|
||||
#include <libopencm3/stm32/rcc.h> // real-time control clock library
|
||||
#include <libopencm3/stm32/gpio.h> // general purpose input output library
|
||||
#include <libopencm3/stm32/timer.h> // timer utilities
|
||||
|
||||
/* own libraries */
|
||||
#include "ir_nec.h" // own definitions
|
||||
#include "global.h" // common methods
|
||||
|
||||
/** @defgroup ir_nec_timer timer peripheral used to measure signal timing for code decoding
|
||||
* @{
|
||||
*/
|
||||
#define IR_NEC_TIMER 4 /**< timer peripheral */
|
||||
#define IR_NEC_CHANNEL 3 /**< channel used as input capture */
|
||||
#define IR_NEC_JITTER 20 /**< signal timing jitter in % tolerated in timing */
|
||||
/** @} */
|
||||
|
||||
volatile bool ir_nec_code_received_flag = false;
|
||||
struct ir_nec_code_t ir_nec_code_received;
|
||||
|
||||
void ir_nec_setup(void)
|
||||
{
|
||||
// setup timer to measure signal timing for bit decoding (use timer channel as input capture)
|
||||
rcc_periph_clock_enable(RCC_TIM_CH(IR_NEC_TIMER, IR_NEC_CHANNEL)); // enable clock for GPIO peripheral
|
||||
rcc_periph_clock_enable(RCC_TIM(IR_NEC_TIMER)); // enable clock for timer peripheral
|
||||
rcc_periph_clock_enable(RCC_AFIO); // enable clock for alternative functions
|
||||
gpio_set(TIM_CH_PORT(IR_NEC_TIMER, IR_NEC_CHANNEL), TIM_CH_PIN(IR_NEC_TIMER, IR_NEC_CHANNEL)); // idle is high (using pull-up resistor)
|
||||
gpio_set_mode(TIM_CH_PORT(IR_NEC_TIMER, IR_NEC_CHANNEL), GPIO_MODE_INPUT, GPIO_CNF_INPUT_PULL_UPDOWN, TIM_CH_PIN(IR_NEC_TIMER, IR_NEC_CHANNEL)); // setup GPIO pin as input
|
||||
timer_reset(TIM(IR_NEC_TIMER)); // reset timer state
|
||||
timer_set_mode(TIM(IR_NEC_TIMER), TIM_CR1_CKD_CK_INT, TIM_CR1_CMS_EDGE, TIM_CR1_DIR_UP); // set timer mode, use undivided timer clock,edge alignment (simple count), and count up
|
||||
// codes are repeated every 110 ms, thus we need to measure at least this duration to detect repeats correctly
|
||||
// the 16-bit timer is by far precise enough to measure the smallest 560 us burst
|
||||
timer_set_prescaler(TIM(IR_NEC_TIMER), (110 * (100 + IR_NEC_JITTER) / 100 * (rcc_ahb_frequency / 1000) / (1 << 16)) + 1 - 1); // set the prescaler so this 16 bits timer allows to wait for 110 ms (+ jitter) from the start signal
|
||||
timer_ic_set_input(TIM(IR_NEC_TIMER), TIM_IC(IR_NEC_CHANNEL), TIM_IC_IN_TI(IR_NEC_CHANNEL)); // configure ICx to use TIn
|
||||
timer_ic_set_filter(TIM(IR_NEC_TIMER), TIM_IC(IR_NEC_CHANNEL), TIM_IC_CK_INT_N_8); // use small filter (noise reduction is more important than timing)
|
||||
timer_ic_set_polarity(TIM(IR_NEC_TIMER), TIM_IC(IR_NEC_CHANNEL), TIM_IC_FALLING); // capture on falling edge (IR bursts are active low on IR demodulators)
|
||||
timer_ic_set_prescaler(TIM(IR_NEC_TIMER), TIM_IC(IR_NEC_CHANNEL), TIM_IC_PSC_OFF); // don't use any prescaler since we want to capture every pulse
|
||||
|
||||
timer_clear_flag(TIM(IR_NEC_TIMER), TIM_SR_UIF); // clear flag
|
||||
timer_update_on_overflow(TIM(IR_NEC_TIMER)); // only use counter overflow as UEV source (use overflow as start time or timeout)
|
||||
timer_enable_irq(TIM(IR_NEC_TIMER), TIM_DIER_UIE); // enable update interrupt for timer
|
||||
|
||||
timer_clear_flag(TIM(IR_NEC_TIMER), TIM_SR_CCIF(IR_NEC_CHANNEL)); // clear input compare flag
|
||||
timer_ic_enable(TIM(IR_NEC_TIMER), TIM_IC(IR_NEC_CHANNEL)); // enable capture interrupt only when IR burst
|
||||
timer_enable_irq(TIM(IR_NEC_TIMER), TIM_DIER_CCIE(IR_NEC_CHANNEL)); // enable capture interrupt
|
||||
|
||||
nvic_enable_irq(NVIC_TIM_IRQ(IR_NEC_TIMER)); // catch interrupt in service routine
|
||||
timer_enable_counter(TIM(IR_NEC_TIMER)); // enable timer
|
||||
}
|
||||
|
||||
/** interrupt service routine called for timer
|
||||
*
|
||||
* @remark normally we want to keep the ISR as short as possible, and do the processing in the main loop,
|
||||
* @remark but because the code needs to be decoded in order to detect repeat burst correctly,
|
||||
* @remark we do the decoding in the ISR and don't trust the user to run the decoding within 42.42 ms (time until the next code is sent)
|
||||
*
|
||||
* @note we don't enforce 110 ms between codes (they can be received earlier), but recognize repeat code after 110 ms
|
||||
*/
|
||||
void TIM_ISR(IR_NEC_TIMER)(void)
|
||||
{
|
||||
static uint8_t burst_count = 0; // the mark or space count
|
||||
static uint32_t burst_start = 0; // time of current mark/space start
|
||||
static uint32_t bits = 0; // the received code bits
|
||||
static struct ir_nec_code_t code; // the last code received (don't trust the user exposed ir_nec_code_received)
|
||||
static bool valid = false; // if the last IR activity is a valid code
|
||||
|
||||
if (timer_get_flag(TIM(IR_NEC_TIMER), TIM_SR_UIF)) { // overflow update event happened
|
||||
timer_clear_flag(TIM(IR_NEC_TIMER), TIM_SR_UIF); // clear flag
|
||||
goto error; // no code or repeat code has been received in time
|
||||
} else if (timer_get_flag(TIM(IR_NEC_TIMER), TIM_SR_CCIF(IR_NEC_CHANNEL))) { // edge detected on input capture
|
||||
uint32_t time = TIM_CCR(IR_NEC_TIMER, IR_NEC_CHANNEL); // save captured bit timing (this also clears the flag)
|
||||
time = (time * (TIM_PSC(TIM(IR_NEC_TIMER)) + 1)) / (rcc_ahb_frequency / 1000000); // calculate time in us
|
||||
if (time < burst_start) { // this should not happen
|
||||
goto error;
|
||||
}
|
||||
time -= burst_start; // calculate mark/space burst time
|
||||
if (0 == burst_count) { // start of very first IR mark for the AGC burst
|
||||
timer_set_counter(TIM(IR_NEC_TIMER), 0); // reset timer counter
|
||||
burst_start = 0; // reset code timer
|
||||
time = 0; // ignore first burst
|
||||
} else if (1 == burst_count) { // end of AGC mark
|
||||
if (time > 9000 * (100 - IR_NEC_JITTER) / 100 && time < 9000 * (100 + IR_NEC_JITTER) / 100) { // AGC mark
|
||||
} else {
|
||||
goto error;
|
||||
}
|
||||
} else if (2 == burst_count) { // end of AGC space
|
||||
if (time > 4500 * (100 - IR_NEC_JITTER) / 100 && time < 4500 * (100 + IR_NEC_JITTER) / 100) { // AGC code space
|
||||
bits = 0; // reset previously received bits
|
||||
valid = false; // invalidate previously received code (since this is not a repeat)
|
||||
} else if (time > 2250 * (100 - IR_NEC_JITTER) / 100 && time < 2250 * (100 + IR_NEC_JITTER) / 100) { // AGC repeat space
|
||||
if (valid) {
|
||||
code.repeat = true;
|
||||
ir_nec_code_received.repeat = code.repeat;
|
||||
ir_nec_code_received.address = code.address;
|
||||
ir_nec_code_received.command = code.command;
|
||||
ir_nec_code_received_flag = true;
|
||||
goto reset; // wait for next code
|
||||
} else {
|
||||
goto error;
|
||||
}
|
||||
} else {
|
||||
goto reset; // not the correct header
|
||||
}
|
||||
} else if (burst_count <= (1 + 32) * 2) { // the code bits
|
||||
if (burst_count % 2) { // bit mark end
|
||||
if (time > 560 * (100 - IR_NEC_JITTER) / 100 && time < 560 * (100 + IR_NEC_JITTER) / 100) { // bit mark
|
||||
} else {
|
||||
goto error;
|
||||
}
|
||||
} else { // bit space end
|
||||
bits <<= 1;
|
||||
if (time > (2250 - 560) * (100 - IR_NEC_JITTER) / 100 && time < (2250 - 560) * (100 + IR_NEC_JITTER) / 100) { // bit 1space
|
||||
bits |= 1; // save bit
|
||||
} else if (time > (1125 - 560) * (100 - IR_NEC_JITTER) / 100 && time < (1125 - 560) * (100 + IR_NEC_JITTER) / 100) { // bit 0 space
|
||||
bits |= 0; // save bit
|
||||
} else {
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
if ((1 + 32) * 2 == burst_count) { // the code is complete
|
||||
uint8_t address = (bits >> 24) & 0xff; // get 8 address bits
|
||||
uint8_t naddress = (bits >> 16) & 0xff; // get negated 8 address bits
|
||||
uint8_t command = (bits >> 8) & 0xff; // get 8 command bits
|
||||
uint8_t ncommand = (bits >> 0) & 0xff; // get negate 8 commend bits
|
||||
if (0xff != (address ^ naddress)) { // the address and its negative do not match
|
||||
goto error;
|
||||
}
|
||||
if (0xff != (command ^ ncommand)) { // the command and its negative do not match
|
||||
goto error;
|
||||
}
|
||||
valid = true; // remember we have a valid signal
|
||||
code.repeat = false; // this is not a repeat code
|
||||
code.address = address; // save decoded address
|
||||
code.command = command; // save decoded command
|
||||
ir_nec_code_received.repeat = code.repeat; // transfer code to user
|
||||
ir_nec_code_received.address = code.address; // transfer code to user
|
||||
ir_nec_code_received.command = code.command; // transfer code to user
|
||||
ir_nec_code_received_flag = true;
|
||||
ir_nec_code_received_flag = true; // notify user about the new code
|
||||
goto reset; // wait for next code
|
||||
}
|
||||
} else { // this should not happen
|
||||
goto error;
|
||||
}
|
||||
if (burst_count % 2) {
|
||||
timer_ic_set_polarity(TIM(IR_NEC_TIMER), TIM_IC(IR_NEC_CHANNEL), TIM_IC_FALLING); // wait for end of space
|
||||
} else {
|
||||
timer_ic_set_polarity(TIM(IR_NEC_TIMER), TIM_IC(IR_NEC_CHANNEL), TIM_IC_RISING); // wait for end of mark
|
||||
}
|
||||
burst_count++; // wait for next burst
|
||||
burst_start += time; // save current burst start
|
||||
} else { // no other interrupt should occur
|
||||
while (true); // unhandled exception: wait for the watchdog to bite
|
||||
}
|
||||
return;
|
||||
error:
|
||||
valid = false; // invalidate previously received code
|
||||
reset:
|
||||
timer_ic_set_polarity(TIM(IR_NEC_TIMER), TIM_IC(IR_NEC_CHANNEL), TIM_IC_FALLING); // wait for next IR mark burst
|
||||
burst_count = 0; // reset state
|
||||
burst_start = 0; // reset state
|
||||
}
|
Loading…
Reference in New Issue