Índice:

Sintetizador de harpa a laser na placa Zybo: 10 etapas (com fotos)
Sintetizador de harpa a laser na placa Zybo: 10 etapas (com fotos)

Vídeo: Sintetizador de harpa a laser na placa Zybo: 10 etapas (com fotos)

Vídeo: Sintetizador de harpa a laser na placa Zybo: 10 etapas (com fotos)
Vídeo: Laser Harp Fully Functional 2024, Julho
Anonim
Sintetizador de harpa a laser na placa Zybo
Sintetizador de harpa a laser na placa Zybo

Neste tutorial, criaremos uma harpa a laser totalmente funcional usando sensores IR com uma interface serial que permitirá ao usuário alterar a afinação e o tom do instrumento. Esta harpa será o remake do século 21 do antigo instrumento. O sistema foi criado usando uma placa de desenvolvimento Xilinx Zybo junto com o Vivado Design Suites. O que você precisará para concluir o projeto:

  • 12 sensores e emissores IR (mais ou menos podem ser usados dependendo do número de cordas)
  • Placa de desenvolvimento Zybo Zynq-7000
  • RTOS grátis
  • Suíte Vivado Design
  • Fio (para conectar os sensores à placa)
  • 3 pedaços de tubo de PVC ((2) 18 polegadas e (1) 8 polegadas)
  • 2 cotovelos de PVC

Etapa 1: Obtenha a demonstração de áudio Zybo DMA da Digilent

O lado FPGA deste projeto é amplamente baseado no projeto de demonstração encontrado aqui. Ele usa acesso direto à memória para enviar dados diretamente da memória que o processador pode gravar por meio do AXI Stream para um bloco de áudio I2S. As etapas a seguir o ajudarão a colocar o projeto de demonstração de áudio DMA em funcionamento:

  1. Uma nova versão do arquivo da placa para a placa Zybo pode ser necessária. Siga estas instruções para obter novos arquivos de placa para Vivado.
  2. Siga as etapas 1 e 2 nas instruções desta página para abrir o projeto demo no Vivado. Use o método Vivado, não a transferência de hardware SDK.
  3. Você pode receber uma mensagem informando que alguns de seus blocos de IP devem ser atualizados. Em caso afirmativo, selecione "Mostrar status de IP" e, em seguida, na guia de status de IP, selecione todos os IP desatualizados e clique em "Atualizar selecionados". Quando terminar e uma janela aparecer perguntando se você deseja gerar o produto de saída, vá em frente e clique em "Gerar". Se você receber uma mensagem de advertência crítica, ignore-a.
  4. Mude do design para a guia de fontes no Vivado para ver os arquivos de origem. Clique com o botão direito no desenho do bloco "design_1" e selecione "Criar Wrapper HDL". Quando solicitado, selecione "copiar invólucro gerado para permitir edições do usuário". Um arquivo wrapper para o projeto será gerado.
  5. Agora que essas etapas críticas que de alguma forma foram deixadas de fora no outro tutorial foram concluídas, você pode retornar ao tutorial anteriormente vinculado e continuar da etapa 4 ao final e certificar-se de que o projeto de demonstração seja executado corretamente. Se você não tiver uma maneira de inserir áudio para gravar, basta gravar com seus fones de ouvido e ouvir um som difuso de 5 a 10 segundos ao pressionar o botão de reprodução. Contanto que algo saia do conector do fone de ouvido quando você pressiona o botão de reprodução, provavelmente está funcionando corretamente.

Etapa 2: Faça algumas mudanças no Vivado

Faça algumas mudanças no Vivado
Faça algumas mudanças no Vivado

