Recepção Serial – RS232
O projeto descrito nesse artigo tem como objetivo a implementação de um receptor serial com base no protocolo RS232, sendo um projeto complementar ao transmissor serial RS232, detalhado em outro artigo.
Essa página demonstrará o exemplo de um projeto descrito em VHDL e simulado no Modelsim-Altera®, através de um testbench, que será a entrada de dados da simulação e é escrito em VHDL.
Os equipamentos necessários para a implementação e teste de projeto são:
- 1x Kit CPLD_7064;
- 2x Placa de LEDs;
- 1x Placa de botões;
- 1x Kit uCduíno para enviar dados.
Descrição do Funcionamento
Como já foi citado acima, o projeto baseia-se no protocolo de comunicação serial RS232. Este protocolo utiliza como inicio de transmissão um pulso em nível lógico baixo e como término um pulso em nível lógico alto, dessa forma pode-se sincronizar a transmissão.
Figura 1: Forma de onda da transmissão serial RS232.
A leitura será feita em uma frequência de 9600 bits/s, tomando apenas uma amostra por pulso de dado. As saídas serão um dado de 8 bits, um sinal de indicação que o dado está pronto para a leitura, um sinal indicando a erro de overrun (Dado não lido sobrescrito) e um indicando o erro de transmissão. Quando houver um erro de overrun o programa continuará funcionando, sendo este sinal a forma de indicar ao dispositivo que está adquirindo os dados de que dado(s) foi/foram perdido(s). O erro de transmissão faz com que o programa pare de receber dados, pois quando ocorre um erro de transmissão o receptor perde a sincronia com o transmissor.
Descrição em VHDL
Nesse projeto serão usados 15 portas do CPLD, sendo 4 entradas e 11 saídas. As entradas são Rx, clock, resetn e leitura (sinal indicando que o dado de saída foi lido). As saídas serão: dado de 8 bits, erro de overrun, erro de transmissão e ready (sinal indicando que o dado está pronto para ser lido).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
entity RECEBE_SERIAL is port ( RX, CLOCK, RESETn, LEITURA : std_logic; READY, ERRO_OVERRUN, ERRO_SERIAL : out std_logic := '0'; SAIDA : out std_logic_vector(7 downto 0) := "00000000" ); end entity; |
Dentro da architecture será declarada a máquina de estados que indicará o estado do receptor, juntamente com o signal auxiliar para fazer a leitura do dado. Os estados serão: espera (onde o receptor está esperando o início de uma transmissão), recepção (onde estará recebendo os dados), entrega (onde atribuirá a saída o dado recebido) e erro (onde ficará quando ocorrer erro de transmissão).
1 2 3 4 5 6 7 8 9 |
signal DADO : std_logic_vector(9 downto 0):= "0000000001"; -- 10 bits por causa do stop bit e do start bit type ESTADOS is ( ESPERA, RECEPCAO, ENTREGA, ERRO); -- maquina de estado do modulo de recepção signal SERIAL : ESTADOS := ESPERA; |
O processo principal tem na sua lista de sensibilidade o clock, o reset e o rx (para identificar o start bit). Dentro do processo principal será utilizado o uma variável de contagem para fazer o preescaler e uma para indicar qual bit está sendo lido. A variável flag_ready funciona para indicar o valor de ready e ela se faz necessária devido a impossibilidade de ler valores de saída dentro do código.
1 2 3 4 5 |
variable CONTA : integer range 0 to 2500 := 0; variable CONT : integer range 0 to 10 := 0; variable FLAG_READY : std_logic := '0'; |
A primeira parte do processo descreve o funcionamento quando o botão de reset está pressionado.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
-- Condições de reset if (RESETn = '0') then DADO <= "0101010101"; CONTA := 0; CONT := 0; ERRO_SERIAL <= '0'; ERRO_OVERRUN <= '0'; SERIAL <= ESPERA; {/code} Em seguida é implementado a identificação do start bit. {code} -- Identifica o envio de um start bit elsif RX = '0' and SERIAL = ESPERA then SERIAL <= RECEPCAO; |
Na rotina principal, primeiramente faz-se a verificação de leitura do dado de saída. Em seguida são declaradas as ações dependendo do estado. Quando está recebendo é necessário fazer o preescaler de 2500, pois a comunicação ocorre a 9600 bits/s, o clock do kit é 24MHz e só está se tomando uma amostra do dado, no centro do pulso. Cada vez que ocorre o estouro de contagem do preecaler o toma-se uma amostra do dado e armazena no signal dado e incrementa-se a variável cont, que indica qual bit está sendo lido. Depois de armazenar o décimo bit, faz-se o teste se o start bit e o stop bit estão corretos, caso estejam o programa segue para o estado entrega, caso não o programa vai para o estado erro.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
-- identifica se DADO na SAIDA foi lido if LEITURA = '0' and SERIAL /= ERRO then FLAG_READY := '0'; ERRO_OVERRUN <= '0'; end if; -- Definições da máquina de ESTADOS case SERIAL is when RECEPCAO => -- preescaler para a correta temporização da comunicação, 9600 Bits/s CONTA := CONTA + 1; if CONTA = 2500 then DADO (CONT) <= RX; CONTA := 0; CONT := CONT + 1; -- Final da transmissão e verificação de ERROs na transmissão elsif CONT = 10 and DADO(0) = '0' and DADO(9) = '1' then SERIAL <= ENTREGA; CONT := 0; elsif CONT = 10 then SERIAL <= ERRO; end if; |
No estado entrega o programa atualiza o valor do dado de saída verifica se o valor antigo foi lido, se não ele acusa erro de overrun, e sinaliza que o dado está pronto para ser lido. Quando o programa esta no estado erro ele acusa o erro de comunicação e fica esperando o programa ser “resetado”. Quando está no estado espera o programa atribui zero a variável responsável por indicar qual o bit esta sendo lido e 1250 ao valor inicial do preescaler, a variável conta, pois na primeira medida é preciso que se conte apenas metade do pulso.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
when ENTREGA => SAIDA <= DADO (8 downto 1); -- Seleciona apenas os DADOs validos if FLAG_READY = '1' then -- Verifica se o DADO antigo havia sido lido ERRO_OVERRUN <= '1'; end if; FLAG_READY := '1'; -- Sinaliza a chegada do DADO SERIAL <= ESPERA; when ERRO => ERRO_SERIAL <= '1'; when others => -- A primeira LEITURA precisa ser na metade de um periodo (centro do pulso) CONTA := 1250; CONT := 0; end case; |
Por ultimo atualiza-se o valor de ready com o da flag ready.
1 |
READY <= FLAG_READY; |
Figura 2: Resultado da Analise e Sintese.
Simulação
A simulação será baseada no envio de dados utilizando os princípios do protocolo RS232 e a verificação de que os dados foram lidos corretamente. Também serão emuladas as situações de erro. Primeiramente fazem-se as declarações dos signals, nesse exemplo serão usados os mesmos nomes utilizados no código fonte.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
signal RX, RESETn, CLOCK, LEITURA : std_logic; signal READY, ERRO_SERIAL, ERRO_OVERRUN : std_logic; signal SAIDA : std_logic_vector(7 downto 0); begin UUT: entity work.recebe_serial port map ( RX, CLOCK, RESETn, LEITURA, READY, ERRO_OVERRUN, ERRO_SERIAL, SAIDA ); |
As primeiras instruções da simulação emulam o pressionamento do botão de reset e os pulsos de clock.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
GERA_CLOCK: process begin -- simulação do pressionamento do botão de reset. RESETn <= '0' after 0 ms, '1' after 1 ms, '0' after 50 ms; CLOCK <= '0'; wait for 1 ms; -- rotina de geração de CLOCK while RESETn = '1' loop CLOCK <= not CLOCK; wait for 20833 ps; end loop; wait; end process; |
A segunda parte da simulação emula o envio dos dados hexadecimais 55, F0, 0F, 91 e FF, de forma a testar no envio dos dois últimos dados a sinalização de erro. Abaixo segue a programação para o envio do primeiro dado:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
process begin RX <= '1'; LEITURA <= '0'; wait for 2ms; -- Enviando 0x"55" RX <='0'; wait for 104 us; RX <='1'; wait for 104us; RX <='0'; wait for 104 us; RX <='1'; wait for 104us; RX <='0'; wait for 104 us; RX <='1'; wait for 104us; RX <='0'; wait for 104 us; RX <='1'; wait for 104us; RX <='0'; wait for 104 us; RX <='1'; wait for 1ms; LEITURA <= '1'; wait for 5 us; LEITURA <= '0'; wait for 100 us; |
As formas de ondas observadas na simulação foram:
Figura 3: Formas de ondas obtidas na simulação.
Estrutura Física
Figura 4: Circuito montado.
Para o funcionamento do projeto é apenas necessário o kit CPLD_7064, mas para teste foi utilizado um kit uCduíno (o programa utilizado está junto aos arquivos de projeto disponíveis no final da página) enviando um número (a cada requisição de envio o microcontrolador incrementa em 1 o número enviado) de forma serial, o CPLD_7064 receberá os dados e apresentará as saídas em duas placas de LEDs (uma apresentará os dados e a outra o sinais de erro) e uma placa de botões controlará os pulsos de reset e leitura. Os fios presente na montagem são responsáveis por: o marrom por interligar o terra das duas placas e o azul pela ligação entre Rx e Tx. O CPLD utlizado foi o EPM7064 da família MAX7000S
Montagem e Roteamento
As placas de LEDs serão colocadas nos conectores CON1 e CON2 e a placa de teclado será conectado no conector CON3, como mostrado nas ilutrações abaixo feitas no software fritizing.
Figura 5: Ligação do kit de LEDs na protoboard.
Figura 6: Conexão da placa de botões.
Utilizando o pino 16 como Rx, a distribuição de pinos do CPLD fica da seguinte forma:
Figura 7: Atribuição de pinos do CPLD.
Gravação e Teste
A gravação do projeto segue os passos descritos no tutorial. Para testar o funcionamento do circuito utilizou-se um kit uCduíno enviando dados para o CPLD e o CPLD mostra os dados nos LEDs, como apresentado no vídeo no início da página.