Nós escrevemos o carregador FPGA no LabVIEW. Parte 1



A maioria dos programadores "normais", para dizer o mínimo, tem uma atitude ambígua em relação à tecnologia LabVIEW . Aqui você pode discutir por um longo tempo e sem sucesso. A situação é agravada pelo fato de existirem muitos exemplos de programas LabVIEW na rede, mas todos eles são orientados a um iniciante e resumem-se a “oh, veja como é simples, conecte a torção ao indicador, gire o botão, o dígito muda” ou, na melhor das hipóteses, ao gráfico no ciclo um número aleatório ou seno é exibido, tudo isso é acompanhado por uma interface furiosa na forma de comutadores gigantes, botões giratórios e indicadores de discagem. Pessoalmente, essa abordagem de simplificação consciente me incomoda. Em uma curta série de artigos, tentarei apresentar ao leitor o processo de desenvolvimento de software aplicativo no LabVIEW. Para não dedicar muito tempo à área de assunto, usaremos o algoritmo detalhado para carregar o arquivo de configuração no FPGA via FTDI no modo MPSSE (fazer o download da configuração no FPGA via USB ou desmontar o FTDI MPSSE ). Neste artigo, mostrarei como implementar o mesmo carregador FPGA, mas na linguagem LabVIEW.


Como mencionado acima, o algoritmo de carregamento FPGA no modo Serial Passive (SP) e o princípio da operação FTDI estão bem descritos no artigo anterior. Eu não vou me repetir. Acreditamos que o algoritmo é testado e reconhecido como adequado. E sim, suponha que o leitor esteja pelo menos superficialmente familiarizado com o conceito LabVIEW e seja versado em programação clássica.


Embora o processo de criação de um carregador de inicialização não demore muito, a descrição do processo não conseguiu se encaixar no volume de um artigo, portanto haverá uma pequena série. Como a etapa dos experimentos já foi concluída e estou confiante na operacionalidade do algoritmo, permitirei iniciar o desenvolvimento a partir da interface do usuário. No primeiro artigo, criaremos a interface do usuário do carregador e implementaremos a estrutura do programa. Na segunda - através da DLL do driver FTDI, implementamos o carregamento do arquivo * .rbf no FPGA.


Para a produtividade da conversa, não é supérfluo lembrar a terminologia adotada no LabVIEW. No LabVIEW, o programa aplicativo é chamado de Instrumento Virtual, também conhecido como VI, também conhecido como Instrumento Virtual ou VI, para abreviar. O vice-presidente tem dois "lados": o painel frontal, onde estão localizados os controles e indicadores, e o diagrama de blocos (diagrama de blocos), onde esses elementos são interconectados e as funções e fluxos de processamento de dados são implementados. Como regra, um vice-presidente tem uma estrutura hierárquica; todos os vice-presidentes em um vice-presidente de nível superior são geralmente chamados de subdispositivo ou SubVI.


Interface do usuário


Nosso aplicativo deve carregar o arquivo de configuração no FPGA. Ao mesmo tempo, queremos dizer que vários FTDIs podem ser conectados a um computador ao mesmo tempo, e alguns deles podem ser potencialmente usados ​​para configurar FPGAs. Sugiro fazer uma escolha de dispositivo na lista suspensa, selecionando um arquivo através de um botão com uma saída de caminho. Para iniciar o download, adicione o botão "programa". Um LED virtual indicará o status da operação. Inicie o LabVIEW.


Adicione os elementos necessários ao painel frontal (painel frontal). Vou usar o estilo "Silver" para o vice-presidente. A figura abaixo mostra o resultado, todos os elementos em sua condição original. Pelo nome, se necessário, eles são bastante fáceis de encontrar na paleta.




Editamos elementos: transferimos, expandimos, adicionamos inscrições - trazemos para o formulário necessário. Aqui, de fato, é necessária a mão do designer, mas como pude:




E o diagrama de blocos:




Eu chamo sua atenção. Cada elemento possui duas propriedades que determinam o nome - Rótulo e Legenda.




A primeira propriedade define o nome do elemento, sob esse nome ele será exibido no diagrama de blocos. O rótulo, ao contrário do Caption, não pode ser alterado durante a execução do VI. Em conexão com esse recurso, recomendo que você oculte a etiqueta em seus VIs no painel frontal e exiba Legenda. Para Label, crie um nome significativo, como uma variável em uma linguagem de programação clássica, de preferência em um layout latino. Para o Caption, apresentaremos um nome orientado para humanos, que pode ser bastante longo, incluir espaços e, se necessário, em russo. Usando o LabVIEW, você pode personalizar a fonte de exibição da legenda. Devo dizer que ninguém nos obriga a mexer com a Legenda: qualquer inscrição pode ser feita diretamente no FP, em qualquer lugar livre.


Implementamos a operação VP de acordo com o esquema clássico: While loop e manipulador de eventos. Adicione um loop While (Programação -> Estruturas -> Loop While) ao diagrama de blocos e uma estrutura de manipulador de eventos (Programação -> Estruturas -> Estrutura de Eventos).


Breve ajuda na estrutura de eventos

A estrutura aguarda o evento ocorrer e, em seguida, o manipulador correspondente é executado. A estrutura dos eventos possui um ou vários subdiagramas - manipuladores de eventos, os chamados casos, um dos quais é executado quando ocorre um evento. Usando o terminal no canto superior esquerdo, você pode especificar o número de milissegundos durante os quais a estrutura espera um evento. Se durante esse período nenhum evento ocorrer, o sub-diagrama “Timeout” será executado. O valor padrão é menos 1, o que significa que o tempo limite nunca expira.




  1. O rótulo do seletor de eventos indica quais eventos acionam o caso atual. Para visualizar outros manipuladores, você pode clicar na seta para baixo ao lado do nome do evento.
  2. O terminal de timeout define o número de milissegundos para aguardar um evento. Se o valor for diferente de -1, o subdiagrama de tempo limite deve ser implementado.
  3. Um terminal para inserir eventos dinâmicos. Este terminal não é exibido por padrão. Para exibi-lo, selecione "Mostrar terminais de eventos dinâmicos" no menu de contexto.
  4. Nó de dados do evento. Quando um evento ocorre, o LabVIEW gera dados associados a esse evento. Este nó fornece esses dados para o manipulador. Você pode usar o mouse para redimensionar o nó verticalmente e selecionar os elementos necessários. Alguns dados, como Type e Time , são comuns a todos os eventos, outros, como Char e VKey , dependem do tipo de evento configurado.
  5. O nó do filtro de eventos define os dados do evento que você pode modificar antes que a interface com o usuário processe esses dados. Este nó é exibido apenas nos manipuladores em que a filtragem está disponível. Você pode conectar e alterar itens do nó de dados do evento para o nó do filtro de eventos. Você também pode alterar esses eventos conectando os novos valores aos terminais do nó. Posso cancelar completamente a reação da interface ao evento se eu enviar true ao terminal Discard? . Se você não conectar o valor ao elemento de filtro, esse elemento de dados permanecerá inalterado.
  6. Como a estrutura Case , a estrutura do evento suporta túneis. Se você adicionar um túnel em um caso, ele será criado automaticamente para cada manipulador. No entanto, por padrão, não há necessidade de conectar os túneis de saída da estrutura de eventos em cada manipulador. Todos os túneis não conectados usam o valor padrão para o tipo de dados do túnel. Potencialmente, isso pode levar a erros difíceis de detectar. Você pode retornar o modo no qual o túnel deve ser conectado em todos os manipuladores; para isso, selecione "Usar padrão se não conectado" no menu de contexto.

Primeiro, cuidaremos de como o ciclo termina e sai do programa. A maneira mais fácil é adicionar um botão de parada, que interromperá o ciclo, mas, na minha opinião, é habitual encerrar o programa com uma cruz vermelha no canto superior da janela.
Adicione o manipulador apropriado à Event Structure . No menu de contexto, selecione "Adicionar caso de evento". Como fonte do evento, selecione "This VI", o evento indica "Panel Close?"


Editar eventos


Imagem clicável


Observe que, se você selecionar "Fechar painel" (sem uma pergunta), esse evento será sem filtro e não poderá ser desfeito. E se com uma pergunta, quando você clicar na cruz, podemos assumir o controle e concluir o programa corretamente de forma independente. No "Painel Fechar?" através do túnel, conectamos a constante booleana true ao terminal para interromper o while . Entrada "Descartar?" também servem de true .


Agora não há nuances óbvias: se o VI atual é lançado não no ambiente LabVIEW, mas na forma de um aplicativo compilado, clicar na cruz não fecha a janela, mas interrompe o aplicativo, o que não é necessário. Para resolver esse problema, após a conclusão do ciclo principal, verificaremos se estamos no ambiente de desenvolvimento ou no modo de programa, se ainda estiver no modo de programa, finalizaremos o aplicativo. Para entender onde o VI atual é executado, use o nó de propriedade (Programação -> Controle de Aplicativo -> Nó de Propriedade). Este nó fornece acesso às propriedades de um objeto por referência a esse objeto. Nesse caso, devemos obter um link para todo o aplicativo como um todo. Selecione a constante VI Server Reference na mesma paleta. Depois que a constante é definida no diagrama de blocos, você precisa alterar seu tipo para This Application (botão esquerdo do mouse). Conectamos o link resultante ao nó da propriedade. Selecione a propriedade Application:Kind Property - retorna o tipo do sistema LabVIEW no qual o VI atual está sendo executado. Nós conectamos a saída da propriedade à Estrutura do Caso, adicionamos o caso "Run Time System", onde encerramos o aplicativo ( Quit LabVIEW ). Para que este bloco seja executado após o ciclo, e não antes, conectamos o error in terminal de entrada ao ciclo através do túnel.



Imagem clicável


Agora, quando você inicia o programa, pode interrompê-lo clicando na cruz da janela. O programa lançado é assim:




Na minha opinião, muito supérfluo. Vá para as propriedades do VI (menu Arquivo -> Propriedades do VI), selecione a categoria "Aparência da janela", defina Personalizado.


Desligue a exibição do menu (ele permanece no modo de edição), desligue a barra de rolagem, oculte a barra de ferramentas quando o aplicativo estiver sendo executado (Mostrar barra de ferramentas durante a execução). Proibimos o redimensionamento da janela, permitimos minimizar e minimizar a janela. Isso é melhor:




Certamente, valeria a pena remover a inscrição "National Instruments. LabVIEW Evaluation Software", mas ainda não quero comprar uma licença para um computador doméstico, vamos aturar a inscrição e gerenciar o período de avaliação de 45 dias.


Naturalmente, você pode ajustar a cor do plano de fundo e cada elemento, escolher fontes, mas não sou designer, mas algo me diz que só vou piorar.


Lista de Instrumentos


O VP deve oferecer ao usuário uma lista de dispositivos conectados ao computador, adequados ao firmware FPGA, para que o usuário possa selecionar o desejado. Na biblioteca FTD2XX, as funções FT_GetDeviceInfoDetail e FT_GetDeviceInfoDetail são para esse fim. Conforme discutido em um artigo anterior , as bibliotecas de drivers podem ser usadas para usar a API FTD2XX. O LabVIEW possui um mecanismo conveniente para interagir com bibliotecas dinâmicas - o nó Nó de Função da Biblioteca de Chamadas, você pode encontrá-lo na paleta "Conectividade -> Bibliotecas e Executáveis". O nó da chamada de função deve ser configurado: primeiro, especifique o caminho para a dll (a guia "Função"), após o qual o sistema varre a biblioteca e oferece a seleção do nome da função na lista "Nome da Função" - selecione FT_CreateDeviceInfoList , convenção de chamada - selecione stdcall (WINAPI) . Em segundo lugar, na guia "Parâmetros", é necessário inserir uma lista de parâmetros de função, onde o primeiro elemento da lista é o valor de retorno. Aqui, seria bom ter em mente a documentação da API ou o arquivo de cabeçalho. Ao definir parâmetros na área "Protótipo de função", o protótipo da função importada é exibido. Quando a assinatura da documentação corresponder ao protótipo configurado, clique em OK.