Agora você tem a demonstração de áudio DMA da Digilent funcionando, mas esse não é o objetivo final aqui. Portanto, temos que voltar ao Vivado e fazer algumas alterações para que nossos sensores possam ser conectados aos cabeçalhos PMOD e possamos usar seu valor no lado do software.

  1. Abra o diagrama de blocos no Vivado
  2. Crie um bloco GPIO clicando com o botão direito em um espaço vazio no diagrama de blocos e selecionando "Adicionar IP" no menu. Localize e selecione "AXI GPIO".
  3. Clique duas vezes no novo bloco de IP e na janela de redefinição de IP vá para a guia de configuração de IP. Selecione todas as entradas e defina a largura para doze, já que teremos 12 "cordas" em nossa harpa e, portanto, precisaremos de 12 sensores. Se você quiser usar menos ou mais sensores, ajuste este número apropriadamente. Também defina a interrupção de ativação.
  4. Clique com o botão direito no novo bloco de IP GPIO e selecione "executar automação de conexão". Marque a caixa AXI e clique em OK. Isso deve conectar a interface AXI automaticamente, mas deixar as saídas do bloco desconectadas.
  5. Para abrir espaço para a interrupção extra, clique duas vezes no bloco de IP xlconcat_0 e altere o número de portas de 4 para 5. Em seguida, você pode conectar o pino ip2intc_irpt do novo bloco GPIO à nova porta não usada no bloco xlconcat.
  6. Clique com o botão direito na saída "GPIO" do novo bloco GPIO IP e selecione "tornar externo". Encontre para onde a linha vai e clique no pequeno pentágono lateral e à esquerda uma janela deve se abrir onde você pode mudar o nome. Mude o nome para "SENSORES". É importante usar o mesmo nome se você quiser que o arquivo de restrições que fornecemos funcione, caso contrário, você terá que alterar o nome no arquivo de restrições.
  7. De volta à guia de fontes, encontre o arquivo de restrições e substitua-o pelo que fornecemos. Você pode optar por substituir o arquivo ou apenas copiar o conteúdo do nosso arquivo de restrições e colá-lo sobre o conteúdo do antigo. Uma das coisas importantes que nosso arquivo de restrições faz é habilitar os resistores pullup nos cabeçalhos PMOD. Isso é necessário para os sensores específicos que usamos, no entanto, nem todos os sensores são iguais. Se seus sensores requerem resistores suspensos, você pode alterar cada instância de "set_property PULLUP true" com "set_property PULLDOWN true". Se eles exigirem um valor de resistor diferente daquele na placa, então você pode remover essas linhas e usar resistores externos. Os nomes dos pinos estão nos comentários no arquivo de restrições e eles correspondem aos rótulos no primeiro diagrama nos Esquemas Zybo página que pode ser encontrada aqui. Se você quiser usar pinos pmod diferentes, basta combinar os nomes no arquivo de restrição com os rótulos no esquema. Usamos os cabeçalhos PMOD JE e JD e seis pinos de dados em cada um, omitindo os pinos 1 e 7. Esta informação é importante ao conectar seus sensores. Conforme mostrado no esquema, os pinos 6 e 12 nos PMODS são VCC e os pinos 5 e 11 são aterrados.
  8. Gere novamente o wrapper HDL como antes e copie e substitua o antigo. Quando isso for feito, gere bitstream e exporte hardware como antes e reinicie o SDK. Se for perguntado se deseja substituir o arquivo de hardware antigo, a resposta é sim. Provavelmente, é melhor fechar o SDK ao exportar o hardware, para que seja substituído de maneira adequada.
  9. Inicie o SDK.

Etapa 3: Obtenha o FreeRTOS em execução

