315 MHz TX/RX module interfacing

Hi all. This is my first post. I didn’t have much luck searching the forums, but please feel free to refer me to previous posts.

I’m having issues interfacing my SFE TX/RX modules to my other circuitry. Can anybody tell me what type of interface the TX module’s data pin expects, and what the RX module’s data pin will put out? That is, does the TX module expect RS232 logic levels? Does the RX module output RS232 levels, or is it something else, such as TTL logic levels? Also, is the stated baud rate just the maximum supported, or do I have to use that exact baud rate?

My goal is to send a very small amount of data (less than 100 bits) several times per day. The data will come from an FPGA’s serial port, and at the receiver I need to interface to a parallel port on a Windows PC.

Here are the SFE TX and RX modules I’m using:

http://www.sparkfun.com/commerce/produc … ts_id=8948

http://www.sparkfun.com/commerce/produc … ts_id=8945

Also, when I searched the forums, I saw somebody mention a “RF-KLP-315 basics thread” - can anybody point me to that thread? I couldn’t find it using the Search feature.

Thanks in advance.

They use logic level inputs and outputs, not RS-232. You will probably need Manchester code to get them to work properly. Try sending ‘U’ continuously and see if you can receive it - this has a constant DC level.

The listed baud rate the the Maximum. You may use lower baud rates.

Also these use OOK modulation so you really should do something like Manchester encoding and need to start you transmission with a couple of sync bytes so the receiver can ‘lock in’.

In the Electronical Characteristics table (page 2 of Data Sheets ):

Rec:

ASK out logic HIGH Min = 0.7*Vcc, LOW Max = 0.3 *Vcc

Tx:

In the Output power : DATA 5V

So the input and output data is TTL level logic.

I’ve had success using these same modules. I’d be glad to post my C code if you’re interested. The library is for a PIC microcontroller, but the basic ideas should be portable to any TTL device. I used something akin to Manchester encoding to get it working, with a sync byte at the start of the data transfer. In my experience, 600 bits per second has given me the longest range with the fewest errors (tried it out to 50+ feet so far, still had a reliable signal). 300 bps and 1200 also work, but they don’t seem to transmit as far. As long as you transition between +5V / +0V on the transmit line at least once every 30 milliseconds, it should be OK. If you hold the voltage for too long, the automatic gain control on the receiver will drift away and you’ll need to send a series of +5V / +0V / +5V transitions to get the receiver’s attention back.

Hope this is helpful…

I’ve also just started working with this RF pair and haven’t had luck getting them to work. Not too familiar with coding so is it possible to get your code?

Thanks for the help, everyone!

Does anybody know if applying -5V to the transmitter’s data pin will damage it? And if not, will -5V be interpreted as a TTL low by the TX? I’m going to connect a serial port to the transmitter, so the input logic will be -5V = high and +5V = low. At the receiver’s data output, the logic inversion and the TTL logic levels are not a problem, but I don’t want to fry the TX with a negative voltage on the data pin. Do I need to use a logic level converter for the TX data input?

Check the data sheet.

I’ve looked over the TX data sheet, and it’s not too helpful (although it does give the minimum baud rate, so shame on me for overlooking that). As waltr posted above, the RX datasheet gives the output levels, but the TX datasheet doesn’t tell me what the input levels need to be.

It looks as though it should be a digital signal between 0V and the supply voltage…

I wouldn’t put -5V on the data line… usually the absolute minimum is only -0.3V or so. But there’s lots of ways to get your signal into the +0V to +5V range that this unit requires.

I’ll post my PIC code here in a minute.

For some reason, it looks like I can’t upload a C program, I’ll try to cut & paste instead:

The RFLink Transmitter program follows. This is for a radio-controlled tank, using a PIC16F630:

/* RC Tank Transmitter */

#include <htc.h>

#pragma warning disable 750

#define OUTPUT 0
#define INPUT 1

#ifndef _XTAL_FREQ
#define _XTAL_FREQ 4000000
#endif

typedef unsigned char uchar;

/* Button inputs */
#define a_fwd_pin RA5
#define b_fwd_pin RA4

/* Left/Right trim control */
#define throttle_a 10
#define throttle_b 7

/* Activity LED */
#define act_led_tris TRISC1
#define act_led RC1

/* ASK radio transmitter pin */
#define ask_xmitter_tris TRISC2
#define ask_xmitter RC2

/* At a TMR0 1:8 prescaler and 600 baud, each bit is about 200 ticks */
#define RS232_BIT_TIMER_TICKS 200


void pause(unsigned int ms)
{
  while (ms-- > 0)
    __delay_us(999);
}

void wait_tmr0(uchar stop_time)
/* Pause until the timer TMR0 reaches the given stop_time, then reset
 * the timer. Make sure TIMER0 is running before calling this. */
{
  while (TMR0 < stop_time) ;
  TMR0 = 0;
}  

