CategoryElectronics

Simple 433MHz Keyfob

After the last two posts, I decided to build a simple PCB to handle various remotes in a single device and also serve as a "general purpose keyfob". I've built it around a PIC12F1840 microcontroller which I had handy. This microcontroller includes an internal oscillator so the external components were reduced to a minimum: just the radio transmitter, some push-buttons and a LED.

remote-sch

Initially, I aimed to power the board directly from a 1-cell Lipo battery (or 3 AA/AAA) but I included support for a higher voltage supply just in case the transmission power was too weak. If you want to power the keyfob with a higher voltage, just solder the regulator and adjust the resistors (R1, R3 and R5) so that the PIC reads no more than VCC volts at its inputs. For my application, it works just fine with a 4.2 battery and I get around 30 meters of transmission distance.

The board remains unpowered until the user presses a button. At that time, the microcontroller boots up, reads which of the button's been pressed and executes the action until released. Theoretically, a single battery should last a few years.

Below you can download the Kicad files so that you can modify anything, as well as the gerber files to order it your own. The transmission module can be easily found on many sites for less than $2.

Keyfob KICAD Files

Keyfob GERBER Files

Below is the C code for the microcontroller that can be compiled using the free version of MPLAB IDE. It's an example of how to send a different command depending on which button is pressed by the user.

/* 
 * File:   main.c
 * Author: Dani
 *
 * Created on 7 de marzo de 2016, 20:06
 */

#include <stdio.h>
#include <stdlib.h>

#include <xc.h>

#define _XTAL_FREQ 16000000 

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

// 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 = ON       // MCLR Pin Function Select (MCLR/VPP pin function is MCLR)
#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 = ON       // Brown-out Reset Enable (Brown-out Reset enabled)
#pragma config CLKOUTEN = OFF   // Clock Out Enable (CLKOUT function is disabled. I/O or oscillator function on the CLKOUT pin)
#pragma config IESO = ON        // Internal/External Switchover (Internal/External Switchover mode is enabled)
#pragma config FCMEN = ON       // Fail-Safe Clock Monitor Enable (Fail-Safe Clock Monitor is enabled)

// CONFIG2
#pragma config WRT = OFF        // Flash Memory Self-Write Protection (Write protection off)
#pragma config PLLEN = ON       // PLL Enable (4x PLL enabled)
#pragma config STVREN = ON      // Stack Overflow/Underflow Reset Enable (Stack Overflow or Underflow will cause a Reset)
#pragma config BORV = LO        // Brown-out Reset Voltage Selection (Brown-out Reset Voltage (Vbor), low trip point selected.)
#pragma config LVP = ON         // Low-Voltage Programming Enable (Low-voltage programming enabled)

/*
 * 
 */


/* 
 * RA0 = S1 (INPUT) LEFT
 * RA1 = S2 (INPUT) INH
 * RA2 = S3 (INPUT) RIGHT
 * RA5 = Tx (OUTPUT)
 * 
 * RA4 = Testpoint
 * 
 */

#define TX_PIN  RA5

#define PKT_LENGTH  48

const unsigned char left_pkt[]   = {0x00,0x00,0x00,0x00,0xAA,0x00,0x00,0x00,0xAA,0xAA,0xAA,0xAA,0x00,0x00,0x00,0x00,0xAA,0x00,0x00,0x00,0xAA,0xAA,0xAA,0xAA,0x00,0x00,0x00,0x00,0xAA,0x00,0x00,0x00,0xAA,0xAA,0xAA,0xAA,0x00,0x00,0x00,0x00,0xAA,0x00,0x00,0x00,0xAA,0xAA,0xAA,0xAA};
const unsigned char right_pkt[]  = {0x00,0x00,0x00,0x00,0xAA,0x00,0x00,0x00,0xAA,0xAA,0xAA,0xAA,0x00,0x00,0x00,0x00,0xAA,0x00,0x00,0x00,0xAA,0xAA,0xAA,0xAA,0x00,0x00,0x00,0x00,0xAA,0x00,0x00,0x00,0xAA,0xAA,0xAA,0xAA,0x00,0x00,0x00,0x00,0xAA,0xAA,0xAA,0xAA,0x00,000,0x00,0x00};

#define BITDELAY    70