A próxima etapa é fazer o FreeRTOS rodar na placa Zybo.

  1. Se você ainda não tem uma cópia, baixe o FreeRTOS aqui e extraia os arquivos.
  2. Importe a demonstração do FreeRTOS Zynq localizada em FreeRTOSv9.0.0 / FreeRTOS / Demo / CORTEX_A9_Zynq_ZC702 / RTOSDemo. O processo de importação é praticamente o mesmo do outro projeto de demonstração, no entanto, como a demonstração do FreeRTOS Zynq depende de outros arquivos na pasta FreeRTOS, você não deve copiar os arquivos para o seu espaço de trabalho. Em vez disso, você deve colocar toda a pasta FreeRTOS dentro da pasta do projeto.
  3. Crie um novo pacote de suporte de placa acessando "arquivo" -> "novo" -> "pacote de suporte de placa". Certifique-se de que standalone esteja selecionado e clique em Concluir. Depois de um momento, uma janela aparecerá, marque a caixa ao lado de lwip141 (isso impede que uma das demos do FreeRTOS falhe na compilação) e clique em OK. Depois que terminar, clique com o botão direito do mouse no projeto RTOSdemo e vá para "propriedades", vá para a guia "referências do projeto" e marque a caixa ao lado do novo bsp que você criou. Esperançosamente, será reconhecido, mas às vezes o SDK do Xilinx pode ser estranho sobre esse tipo de coisa. Se você ainda receber um erro após esta etapa informando que xparameters.h está faltando ou algo parecido, tente repetir esta etapa e talvez sair e reiniciar o SDK.

Etapa 4: adicionar código de harpa a laser

Agora que o FreeRTOS foi importado, você pode trazer os arquivos do projeto de harpa a laser para a demonstração do FreeRTOS

  1. Crie uma nova pasta sob a pasta src na demonstração do FreeRTOS e copie e cole todos os arquivos c fornecidos, exceto main.c nesta pasta.
  2. Substitua o RTOSDemo main.c pelo main.c fornecido.
  3. Se tudo for feito corretamente, você deve ser capaz de executar o código da harpa a laser neste ponto. Para fins de teste, a entrada de botão que foi usada no projeto de demonstração DMA agora é usada para reproduzir sons sem sensores anexados (qualquer um dos quatro botões principais funcionará). Ele tocará uma corda cada vez que você pressioná-lo e percorrerá todas as cordas do sistema em vários toques. Conecte alguns fones de ouvido ou alto-falantes ao conector de fone de ouvido na placa Zybo e certifique-se de ouvir os sons das cordas ao pressionar um botão.

Etapa 5: Sobre o Código

Muitos de vocês que estão lendo este tutorial provavelmente aprenderão como configurar o áudio ou usar o DMA para fazer algo diferente ou criar um instrumento musical diferente. Por esse motivo, as próximas seções são dedicadas a descrever como o código fornecido funciona em conjunto com o hardware descrito anteriormente para obter uma saída de áudio funcional usando DMA. Se você entende por que as partes do código estão lá, você deve ser capaz de ajustá-las para o que quer que você queira criar.

Interrupções

Primeiro, mencionarei como as interrupções são criadas neste projeto. A maneira como fizemos isso foi criando primeiro uma estrutura de tabela de vetor de interrupção que mantém o controle do ID, manipulador de interrupção e uma referência ao dispositivo para cada interrupção. Os IDs de interrupção vêm de xparameters.h. O manipulador de interrupção é uma função que escrevemos para DMA e GPIO, e a interrupção I2C vem do driver Xlic I2C. A referência de dispositivo aponta para instâncias de cada dispositivo que inicializamos em outro lugar. Perto do final da função _init_audio, um loop passa por cada item na tabela de vetor de interrupção e chama duas funções, XScuGic_Connect () e XScuGic_Enable () para conectar e habilitar as interrupções. Eles fazem referência ao xInterruptController, que é um controlador de interrupção criado no FreeRTOS main.c por padrão. Então, basicamente, anexamos cada uma de nossas interrupções a este controlador de interrupção que já foi criado para nós pelo FreeRTOS.

DMA

