Como criar um sistema de hardware/software para atingir os objetivos de design

Particionamento de hardware/software é o problema de dividir os cálculos de um aplicativo em uma parte que é executada como instruções sequenciais em um microprocessador (o “software”) e uma parte que é executada como circuitos paralelos em alguma estrutura de circuito integrado, como um ASIC ou FPGA (o “hardware”), de modo a atingir metas de design definidas para métricas como desempenho, potência, tamanho e custo.

Introdução:

O objetivo do projeto é implementar um sistema baseado em Co-Design de Hardware/Software em uma placa Spartan 3E FPGA e um microcontrolador usando a ferramenta Xilinx IDE. Alfabetos e/ou números são inseridos através do teclado (botões no FPGA) e por sua vez exibidos no display LCD (no Microcontrolador), ao mesmo tempo em que o áudio correspondente à tecla pressionada será reproduzido em um alto-falante. O sistema é implementado tanto em hardware quanto em software. VHDL é usado como linguagem de descrição de hardware e C como software. Embora este projeto possa ser implementado apenas usando FPGA ou microcontrolador, esse não é o objetivo aqui. Queremos introduzir este conceito para que possa ser aplicado posteriormente em problemas onde o particionamento pode realmente fazer diferença no desempenho.

Arquitetura do sistema:

Visão geral do co-design de hardware e software baseado em FPGA

Fig. 1: Visão geral do co-design de software de hardware baseado em FPGA

Componentes necessários:

1. Placa FPGA Nexys 2 Spartan 3E

2. Microcontrolador – Atmega16 + LCD

3. Alto-falante de 8 ohms

4. Conectores

5. CI 74LS245

Construção:

A. FPGA

· O FPGA precisa ser configurado para receber a entrada do usuário por meio de seus interruptores de entrada (ou interruptores de botão).

· Portanto, o primeiro passo é configurar alguns interruptores como entrada para o FPGA.

· Em seguida, precisamos ter certeza de que cada chave corresponde a uma entrada diferente. Portanto, forneceremos um código (4 bits) para identificar exclusivamente cada entrada.

· Como se trata de um protótipo, definiremos apenas 4 entradas. E, portanto, codifique-os como:

Um => “0001”

B => “0010”

C => “0011”

E => “0100”

· Ou seja, assim que um interruptor for pressionado, nós o identificaremos usando o código de 4 bits exclusivo para ele.

· A próxima coisa a ser feita pelo FPGA é enviar esses 4 bits para o microcontrolador.

· Comunicação:

A comunicação pode ser Síncrona ou Assíncrona.

Se você preferir o modo assíncrono, a transmissão paralela será o modo mais rápido a ser escolhido.

Se você preferir o modo Síncrono, recomendo configurar o SPI entre o FPGA (mestre) e o microcontrolador (escravo).

Aqui usarei a Transmissão Paralela Assíncrona.

· Para transmissão, usaremos Conectores Periféricos (Conectores PMod) na placa Spartan 3E. A figura abaixo explica isso em detalhes.

Diagrama de circuito do conector Nexys2 Pmod

Fig. 2: Diagrama de circuito do conector Nexys2 Pmod

· Podemos configurar os PMods como Input e Output. Aqui eles serão definidos como output.

B. Microcontrolador

· Esta unidade tem principalmente duas tarefas. Primeiro, exibindo o caractere no LCD. Segundo, reproduzir o som correspondente ao personagem no alto-falante.

· Tarefa 1: Mostrar

Como estamos usando comunicação paralela, cada uma dessas linhas de transmissão é conectada aos pinos de entrada correspondentes no microcontrolador. Agora, pode haver duas maneiras de garantir que os dados sejam lidos corretamente;

1. Os pinos são pesquisados ​​continuamente para verificar se há alguma atividade. Assim que houver, o valor é lido e armazenado em uma variável.

2. Nós configuramos uma interrupção. Essa interrupção é disparada do FPGA quando as linhas de transmissão vão enviar os bits. Uma vez que a interrupção é recebida, o valor é lido e então armazenado na variável.

· O 2e método é mais eficiente que o 1st um. Portanto, vamos continuar com isso.