#define delayMicroseconds   __delay_us
void tx_frame(const unsigned char *ptr, unsigned int length)
{
    unsigned int i;
    for(i=0;i<length;i++)
    {
        
        TX_PIN = ((ptr[i] & 0x80) != 0);    delayMicroseconds(BITDELAY);
        TX_PIN = ((ptr[i] & 0x40) != 0);    delayMicroseconds(BITDELAY);
        TX_PIN = ((ptr[i] & 0x20) != 0);    delayMicroseconds(BITDELAY);
        TX_PIN = ((ptr[i] & 0x10) != 0);    delayMicroseconds(BITDELAY);
        TX_PIN = ((ptr[i] & 0x08) != 0);    delayMicroseconds(BITDELAY);
        TX_PIN = ((ptr[i] & 0x04) != 0);    delayMicroseconds(BITDELAY);
        TX_PIN = ((ptr[i] & 0x02) != 0);    delayMicroseconds(BITDELAY);
        TX_PIN = ((ptr[i] & 0x01) != 0);    delayMicroseconds(BITDELAY);
    }
    __delay_ms(20);
}


int main(int argc, char** argv)
{
  unsigned char left=0, right=0, extra=0;
  
  OSCCON |= (0x0F<<3);  // Internal oscillator @ 16MHz
  
  ANSELA = 0;                       // No analog inputs 
  WPUA=0;                           // No internal pullups
  TRISA |= ((1<<0)|(1<<1)|(1<<2));  // Buttons as Inputs
  TRISA &= ~(1<<4);                 // RA4 (testpoint) as Output PIN
  
  RA4=0;

  // Allow some time to set things up
  __delay_ms(100); 
    
  //Check what actions are requested:
  if((PORTA & 1) == 1)  left    = 1;
  if((PORTA & 2) == 2)  extra   = 1;
  if((PORTA & 4) == 4)  right   = 1;
  
  while(1)
  {
      if(left==1)
          tx_frame(left_pkt,PKT_LENGTH);
      if(right==1)
          tx_frame(right_pkt,PKT_LENGTH);
  }
        
  return (EXIT_SUCCESS);
}

Below, a couple of pictures of the board with the transmission module attached:

IMG_7743

IMG_7744

 

 

 

 

 

 

I'll build a second version of the board to fit a 3-button case or maybe I get one case 3D printed :)

 

RFCat, TI Chronos and replaying RF signals :)

After my first contact with the RTL-SDR a couple of days ago , I've been researching a bit more and found this fantastic blog post by Adam Laurie which describes how to use a TI Chronos development kit to send arbitrary sub-1GHz signals. It happens that I had such a kit so I decided to emulate another garage door opener, but this time using RFCat.

Loading RFCat firmware into the Chronos USB Dongle

First thing I did was to flash the USB dongle with the RFCat firmware so that I could emulate the remote from a python script. As I had a CC Programmer handy (you can also use GoodFET), I wired it up by following the diagram below and flashed the RFCat bin for the ez Chronos dongle using the SmartRF Flash Programmer tool.

ez_jtag_diagram

 

ez_jtag

ez_rfcat

ez_hack2

You can either flash the dongle with the RFCat binary itself or with the CC Bootloader which will allow you to update the dongle further without having to use the JTAG. I took the second approach so after flashing the bootloader, you'll need to flash the actual RFCat firmware:

python bootloader.py /dev/ttyACM0 download RfCatChronosCCBootloader-150225.hex

After successfully flashing the dongle, it should show up as "RFCat" and you should be able to communicate with it from the rfcat interpreter:

RFCat_enumRFCat_r

As the communication with the dongle was good, it was time to analyze the signal sent by the remote and write some code to replay it using RFCat.

Signal Analysis

For the analysis part, I used the SDR# tool for Windows: tuned the right frequency (433.92MHz) and saved the signal into a Wav file for later analysis with Audacity.

audacity_ref1_mod

It's a fixed code and looks pretty straightforward: short and long pulses. We can estimate the length of each type by measuring the number of samples. In this case, short pulses took 3000 samples or 1200us (sample rate was 2.4Ms on SDRSharp).

A good way to represent the signal is to encode the "long pulse plus silence" as "1" and the "short pulse plus silence" as "0". Then, the frame would look like this:

1  0  1  1  0  1  1  1  1  1  1  0  0  0  0  0  1  0  1  1  0  0  1  1  0  0  1  1  0  0  1  1  1

As the "1" is formed by two high and one low short pulses of equal duration, we can express it as "110". Similarly, our "0" can be represented as "100" and the frame now would be:

110 100 110 110 100 110 110 110 110 110 110 100 100 100 100 100
110 100 110 110 100 100 110 110 100 100 110 110 100 100 110 110 110

However, if we zoom in on the signal, we can see that the pulses are divided in more little pulses that we'll need to encode in some way:

audacity_ref2

So, the final frame would make us rewrite the previous one changing every "1" bit by \xAA\xAA  and every "0" bit by \x00\x00 to maintain the length of each bit (see code below).  The duration of each bit is now about 80 us.

