/*! \file rc5_decode.c \brief the rc5 infrared decoding file */ /* Copyright (C) 2017 GigaDevice 2017-06-06, V1.0.0, firmware for GD32F3x0 */ #include "rc5_decode.h" #include "ir_decode.h" /* logic table for rising edge: every line has values corresponding to previous bit. in columns are actual bit values for given bit time. */ const trc5_last_bit_type rc5_logic_table_rising_edge[2][2] = { /* last_bit = ZERO */ {RC5_ZER ,RC5_INV}, /* last_bit = ONE */ {RC5_NAN ,RC5_ZER}, }; /* logic table for falling edge: every line has values corresponding to previous bit. in columns are actual bit values for given bit time. */ const trc5_last_bit_type rc5_logic_table_falling_edge[2][2] = { /* last_bit = ZERO */ {RC5_NAN ,RC5_ONE}, /* last_bit = ONE */ {RC5_ONE ,RC5_INV}, }; /* rc5 frame state */ __IO status_yes_or_no rc5_frame_received = NO; /* first empty packet */ __IO trc5_packet_struct rc5_tmp_packet; /* rc5 bits time definitions */ uint16_t rc5_mint = 0; uint16_t rc5_maxt = 0; uint16_t rc5_min2t = 0; uint16_t rc5_max2t = 0; uint32_t rc5_data = 0; /* timer clock */ static uint32_t timer_clk_value_khz = 0; static uint8_t rc5_get_pulse_length (uint16_t pulse_length); static void rc5_modify_last_bit(trc5_last_bit_type bit); static void rc5_write_bit(uint8_t bit_val); static uint32_t timer_get_counter_clk_value(void); /*! \brief de-initializes the peripherals \param[in] none \param[out] none \retval none */ void rc5_decode_deinit(void) { timer_deinit(IR_TIMER); gpio_deinit(IR_GPIO_PORT); } /*! \brief initialize the rc5 decoder module \param[in] none \param[out] none \retval none */ void rc5_decode_init(void) { __IO uint16_t rc5_time_out = 0; /* clock configuration for TIMER */ rcu_periph_clock_enable(IR_TIMER_CLK); /* enable GPIO clock */ rcu_periph_clock_enable(IR_GPIO_PORT_CLK); /* IR_decode */ /* GD32330R-EVAL:TIMER2_CH0 PC6 GD32350R-EVAL:TIMER2_CH1 PA7 */ { /* configure the GPIO ports */ gpio_mode_set(IR_GPIO_PORT, GPIO_MODE_AF, GPIO_PUPD_NONE, IR_GPIO_PIN); gpio_output_options_set(IR_GPIO_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ,IR_GPIO_PIN); } /* enable the TIMER global interrupt */ nvic_irq_enable(IR_TIMER_IRQn, 0, 1); { timer_ic_parameter_struct timer_icinitpara; timer_parameter_struct timer_initpara; /* deinit IR_TIMER */ rcu_periph_clock_enable(IR_TIMER_CLK); /* TIMER0 configuration */ timer_deinit(IR_TIMER); /* timer base configuration for timer IR_TIMER */ timer_initpara.prescaler = 0x0; timer_initpara.alignedmode = TIMER_COUNTER_EDGE; timer_initpara.counterdirection = TIMER_COUNTER_UP; timer_initpara.period = 0x00; timer_initpara.clockdivision = TIMER_CKDIV_DIV1; timer_initpara.repetitioncounter = 0; timer_init(IR_TIMER,&timer_initpara); /* prescaler configuration */ timer_prescaler_config(IR_TIMER,TIMER_PRESCALER,TIMER_PSC_RELOAD_NOW); /* TIMER2 CH0 input capture configuration */ timer_icinitpara.icpolarity = TIMER_IC_POLARITY_FALLING; timer_icinitpara.icselection = TIMER_IC_SELECTION_DIRECTTI; timer_icinitpara.icprescaler = TIMER_IC_PSC_DIV1; timer_icinitpara.icfilter = 0x0; timer_input_pwm_capture_config(IR_TIMER,IR_TIMER_Channel,&timer_icinitpara); } /* timer clock */ timer_clk_value_khz = timer_get_counter_clk_value()/1000; /* select the TIMER input trigger */ timer_input_trigger_source_select(IR_TIMER, TIMER_SMCFG_TRGSEL_CI0FE0); /* select the slave mode: reset mode */ timer_slave_mode_select(IR_TIMER, TIMER_SLAVE_MODE_RESTART); /* configures the TIMER update request interrupt source: counter overflow */ timer_update_source_config( IR_TIMER, TIMER_UPDATE_SRC_REGULAR ); rc5_time_out = timer_clk_value_khz * RC5_TIME_OUT_US/1000; /* set the TIMER auto-reload register for each IR protocol */ TIMER_CAR(IR_TIMER) = rc5_time_out; /* clear flag */ timer_interrupt_flag_clear(IR_TIMER, TIMER_INT_UP | TIMER_INT_CH0 | TIMER_INT_CH1); /* enable the CH1 interrupt request */ timer_interrupt_enable(IR_TIMER, TIMER_INT_CH1); /* enable the CH0 interrupt request */ timer_interrupt_enable(IR_TIMER, TIMER_INT_CH0); /* enable the timer */ timer_enable(IR_TIMER); /* bit time range */ rc5_mint = ( RC5_T_US - RC5_T_TOLERANCE_US ) * timer_clk_value_khz / 1000 ; rc5_maxt = ( RC5_T_US + RC5_T_TOLERANCE_US ) * timer_clk_value_khz / 1000 ; rc5_min2t = ( 2 * RC5_T_US - RC5_T_TOLERANCE_US ) * timer_clk_value_khz / 1000 ; rc5_max2t = ( 2 * RC5_T_US + RC5_T_TOLERANCE_US ) * timer_clk_value_khz / 1000 ; /* default state */ rc5_reset_packet(); } /*! \brief decode the IR frame when all the frame is received, the rc5_frame_received will equal to YES \param[in] rc5_frame: pointer to rc5_frame_struct structure that contains the IR protocol fields \param[out] none \retval none */ void rc5_decode(rc5_frame_struct *rc5_frame) { /* if frame received */ if(rc5_frame_received != NO){ rc5_data = rc5_tmp_packet.data ; /* rc5 frame field decoding */ rc5_frame->address = (rc5_tmp_packet.data >> 6) & 0x1F; rc5_frame->command = (rc5_tmp_packet.data) & 0x3F; rc5_frame->field_bit = (rc5_tmp_packet.data >> 12) & 0x1; rc5_frame->toggle_bit = (rc5_tmp_packet.data >> 11) & 0x1; /* check if command ranges between 64 to 127:upper field */ if(rc5_frame->field_bit == 0x00){ rc5_frame->command = (1<<6)| rc5_frame->command; } /* default state */ rc5_frame_received = NO; rc5_reset_packet(); } } /*! \brief set the incoming packet structure to default state \param[in] none \param[out] none \retval none */ void rc5_reset_packet(void) { rc5_tmp_packet.data = 0; rc5_tmp_packet.bit_count = RC5_PACKET_BIT_COUNT - 1; rc5_tmp_packet.last_bit = RC5_ONE; rc5_tmp_packet.status = RC5_PACKET_STATUS_EMPTY; } /*! \brief identify the rc5 data bits \param[in] raw_pulse_length: low/high pulse duration \param[in] edge: '1' for rising or '0' for falling edge \param[out] none \retval none */ void rc5_data_sampling(uint16_t raw_pulse_length, uint8_t edge) { uint8_t pulse; trc5_last_bit_type tmp_last_bit; /* decode the pulse length in protocol units */ pulse = rc5_get_pulse_length(raw_pulse_length); /* on rising edge */ if(1 == edge){ if(pulse <= RC5_2T_TIME){ /* bit determination by the rising edge */ tmp_last_bit = rc5_logic_table_rising_edge[rc5_tmp_packet.last_bit][pulse]; rc5_modify_last_bit(tmp_last_bit); }else{ rc5_reset_packet(); } } /* on falling edge */ else { /* if this is the first falling edge - don't compute anything */ if(rc5_tmp_packet.status & RC5_PACKET_STATUS_EMPTY){ rc5_tmp_packet.status &= (uint8_t)~RC5_PACKET_STATUS_EMPTY; }else{ if(pulse <= RC5_2T_TIME){ /* bit determination by the falling edge */ tmp_last_bit = rc5_logic_table_falling_edge[rc5_tmp_packet.last_bit][pulse]; rc5_modify_last_bit(tmp_last_bit); }else{ rc5_reset_packet(); } } } } /*! \brief convert raw pulse length expressed in timer ticks to protocol bit times \param[in] pulse_length: pulse duration \param[out] none \retval bit time value */ static uint8_t rc5_get_pulse_length (uint16_t pulse_length) { /* valid bit time */ if((pulse_length > rc5_mint) && (pulse_length < rc5_maxt)){ /* found the length,return the correct value */ return (RC5_1T_TIME); }else if((pulse_length > rc5_min2t) && (pulse_length < rc5_max2t)){ /* found the length,return the correct value */ return (RC5_2T_TIME); } /* error */ return RC5_WRONG_TIME; } /*! \brief perform checks if the last bit was not incorrect \param[in] bit: where bit can be RC5_NAN or RC5_INV or RC5_ZER or RC5_ONE \param[out] none \retval none */ static void rc5_modify_last_bit(trc5_last_bit_type bit) { if(RC5_NAN != bit){ if(RC5_INV != rc5_tmp_packet.last_bit){ /* restore the last bit */ rc5_tmp_packet.last_bit = bit; /* insert one bit into the rc5 packet */ rc5_write_bit(rc5_tmp_packet.last_bit); }else{ rc5_reset_packet(); } } } /*! \brief insert one bit into the final data word \param[in] bit_val: bit value 'RC5_ONE' or 'RC5_ZER' \param[out] none \retval none */ static void rc5_write_bit(uint8_t bit_val) { /* first convert rc5 symbols to ones and zeros */ if(bit_val == RC5_ONE){ bit_val = 1; }else if(bit_val == RC5_ZER){ bit_val = 0; }else{ rc5_reset_packet(); return; } /* write this particular bit to data field */ rc5_tmp_packet.data |= bit_val; /* test the bit number determined */ /* if this is not the last bit */ if(0 != rc5_tmp_packet.bit_count){ /* shift the data field */ rc5_tmp_packet.data = rc5_tmp_packet.data << 1; /* decrement the bit_count */ rc5_tmp_packet.bit_count--; }else{ rc5_frame_received = YES; } } /*! \brief identify TIMER clock \param[in] none \param[out] none \retval timer clock */ static uint32_t timer_get_counter_clk_value(void) { uint32_t apb_prescaler = 0, apb_frequency = 0; uint32_t timer_prescaler = 0; rcu_clock_freq_get(CK_APB1); /* get the clock prescaler of APB1 */ apb_prescaler = ((RCU_CFG0>> 8) & 0x7); apb_frequency=rcu_clock_freq_get(CK_APB1); timer_prescaler = TIMER_PRESCALER; /* if APBx clock div >= 4 */ if(apb_prescaler >= 4){ return((apb_frequency * 2)/(timer_prescaler + 1)); }else{ return(apb_frequency/(timer_prescaler+ 1)); } }