Continue to Site

Welcome to EDAboard.com

Welcome to our site! EDAboard.com is an international Electronics Discussion Forum focused on EDA software, circuits, schematics, books, theory, papers, asic, pld, 8051, DSP, Network, RF, Analog Design, PCB, Service Manuals... and a whole lot more! To participate you need to register. Registration is free. Click here to register now.

[SOLVED] RC5 decoding function

Status
Not open for further replies.

denny9167

Member level 1
Member level 1
Joined
Apr 23, 2022
Messages
35
Helped
0
Reputation
0
Reaction score
0
Trophy points
6
Activity points
170
I have an RC5 read function, that I have come up with that is simplified compared to most, any input appreciated

Code:
void readRC5() { 
    uint8_t address = 0;
    uint8_t command = 0;
     __bit toggle = 0;
     uint8_t count;

    // Wait for the IR signal to go low, indicating the start bit
    while(IR_PIN == 1);

    // Reset and start the timer
    TMR1 = 0;
    TMR1ON = 1;

    // Wait for the start bit period
    while(IR_PIN == 0);
    if (TMR1 > 2000 || TMR1 < 700) {
        TMR1ON = 0;
        return; // Error: Invalid timing for the first start bit
    }

    // Wait for the second start bit
    while(IR_PIN == 1);
    TMR1 = 0;
    while(IR_PIN == 0);
    if (TMR1 > 2000 || TMR1 < 700) {
        TMR1ON = 0;
        return; // Error: Invalid timing for the second start bit
    }

    // We have both start bits

    // Now read the toggle bit
    toggle = IR_PIN;

    // Read the 5-bit address
    for (count = 0; count < 5; count++) {
        TMR1 = 0;           // Reset timer1 and begin counting
        while (IR_PIN == 1); // Wait for the pin to go low for the next bit
        while (TMR1 <= 1730); // Wait for next bit period

        address <<= 1;      // Shift the address to the left to make room for the next bit
        if (IR_PIN == 0)    // If the IR pin is low, it means its a 1, in IR,logic 1 is now inverted, high to low.
            address |= (0x01);// Set the LSB of address
        else
            address &= ~(0x01);  // Clear the bit
    }

    // Read the 6-bit command
    for (count = 0; count < 6; count++) {
        TMR1 = 0;           // Reset timer1 and begin counting
        while (IR_PIN == 1); // Wait for the pin to go low for the next bit
        while (TMR1 <= 1730); // Wait for next bit period

        command <<= 1;      // Shift the command to the left to make room for the next bit
        if (IR_PIN == 0)    // If the IR pin is low, it means the bit is 1
            command |= (0x01);  // Set the LSB of command
        else
            command &= ~(0x01); // Clear the bit
    }

    // Ensure the timer is stopped
    TMR1ON = 0;

    // Now address and command contain the decoded RC5 data
    // Process the address and command as needed
}
 

Does the timing period between bit reads look good?
Based on what you have posted in this topic your question cannot be answered in any useful way.

To answer requires more context about your implementation, such as:
  • Type of controller
  • Clock rate of timer
  • C compiler vendor and version
This would only be the minimum information for a somewhat useful reply.

The less work someone needs to do to create an answer the more likely you are to get something useful.
 

Based on what you have posted in this topic your question cannot be answered in any useful way.

To answer requires more context about your implementation, such as:
  • Type of controller
  • Clock rate of timer
  • C compiler vendor and version
This would only be the minimum information for a somewhat useful reply.

The less work someone needs to do to create an answer the more likely you are to get something useful.

Sure, I guess those things would be useful info. I"m using a PIC16F1827, my clock rate for the timer is 1Mhz, Oscilliator setting is 8Mhz, timer1 is using Fosc/4, with a 1:2 prescale setting, this makes my resolution around 1uS per timer tick.My compiler is XC8 2.46
 

If you use External Interrupt + Timer Interrupt then it would be much easier to read the coming data stream.
 
If you use External Interrupt + Timer Interrupt then it would be much easier to read the coming data stream.

I´ve spent a coupl of minutes on the code.
Indeed there are some issues
* I personally don´t like the "busy wait" concept. During RC5 receive .. the microcontroller can´t do anything else, consumes 100% of processing power. And it stalls for a longer time if no RC5 is detected.
* As far as I can see it stalls completely if the RC5 is not detected completely. It hangs for infinite time. A no go.
* I don´t see any synchronizing concept. Thus it´s likely you miss a lot of RC5 commands because you only see fragments.

This code may be good for a school project .. but I doubt it is useful for a real application.

****
There are many really useful RC5 reciever codes in the internet. .. if you need a useful code.

****
I´ve once written my own RC5 receiver.
* it worked on the "interrupt on pin change" / "input capture interrupt" concept. Thus it used zero procesing power while "waiting" for RC5 command. And it did not miss a single signal edge.
* it used a timer as some kind of timeout. It detected the "space" between 2 IR commands to detect when it´s time to process the incoming data .. it´s easy and reliable to detect incomplete command and thus it´s easy to "synchronize" to the next incoming RC5 command. Also this timer is used to detect when the key is released on the remote control ... i. e. when the RC5_REPEAT command is missing.
(I guess this is what Hayee meant)
So you have 100% of processing power when no code is received. And still 99% processing power available during incoming RC5 command...

