376 lines
12 KiB
C
376 lines
12 KiB
C
/*!
|
|
\file rc5_decode.c
|
|
\brief the rc5 infrared decoding file
|
|
|
|
\version 2021-10-30, V1.0.0, firmware for GD32W51x
|
|
*/
|
|
|
|
/*
|
|
Copyright (c) 2021, GigaDevice Semiconductor Inc.
|
|
|
|
All rights reserved.
|
|
|
|
Redistribution and use in source and binary forms, with or without modification,
|
|
are permitted provided that the following conditions are met:
|
|
|
|
1. Redistributions of source code must retain the above copyright notice, this
|
|
list of conditions and the following disclaimer.
|
|
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
this list of conditions and the following disclaimer in the documentation
|
|
and/or other materials provided with the distribution.
|
|
3. Neither the name of the copyright holder nor the names of its contributors
|
|
may be used to endorse or promote products derived from this software without
|
|
specific prior written permission.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
|
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
|
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
|
OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#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;
|
|
rcu_timer_clock_prescaler_config(RCU_TIMER_PSC_MUL4);
|
|
/* clock configuration for TIMER */
|
|
rcu_periph_clock_enable(IR_TIMER_CLK);
|
|
/* enable GPIO clock */
|
|
rcu_periph_clock_enable(IR_GPIO_PORT_CLK);
|
|
|
|
/* IR_decode */
|
|
/* GD32W515P-EVAL:TIMER3_CH1 PB7 */
|
|
{
|
|
/*configure PB7 (TIMER3 CH1) as alternate function*/
|
|
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_10MHZ , IR_GPIO_PIN);
|
|
gpio_af_set(IR_GPIO_PORT, GPIO_AF_2, 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 */
|
|
|
|
/* TIMER3 configuration */
|
|
timer_deinit(IR_TIMER);
|
|
|
|
/* timer base configuration for timer IR_TIMER */
|
|
timer_initpara.prescaler = 0x00;
|
|
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, 179, 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 */
|
|
#if defined TIMER_CHANNEL1
|
|
timer_input_trigger_source_select(IR_TIMER, TIMER_SMCFG_TRGSEL_CI1FE1);
|
|
#else
|
|
timer_input_trigger_source_select(IR_TIMER, TIMER_SMCFG_TRGSEL_CI0FE0);
|
|
#endif
|
|
|
|
/* 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));
|
|
}
|
|
}
|