void setup_timer0(void)
{
  /* Put TIMER0 into TIMER mode (counts using internal clock. External
   * clock signals and pins will be ignored. */
  T0CS = 0;
  
  /* Set the pre-scaler to 1:8 */
  PSA = 0;
  PS2 = 0;
  PS1 = 1;
  PS0 = 0;

  /* Reset the timer */
  TMR0 = 0;
}  

/*** ASK 4-bit wireless protocol implementation: Transmitter **********/
void ASK_xmit_sync(void)
{
  uchar bit_count;
  
  wait_tmr0(RS232_BIT_TIMER_TICKS);

  /* Send a SYNC signal */
  ask_xmitter = 1;
  for (bit_count = 1; bit_count <= 9; bit_count++)
    wait_tmr0(RS232_BIT_TIMER_TICKS);

  /* Send the Start bit */  
  ask_xmitter = 0;
}

void ASK_xmit_4bit(uchar word)
/* Transmits a 4-bit data word. One or more data words can be
 * sent immediately following ASK_xmit_sync() */
{
  uchar bit_count;
  uchar parity;
  
  /* Strip off the high 4 bits */
  word = word & 0x0F;

  /* Calculate the parity */  
  parity = (word != 0xF);
  
  /* Send bits in MSB-first order */
  for (bit_count = 0; bit_count < 4; bit_count++)
  {
    wait_tmr0(RS232_BIT_TIMER_TICKS);
    ask_xmitter = ((word & 0x8) >> 3); /* Transmit the high bit (bit 4) */
    word = (word << 1);
  }

  /* Parity */
  wait_tmr0(RS232_BIT_TIMER_TICKS);
  ask_xmitter = parity;
}  

void ASK_xmit_stop(void)
/* Send the stop bit */
{
  wait_tmr0(RS232_BIT_TIMER_TICKS);
  ask_xmitter = 0;
}

/* Level 1 **********************************************************/
void get_commands(uchar* ctl_bits)
{
  static uchar trim = 0;
  
  *ctl_bits = ((b_fwd_pin && trim < throttle_b) * 1) 
            | ((a_fwd_pin && trim < throttle_a) * 4);

  trim = (trim >= 9) ? (0) : (trim + 1);
}  

void send_commands(uchar control_bits)
{
  ASK_xmit_sync();
  ASK_xmit_4bit(control_bits);
  ASK_xmit_stop();  
}  

/* Main Level *******************************************************/
int main(void)
{
  uchar control_bits;

  /* Turn off the voltage comparator on RA0/1/2 */
  CMCON = 0x07; 
  
  /* Start the timer */
  setup_timer0();
    
  /* Set up transmitter and LED pins */
  ask_xmitter_tris = OUTPUT;  
  act_led_tris = OUTPUT;
  act_led = 1;
  
  while (-1)
  {
    get_commands(&control_bits);
    send_commands(control_bits);
  }
  return 0;
}

This is the receiver program, also using a PIC16F630:

#include <htc.h>

#pragma warning disable 750

#define OUTPUT 0
#define INPUT 1

#ifndef _XTAL_FREQ
#define _XTAL_FREQ 4000000
#endif

/* At a TMR0 1:8 prescaler, 200 ticks = 600 baud */
/* This has been tested to about 50 feet, and seems to work fairly well. */
#define RS232_BIT_TIMER_TICKS 200

typedef unsigned char uchar;

/* Motor control output pins */
#define motorctl_a_tris TRISC2
#define motorctl_a_pin     RC2
#define motorctl_b_tris TRISC3
#define motorctl_b_pin     RC3

/* ASK radio receiver pin */
#define ask_receiver RC4

/* "Signal Good" LED pin */
#define good_signal_tris TRISC1
#define good_signal_led     RC1
#define good_signal_vss_tris  TRISC0

/* Interrupt level **************************************************/
void interrupt kill_switch(void)
/* In the event of a signal loss, it will stop the motors and
 * turn off the signal_good LED. */
{
  if (TMR1IE && TMR1IF)
  {
    /* Acknowledge TIMER1 interrupt */
    TMR1IF = 0;
    
    /* Turn off the "good signal" LED */
    good_signal_led = 0;
  
    /* Turn off both motors */
    motorctl_a_pin = 0;
    motorctl_b_pin = 0;
  }
}

/* Level 2 **********************************************************/
void pause(unsigned int ms)
{
  while (ms-- > 0)
    __delay_us(999);
}

void wait_tmr0(uchar stop_time)
/* Pause until the timer TMR0 reaches the given stop_time, then reset
 * the timer. Make sure TIMER0 is running before calling this. */
{
  while (TMR0 < stop_time) ;
  TMR0 = 0;
}  

