Forums

FPGA project for encoding/decoding multi channel RC servo data

Started by Unknown April 11, 2006
Objective:
========
I want to start learning FPGA by experimenting with them.  (I do have 
experience in digital electronics, microcontrollers and programming. But I 
do not have FPGA experince.) What I want to use FPGA for is as follows;

PPM/PCM Decoding:
================
I want to use FPGA to decode PPM and/or PCM signals from a 8-16 channels RC 
(radio control) transmitters. This is equivalent to measuring pulse widths 
of 8 pulses in a repeating pattern as shown in the following liks
    * http://www.mh.ttu.ee/risto/rc/electronics/pctorc.htm
    * http://adamone.rchomepage.com/guide1.htm#ppm

I want to be able to measure servo data in 10 bits resolution and send 
channel data via an RS232 port similar to one shown in the following links
    * http://www.customelectronics.co.uk/fmspic.htm
    * http://www.webx.dk/rc/sim-serial/sim-serial.htm
    * http://www.veetail.com/FMSInt1.shtml

Driving multi-channel RC servo motors :
============================
I also want to use the FPGA to read in  multi-channel servo motors from a 
serial RS232 port and control multi channel (8-16  RC servo motors) similar 
to one shown in the following links
    * http://www.seattlerobotics.org/encoder/200106/16csscnt.htm
    * http://www.seetron.com/ssc.htm
    * http://home.planet.nl/~j.havinga/servo/servo.htm
    * 
http://www.yostengineering.com/index.cgi?section=Hardware&subsection=ServoCenter/ServoCenter232.html

Questions:
=======
Can you advise me
    * A "low cost" FPGA development board
    * Free or "low cost" software development tools
    * Example FPGA code for above mentioned projects (PPM/PCM decoding and 
driving multi-channel RC servo motors)


Thank you.

FPGA Newbee 


Newbee wrote:
> Objective: > ======== > I want to start learning FPGA by experimenting with them. (I do have > experience in digital electronics, microcontrollers and programming. But I > do not have FPGA experince.) What I want to use FPGA for is as follows; > > PPM/PCM Decoding: > ================ > I want to use FPGA to decode PPM and/or PCM signals from a 8-16 channels RC > (radio control) transmitters. This is equivalent to measuring pulse widths > of 8 pulses in a repeating pattern as shown in the following liks > * http://www.mh.ttu.ee/risto/rc/electronics/pctorc.htm > * http://adamone.rchomepage.com/guide1.htm#ppm > > I want to be able to measure servo data in 10 bits resolution and send > channel data via an RS232 port similar to one shown in the following links > * http://www.customelectronics.co.uk/fmspic.htm > * http://www.webx.dk/rc/sim-serial/sim-serial.htm > * http://www.veetail.com/FMSInt1.shtml > > Driving multi-channel RC servo motors : > ============================ > I also want to use the FPGA to read in multi-channel servo motors from a > serial RS232 port and control multi channel (8-16 RC servo motors) similar > to one shown in the following links > * http://www.seattlerobotics.org/encoder/200106/16csscnt.htm > * http://www.seetron.com/ssc.htm > * http://home.planet.nl/~j.havinga/servo/servo.htm > * > http://www.yostengineering.com/index.cgi?section=Hardware&subsection=ServoCenter/ServoCenter232.html > > Questions: > ======= > Can you advise me > * A "low cost" FPGA development board > * Free or "low cost" software development tools > * Example FPGA code for above mentioned projects (PPM/PCM decoding and > driving multi-channel RC servo motors) > > > Thank you. > > FPGA Newbee > >
Avnet has fairly low cost boards, less than $200 a couple of years ago. If you need something even lower cost try a web search. Xilinx gives away their "Web pack" tools which are quite good. I'll bet that Altera does also. You could also check the Xilinx and Altera web sites for boards, but the last time I checked (two-three years ago) Xilinx just pointed you to various 3rd-party vendors. PPM decoding is easy, PCM decoding will take extensive reverse engineering, and varies from one manufacturer to the next. I would suggest you stick to PPM. I can't point you to code, but PPM code is _so_ easy that if you can't figure it out you should probably stay away from FPGA work entirely. I suggest you start by encoding a servo pulse, go from there to measuring a pulse width, then tackle sensing the frame sync in a PPM train. You may find that getting the RS-232 to work is the hardest part, but once you have it, you'll just need to put pieces together. -- Tim Wescott Wescott Design Services http://www.wescottdesign.com Posting from Google? See http://cfaj.freeshell.org/google/
Newbee wrote:

> Objective: > ======== > I want to start learning FPGA by experimenting with them. (I do have > experience in digital electronics, microcontrollers and programming. But I > do not have FPGA experince.) What I want to use FPGA for is as follows;
I did that. with an Altera FPGA. We wanted 14 bit resolution on the pulse. http://www.ibrtses.com/electronics/altera.html http://www.ibrtses.com/electronics/project1.jpg http://www.ibrtses.com/electronics/project1i.jpg The hardware is a bit outdated and too expensive. An AVR does the stuffing and the communication. You basically need one counter for the repetition, 20ms, and a set of counters for the pulsewidth. Try the schematic approach. Rene -- Ing.Buero R.Tschaggelar - http://www.ibrtses.com & commercial newsgroups - http://www.talkto.net
Newbee wrote:

> > PPM/PCM Decoding: > ================ > I want to use FPGA to decode PPM and/or PCM signals from a 8-16 channels RC > (radio control) transmitters. This is equivalent to measuring pulse widths > of 8 pulses in a repeating pattern as shown in the following liks
PCM data formats are manufacturer specific and not published anywhere that I have seen. PPM is easily done as it is relatively standard. I have done it with a simple piece of code in a microcontroller using a single timer capture input. I have also done it with RTL for much the same reason that you are interested.
> Driving multi-channel RC servo motors : > ============================ > I also want to use the FPGA to read in multi-channel servo motors from a > serial RS232 port and control multi channel (8-16 RC servo motors) similar > to one shown in the following links
This is most easily done with a single timer channel on a microcontroller driving an external 8 bit serial in parallel out shift register and a bit of firmware.
> Questions: > ======= > * Example FPGA code for above mentioned projects (PPM/PCM decoding and > driving multi-channel RC servo motors)
The reason I mentioned using the microcontroller is mostly as a disclaimer, so nobody would point and laugh at the huge use of resources for so simple of a problem. That said, here is a copy of the verilog I wrote a few years ago for measuring the pulse widths of up to 8 channels, to 12 bit resolution. It was designed to be a microcontroller peripheral for an 8 bit micro. I have similar code for the output function if you are interested, email me (after de-munging the address). This is not in the shiniest verilog style, but here it is: /****************************************************************************** * * Inport * This is an RC system interface. Input data is a composite * pulse train. * 8.0 mHz input clock rate. * Negative active reset. * * The values of the pulse widths may be read as unsigned 12 bit numbers * 8 bits at a time via the CPU bus interface. * * * Written by Bob Harbour 1/97 * *****************************************************************************/ `timescale 1ns/1ns module Inport (PulseIn, Data, Busy, Addr, nCS, nWE, nRst, Clk); input PulseIn; // Pulse train in inout [7:0] Data; // CPU data bus output Busy; // Indicates Pulse is in Sync area input [3:0] Addr; // CPU address lines input nCS; // CPU chip select input nWE; // CPU write enable input nRst; // reset input input Clk; // clock input wire PulseIn; wire [7:0] Data; wire [3:0] Addr; wire nCS, nWE, nRst, Clk; // // Instantiate CPU interface. // nCS Addr nWE register // H XXXX X none // X XXXX L none // L 0000 H Chan1 Latch read Hi Byte // L 0001 H Chan1 Latch read Lo Byte // L 0010 H Chan2 Latch read Hi Byte // L 0011 H Chan2 Latch read Lo Byte // L 0100 H Chan3 Latch read Hi Byte // L 0101 H Chan3 Latch read Lo Byte // L 0110 H Chan4 Latch read Hi Byte // L 0111 H Chan4 Latch read Lo Byte // L 1000 H Chan5 Latch read Hi Byte // L 1001 H Chan5 Latch read Lo Byte // L 1010 H Chan6 Latch read Hi Byte // L 1011 H Chan6 Latch read Lo Byte // L 1100 H Chan7 Latch read Hi Byte // L 1101 H Chan7 Latch read Lo Byte // L 1110 H Chan8 Latch read Hi Byte // L 1111 H Chan8 Latch read Lo Byte // reg [11:0] Latch1, Latch2, Latch3, Latch4, Latch5, Latch6, Latch7, Latch8; wire ReadEn; reg [7:0] readback; assign ReadEn = ~nCS & nWE; always @(Latch1 or Latch2 or Latch3 or Latch4 or Latch5 or Latch6 or Latch7 or Latch8 or Addr) case (Addr) 4'b0000: readback = {4'h0,Latch1[11:8]}; 4'b0001: readback = Latch1[7:0]; 4'b0010: readback = {4'h0,Latch2[11:8]}; 4'b0011: readback = Latch2[7:0]; 4'b0100: readback = {4'h0,Latch3[11:8]}; 4'b0101: readback = Latch3[7:0]; 4'b0110: readback = {4'h0,Latch4[11:8]}; 4'b0111: readback = Latch4[7:0]; 4'b1000: readback = {4'h0,Latch5[11:8]}; 4'b1001: readback = Latch5[7:0]; 4'b1010: readback = {4'h0,Latch6[11:8]}; 4'b1011: readback = Latch6[7:0]; 4'b1100: readback = {4'h0,Latch7[11:8]}; 4'b1101: readback = Latch7[7:0]; 4'b1110: readback = {4'h0,Latch8[11:8]}; 4'b1111: readback = Latch8[7:0]; endcase assign Data = ReadEn ? readback : 8'bz; // // Generate a 500Khz clock period wide pulse // reg [2:0] ClockDiv; reg DelClockDiv2; wire [2:0] NxtClockDiv; wire Clk1Mhz; assign NxtClockDiv = ClockDiv +1; always @(posedge Clk or negedge nRst) if (!nRst) begin ClockDiv <= 3'h0; DelClockDiv2 <= 1'b0; end else begin ClockDiv <= NxtClockDiv; DelClockDiv2 <= ClockDiv[2]; end assign Clk1Mhz = DelClockDiv2 & ~ClockDiv[2]; // // Generate a synchronous version of the signal input. // Instantiate postitive and negative edge triggered one shots. // reg In, InDel, PulseIn_d1; wire PosEdge, NegEdge; always @(posedge Clk or negedge nRst) if (!nRst) begin PulseIn_d1 <= 1'b0; In <= 1'b0; InDel <= 1'b0; end else begin PulseIn_d1 <= PulseIn In <= PulseIn_d1; InDel <= In; end assign PosEdge = In & ~InDel; assign NegEdge = ~In & InDel; // // Instantiate a counter to measure the pulse widths. // reg [11:0] SignalCtr; wire [11:0] SignalCtrNxt; wire SignalCtrZero, SignalCtrOverFlow; assign SignalCtrZero = ~|SignalCtr; assign SignalCtrOverFlow = &SignalCtr; assign SignalCtrNxt = PosEdge? 12'h0 : Clk1Mhz ? (SignalCtr+1) : SignalCtr; always @(posedge Clk or negedge nRst) if (!nRst) SignalCtr <= 12'h0; else SignalCtr <= SignalCtrNxt; // // Instantiate the latches to hold the Signal counter value at // the end of a pulse. // wire [11:0] NxtLatch1, NxtLatch2, NxtLatch3, NxtLatch4; wire [11:0] NxtLatch5, NxtLatch6, NxtLatch7, NxtLatch8; wire Store1, Store2, Store3, Store4, Store5, Store6, Store7, Store8; assign NxtLatch1 = Store1 ? SignalCtr : Latch1; assign NxtLatch2 = Store2 ? SignalCtr : Latch2; assign NxtLatch3 = Store3 ? SignalCtr : Latch3; assign NxtLatch4 = Store4 ? SignalCtr : Latch4; assign NxtLatch5 = Store5 ? SignalCtr : Latch5; assign NxtLatch6 = Store6 ? SignalCtr : Latch6; assign NxtLatch7 = Store7 ? SignalCtr : Latch7; assign NxtLatch8 = Store8 ? SignalCtr : Latch8; always @(posedge Clk or negedge nRst) if (!nRst) begin Latch1 <= 11'h0; Latch2 <= 11'h0; Latch3 <= 11'h0; Latch4 <= 11'h0; Latch5 <= 11'h0; Latch6 <= 11'h0; Latch7 <= 11'h0; Latch8 <= 11'h0; end else begin Latch1 <= NxtLatch1; Latch2 <= NxtLatch2; Latch3 <= NxtLatch3; Latch4 <= NxtLatch4; Latch5 <= NxtLatch5; Latch6 <= NxtLatch6; Latch7 <= NxtLatch7; Latch8 <= NxtLatch8; end // // Instantiate a 4 bit counter to select which latch stores the signal counter // value. A 4 bit counter is used for two reasons. First, it allows pulse // trains with more than 8 pulses to have valid readings for the first 8. // Second, it simplifies handling the sync pulse. // reg [3:0] Channel; wire [3:0] NxtChannel; assign NxtChannel = PosEdge ? (Channel+1) : SignalCtrOverFlow ? 4'h0 : Channel; always @(posedge Clk or negedge nRst) if (!nRst) Channel = 4'h0; else Channel = NxtChannel; // // Instantiate the latch store signals // assign Store1 = PosEdge & ~Channel[3] & ~Channel[2] & ~Channel[1] & Channel[0]; assign Store2 = PosEdge & ~Channel[3] & ~Channel[2] & Channel[1] & ~Channel[0]; assign Store3 = PosEdge & ~Channel[3] & ~Channel[2] & Channel[1] & Channel[0]; assign Store4 = PosEdge & ~Channel[3] & Channel[2] & ~Channel[1] & ~Channel[0]; assign Store5 = PosEdge & ~Channel[3] & Channel[2] & ~Channel[1] & Channel[0]; assign Store6 = PosEdge & ~Channel[3] & Channel[2] & Channel[1] & ~Channel[0]; assign Store7 = PosEdge & ~Channel[3] & Channel[2] & Channel[1] & Channel[0]; assign Store8 = PosEdge & Channel[3] & ~Channel[2] & ~Channel[1] & ~Channel[0]; // // Sense the Signal Counter Overflow and clear a Busy flag. Set it on the next // PosEdge. This circuit is Set Dominant. // reg Busy; wire NxtBusy; assign NxtBusy = PosEdge ? 1'b1 : SignalCtrOverFlow ? 1'b0 : Busy; always @(posedge Clk or negedge nRst) if (!nRst) Busy <= 1'b1; else Busy <= NxtBusy; endmodule