I usually use the "capture feature" by subtracting consecutive values ... without ever resetting the counter. This ensures perfect timing data.

Klaus
 
I´ve spent a coupl of minutes on the code.
Indeed there are some issues
* I personally don´t like the "busy wait" concept. During RC5 receive .. the microcontroller can´t do anything else, consumes 100% of processing power. And it stalls for a longer time if no RC5 is detected.
* As far as I can see it stalls completely if the RC5 is not detected completely. It hangs for infinite time. A no go.
* I don´t see any synchronizing concept. Thus it´s likely you miss a lot of RC5 commands because you only see fragments.

This code may be good for a school project .. but I doubt it is useful for a real application.

****
There are many really useful RC5 reciever codes in the internet. .. if you need a useful code.

****
I´ve once written my own RC5 receiver.
* it worked on the "interrupt on pin change" / "input capture interrupt" concept. Thus it used zero procesing power while "waiting" for RC5 command. And it did not miss a single signal edge.
* it used a timer as some kind of timeout. It detected the "space" between 2 IR commands to detect when it´s time to process the incoming data .. it´s easy and reliable to detect incomplete command and thus it´s easy to "synchronize" to the next incoming RC5 command. Also this timer is used to detect when the key is released on the remote control ... i. e. when the RC5_REPEAT command is missing.
(I guess this is what Hayee meant)
So you have 100% of processing power when no code is received. And still 99% processing power available during incoming RC5 command...

I usually use the "capture feature" by subtracting consecutive values ... without ever resetting the counter. This ensures perfect timing data.

Klaus
I kind of figured that and timer1 on all the time with no interrupts consumes alot, it was just an experimentation on my part. The bi-phase decoding makes it more complex,than other traditional decoders also. Thanks again.
 

Hi,

What do you mean?
Timer1 is hardware. It does not consume processing power at all, regardless whether it is ON or OFF.
Processing power is consumed by software only.

Klaus
 

Hi,

What do you mean?
Timer1 is hardware. It does not consume processing power at all, regardless whether it is ON or OFF.
Processing power is consumed by software only.

Klaus
Of course it is!! and software can put a huge demand on that hardware.
 

I do alot of RF, 433Mhz, and I have a couple of protocols that I use with traditional bit sequencing for both logic 1 and logic 0, the bi-phasing was a different approach, so I thought about at least trying it. After much reading I realized that Manchester based protocols don't offer any advantage over traditional encoding methods, so this was just a shot in the dark for Me, and I appreciate all the input!
--- Updated ---

One more thought, it would seem to me there should be a better more efficent way,to decode the long pulse and long space setting both the logic 1 and logic 0 at the same time!
 

Your approach to decoding the Philips RC5 protocol does not account for just how bad the pulse timing is from the remote control transmitter found in the real world.

The descriptions found online for the Philips RC5 protocol do not cover how to implement a robust decoding method. What they do is to describe a method based on the ideal pulse times. The robust methods accomplish more by finding when the transmitter has gone quiet then measure the width of the sync pulses of the next frame to have a reference of what the transmitter time base is and decide what the pulse widths for the short and long pulses should be. Additional error detection is implemented to reject pulses that are too long to be a valid data frame.

Perhaps you could think some more about how well you need this to work.
 
I came up with a function that works good! And a completed source code.

Code:
/*
 * File:   RC5_receiver.c
 * Author: Denny Brown
 * Device: PIC16F1827
 * Created on March 18, 2022
 */
// CONFIG1
#pragma config FOSC = INTOSC    // Oscillator Selection (INTOSC oscillator: I/O function on CLKIN pin)
#pragma config WDTE = OFF       // Watchdog Timer Enable (WDT disabled)
#pragma config PWRTE = OFF      // Power-up Timer Enable (PWRT disabled)
#pragma config MCLRE = OFF      // MCLR Pin Function Select (MCLR/VPP pin function is digital input)
#pragma config CP = OFF         // Flash Program Memory Code Protection (Program memory code protection is disabled)
#pragma config CPD = OFF        // Data Memory Code Protection (Data memory code protection is disabled)
#pragma config BOREN = OFF      // Brown-out Reset Enable (Brown-out Reset disabled)
#pragma config CLKOUTEN = OFF   // Clock Out Enable (CLKOUT function is disabled. I/O or oscillator function on the CLKOUT pin)
#pragma config IESO = OFF       // Internal/External Switchover (Internal/External Switchover mode is disabled)
#pragma config FCMEN = OFF      // Fail-Safe Clock Monitor Enable (Fail-Safe Clock Monitor is disabled)