Replaying signal with RFCat

Now that we have analyzed the signal, it's time to write a Python script to interface the RFCat dongle so that it generates the frames accordingly. Afterwards, we'll capture the signal back to make sure that both the waveform and timing are correct:

from rflib import*
from time import sleep

pkt = '\xAA\xAA\xAA\xAA\x00\x00\xAA\xAA\x00\x00\x00\x00\xAA\xAA\xAA\xAA\x00\x00\xAA\xAA\xAA\xAA\x00\x00\xAA\xAA\x00\x00\x00\x00\xAA\xAA\xAA\xAA\x00\x00\xAA\xAA\xAA\xAA\x00\x00\xAA\xAA\xAA\xAA\x00\x00\xAA\xAA\xAA\xAA\x00\x00\xAA\xAA\xAA\xAA\x00\x00\xAA\xAA\xAA\xAA\x00\x00\xAA\xAA\x00\x00\x00\x00\xAA\xAA\x00\x00\x00\x00\xAA\xAA\x00\x00\x00\x00\xAA\xAA\x00\x00\x00\x00\xAA\xAA\x00\x00\x00\x00\xAA\xAA\xAA\xAA\x00\x00\xAA\xAA\x00\x00\x00\x00\xAA\xAA\xAA\xAA\x00\x00\xAA\xAA\xAA\xAA\x00\x00\xAA\xAA\x00\x00\x00\x00\xAA\xAA\x00\x00\x00\x00\xAA\xAA\xAA\xAA\x00\x00\xAA\xAA\xAA\xAA\x00\x00\xAA\xAA\x00\x00\x00\x00\xAA\xAA\x00\x00\x00\x00\xAA\xAA\xAA\xAA\x00\x00\xAA\xAA\xAA\xAA\x00\x00\xAA\xAA\x00\x00\x00\x00\xAA\xAA\x00\x00\x00\x00\xAA\xAA\xAA\xAA\x00\x00\xAA\xAA\xAA\xAA\x00\x00\xAA\xAA\xAA\xAA\x00\x00'

NUM_REPS	= 10		# times the frame will be sent
DELAY 		= 0.02	# seconds between frames

try:

	d = RfCat()
	d.setMdmModulation(MOD_ASK_OOK)
	d.setFreq(433290000)	# Set freq to 433.92MHz
	d.setMaxPower()
	d.setMdmSyncMode(0)		# Don't send preamble/sync word
	d.setMdmDRate((int)(1.0/0.000080))	# Our bits are 80us long
 	d.makePktFLEN(len(pkt))

	print "Sending frames "
	for i in range(0,NUM_REPS):
		sys.stdout.write(".")
		d.RFxmit(pkt)
		sleep(DELAY)
	print " Done\n"
	d.setModeIDLE()

except Exception, e:
	sys.exit("Error %s" % str(e))

Now let's run the script and capture the signal back with SDR# to check if it looks like it should:

audacity_ref_captured1_mod

audacity_captured_zoom1

The first picture shows both our reference signal (sent by the remote) and the one generated with the RFCat dongle. The second picture shows the detail of each bit. As expected, it opened the door although the output power seemed a bit too low . Maybe there's a hack to improve the antenna of the Chronos dongle?  :)

Replaying the signal with the Chronos Sports Watch

Okay, the hard part is over so let's have some fun and replay the signal directly from our wrist :)