O código de inicialização DMA começa em lh_main.c. Primeiro, uma instância estática de uma estrutura XAxiDma é declarada. Então, na função _init_audio (), ele é configurado. Primeiro, a função configure do projeto demo é chamada, que está em dma.c. Está muito bem documentado e vem direto da demonstração. Em seguida, a interrupção é conectada e ativada. Para este projeto apenas a interrupção mestre-escravo é necessária, pois todos os dados estão sendo enviados pelo DMA para o controlador I2S. Se desejar gravar áudio, você também precisará da interrupção escravo para mestre. A interrupção mestre para escravo é chamada quando o DMA termina de enviar todos os dados que você disse para enviar. Esta interrupção é extremamente importante para nosso projeto porque toda vez que o DMA termina de enviar um buffer de amostras de áudio, ele deve imediatamente começar a enviar o próximo buffer, ou então um atraso audível ocorreria entre os envios. Dentro da função dma_mm2s_ISR () você pode ver como lidamos com a interrupção. A parte importante está perto do fim, onde usamos xSemaphoreGiveFromISR () e portYIELD_FROM_ISR () para notificar _audio_task () que pode iniciar a próxima transferência DMA. A maneira como enviamos dados de áudio constantes é alternando entre dois buffers. Quando um buffer está sendo transmitido para o bloco I2C, o outro buffer está tendo seus valores calculados e armazenados. Então, quando a interrupção vem do DMA, o buffer ativo muda e o buffer escrito mais recentemente começa a ser transferido enquanto o buffer transferido anteriormente começa a ser sobrescrito com novos dados. A parte principal da função _audio_task é onde fnAudioPlay () é chamado. fnAudioPlay () leva na instância DMA, o comprimento do buffer e um ponteiro para o buffer do qual os dados serão transferidos. Alguns valores são enviados aos registradores I2S para avisá-lo que mais amostras estão chegando. Então XAxiDma_SimpleTransfer () é chamado para iniciar a transferência.

Áudio I2S

audio.c e audio.h são onde ocorre a inicialização do I2S. O código de inicialização I2S é um pedaço de código muito comum que está flutuando em vários lugares, você pode encontrar pequenas variações de outras fontes, mas este deve funcionar. Está muito bem documentado e não precisou ser muito alterado para o projeto da harpa. A demonstração de áudio DMA da qual veio tem funções para alternar para as entradas de microfone ou linha para que você possa usá-las se precisar dessa funcionalidade.

Síntese de Som

Para descrever como funciona a síntese de som, vou listar cada um dos modelos de som usados no desenvolvimento que levaram ao método final, pois isso dará a você uma noção de por que isso é feito da maneira que é feito.

Método 1: Um período de valores de seno é calculado para cada corda na frequência correspondente para a nota musical daquela corda e armazenado em uma matriz. Por exemplo, o comprimento da matriz será o período da onda senoidal nas amostras, que é igual a # de amostras / ciclo. Se a taxa de amostragem for 48kHz e a frequência da nota for 100Hz, então haverá 48.000 amostras / segundo e 100 ciclos / segundo levando a 4800 amostras por ciclo, e o comprimento da matriz será 4800 amostras e conterá os valores de um completo período da onda senoidal. Quando a string é tocada, o buffer de amostra de áudio é preenchido pegando um valor da matriz de onda senoidal e colocando-o no buffer de áudio como uma amostra e, em seguida, incrementando o índice na matriz de onda senoidal para que, usando nosso exemplo anterior ao longo do curso de 4800 amostras, um ciclo de onda senoidal é colocado no buffer de áudio. Uma operação de módulo é usada no índice do array para que sempre fique entre 0 e o comprimento, e quando o índice do array ultrapassa um certo limite (como talvez 2 segundos de amostras), a string é desligada. Para tocar várias strings ao mesmo tempo, acompanhe o índice de array de cada string separadamente e adicione o valor de cada onda senoidal das strings para obter cada amostra.

