Le port série ou RS-232 est un bus de communication qui permet de transférer des données en série sur deux fils, TxD (Tansmission Data) et RxD(Reception Data) en utilisant un UART (Universal Asynchronous Receiver Transmitter).
Pour la bonne transmission des données il est nécessaire que l'émetteur et le récepteur utilisent le même protocole.
Composition de la trame:
- 1 bit de start : ce bit correspond à un niveau logique 0, il permet la synchronisation de l'horloge du recepteur
- 5 à 8 bits de données : ce sont les données à transmettre
- 0 ou 1 bit de parité : pas obligatoire, ce bit permet de verifier la présence d'erreurs
- Bit(s) de stop : on retourne au niveau logique 1 qui est l'état de repos de la ligne
Programme VHDL
Dans ce programme nous travaillerons avec un débit de 19 200 baud. Ce paramètre peut être ajusté directement dans le programme. Pour plus de détails sur le fonctionnement consulter Design Example : UART pdf
- Composant générateur d'horloge d'échantillonnage:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use ieee.numeric_std.all;
entity mod_m_cnter is
generic(
N: integer := 8;
M: integer := 163);
port(
clk, reset: in std_logic;
clk_cnt: out std_logic);
end mod_m_cnter;
architecture arch of mod_m_cnter is
signal r_reg: unsigned(N-1 downto 0);
signal r_next: unsigned(N-1 downto 0);
begin
process(clk, reset)
begin
if (reset = '0') then
r_reg <= (others => '0');
elsif (clk'event and clk='1') then
r_reg <= r_next;
end if;
end process;
-- next state logic
r_next <= (others => '0') when r_reg=(M-1) else r_reg + 1;
clk_cnt <= '1' when r_reg=(M-1) else '0';
-- output logic
end arch;
- Composant UART Rx
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity uart_rx is
generic(
DBIT: integer := 8;
SB_TICK: integer := 16 -- (16, 24 and 32 for 1, 1.5 and 2 stop bits)
);
port(
reset, clk: in std_logic;
rx: in std_logic;
s_tick: in std_logic;
rx_done_tick: out std_logic;
dout: out std_logic_vector(7 downto 0)
);
end uart_rx;
architecture arch of uart_rx is
type state_type is (idle, start, data, stop);
signal state_reg, state_next: state_type;
signal s_reg, s_next: unsigned(3 downto 0);
signal n_reg, n_next: unsigned(2 downto 0);
signal b_reg, b_next: std_logic_vector(7 downto 0);
begin
process(clk, reset) -- FSMD state and data regs.
begin
if (reset = '0') then
state_reg <= idle;
s_reg <= (others => '0');
n_reg <= (others => '0');
b_reg <= (others => '0');
elsif (clk'event and clk='1') then
state_reg <= state_next;
s_reg <= s_next;
n_reg <= n_next;
b_reg <= b_next;
end if;
end process;
-- next state logic
process (state_reg, s_reg, n_reg, b_reg, s_tick, rx)
begin
state_next <= state_reg;
s_next <= s_reg;
n_next <= n_reg;
b_next <= b_reg;
rx_done_tick <= '0';
case state_reg is
when idle =>
if (s_tick = '1') then
if (rx = '0') then
state_next <= start;
s_next <= (others => '0');
end if;
end if;
when start =>
if (s_tick = '1') then
if (s_reg = 7) then
state_next <= data;
s_next <= (others => '0');
n_next <= (others => '0');
else
s_next <= s_reg + 1;
end if;
end if;
when data =>
if (s_tick = '1') then
if (s_reg = 15) then
s_next <= (others => '0');
b_next <= rx & b_reg(7 downto 1);
if (n_reg = (DBIT - 1)) then
state_next <= stop;
else
n_next <= n_reg + 1;
end if;
else
s_next <= s_reg + 1;
end if;
end if;
when stop =>
if (s_tick = '1') then
if (s_reg >= (SB_TICK-1)) then
state_next <= idle;
rx_done_tick <= '1';
else
s_next <= s_reg + 1;
end if;
end if;
end case;
end process;
dout <= b_reg;
end arch;
- Composant UART Tx
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity uart_tx is
generic(
DBIT: integer := 8;
SB_TICK: integer := 16
);
port(
reset,clk: in std_logic;
tx_start: in std_logic;
s_tick: in std_logic;
din: in std_logic_vector(7 downto 0);
tx_done_tick: out std_logic;
tx: out std_logic
);
end uart_tx;
architecture arch of uart_tx is
type state_type is (idle, start, data, stop);
signal state_reg, state_next: state_type;
signal s_reg, s_next: unsigned(3 downto 0);
signal n_reg, n_next: unsigned(2 downto 0);
signal b_reg, b_next: std_logic_vector(7 downto 0);
signal tx_reg, tx_next: std_logic;
begin
process(clk, reset) -- FSMD state and data regs.
begin
if (reset = '0') then
state_reg <= idle;
s_reg <= (others => '0');
n_reg <= (others => '0');
b_reg <= (others => '0');
tx_reg <= '1';
elsif (clk'event and clk='1') then
state_reg <= state_next;
s_reg <= s_next;
n_reg <= n_next;
b_reg <= b_next;
tx_reg <= tx_next;
end if;
end process;
-- next state logic
process (state_reg, s_reg, n_reg, b_reg, s_tick,tx_reg, tx_start, din)
begin
state_next <= state_reg;
s_next <= s_reg;
n_next <= n_reg;
b_next <= b_reg;
tx_next <= tx_reg;
tx_done_tick <= '0';
case state_reg is
when idle => tx_next <= '1';
if (tx_start= '1') then
state_next <= start;
s_next <= (others => '0');
b_next <= din;
end if;
when start => tx_next <= '0';
if (s_tick = '1') then
if (s_reg = 15) then
state_next <= data;
s_next <= (others => '0');
n_next <= (others => '0');
else
s_next <= s_reg + 1;
end if;
end if;
when data => tx_next <= b_reg(0);
if (s_tick = '1') then
if (s_reg = 15) then
s_next <= (others => '0');
b_next <= '0' & b_reg(7 downto 1);
if (n_reg = (DBIT - 1)) then
state_next <= stop;
else
n_next <= n_reg + 1;
end if;
else
s_next <= s_reg + 1;
end if;
end if;
when stop => tx_next <= '1';
if (s_tick = '1') then
if (s_reg = (SB_TICK-1)) then
state_next <= idle;
tx_done_tick <= '1';
else
s_next <= s_reg + 1;
end if;
end if;
end case;
end process;
tx <= tx_reg;
end arch;
- Composant générateur d'impulsion de durée réglable
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use ieee.std_logic_unsigned.all;
entity pulse is
generic(
lenght: integer := 159 --clock period
);
port(
clock,Key :in std_logic;
s :out std_logic
);
end;
architecture behavior of pulse is
type state_type is (idle, impulse, stop);
signal state : state_type;
signal count : integer := 0;
begin
process (clock,state)
begin
if (clock'EVENT and clock ='1') then
case state is
when idle => s<='0';
if key ='1' then state <= impulse;
else state <= idle;
end if;
when impulse => s<='1';
if count=lenght then state <= stop;
else count<=count+1;
end if;
when stop => s<='0'; count<=0;
if Key ='0' then state <= idle;
else state <= stop;
end if;
end case;
end if;
end process;
end behavior;
- Composant registre
use ieee.std_logic_1164.all;
entity registre is
port (d_out : in std_logic_vector(7 downto 0);
enable, reset : in std_logic;
out_d : out std_logic_vector(7 downto 0));
end;
architecture arch of registre is
begin
process (enable)
begin
if (reset = '0') then
out_d <= (others=>'0');
elsif (enable'event and enable='1') then out_d <= d_out;
end if;
end process;
end arch;
- Fichier principal : module RS232
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity module_RS232 is
port (
uart_rxd : in std_logic;
uart_txd : out std_logic;
KEY : in std_logic_vector(1 downto 0);
SW : in std_logic_vector(7 downto 0);
CLOCK_50 : in std_logic;
LEDG : out std_logic_vector(7 downto 0));
end;
architecture behavior of module_RS232 is
---------------------------------------------------
component mod_m_cnter
generic(
N: integer := 8;
M: integer := 163);
port(
clk, reset: in std_logic;
clk_cnt: out std_logic
);
end component;
---------------------------------------------------
component uart_rx is
generic(
DBIT: integer := 8;
SB_TICK: integer := 16 -- (16, 24 and 32 for 1, 1.5 and 2 stop bits)
);
port(
clk, reset: in std_logic;
rx: in std_logic;
s_tick: in std_logic;
rx_done_tick: out std_logic;
dout: out std_logic_vector(7 downto 0));
end component;
------------------------------------------------------
component uart_tx is
generic(
DBIT: integer := 8;
SB_TICK: integer := 16);
port(
reset,clk: in std_logic;
tx_start: in std_logic;
s_tick: in std_logic;
din: in std_logic_vector(7 downto 0);
tx_done_tick: out std_logic;
tx: out std_logic
);
end component;
-------------------------------------------------PULSE
component pulse IS
generic(
lenght: integer := 159 --clock period
);
PORT(
clock,Key :IN std_logic;
s :OUT std_logic
);
END component;
-------------------------------------------------
signal wire_clk_cnt, s_tick, wire_rx_done ,wire_tx_done, wire_reg_enable, wire_pulse : std_logic;
signal wire_dataout: std_logic_vector(7 downto 0);
begin
I1 : mod_m_cnter port map (clk=>CLOCK_50, reset=>(KEY(0)), clk_cnt=>wire_clk_cnt);
I2 : uart_rx port map (clk=>CLOCK_50, s_tick=>wire_clk_cnt, reset=>(KEY(0)), rx=>uart_rxd, dout=>wire_dataout, rx_done_tick=>wire_reg_enable);
I3 : uart_tx port map (clk=>CLOCK_50, s_tick=>wire_clk_cnt, reset=>(KEY(0)), tx=>uart_txd, din=>SW,tx_done_tick=>wire_tx_done, tx_start=>wire_pulse);
I4 : registre port map (reset=>(KEY(0)), d_out=>wire_dataout, out_d =>LEDG, enable=>wire_reg_enable);
I5 : pulse port map(clock => CLOCK_50, Key => not(KEY(1)), s => wire_pulse);
end behavior;
Quelques
modifications ont été apportées pour le générateur d'horloge d'échantillonage
et pour l'UART ou la machine à état s'arrête parfois à cause d'un
défaut de synchronisation du signal reçu. Des circuits
ont été supprimés pour plus de simplicité et un générateur d'impulsion réglable a été ajouté pour envoyer les données une à une vers l'ordinateur. Lorsque l'on appuie sur le bouton pour émettre, on applique sur l'entrée tx_data une impulsion de même durée que la trame de donnée afin de s'assurer que toute cette trame à bien été envoyée.
Bonjour,
RépondreSupprimerj'essaye de reproduire la transition d'information sur une DE1 d'Altera.
Il apparait que le compilateur donne une erreur sur :
I4 : registre port map (reset=>(KEY(0)), d_out=>wire_dataout, out_d =>LEDG, enable=>wire_reg_enable);
car le composant registre n'est pas renseigné dans le module rs232.
J'ai commenté la ligne et je n'arrive pas à avoir de communication.
Votre travail m’intéresse énormément car j'ai pour objectif de faire dialoguer un arm avec cette carte de dev.
Bonjour,
RépondreSupprimerVous avez essayé d'ajouter le composant registre au fichier principal "module RS232"? Je l'ai effectivement oublié dans les lignes de code.
Le voici:
-----------------------------------------------REGISTRE
component registre is
port (d_out : in std_logic_vector(7 downto 0);
enable,reset : in std_logic;
out_d : out std_logic_vector(7 downto 0));
end component;
Il sert à synchroniser le signal issu de l'ordinateur pour ne pas bloquer la machine à état.
Si vous avez un autre problème n'hésiter pas.
Bonjour,
RépondreSupprimermerci cela fonctionne, je vais maintenant travailler sur le protocole pour transférer une instruction de 32 bits.
salut.svp pouvez vous m'envoyer le code qui fonctionne..j'arrive pas a simuler..
Supprimervoila mon e-mail (lahlouhamza780@gmail.com)
merci d'avance frère
Bonjour
RépondreSupprimerQuelles valeurs dois-je modifier pour avoir un baud rate à 9600 bauds s'il vous plaît?
Cest bon, j'ai pu résoudre mon problème. Il m'a fallu utiliser l'horloge à 28MHz, plutôt que celle à 50MHz et changer le rapport de 163 à 188.
Supprimerbnsr.pouvez vous m'envoyer le code..j'arrive pas trouver un qui fonctione directement.
Supprimermerci de l'envoyer vers mon e-mail ( lahlouhamza780@gmail.com )
Bonjour,
RépondreSupprimerJe souhaite vous faire part de mon application, je souhaite recevoir des trames UART du pc et ensuite les transférer en série vers un PMOD.
J'ai testé le programme de réception et ça marche (Je visualise DOUT sur les LEDs) et j'ai les bons bits affichés.
J'ai écris ensuite un programme (parallèle to série) qui marche en simulation, aussi en pratique j'ai la bonne trame mais qui n'est pas bien codée ( Des breaks apparaissent sur l'analyseur de bus).
Je pense c'est dû au Bits de start et de stop que je perds entre le component RECEIVE et celui de Serialisation.
Que pensez vous?
Bonjour a tous! je cherche a coder le fichier vhdl du registre pour un recepteur mot!
RépondreSupprimerdes reponses ou des idées svp