The ChronIC project is pretty much like the RFCat firmware but can loaded directly into the Chronos Sports Watch so that pre-loaded signals can be sent just by pressing the up/down buttons. I modified the code to make the watch send our frame every time I pressed the UP button. Below is the code that will do the magic, and a couple of useful Python functions (from Adam's code) to calculate the register values for your bitrate and frequency:

 

 def setfreq(freq):
	mhz= 26
	freqmult = (0x10000 / 1000000.0) / mhz
	num = int(freq * freqmult)
	freq0= num & 0xff
	payload= chr(freq0)
	freq1= (num >> 8) & 0xff
	payload += chr(freq1)
	freq2= (num >> 16) & 0xff
	payload += chr(freq2)
	print '- FREQ2: %02x FREQ1: %02x FREQ0: %02x -' % (freq2, freq1, freq0)

def setdatarate(drate):
	mhz= 26
	drate_e = None
	drate_m = None
	for e in range(16):
		m = int((drate * pow(2,28) / (pow(2,e)* (mhz*1000000.0))-256) + .5)        # rounded evenly
		if m < 256:
			drate_e = e
			drate_m = m
			break
	if drate_e is None:
		return False, None
	drate = 1000000.0 * mhz * (256+drate_m) * pow(2,drate_e) / pow(2,28)
	print 'drate_e: %02x  drate_m: %02x' %(drate_e,drate_m)
void config_garage(u8 line)
{
	// gap between data pulses
	//Button_Delay= 0;
	Button_Delay= 20;
	// how many times to send per button press
	Button_Repeat= 10;

	// set button content

	Up_Buttons= 1;
	// packet length
	Button_Up_Data[0][0]= 198;
	// payload
	memcpy(&Button_Up_Data[0][1],"\xAA,\xAA,\xAA,\xAA,\x00,\x00,\xAA,\xAA,\
 \x00,\x00,\x00,\x00,\xAA,\xAA,\xAA,\xAA,\x00,\x00,\xAA,\xAA,\xAA,\xAA,\x00,\x00,\
 \xAA,\xAA,\x00,\x00,\x00,\x00,\xAA,\xAA,\xAA,\xAA,\x00,\x00,\xAA,\xAA,\xAA,\xAA,\
 \x00,\x00,\xAA,\xAA,\xAA,\xAA,\x00,\x00,\xAA,\xAA,\xAA,\xAA,\x00,\x00,\xAA,\xAA,\
 \xAA,\xAA,\x00,\x00,\xAA,\xAA,\xAA,\xAA,\x00,\x00,\xAA,\xAA,\x00,\x00,\x00,\x00,\
 \xAA,\xAA,\x00,\x00,\x00,\x00,\xAA,\xAA,\x00,\x00,\x00,\x00,\xAA,\xAA,\x00,\x00,\
 \x00,\x00,\xAA,\xAA,\x00,\x00,\x00,\x00,\xAA,\xAA,\xAA,\xAA,\x00,\x00,\xAA,\xAA,\
 \x00,\x00,\x00,\x00,\xAA,\xAA,\xAA,\xAA,\x00,\x00,\xAA,\xAA,\xAA,\xAA,\x00,\x00,\
 \xAA,\xAA,\x00,\x00,\x00,\x00,\xAA,\xAA,\x00,\x00,\x00,\x00,\xAA,\xAA,\xAA,\xAA,\
 \x00,\x00,\xAA,\xAA,\xAA,\xAA,\x00,\x00,\xAA,\xAA,\x00,\x00,\x00,\x00,\xAA,\xAA,\
 \x00,\x00,\x00,\x00,\xAA,\xAA,\xAA,\xAA,\x00,\x00,\xAA,\xAA,\xAA,\xAA,\x00,\x00,\
 \xAA,\xAA,\x00,\x00,\x00,\x00,\xAA,\xAA,\x00,\x00,\x00,\x00,\xAA,\xAA,\xAA,\xAA,\
 \x00,\x00,\xAA,\xAA,\xAA,\xAA,\x00,\x00,\xAA,\xAA,\xAA,\xAA,\x00,\x00",Button_Up_Data[0][0]);

	Down_Buttons= 0;

	// set frequency (433920000)
	ChronicRF.freq0= 0x71;
	ChronicRF.freq1= 0xB0;
	ChronicRF.freq2= 0x10;

	// set data rate (pulsewidth 80us)
	// drate_m
	ChronicRF.mdmcfg3= 0xf8;
	// drate_e
	ChronicRF.mdmcfg4 &= 0xf0;
	ChronicRF.mdmcfg4 |= 8;

	// set modulation
	ChronicRF.mdmcfg2 &= ~MASK_MOD_FORMAT;
	ChronicRF.mdmcfg2 |= MOD_OOK;
	// set sync mode
	ChronicRF.mdmcfg2 &= ~MASK_SYNC_MODE;
	ChronicRF.mdmcfg2 |= SYNC_MODE_NONE;
	// set manchester false
	ChronicRF.mdmcfg2 &= ~MASK_MANCHESTER;
	display_symbol(LCD_ICON_RECORD, SEG_ON);
	Emulation_Mode= EMULATION_MODE_GARAGE;
}

After  building the code with Code Composer and loading it into the Watch with the JTAG included in the kit, a new menu is available and the signal's going to be sent every time we press the UP button.

ez_menu

:) :)

All the information in this blog is for educational  purposes only.  You shall not misuse the information to gain unauthorized access.

Hello RTL-SDR

A new cool gadget has fallen into my hands: a SDR DVB-T dongle based on the Realtek RTL2832U chipset. Little did I know I was gonna get so fascinated about this world and first thing I wanted to try is to open my garage door from an Arduino as a "Hello World" exercise.

remote

Firstly, I installed the necessary software on a Kali Linux distribution and checked the frequency of the remote with the gqrx tool:

GQRX

Then, I dumped the signal for further analysis into a wav file using the gnuradio-companion software:

gnuradio-companion

This flowgraph will let us sample the signal sent by the remote and write it into a Wav file. After pressing the buttons on the remote we can see how it looks like in Audacity:

The modulation used by the remote is OOK and looks like it uses Manchester codification. Using the rtl_433 tool, I was able to decode the frame and correlate it with the waveform above:

*** signal_start = 2189802, signal_end = 2300238
signal_len = 110436,  pulses = 86
Iteration 1. t: 404    min: 110 (47)    max: 699 (39)    delta 241
Iteration 2. t: 404    min: 110 (47)    max: 699 (39)    delta 0
Pulse coding: Short pulse length 110 - Long pulse length 699

Short distance: 83, long distance: 675, packet distance: 4804

p_limit: 404
bitbuffer:: Number of rows: 6 
[00] {4} f0 : 1111
[01] {18} 23 23 c0 : 00100011 00100011 11
[02] {18} 23 23 c0 : 00100011 00100011 11
[03] {18} 23 23 c0 : 00100011 00100011 11
[04] {18} 23 23 c0 : 00100011 00100011 11
[05] {10} 23 00 : 00100011 00

As long as the button's pressed, the remote will keep on transmitting the 18-bit frame which we have identified as:   00100011 00100011 11. This bitstream can be clearly seen on Audacity.

From the output above, the short pulses got 110 counts and the long pulses 699. Since the rtl_433 tool samples at 250KHz, it means that they last 440us while the long ones last 2800us. All we need to do now is write the software for the Arduino board to replicate the signal:

#define rfTransmitPin 4
#define ledPin 13
#define buttonPin 9      

void setup(){

	pinMode(rfTransmitPin, OUTPUT);
	pinMode(ledPin, OUTPUT);
	pinMode(botonPin, INPUT);       

	digitalWrite(rfTransmitPin, LOW);
 }

  void loop(){
    if(digitalRead(buttonPin) == HIGH)   // if the button is pressed, tx the code
      transmitCode();
  }

#define SHORT_WAIT	delayMicroseconds(440)
#define LONG_WAIT	delayMicroseconds(2800)
#define TX_LOW		digitalWrite(rfTransmitPin, LOW)
#define TX_HIGH		digitalWrite(rfTransmitPin, HIGH)
#define OUTPUT_0	{TX_HIGH; SHORT_WAIT; TX_LOW; LONG_WAIT;}
#define OUTPUT_1	{TX_HIGH; LONG_WAIT;  TX_LOW; SHORT_WAIT;}

#define FRAME_SIZE        18

unsigned char code[] = {0,0,1,0,0,0,1,1,0,0,1,0,0,0,1,1,1,1};

void transmitCode() {

    digitalWrite(ledPin, HIGH);

	for(int i=0;i<CODE_SIZE;i++)
	{
		if(code_left[i] == 1)
		{
			OUTPUT_1;
		}
		else
		{
			OUTPUT_0;
		}
	}
	digitalWrite(rfTransmitPin, LOW);
	delay(200);

    digitalWrite(ledPin, LOW);
 }
 

Now, let's download it into the microcontroller and capture what it's sent to see if it matches the original code:

Arduino Sample

 

 

The waveform looks pretty much the same as the one sent by the remote. Also, the rtl_433 tool is able to decode it properly and the timing looks quite nice too:

*** signal_start = 12434285, signal_end = 12748315
signal_len = 314030,  pulses = 144
Iteration 1. t: 413    min: 115 (84)    max: 711 (60)    delta 8
Iteration 2. t: 413    min: 115 (84)    max: 711 (60)    delta 0
Pulse coding: Short pulse length 115 - Long pulse length 711
Short distance: 112, long distance: 708, packet distance: 25527

p_limit: 413
bitbuffer:: Number of rows: 8
[00] {18} 23 23 c0 : 00100011 00100011 11
[01] {18} 23 23 c0 : 00100011 00100011 11
[02] {18} 23 23 c0 : 00100011 00100011 11
[03] {18} 23 23 c0 : 00100011 00100011 11
[04] {18} 23 23 c0 : 00100011 00100011 11
[05] {18} 23 23 c0 : 00100011 00100011 11
[06] {18} 23 23 c0 : 00100011 00100011 11
[07] {18} 23 23 c0 : 00100011 00100011 11

Now we're sure that the Arduino board will transmit the same signal, it's time to try it by the garage door and... it WORKS! :)

It is a very simple project but as a first contact with the RTL-SDR world I had a lot of fun. I'm looking forward to learning more about it, especially the gnuradio-companion software for signal processing and analysis.

 

SPI Communications - Slave Core VHDL

