2018-10-28 22:46:52 +01:00
/* 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 >
2020-02-17 14:00:48 +01:00
* @ date 2018 - 2020
2018-10-28 22:46:52 +01:00
* @ 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 40 /**< signal timing jitter in % tolerated in timing */
/** @} */
volatile bool ir_nec_code_received_flag = false ;
struct ir_nec_code_t ir_nec_code_received ;
/** if the extended address in the code is used
* the extended address uses all 16 - bits instead of having redundant / robust 2 x8 - bits address
*/
static bool ir_nec_extended = false ;
void ir_nec_setup ( bool extended )
{
ir_nec_extended = extended ; // remember setting
// 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
2020-02-17 13:59:49 +01:00
rcc_periph_reset_pulse ( RST_TIM ( IR_NEC_TIMER ) ) ; // reset timer state
2018-10-28 22:46:52 +01:00
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 ( ! ir_nec_extended ) { // the 8-bits address has its inverse
if ( 0xff ! = ( address ^ naddress ) ) { // the address and its inverse do not match
goto error ;
}
}
if ( 0xff ! = ( command ^ ncommand ) ) { // the command and its inverse do not match
goto error ;
}
valid = true ; // remember we have a valid signal
code . repeat = false ; // this is not a repeat code
if ( ir_nec_extended ) {
code . address = ( address < < 8 ) + naddress ;
} else {
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
}