Communicating with the outside world
So, after displaying something on a VGA monitor, how do we talk to a PC?
In this chapter you will build the transmit part of a serial (RS-232) interface, using shift registers. On the Papilio One you can talk directly to the USB interface, but on the Basys2 you will need a USB to 3.3V Serial breakout board.
What is RS-232?
RS-232 is a very old standard originally used to interface digital systems with analogue phone lines and other data circuits. It enables relatively low speed communication between devices, and is relatively simple to implement.
If hardware handshaking is not used only three wires are needed:
Wire Use
GND Signal Ground
TX Transmitted data
RX Received data
Which signal a device listens to for incoming data and which signal it actively sends data is very confusing. If the device is a "DTE" (Data Terminating Equipment) it transmits on TX and listens on RX. If the device is "Data Communicating Equipment" (e.g., a modem) it listens for data on TX and transmits on RX.
The standard speeds range from 75 baud up to 115,200 baud, with 9600 or 19200 being the most common speeds for data that is presented to people (such as on a serial console).
As well as baud speed, both ends of a connection must be using the same frame parameters - the most common being one start bit, eight data bits, no parity bit and one stop bit. As the frame is ten bits long, at 9600 baud you can send 960 bytes per second.
There is a whole lot more to the standard, mostly around how senders and receivers control the flow of data to ensure that data does not overrun receiving buffers. When using modern hardware at slow speeds handshaking isn’t really an issue.
Here is what the signal should look like on the wire:
Generating an RS-232 signal
For this project we need a shift register (well two actually). So what does a shift register look like in VHDL?
Here is a 16-bit register that loops from bit 0 to bit 15 - a much simpler way to generate one pulse every 16 cycles than using a counter.
...
signal shiftreg : std_logic_vector(15 downto 0) := "0000000000000001";
...
if rising_edge(clk) then
shiftreg <= shiftreg(0) & shiftreg(15 downto 1);
end if;
For RS-252 we use pretty much this construct, but feed in the idle bit value (1). This code will send the Z character once (after which the shift register is filled with ’1’s):
...
signal shiftreg : std_logic_vector(9 downto 0) := "1010110100";
...
data_out <= shiftreg(0);
...
if rising_edge(clk) then
shiftreg <= ’1’ & shiftreg(9 downto 1)
end if;
The user data is bits 8 downto 1 - this is the "byte" of user data - bit 0 is the start bit, and bit 9 is the stop bit. I chose the ASCII code for Z as it will still be a Z regardless of if the least or most significant bit gets transferred first - very useful for initial testing!
The only problem with the code so far is that we are transmitting at the clock speed - either 32,000,000 or 50,000,000 baud! To control the rate of sending we also need a counter that allows a bit to be sent at 9600 baud - once every 3,333 cycles (at 32MHz) or once every 5,208 cycles (@50MHz):
...
signal shiftreg : std_logic_vector(9 downto 0) := "1010110100";
signal counter : std_logic_vector(12 downto 0) := (others => ’0’);
...
data_out <= shiftreg(0);
...
if rising_edge(clk) then
if counter = 3332 then
shiftreg <= ’1’ & shiftreg(9 downto 1);
counter <= (others => ’0’);
else
counter <= counter+1;
end if;
end if;
We can make it send the same data over and over again by making the shift register longer and looping the shift register’s output back on its input. To do this it needs a longer shift register, ensuring that we have some quiet space following the stop bit to allow the receiver to frame the data correctly:
...
signal shiftreg : std_logic_vector(15 downto 0) := "1111111010110100";
signal counter : std_logic_vector(12 downto 0) := (others => ’0’);
...
data_out <= shiftreg(0);
...
if rising_edge(clk) then
if counter = 3332 then
shiftreg <= shiftreg(0) & shiftreg(15 downto 1);
counter <= (others => ’0’);
else
counter <= counter+1;
end if;
end if;
This code should be enough to enable you to test your RS-232 port actually sends data as expected.
Sending variable data
To make this useful you really need to be able to send different data bytes. And to do this correctly you have to know when the interface is busy.
The easiest way to do this is to have a second shift register which is filled with ’1’s when the character is loaded into ’shiftreg’ and filled with \’0’s as bits are transmitted. Once this second shift register is all zeros, then things are ready for the next byte to be sent:
...
signal busyshiftreg : std_logic_vector(9 downto 0) := (others => ’0’);
signal datashiftreg : std_logic_vector(9 downto 0) := (others => ’1’);
signal counter : std_logic_vector(12 downto 0) := (others => ’0’);
...
data_out <= datashiftreg(0);
busy_out <= busyshiftreg(0);
...
if rising_edge(clk) then
if busyshiftreg(0) = ’0’ then
busyshiftreg <= (others => ’1’);
datashiftreg <= ’1’ & databyte & ’0’;
counter <= (others <= ’0’);
else
if counter = 3332 then
datashiftreg <= ’1’ & datashiftreg(9 downto 1);
busyshiftreg <= ’0’ & busyshiftreg(9 downto 1);
counter <= (others => ’0’);
else
counter <= counter+1;
end if;
end if;
end if;
The important bit is to remember to reset counter when a new byte is loaded into datashiftreg. Failing to do this will cause the start bit to be of different lengths - the project will work correctly when streaming bytes to the host, but will sometimes get garbage for the first few bytes of a message until it recovers from the bad bit.
Connecting your FPGA board to a PC
Caution
Connecting the FPGA directly to your serial port will most likely ruin your FPGA Most modern PCs do not have RS-232 ports, and if they do they are expecting the higher voltage levels that standard RS-232 uses - the standard uses up to +/- 25V!
To connect to a PC over USB you can use something like Sparkfun’s "FTDI Basic 3.3V - USB to Serial" (http://www.sparkfun.com/- products/9893) and jumper wires. Here’s my setup:
Tip
If you are using the Basys2 and want to talk to a true standards compliant RS-232 port, or if you want to avoid issues caused by loose wires you can use the RS-232 PMOD http://www.digilentinc.com/Products/Detail.cfm?Prod=PMOD-RS232 with your Basys2.
Project 16
• Create a project that sends ’Z’ over RS-232
• Create a project that sends the state of switches(3 downto 0) over RS-232
– You could increase the length of the shift register and send multiple bytes
– You could convert the data to ASCII and send four switches in a single byte
– You could map the 16 possible values into 16 contiguous printable characters (maybe characters A through P)
• Change it to only send a byte when the switches change
• Extend the project to send the state of all eight switches
Challenge
• What would happen if the input to the RS-232 TX component was to change, and then change back to its original state in less than 1/960th of a second? Can loss of data be avoided?
No comments:
Post a Comment