· Portanto, o valor é lido usando a variável PIN e armazenado em alguma variável de nossa escolha.

· Tarefa 2: Som

Temos que tocar um som no alto-falante de 8?. Para isso, usaremos o flash interno do microcontrolador. Armazenaremos um som de amostra (digamos um bipe) na memória. E o tocaremos de acordo com a entrada. Ou seja, 'A' significa tocar o bipe uma vez. 'B' significa tocá-lo duas vezes, e assim por diante.

O microcontrolador que estamos usando aqui é o Atmega16 que possui apenas 16K Bytes de flash interno. Portanto, não podemos armazenar muitos sons diferentes nesta memória. Para conseguir isso, precisamos usar um flash externo e fazer a interface com o microcontrolador. Este não é o foco aqui, então não iremos por aí.

EPara reproduzir áudio PCM no microcontrolador AVR, usaremos o PWM de alta velocidade (modo PWM rápido) do AVR. Parece quase bom e pode ser usado para projetos simples que requerem efeitos sonoros.

Passo 1: Crie um arquivo .wav. Se você não tiver nenhum, use o link a seguir e converta texto em fala. Salve-o como arquivo .wav.

http://www2.research.att.com/~ttsweb/tts/demo.php

Passo 2: Converta o arquivo wav criado em um arquivo wav de 8.000 amostras por segundo sem sinal de 8 bits usando a opção de software da NCH. Pode ser encontrado aqui:

Etapa 3: Converta o arquivo wav de 8 bits em um arquivo c de amostra usando o software abaixo.

Passo 4: Copie as amostras criadas no arquivo de cabeçalho pcm_sample e salve-o.

Em essência, o que estamos fazendo aqui é pegar uma amostra de som e então amostrá-la para criar amostras finitas (8000) de 8 bits cada. Essas amostras são armazenadas no flash usando o comando PROG_MEM (devido ao uso da biblioteca avr/pgmspace). Essas amostras agirão como o ciclo de trabalho para o PWM que será gerado. Definimos o ciclo de trabalho inicial (valor no registro OCR1A) como 0. Depois disso, as amostras serão lidas continuamente do flash e serão gravadas no OCR1A. Finalmente, o alto-falante será conectado à saída do PWM (OC1A) e poderemos ouvir os sons.

Para utilizar o flash interno, utilizamos as seguintes funções:

1. const longo pcm_length=6800;

const unsigned char pcm_samples PROGMEM ={ insira amostras aqui };

Esta função é usada para criar o arquivo de cabeçalho “pcm_sample” que será usado posteriormente.

2. pgm_read_byte(&pcm_samples(amostra++));

Esta função é usada para ler byte após byte do arquivo de cabeçalho.

Conexões:

Para o FPGA:

1. Os 4 interruptores deslizantes SW0, SW1, SW2 e SW3 serão usados ​​como entrada.

Tabela listando conectores PMod

Fig. 3: Tabela listando conectores PMod

2. A seguir, para saída do FPGA, usaremos PMod JA. As conexões são:

A interrupção (O) está conectada ao JA1 “L15”

Linha de Transmissão “Z0” conectada a JA2 “K12”

Linha de Transmissão “Z1” conectada à JA3 “L17”

Linha de Transmissão “Z2” conectada ao JA4 “M15”

Linha de Transmissão “Z3” conectada à JA7 “K13”

Nota 1: Não podemos usar JA5, JA6, pois eles são predefinidos para serem usados ​​como GND, VCC.

Nota2: Como o Spartan 3E funciona em 3,3V Logic e o Atmega16 em 5V Logic, precisamos conectar um Logic Shifter IC como 74LS245 para nivelar as linhas de transmissão antes de conectá-las à PORTA do microcontrolador.

Para o microcontrolador:

1. Primeiro conecte a interrupção ao PD2 (já que estamos usando INT0, pode ser qualquer coisa dependendo da sua escolha). Em seguida, conecte as linhas de transmissão aos pinos 0-3 de qualquer porta. Aqui, a Porta B é usada.

2. Conecte o alto-falante através de um filtro passa-baixa (resistor (1k) + capacitor (470uf) conectado ao GND) ao PD5 (saída OC1A-PWM).

Trabalhando:

Pode-se observar que assim que pressionamos um botão no FPGA, podemos ver a letra correspondente no LCD e ouvir o número de batidas no alto-falante.

Conclusão:

Este foi um experimento simples para mostrar o conceito de co-design de Hardware-Software. Embora este projeto por si só não explore todo o potencial deste conceito, é um bom começo para iniciantes que podem aprender com ele e usá-lo em seus próprios projetos.

Diagramas de pinos:

Imagem de um interruptor de 4 slides em uma placa FPGA

Fig. 4: Imagem do interruptor de 4 slides na placa FPGA

74LS245

Código fonte do projeto

###


// Programa para usar interrupções externas (hardware) do microcontrolador AVR (ATmega16)
#incluir "lcd.h"
#incluir #incluir #incluir #include "pcm_sample.h" #incluir #incluir #define TAXA_DE_AMOSTRA 8000; amostra volátil uint16_t; int amostra_contagem; /* inicializa o PWM */ vazio pwm_init(vazio) { lcd_init ; /* usa o pino OC1A como saída */ DDRD = _BV(PD5); /* * limpe OC1A na comparação de correspondência * defina OC1A em BOTTOM, modo não inversor * PWM rápido, 8 bits */ TCCR1A = _BV(COM1A1) _BV(WGM10); /* * PWM rápido, 8 bits * Pré-escalador: clk/1 = 8MHz * Frequência PWM = 8 MHz / (255 + 1) = 31,25 kHz */ TCCR1B = _BV(WGM12) _BV(CS10); /* define o ciclo de trabalho inicial como zero */ OCR1A = 0; /* Configurar temporizador0 */ interno; a = PINB; //como os pinos estão conectados em PB0, PB1, PB2, PB3 os códigos formarão inteiros como 1,2,3,4 //lcd_print(a,10); se(a==1) { _atraso_ms(100); OCR1A = pgm_read_byte(&pcm_samples(sample++)); //lendo o flash serial completamente uma vez if(amostra>comprimento_pcm)amostra=0; //depois de ler fazendo o contador=0 lcd_send_string("Uma"); } se(a==2) { lcd_send_string("B"); _atraso_ms(100); OCR1A = pgm_read_byte(&pcm_samples(amostra++)); if(amostra>comprimento_pcm)amostra=0; _atraso_ms(200); OCR1A = pgm_read_byte(&pcm_samples(amostra++)); se(amostra>comprimento_pcm)amostra=0; } se(a==3) { lcd_send_string("C"); _atraso_ms(100); OCR1A = pgm_read_byte(&pcm_samples(amostra++)); if(amostra>comprimento_pcm)amostra=0; _atraso_ms(200); OCR1A = pgm_read_byte(&pcm_samples(amostra++)); se(amostra>comprimento_pcm)amostra=0; _atraso_ms(300); OCR1A = pgm_read_byte(&pcm_samples(amostra++)); se(amostra>comprimento_pcm)amostra=0; } se(a==4) { lcd_send_string("D"); _atraso_ms(100); OCR1A = pgm_read_byte(&pcm_samples(amostra++)); if(amostra>comprimento_pcm)amostra=0; _atraso_ms(200); OCR1A = pgm_read_byte(&pcm_samples(amostra++)); if(amostra>comprimento_pcm)amostra=0; _atraso_ms(300); OCR1A = pgm_read_byte(&pcm_samples(amostra++)); if(amostra>comprimento_pcm)amostra=0; _atraso_ms(400); OCR1A = pgm_read_byte(&pcm_samples(amostra++)); if(amostra>comprimento_pcm)amostra=0; } } ISR (INT0_vect) { pwm_init ; } int principal(vazio) { lcd_init ; cli ; GICR =(1< //Defina o Bit6 do GICR para desmascarar a interrupção INT0. MCUCR =(3<

sei ; enquanto(1) { } }

Dúvidas relacionadas a este artigo?
👉Pergunte e discuta nos fóruns Electro-Tech-Online.com e EDAboard.com.

Nos diga o que você acha!! Cancelar resposta

Conteúdo Relacionado

Voltar para o blog

Deixe um comentário

Os comentários precisam ser aprovados antes da publicação.