Я хочу сделать приемник UART, который читает 8 последовательных битов с битом четности в конце и с простым стоповым битом. Моя FPGA имеет тактовую частоту 100 МГц, а данные, которые передаются на uart, имеют скорость 56700 бод. Коэффициент деления - 1736 (56700 * 1736 ≈ 100 МГц). Два выхода - это сообщение о входе, декодированное uart, и сигнал ошибки, который указывает, правильно ли uart прочитал ввод. Вот что у меня есть:
library ieee;
use ieee.std_logic_1164.ALL;
use ieee.numeric_std.all;
entity uart_receiver is
generic (
clksPerBit : integer := 1736 -- Needs to be set correctly
);
port (
clk : in std_logic;
clk_en_uart : in std_logic ;
reset : in std_logic;
uart_rx : in std_logic;
error : out std_logic;
char : out std_logic_vector(7 downto 0)
);
end uart_receiver;
architecture uart_receiver_arch of uart_receiver is
type etat is (init, start_bit, receiving_bits, parity_bit,
stop_bit );
signal current_state : etat := init ;
signal error_signal : std_logic := '0';
signal clk_count : integer range 0 to clksPerBit-1 := 0;
signal bit_index : integer range 0 to 7 := 0; -- 8 Bits Total
signal data_byte : std_logic_vector(7 downto 0) := (others => '0');
begin
process (clk_en_uart)
begin
if rising_edge(clk_en_uart) then
end if;
end process;
process (clk,reset)
variable check_parity : integer range 0 to 7 := 0;
begin
if (reset = '1') then
current_state <= init;
error_signal <= '0';
clk_count <= 0;
bit_index <= 0;
data_byte <= (others => '0');
elsif rising_edge(clk) then
case current_state is
when init =>
clk_count <= 0;
Bit_Index <= 0;
if uart_rx = '0' then -- Start bit detected
current_state <= start_bit;
else
current_state <= init;
end if;
when start_bit =>
if clk_count = (clksPerBit-1)/2 then
if uart_rx = '0' then
clk_count <= 0; -- reset counter since we found the middle
current_state <= receiving_bits;
else
current_state <= init;
end if;
else
clk_count <= clk_count + 1;
current_state <= start_bit;
end if;
when receiving_bits =>
if clk_count < clksPerBit-1 then
clk_count <= clk_count + 1;
current_state <= receiving_bits;
else
clk_count <= 0;
data_byte(bit_index) <= uart_rx;
if bit_index < 7 then
bit_index <= bit_index + 1;
current_state <= receiving_bits ;
else
bit_index <= 0;
current_state <= parity_bit;
end if;
end if;
when parity_bit =>
if clk_count < clksPerBit-1 then
clk_count <= clk_count + 1;
current_state <= parity_bit;
else
for k in 0 to 7 loop
if ( data_byte(k) = '1' ) then
check_parity := check_parity + 1 ;
end if;
end loop;
if ((uart_rx = '1' and check_parity mod 2 = 0) or (uart_rx = '0' and check_parity mod 2 = 1)) then
error_signal <= '1' ;
else
error_signal <= '0';
end if ;
current_state <= stop_bit;
end if;
when stop_bit =>
if clk_count < clksPerBit-1 then
clk_count <= clk_count + 1;
current_state <= stop_bit ;
else
clk_count <= 0;
current_state <= init;
end if;
when others =>
current_state <= init;
end case;
end if;
char <= data_byte ;
error <= error_signal ;
end process;
end uart_receiver_arch;
Таким образом, существует фазовый сдвиг между данными, которые передаются на uart, и его часами. Если есть фазовый сдвиг, я не читаю данные в нужное время. Я думаю, что этого кода достаточно для решения этой проблемы. Но я создал clock_divider и не могу найти способ использовать его в этом коде. Это мой делитель часов:
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
entity clock_divider is
generic (divfactor : positive := 1736);
Port (clk,clk2, reset : in STD_LOGIC ;
clkdiv, activationsig : out STD_LOGIC );
end clock_divider;
architecture clock_divider_arch of clock_divider is
begin
process(clk,reset)
variable clksigv : std_logic := '0' ;
variable activationsigv : std_logic := '0' ;
variable count : integer := 0 ;
begin
if (reset = '1') then
clksigv := '0' ;
activationsigv := '0' ;
count := 0 ;
elsif ( rising_edge(clk) ) then
count := count + 2 ;
if (activationsigv = '1') then
activationsigv := '0';
end if;
if ( count >= divfactor - 1 ) then
clksigv := not(clksigv) ;
if ( clksigv = '1' ) then
activationsigv := '1' ;
end if;
count := 0 ;
end if ;
end if ;
clkdiv <= clksigv ;
activationsig <= activationsigv;
end process ;
end clock_divider_arch;
Выходы этого делителя тактовых импульсов - это тактовая частота и сигнал активации, который, когда он равен «1», я должен прочитать данные в uart. Итак, два выхода также должны быть входами uart. В uart_recevier clk_en_uart на самом деле представляет собой разделенные часы, но я не использую его, потому что не знаю, как это сделать.
Я думаю, что решение состоит в том, чтобы «активировать» эти разделенные часы, когда я вхожу в случай start_bit, чтобы у меня было два тактовых генератора с одинаковой фазой и одинаковой частотой, но я также считаю, что невозможно установить фазу для часов.
Я не уверен, что ясно рассмотрел свою проблему. Если в моем коде или моем объяснении есть что-то, чего вы не понимаете, не стесняйтесь задавать вопросы.
Спасибо за вашу помощь, надеюсь, что я найду решение.
Похоже, предлагаемое решение этой проблемы является сложным.
Обычный подход заключается в том, что justs приемника ищут спад стартового бита, затем считают в течение половины времени бита (1736/2 цикла в вашем случае), затем выбирают значение стартового бита там, а затем выбирают данные, четность и значения стоповых битов после каждого полного битового времени (1736 циклов в вашем случае). После этого начните поиск нового падающего фронта стартового бита.
Разница между частотами передатчика и приемника тогда (обычно) настолько мала, что время выборки будет практически посередине для сообщений всего 11 бит при относительно низкой скорости передачи битов, а перезапуск счетчика при заднем фронте стартового бита гарантирует, что любой эффект устранена длительная разность частот.