Método 2: para criar um tom mais musical, começamos com o modelo anterior e adicionamos harmônicos a cada frequência fundamental. As frequências harmônicas são frequências que são múltiplos inteiros da frequência fundamental. Ao contrário de quando duas frequências não relacionadas são somadas, o que resulta em dois sons distintos sendo tocados simultaneamente, quando os harmônicos são adicionados juntos, ele continua a soar como apenas um som, mas com um tom diferente. Para conseguir isso, cada vez que adicionamos o valor da onda senoidal no local (índice da matriz% comprimento da matriz) à amostra de áudio, também adicionamos (2 * índice da matriz% comprimento da matriz) e (3 * índice da matriz% comprimento da matriz), e assim por diante para quantos harmônicos forem desejados. Esses índices multiplicados atravessarão a onda senoidal em frequências que são múltiplos inteiros da frequência original. Para permitir mais controle do tom, os valores de cada harmônico são multiplicados por uma variável que representa a quantidade daquele harmônico no som geral. Por exemplo, a onda senoidal fundamental pode ter seus valores todos multiplicados por 6 para torná-la mais um fator no som geral, enquanto o 5º harmônico pode ter um multiplicador de 1, o que significa que seus valores contribuem muito menos para o som geral.

Método 3: Ok, agora temos um tom muito bom nas notas, mas ainda há um problema crucial: elas tocam em um volume fixo por uma duração fixa. Para soar como um instrumento real, o volume de uma corda que está sendo tocada deve diminuir suavemente com o tempo. Para fazer isso, uma matriz é preenchida com os valores de uma função em declínio exponencial. Agora, quando as amostras de áudio estão sendo criadas, o som proveniente de cada string é calculado como no método anterior, mas antes de ser adicionado à amostra de áudio, ele é multiplicado pelo valor no índice da matriz dessas strings na matriz da função de decaimento exponencial. Isso faz com que o som se dissipe suavemente com o tempo. Quando o índice da matriz atinge o final da matriz de decaimento, a string é interrompida.

Método 4: Esta última etapa é o que realmente dá aos sons das cordas seu som de cordas realista. Antes eles soavam agradáveis, mas claramente sintetizados. Para tentar emular melhor uma corda de harpa do mundo real, uma taxa de decaimento diferente é atribuída a cada harmônico. Em cordas reais, quando a corda é tocada pela primeira vez, há um alto conteúdo de harmônicos de alta frequência que cria o tipo de som de dedilha que esperamos de uma corda. Esses harmônicos de alta frequência são brevemente a parte principal do som, o som da corda sendo tocada, mas eles decaem muito rapidamente conforme os harmônicos mais lentos assumem o controle. Uma matriz de decaimento é criada para cada número harmônico usado na síntese de som, cada um com sua própria taxa de decaimento. Agora, cada harmônico pode ser multiplicado independentemente pelo valor de sua matriz de decaimento correspondente no índice de matriz da corda e adicionado ao som.

No geral, a síntese de som é intuitiva, mas exige muito cálculo. Armazenar todo o som da corda na memória de uma vez consumiria muita memória, mas calcular a onda senoidal e a função exponencial entre cada quadro levaria muito tempo para acompanhar a taxa de reprodução de áudio. Vários truques são usados no código para acelerar o cálculo. Toda a matemática, exceto na criação inicial das tabelas de decaimento seno e exponencial, é feita em formato inteiro, o que requer espalhar o espaço numérico disponível na saída de áudio de 24 bits. Por exemplo, a mesa senoidal tem amplitude 150, de modo que é suave, mas não tão grande que muitas cordas tocadas juntas possam somar mais de 24 bits. Da mesma forma, os valores da tabela exponencial são multiplicados por 80 antes de serem arredondados para inteiros e armazenados. Os pesos harmônicos podem assumir valores discretos entre 0 e 10. Além disso, todas as amostras são duplicadas e as ondas senoidais são indexadas por 2, efetivamente reduzindo pela metade a taxa de amostragem. Isso limita a frequência máxima que pode ser tocada, mas foi necessário para que o número atual de cordas e harmônicos fosse calculado com rapidez suficiente.

Criar este modelo de som e fazê-lo funcionar exigiu um esforço considerável do lado do processador, e teria sido incrivelmente difícil fazê-lo funcionar do zero no lado do fpga no período de tempo deste projeto (imagine ter que recriar o fluxo de bits a cada vez que um pedaço de verilog foi alterado para testar o som). No entanto, fazê-lo no fpga poderia ser a melhor maneira de fazê-lo, possivelmente eliminando o problema de não ser capaz de calcular amostras com rapidez suficiente e permitir que mais cordas, harmônicos e até mesmo efeitos de áudio ou outras tarefas sejam executados no lado do processador.