void ASK_listen_sync()
/* Attempt to gain ASK synchronization. It will look for the sync code of
 * many 1s followed by the start bit 0 on the ask_receiver input pin. The
 * function returns when a signal is found and the first data bit is on
 * the input pin. */
{
  const uchar sync_code_len = 10;
  const uchar sync_code[] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 0};
  uchar got_sync = 0;
  uchar bit_count = 0;
  uchar retries = 0;

  /* Reset the "kill switch" timer to zero */  
  TMR1H = 0;
  TMR1L = 0;

  bit_count = 0;
  while (!got_sync)
  {
    while (ask_receiver != sync_code[bit_count])
      ; /* keep waiting for the first bit */

    /* Got the first bit - reset the timer here */      
    TMR0 = 0;
    bit_count = 1;
    
    /* Try to position our reads in the center of the sender's output phase */
    wait_tmr0(RS232_BIT_TIMER_TICKS / 2);
    
    /* Listen for the rest of the bits */
    while ((bit_count >= 1) && (!got_sync))   
    {
      wait_tmr0(RS232_BIT_TIMER_TICKS);
      if (ask_receiver != sync_code[bit_count])
        bit_count = 0; /* Lost the signal - try again. */
      else
      {
        ++bit_count;
        if (bit_count == sync_code_len)
          got_sync = 1; /* Found it! */
      }
    }
  }
}

void ASK_receive_4bit(uchar* word)
{
  uchar bit_count;
  uchar input = 0;
  
  for (bit_count = 0; bit_count < 4; bit_count++)
  {
    wait_tmr0(RS232_BIT_TIMER_TICKS);
    input = (input * 2) + ask_receiver;
  }
  wait_tmr0(RS232_BIT_TIMER_TICKS); /* Parity bit */
  *word = input;
}

/* Level 2 **********************************************************/
void set_motor_power(uchar ask_data)
{
  uchar ctl_value = 0;

/* Eventually, the plan is to have full bi-directional motor control. With
   an H-bridge or similar, the following code would give tristate control
   over two motors. 
   
  motorctl_aa = !((ask_data & 0x8) >> 3);
  motorctl_ab = (ask_data & 0x4) >> 2;
  motorctl_ba = !((ask_data & 0x2) >> 1);
  motorctl_bb = (ask_data & 0x1);
*/

  /* But for now, just move forward if any movement is requested. */ 
  ctl_value = ((ask_data & 0x8) >> 3) ^ ((ask_data & 0x4) >> 2);
  motorctl_a_pin = ctl_value;

  ctl_value = ((ask_data & 0x2) >> 1) ^ ((ask_data & 0x1));
  motorctl_b_pin = ctl_value;
}  

/* Level 1 **********************************************************/
void read_and_execute_cmd(void)
{
  uchar ask_data;
  
  ASK_listen_sync();
  good_signal_led = 1;
  ASK_receive_4bit(&ask_data);
  set_motor_power(ask_data);
}

void setup_timer0(void)
{
  /* Put TIMER0 into TIMER mode (counts using internal clock. External
   * clock signals and pins will be ignored. */
  T0CS = 0;
  
  /* Set the pre-scaler to 1:8 */
  PSA = 0;
  PS2 = 0;
  PS1 = 1;
  PS0 = 0;
  
  /* Reset the timer */
  TMR0 = 0;
}

void setup_io_pins(void) 
{
  /* Set up the "good signal" LED */
  good_signal_tris = OUTPUT;
  good_signal_vss_tris = OUTPUT;

  /* Set motor H-bridge control pins as outputs */
  motorctl_a_tris = OUTPUT;
  motorctl_b_tris = OUTPUT;

  /* Lamp test */
  good_signal_led = 1;
  pause(300);
  good_signal_led = 0;
  pause(300);
}

void setup_timer1()
{
  /* Reset the timer to zero */  
  TMR1H = 0;
  TMR1L = 0;
  
  /* Set up the timer1 configuration and start the timer */
  /* TMR1GE=0, T1CKPS=11, T1OSCEN=0, T1SYNC=1, TMR1CS=0, TMR1ON=1 */
  T1CON = 0x35;

  /* Set the Timer1 Interrupt Enable flag */
  TMR1IF = 0; /* Acknowledge any prior interrupt */
  TMR1IE = 1; /* Unmask the TIMER1 interrupt */

  /* Unmask the timer peripheral device interrupt */
  PIE1 = PIE1 | 0x01;
  
  /* Enable peripherial device interrupts */
  PEIE = 1;
}  
  
/* Main Level *******************************************************/
int main(void)
{
  setup_io_pins();  
  setup_timer0();
  setup_timer1();
  GIE = 1;  /* Enable interrupts */
  while (-1)
    read_and_execute_cmd();
  
  return 0;
}