SPI (Serial Peripheral Interface) protocol is a synchronous serial data link which operates in full duplex mode. It's got a higher throughput compared to I2C or SMBus  and it's very useful for high speed short-range communications.

The SPI protocol specifies the following signals:

  • SCLK — Serial Clock (output from master)
  • MOSI/SIMO — Master Output, Slave Input (output from master)
  • MISO/SOMI — Master Input, Slave Output (output from slave)
  • SS — Slave Select (active low, output from master)

All lines can be shared for every slave device in the bus except the Slave Select signal which has to be different (out of band selection) for each slave.

This is the basic operation depending on the CPOL and CPHA values:

spi-timing

The SPI core I've implemented is fixed for Slave and CPOL=0/CPHA=0 modes. This means that the MOSI line will be sampled at every rising edge of the SPI Clock whilst the MISO signal will be set right before this rising edge (actually, on the falling edge) so that it can be properly sampled by the Master.

SPI Module

As the FPGA will have its own clock, we'll be having two different clock domains since the SPI master signals will be generated externally and they won't be necessarily synchronized to the FPGA internal clock. Thus, the approach will be sampling all the external signals to get them synchronized and using two shift registers for input and output data.

 Source code:

entity spi_slave is
  port (
    RESET_in    : in  std_logic;
    CLK_in      : in  std_logic;
    SPI_CLK     : in std_logic;
    SPI_SS      : in std_logic;
    SPI_MOSI    : in  std_logic;
    SPI_MISO    : out std_logic;
    SPI_DONE    : out std_logic;
    DataToTx    : in std_logic_vector(7 downto 0);
    DataToTxLoad: in std_logic;
    DataRxd     : out std_logic_vector(7 downto 0)
    );
end spi_slave;

architecture Behavioral of spi_slave is

    signal SCLK_latched, SCLK_old : std_logic;
    signal SS_latched, SS_old : std_logic;
    signal MOSI_latched: std_logic;
    signal TxData : std_logic_vector(7 downto 0);
    signal index: natural range 0 to 7;
    signal RxdData : std_logic_vector(7 downto 0);

begin

 --
 -- Sync process
 --

  process(CLK_in, RESET_in)

 begin
    if (RESET_in = '1') then
      RxdData  '0');
      index <= 7;
      TxData  '0');
      SCLK_old <= '0';
      SCLK_latched <= '0';
      SS_old <= '0';
      SS_latched <= '0';
      SPI_DONE <= '0';
      MOSI_latched <= '0';

    elsif( rising_edge(CLK_in) ) then

      SCLK_latched <= SPI_CLK;
      SCLK_old <= SCLK_latched;
      SS_latched <= SPI_SS;
      SS_old <= SS_latched;
      SPI_done <= '0';
      MOSI_latched <= SPI_MOSI;

      if(DataToTxLoad = '1') then
          TxData <= DataToTx;
      end if;

      if (SS_old = '1' and SS_latched = '0') then
          index <= 7;
      end if;

      if( SS_latched = '0' ) then
         if(SCLK_old = '0' and SCLK_latched = '1') then
            RxdData <= RxdData(6 downto 0) & MOSI_latched;
            if(index = 0) then -- cycle ended
               index <= 7;
            else
               index <= index-1;
            end if;
         elsif(SCLK_old = '1' and SCLK_latched = '0') then
            if( index = 7 ) then
               SPI_DONE <= '1';
            end if;
            TxData <= TxData(6 downto 0) & '1';
         end if;
      end if;
     end if;
   end if;
   end process;

   --
   -- Combinational assignments
   --

   SPI_MISO <= TxData(7);
   DataRxd <= RxdData;

end Behavioral;

In order to test this core I wrote a simple testbench wich basically waits for a command from the master and answers it. This will serve as a base for the microcontroller code which will send this same command (0xA0) and wait for the answer (0xA4). This is the simulation view:

spi_simulation

As you can see, when the spi_done signal goes high, the datarxd register is loaded with the value 0xA0. Afterwards, the answer to this command (0xA4) is loaded into the datatoxload register and the MISO line is set to the right bit at every falling edge of the SPI clock signal.

As the simulation looks good, I decided to wire up the FPGA to an LPC2148 microcontroller and test the SPI core for real. The following code will run in the LPC:

 

void send_fpga_cmd(unsigned char cmd)
{
    unsigned char dummy;
    IO0PIN &= ~(1<<11);    // Select FPGA
    while ( !(SSPSR & 0x02) );
    SSPDR=(unsigned int) cmd;
    while((SSPSR & (1<<4)));
    IO0PIN |= (1<<11);    // deselect FPGA
    dummy = SSPDR;         //flush the RxFIFO

}

unsigned char read_fpga_byte()
{
    unsigned char data;
    SSPDR= 0xFF;         // write dummy data out to gen clock
    while((SSPSR & (1<<4)));
    data = SSPDR;
    return data;
}

while(1)
{
    send_fpga_cmd(0xA0);
    data = read_fpga_byte();
}

I configured the SPI bus to run at 2MHz on the microcontroller. Here you can see the logic analyzer output which shows that the SPI core works as expected.

spi_ping_la

As you can see, the microcontroller sends a ping (0xA0 byte) and the FPGA answers with a response command (0xA4 byte). The SPI clock frequency is exactly 2.000MHz and the behavior is the expected one.

Silvestre FPGA - SPI Test
Silvestre FPGA - SPI Test

Daniel

Playing with inertial sensors and Kalman Filter

I've started to build some sort of two-wheel Balancing Robot and before getting the party started I'm having to deal with mixing up the data gathered from my inertal sensors.

My homebrew IMU (Inertial Measurement Unit) is composed by one 3-axis accelerometer and 1-axis gyroscope. The gyro sensor's got an analog output (.67mV per degree/s) and the accelerometer's got an i2c interface. I would love to share its part number with you but, since I liked this unit to be as cheap as possible, the sensors were ripped out (and reverse-engineered with a logic analyzer) from a cheap PS3 gamepad bought on eBay ;).

Since the output of the gyro sensor is 'too low' for my ADC, I built a simple non-inverter amplifier and a low pass filter with a cutoff frequency of 1KHz. Also I placed a high pass filter (cutoff frequency at .3Hz) in order to compensate the temperature drift. The data from the accelerometer's digitally filtered on the microcontroller with a simple 1st order Butterworth filter.

Inverted Pendulum

The main idea of this kind of robots (inverted pendulum) is to measure the tilt angle in order to drive the wheels just below the mass. The higher the center of gravity, the easier balancing will be. Why do we need both an accelerometer and a gyro?

- The acclerometer senses not only the gravity (tilt) but the acceleration forces on its axis. So it would be useful if it was static (no acceleration due to movement).

- The gyro outputs angular velocity (degrees per second) and it's not sensitive to acceleration. In order to get the angular position we have to integrate this signal. However this will drift over the time and the estimated tilt angle wouldn't be accurate after some seconds.

The 'trick' is to take out the best of each sensor: the long-term information from the accelerometer and the short-term response from the gyro sensor. One way to do this is using a 'black-box' known as Kalman Filter (if you are brave enough, have a look at the theory; I'm not :)) which mixes up both signals predicting the actual tilt angle.

I got some source code of this filter from rotomotion (http://scratchpad.wikia.com/wiki/RotomotionCode) and pushed it directly onto my microcontroller. The sensors are sampled at 50Hz and fed to the KF at the same rate. In the next plot you can see the raw tilt angle (atan2(raw_accy, raw_accx)), the integral of the gyro sensor (using the trapezoidal rule) and the output of the Kalman Filter.

Kalman Filter

As you can see, the raw tilt angle is a little bit noisy while the gyro integration is very clean. Also, there's a lot of drift in this signal but magically the KF manages to estimate the angle very accuratelly and free of noise.

Now it's time to try the filter with stronger movements and vibrations before feeding its output to the PID which will - hopefully - make the robot balance :)

I 'll post more on this soon!

Daniel

I2C Protocol - Clock Stretching

I'm working on a project which involves an i2c communication between a master and some slaves. The master device is using an LPC2148 microcontroller running at 60 MHz and the slave ones have a low-cost PIC microcontroller. Each slave device runs a different task and some of them are more CPU-intensive than others.

The nature of this i2c communication is essentially some kind of Query-Response protocol in which the master requests some processing from the slaves and they send back the results to the master. The processing time varies from one slave to another and sometimes it will be higher than the master 'clock' time.

This leaded me to find a way to stop the master until the slave is done with its processing: clock stretching. The slave will pull the clock line down, causing the master to stop until it's done with its task and then releases the SCL line (going high due to the required pull-ups of the i2c signals).

As I was writing the slave C code using PICC compiler, there was no way to implement this technique directly from the supplied i2c functions therefore I'd got to implement it by myself:


#byte PIC_SSPCON=0x14
#bit CKP = PIC_SSPCON.4 // 1 = Release Clock, 0 = Holds Clock Low (clock stretch)
#define SCL_STRECTHING_ON bit_clear(PIC_SSPCON, 4); // CLK STRETCHING
#define SCL_STRETCHING_OFF bit_set(PIC_SSPCON, 4); // release clock to master

...
ISR:

if(state >= 0x80) //Master is requesting data
{
SCL_STRECTHING_ON
delay_us(400); // Simulate processing delay
i2c_write(buffer[cnt++]);
SCL_STRETCHING_OFF
}

Please, note that the values defined for SSPCON register / bits are for PIC16F677 microcontroller and it might differ from the one you're using.

Below you can see an screenshot of the Logic Analyzer output I used for testing purposes. You can see the 400 us delay between two consecutive readings from the master while the SCL line remains low.

I2C Protocol - Clock Stretching

All in all, it's a well known technique described in the protocol specification but as far as I'm concerned by googling a little bit, its usage is not very extended and there are not so much source code out there addressing this issue.

Daniel

H-Bridge MOSFET Board - RL-AMC-50NP04

RL-AMC-01

RL-AMC-50NP04 is an advanced Full H-Bridge board based on high performance MOSFETs capable of driving two high power DC motors. It has been designed to achieve a great efficiency and usage simplicity since just 3 pins are needed to control a motor. Based on the large experience working with Robotics we designed this board keeping in mind some important factors such as power dissipation, heating, simplicity and functionalities like braking, bi-directional control of the motors and a wide range of PWM frequencies from 3.3v or 5V microcontrollers.

Its small size makes this H-Bridge MOSFET board perfect to fit in your Robotics designs: 50 x 42 mm (1,97 x 1,65 inch) .

Main features:

  • 2 high power DC Motors control.
  • PWM frequencies up to 20 KHz for speed control.
  • Bi-directional control.
  • Braking function (coast and brake functions).
  • 3.3v / 5V logic capable.
  • Up to 8A continuous operation with very low internal resistance (0.038 ohms).
  • Up to 35A pulsed current.
  • Wide range of power supply voltage ranging from 3 to 40V.
  • Current sense (just 6mV per amp to minimize the total voltage drop when high currents are demanded).
  • Just 3 pins needed from your microcontroller to drive each motor.
  • Two LEDs per leg indicating the rotating direction of each motor.
  • Easy interface connections through an standard 10-pin male header.
  • Three two-pin terminal blocks for connecting the motors and their power supply.
  • Logic power supply ranging from 2V to 6V.

Here you can see the output of the board when driving one of its LEDs with a voltage supply of 8,4V. The voltage drop accross the LED is about 1 Volt and in this graphic you can appreciate that the rising and falling times are extremely small since it has been designed for operating in high-speed environments with the maximum efficiency. This minimizes the transition time of the MOSFETs (time when their internal Rds resistance is higher) and, thus, also minimizes the total power dissipation due to switching power losses.

RL-AMC Output 7.2KHz

If you are interested in this board or have any further questions / suggestions don't hesitate  to contact me.

Documentation:

More to come,

Daniel

X-PIC Development System

robot-arm-xpic

X-PIC-3The X-PIC Development System was designed by my colleague and friend Alberto Calvo and me during our studies at University. It is composed of two main boards: X-PIC and X-BOT which I will describe briefly. Our goal was to design a microcontroller based system powerful enough for our Robotics projects. However, we realized that it could be a good idea to design a more generic system which could be used as a small development platform without the need to have a big amount of LED's, buttons, LCD, switches, jumpers, etc. as most boards use to have. So it is suitable for any projects which require a microcontroller.

  • X-PIC

X-PIC-1 This is the main board. It has got a PIC16F877A microcontroller at a clock frequency of 20MHz. There's also an EEPROM serial memory which can be interfaced both externally and through the microcontroller. All the GPIO pins can be easily accessed through the 10-Pin connectors, all of them including VCC and GND signals.
For testing purposes, the board's got a general purpose button and one LED. It can be programmed through its ICSP connector or through its RS232 interface if a bootloader is previously loaded into its internal Flash memory.

  • X-BOT

X-BOT-1

This board was designed specifically for Robotics applications and can be easily connected to the X-PIC board and mounted together. It's got eight connectors which allow the microcontroller to read their value through selectable digital or analog IO pins. Also it includes two DC Motor drivers to drive up to 4 motors (1A per channel) which make this board suitable for a huge number of Robotics and Electronics applications.

X-TRK-1As an extension to this system we designed the X-TRK sensor board specially aimed to Sniffer Robots. It can drive up to 8 IR sensors with just 4 IO pins thanks to its 3 to 8 line decoder. In the picture you can see a 3D version of the board, which has not been yet manufactured (just two home-made protoypes).

You can download all the documentation and schematics in Spanish (sorry for the inconvenience):

X-PIC User Guide

X-BOT User Guide

X-PIC Schematics

X-BOT Schematics

Regards,

Daniel