Etapa 6: Conexão dos sensores

Fiação dos Sensores
Fiação dos Sensores

Para criar as cordas, usamos sensores infravermelhos que detectam quando a corda está sendo tocada. Solicitamos nossos sensores no link a seguir. Os sensores possuem um cabo de alimentação, aterramento e dados, enquanto os emissores possuem apenas um cabo de alimentação e aterramento. Usamos os pinos de 3,3 V e de aterramento dos conectores PMOD para alimentar os emissores e sensores. Para alimentar todos os sensores e emissores é necessário conectar todos os sensores e emissor em paralelo. Os fios de dados dos sensores precisarão cada um ir para seu próprio pino pmod.

Etapa 7: Construindo o esqueleto

Construindo o esqueleto
Construindo o esqueleto

Para criar a forma da harpa, as três peças são usadas como esqueleto para colocar os sensores e emissores. Em um dos dois pedaços de tubo de PVC de 18 polegadas, alinhe os sensores e emissores em ordem alternada de 1,5 polegadas um do outro e, em seguida, prenda-os com fita adesiva no tubo. No outro tubo de PVC de 18 polegadas, alinhe os sensores e emissores em ordem alternada, mas certifique-se de deslocar a ordem (ou seja, se o primeiro tubo tinha um sensor primeiro, o segundo deveria ter um emissor primeiro e vice-versa). Será necessário soldar fios mais longos nos fios de dados, energia e aterramento para garantir que eles possam alcançar a placa.

Etapa 8: Construindo o Exterior de Madeira

Construindo o Exterior de Madeira
Construindo o Exterior de Madeira

Esta etapa é opcional, mas altamente recomendada. O exterior de madeira não só torna a harpa bonita, mas também protege os sensores e fios contra danos. A moldura de madeira pode ser criada por um anel retangular sagrado de madeira. O interior do retângulo precisa ter uma abertura de pelo menos 1-1 / 2 polegadas para se encaixar no tubo e no esqueleto do sensor. Assim que a estrutura estiver construída, faça dois orifícios que permitirão que os fios do sensor e dos emissores saiam para conectá-los à placa.

* Nota: Recomenda-se adicionar pontos de acesso para poder remover e inserir o esqueleto do tubo no caso de serem necessários reparos ou pequenos ajustes.

Etapa 9: juntando todas as peças

Juntando todas as peças
Juntando todas as peças

Depois que todas as etapas anteriores forem concluídas, é hora de construir a harpa. Primeiro coloque o esqueleto do tubo dentro do exterior de madeira. Em seguida, conecte os fios dos sensores e emissores no local correto da placa. Em seguida, abra o SDK e clique no botão de depuração para programar a placa. Assim que a placa estiver programada, conecte um par de fones de ouvido ou um alto-falante. Dependendo de qual sensor termina em qual porta pmod, as cordas de sua harpa provavelmente estarão danificadas para começar. Como pode ser difícil dizer qual fio vai para qual sensor quando tantos fios estão envolvidos, incluímos uma maneira de mapear os números das strings para interromper as posições dos bits no software. Encontre "static int sensor_map [NUM_STRINGS]" e ajuste os valores na matriz até que as cordas sejam reproduzidas da ordem mais baixa para a mais alta.

O menu pode ser usado abrindo um terminal serial (por exemplo, RealTerm) e definir a taxa de baud para 115200 e o display para ANSI. O menu pode ser navegado usando as teclas w e s para mover para cima e para baixo e as teclas a e d para alterar os valores.

Etapa 10: ROCK OUT

Assim que a harpa estiver totalmente funcional. Domine a harpa e ouça o doce som da sua própria música!

Recomendado: