Newbee said:
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