Suponha que a digitalização seja realizada uma vez por segundo. Colocamos o nó de chamada na estrutura do manipulador de eventos na guia "Timeout", configuramos o tempo de espera para 1000 ms. Adicionamos indicadores aos pinos do nó e, se tudo for feito corretamente, ao iniciar o VI, o número de dispositivos conectados com o FTDI deverá ser exibido:


Painel frontal e diagrama de blocos


Imagem clicável



Da mesma forma, crie um nó para a função FT_GetDeviceInfoDetail . O protótipo da função é:


 FTD2XX_API FT_STATUS WINAPI FT_GetDeviceInfoDetail( DWORD dwIndex, LPDWORD lpdwFlags, LPDWORD lpdwType, LPDWORD lpdwID, LPDWORD lpdwLocId, LPVOID lpSerialNumber, LPVOID lpDescription, FT_HAN 

Ao descrever os parâmetros, deve-se observar que lpdwFlags , lpdwType , lpdwID , lpdwLocId são passados ​​como ponteiros para o uint32 . Os parâmetros lpSerialNumber e lpDescription são a essência das cadeias de bytes (matrizes de char com um terminador nulo). Os parâmetros desse tipo no nó de chamada podem ser formatados de várias maneiras. Você pode inseri-los em uma matriz de palavras de 8 bits, mas acho mais conveniente indicar imediatamente que essa é uma string e definir o tamanho esperado. Nesse caso, a saída será imediatamente uma linha "labirinto" válida e nenhuma transformação adicional será necessária.



Função de biblioteca de chamadas


Esta função retorna informações pelo número de série do dwIndex . Se vários FTDIs estiverem conectados ao computador, para ler as informações de cada conversor, a função deverá ser chamada em um loop. O número de iterações do loop será fornecido pela função anterior FT_CreateDeviceInfoList .


Painel frontal e diagrama de blocos


Imagem clicável



Há um recurso desagradável: todas as portas do nó de chamada devem estar conectadas pelo menos em um lado. Portanto, um túnel é feito no loop para os terminais de saída que não vamos usar.


Types matriz de Types contém tipos de chip FTDI, precisamos deles para limitar a escolha apenas àqueles que suportam MPSSE e podem ser potencialmente usados ​​para programação FPGA. No entanto, operar com "números mágicos" é inconveniente - proponho organizar os tipos de FTDI na forma de enum . Além disso, essa enumenum no arquivo de cabeçalho ftd2xx.h. No LabVIEW, você pode usar dois controles para criar uma enumeração: o Text Ring e o próprio Enum. Ambos contêm listas de cadeias com valores numéricos entre os quais você pode alternar. A principal diferença é que "Enum" exige que os valores numéricos sejam números consecutivos inteiros, enquanto o "Anel de texto" tem mais liberdade - você pode especificar quaisquer valores.


Enum. Criação

Nós inserimos manualmente os valores, é uma pena que não haja uma função de importação enum de C



No painel frontal, este indicador se parece com este



Selecionando um valor clicando com o botão esquerdo


Para vincular e sincronizar todas as instâncias futuras da lista criada, é conveniente usar o "Make Type Def". (seleção através do menu de contexto do item). Como resultado, um tipo de dados personalizado será criado. O novo tipo é colocado em um arquivo separado com a extensão * .ctl. A edição deste arquivo modificará todas as instâncias desse elemento. Eu acho que não vale a pena explicar o quão conveniente isso pode ser. O acesso ao arquivo de definição pode ser obtido no menu de contexto da instância, selecionando "Open Type Def"; no mesmo menu, você deve prestar atenção aos itens "Atualização automática do Type Def". e "Desconectar do Tipo Def".


Alteramos o indicador de Types para uma matriz de indicadores do FTDI Type e, como resultado, quando o VI é iniciado, o tipo do conversor conectado é exibido:



Três dispositivos encontrados


É fácil perceber que a funcionalidade do código resultante no caso "Timeout" está completa, portanto, pode ser movida para um SubVI separado. Selecione os elementos que queremos transferir para o subdispositivo e selecione "Criar SubVI" no menu principal Editar.


diagrama de blocos com sub-instrumento criado

Todos os indicadores permaneceram e um novo VI com um ícone padrão foi formado no lugar dos nós de chamada.

Imagem clicável


Clicar duas vezes no novo subVI abrirá sua janela de edição. Primeiro, salvamos e fornecemos um nome significativo, por exemplo, "FT_GetDeviceInfo". Configure os terminais de E / S. Para fazer isso, use o Painel Conector:



O painel é um conjunto de terminais correspondentes aos controles e indicadores VI.


Se você selecionar um terminal no painel de conexão, o elemento correspondente no painel frontal será destacado. Se você selecionar um terminal vazio e clicar em um elemento no painel frontal, o elemento será anexado ao terminal, mas antes disso não deverá ser atribuído a outro terminal. No menu de contexto, você pode desconectar individualmente ou de uma só vez os terminais dos elementos, alterar o padrão do painel como um todo.


Não gosto de como os terminais foram atribuídos ao criar o subVI atual. Selecione "Desconectar todos os terminais" e marque-o manualmente. Eu recomendo colocar os terminais de entrada à esquerda e a saída à direita, a entrada opcional pode ser colocada na parte superior e a saída opcional na parte inferior. Isso garantirá boa legibilidade do código e ordem visual no diagrama de blocos.


Para controlar erros, crie dois elementos adicionais, Error in e Error out . O tópico de controle de erros do LabVIEW é muito extenso e vai além do escopo deste artigo, portanto, nos limitaremos a um mínimo de explicações e seguiremos o princípio "faça como eu". Então, criamos dois terminais para erros - entrada e saída.


É conveniente criá-los usando o menu de contexto

Clique com o botão direito do mouse no terminal de erro de qualquer nó:



No LabVIEW, é habitual colocar o terminal de entrada do erro no painel de conexão na parte inferior esquerda e o terminal de saída na parte inferior direita.


Será mais conveniente combinar a saída em uma estrutura. Para saída, faremos duas matrizes: a primeira matriz conterá todos os dispositivos FTDI encontrados, a segunda matriz conterá apenas aqueles que podem MPSSE e, teoricamente, podem ser usados ​​para configurar FPGAs.


O toque final ao criar um subinstrumento é configurar o ícone. Clicar duas vezes no ícone no canto superior direito da janela inicia o editor. Estamos tentando criar algum tipo de imagem significativa que nos permita interpretar inequivocamente o objetivo do dispositivo no diagrama de blocos.


FT_GetDeviceInfo.vi

Painel frontal



Diagrama de blocos


E é assim que o caso "Timeout" cuida da organização das coisas:

Imagem clicável


Até esse momento, a lista suspensa com o nome "Selecionar um dispositivo" estava vazia, agora temos os dados para preenchê-lo. Criamos um nó de propriedade para a lista com a propriedade "Strings []" (menu de contexto -> Criar -> Nó da propriedade -> Strings []). Qualquer propriedade é gravável e legível; o modo atual é selecionado no menu de contexto do Nó da propriedade. Ao criar um nó por padrão, as propriedades são configuradas para serem lidas. Altere para escrever: "Alterar para escrever".


Na matriz de estruturas, selecione a matriz com a descrição e alimente-a com Strings[] . Você pode selecionar uma matriz usando o For Loop .


Após iniciar o VI, pressione o botão esquerdo no elemento "Selecionar um dispositivo", você pode especificar o dispositivo para configuração. Nesse caso, a lista de dispositivos é atualizada duas vezes por segundo. Obviamente, seria possível atualizar a propriedade apenas se a lista fosse atualizada, mas até agora isso resultará em uma confusão desnecessária no diagrama de blocos.


O que aconteceu?

Painel frontal



Imagem clicável


Antes, esqueci de mencionar um recurso tão interessante, o loop For não precisa especificar explicitamente o número de iterações, basta criar um túnel de entrada da matriz e o loop será executado para cada elemento, esse comportamento se assemelha ao foreach no C ++ 11. No entanto, você precisa ter cuidado quando mais de uma matriz chegar na entrada do loop.


Na estrutura do evento, adicione um manipulador para pressionar o botão "programa". Embora não tenhamos um VI responsável por carregar o arquivo no FPGA, criaremos um sub-dispositivo "stub". Suponha que ele use o caminho para o arquivo de configuração e o descritor FTDI como entrada e, de acordo com os resultados da operação, ele retornará o status do firmware: bem-sucedido ou não. E para tornar mais interessante testar a interface do programa, tornaremos esse status aleatório.


Stub FT_MPSSE_SP_FPGA.vi

Painel frontal



Diagrama de blocos


Passaremos o descritor FTDI para a entrada stub através da propriedade list (menu de contexto -> Criar -> Nó da propriedade -> Texto do anel -> Texto), para transferir o caminho do arquivo para o elemento "Caminho do arquivo", crie uma variável local (menu de contexto -> Criar -> Local Variável) e defina-o para ler (Alterar para ler). E conecte a saída de status diretamente ao indicador de Status . O botão Programm é arrastado para o manipulador de eventos. É uma boa prática colocar elementos nos quais o evento está configurado nos manipuladores - agora, clicar duas vezes nesse elemento no painel frontal mostrará não apenas o elemento correspondente no diagrama de blocos, mas também o manipulador de eventos associado a esse elemento.


Agora, pressionando o botão "Programa", o indicador fica verde (sucesso) ou verde escuro (sem sucesso). Não é muito claro. Nas propriedades do indicador, altere a cor "Desligado" para vermelho. Isso é melhor Se o indicador estiver verde, podemos dizer que o FPGA no dispositivo selecionado está configurado por um arquivo, cujo caminho é indicado na janela:



Painel frontal


Mas essa declaração se torna falsa se alteramos o arquivo ou selecionamos outro dispositivo. Ao mesmo tempo, não podemos colorir o indicador em vermelho, porque não ocorreu um erro de programação. No caso de uma alteração de arquivo ou dispositivo, é conveniente enfatizar que o valor do indicador não é relevante - escureça-o. Para isso, você pode usar a propriedade Indicador Disabled . Essa propriedade pode receber três valores: Enabled - exibição normal, o usuário pode controlar o objeto; Disabled - o objeto é exibido no painel frontal como de costume, mas o usuário não pode controlar o objeto; Disabled and Grayed Out - o objeto é exibido no painel frontal escurecido e o usuário não pode controlar o objeto.


Criamos manipuladores de eventos para a Devices list e File Path , e neles escurecemos o indicador de status e, no manipulador, atribuímos o botão "Programa" à propriedade Enabled .


O que aconteceu


Manipulador "Programm": alteração de valor.



Manipulador da lista de dispositivos: alteração de valor



É assim que o indicador escuro se parece


Vamos tornar a busca pelo arquivo de configuração conveniente para o usuário - configuraremos a janela de visualização do arquivo. Vá para as propriedades do elemento File Path do File Path , na guia "Opções de navegação", preencha Prompt, Rótulo do padrão, especifique o filtro do tipo de arquivo (Padrão) e o nome do botão (Texto do botão).


Janela de seleção de arquivo

Somente arquivos rbf são exibidos



A criação de uma interface com o usuário pode ser considerada completa.



Executando o gerenciador de inicialização


O que você conheceu hoje?


Neste artigo, como exemplo de criação de um aplicativo funcionalmente completo com uma interface minimalista, tentei mostrar várias abordagens para trabalhar no LabVIEW.


Como resultado, tocamos neles:


  • Configurando as propriedades do dispositivo virtual. .
  • : .
  • .
  • .
  • dll.
  • .

API FTD2XX MPSSE. .



  1. USB FTDI MPSSE
  2. labview_mpsse . .
  3. .
  4. Software Application Development D2XX Programmer's Guide . API D2XX.

Source: https://habr.com/ru/post/pt427529/


All Articles