// CONFIG2
#pragma config WRT = OFF        // Flash Memory Self-Write Protection (Write protection off)
#pragma config PLLEN = OFF      // PLL Enable (4x PLL disabled)
#pragma config STVREN = OFF     // Stack Overflow/Underflow Reset Enable (Stack Overflow or Underflow will not cause a Reset)
#pragma config BORV = HI        // Brown-out Reset Voltage Selection (Brown-out Reset Voltage (Vbor), high trip point selected.)
#pragma config LVP = OFF        // Low-Voltage Programming Enable (High-voltage on MCLR/VPP must be used for programming)

// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.

#include <xc.h>
#include <stdint.h>
#include <stdbool.h>

// Oscillator Defines

#define _XTAL_FREQ    8000000

//Port Defines
 
#define DATA_PIN   PORTBbits.RB0    
#define OUT        PORTAbits.RA2        //Switch pin definitions
#define status_LED PORTAbits.RA1

#define Dev_ID  0
#define CMD     3

uint8_t address = 0;
uint8_t command = 0;
uint16_t count;

// Global variable for storing the IR code
uint16_t ir_code = 0;


void Reset_peripherals()
    {
    OSCCON = 0x70;
    LATA   = 0x00;
    ANSELA = 0; // all analog pins are selected as digital
    ANSELB = 0;
    CM1CON0 = 7; // comparators off and power-down
    T1CON = 0x10;
    TRISB0  = 1;
    TRISA2 = 0;
    TRISA1 = 0;
    }

void Idle(){
    char F_momentary;
    char TimerL = 30;
    char TimerH = 30;
 
 // IR receiver idles with output high, so wait until it goes low
    // When hold down timers expire, all momentary outputs will be forced off
    while(DATA_PIN) {
        if(--TimerL == 0){
         if(--TimerH == 0){
         F_momentary = 1;
          if(F_momentary) {
              OUT = 0;
          }
       }
    }
  }
}
uint8_t test_pulse() {
    TMR1 = 0;
    status_LED = 0;
    while (!DATA_PIN && (count < 2000));
    count = TMR1;
    if ((count > 1999) || (count < 700))
        return 0; // Error: invalid pulse timing
    if (count > 1200)
        return 1; // Long pulse
    else
        return 2; // Short pulse
}

uint8_t test_space() {
    TMR1 = 0;
    status_LED = 1;
    while (DATA_PIN && (TMR1 < 2000));
    count = TMR1;
    if ((count > 1999) || (count < 700))
        return 0; // Error: invalid space timing
    if (count > 1200)
        return 1; // Long space
    else
        return 2; // Short space
}

uint16_t readRC5() {
    uint8_t i = 0, check;
    TMR1ON = 1;

        MID1:
        check = test_pulse();
        ir_code <<= 1;
        if (check == 0)
        return false;  // Error
        ir_code |= 1;
        i++;
        if (i > 13)
        return true; // Successfully read all bits
        if (check == 1)
        goto MID0;
        else
        goto START1;
 
        MID0:
        check = test_space();
        ir_code <<= 1;
        if ((check == 0) && (i != 13))
        return false;  // Error
        ir_code &= ~1;
        i++;
        if (i > 13)
        return true; // Successfully read all bits
        if (check == 1)
        goto MID1;
        else
        goto START0;
 
        START1:
        check = test_space();
        if (check != 2)
        return false; // Error
        goto MID1;
 
        START0:
        check = test_pulse();
        if (check != 2)
        return false; // Error
        goto MID0;
   
 
    }

void main(void)
{
    Reset_peripherals();
    OUT = 0;
    status_LED = 0;
 
    while(1){
     
        count = 0;
        Idle();
        if(readRC5()){
            address = (ir_code >> 6)& 0x1F;
            command = ir_code & 0x3F;
            __delay_ms(100);
            if(address == Dev_ID){
                if(command == CMD){
                OUT = 1;
                 }
               }
             TMR1ON = 0;
              }
           }
}
--- Updated ---

The function could also be used in a switch/case statement.I haven't tried it yet.

Code:
enum State {
    MID1,
    MID0,
    START1,
    START0
};

uint16_t readRC5() {
    uint8_t i = 0, check;
    uint16_t ir_code = 0;
    enum State state = MID1;
  
    TMR1ON = 1;

    while (1) {
        switch (state) {
            case MID1:
                check = test_pulse();
                if (check == 0) return 0; // Error
                ir_code <<= 1;
                ir_code |= 1;
                i++;
                if (i > 13) return 1; // Successfully read all bits
                if (check == 1)
                    state = MID0;
                else
                    state = START1;
                break;

            case MID0:
                check = test_space();
                if ((check == 0) && (i != 13)) return 0; // Error
                ir_code <<= 1;
                ir_code &= ~1;
                i++;
                if (i > 13) return 1; // Successfully read all bits
                if (check == 1)
                    state = MID1;
                else
                    state = START0;
                break;

            case START1:
                check = test_space();
                if (check != 2) return 0; // Error
                state = MID1;
                break;

            case START0:
                check = test_pulse();
                if (check != 2) return 0; // Error
                state = MID0;
                break;
        }
    }
}
 
Last edited:

Status
Not open for further replies.

Similar threads

Part and Inventory Search

Welcome to EDABoard.com

Sponsor

Back
Top