29
Um agente embutido para conversão de uma interface serial em USB NÚMERO Ana Luiza de Almeida Pereira Zuquim [email protected] MÊS ANO PUBLICAÇÃO UFMG - ICEx DEPARTAMENTO DE CIÊNCIA DA C O M P U T A Ç Ã O UNIVERSIDADE FEDERAL DE MINAS GERAIS

Um agente embutido para conversão de uma interface serial em USB

  • Upload
    lynhan

  • View
    218

  • Download
    0

Embed Size (px)

Citation preview

Page 1: Um agente embutido para conversão de uma interface serial em USB

Um agente embutido para conversão de

uma interface serial em USB

NÚMERO

Ana Luiza de Almeida Pereira Zuquim [email protected]

MÊS ANO

PUBLICAÇÃO

UFMG - ICEx DEPARTAMENTO DE CIÊNCIA DA C O M P U T A Ç Ã O

UNIVERSIDADE FEDERAL DE MINAS GERAIS

Page 2: Um agente embutido para conversão de uma interface serial em USB

Resumo Este documento tem como objetivo descrever o processo de desenvolvimento de um conversor de interface serial para USB – Universal Serial Bus – genérico, utilizando como exemplo de dispositivo o no-break da Engetron. USB é um novo padrão de conexão de periféricos criado com o objetivo de facilitar a conexão de periféricos e torná-la eficiente. Permite a conexão de até 127 dispositivos simultaneamente e reduz o custo para o usuário final. Foi criado em função da crescente demanda por interfaces de comunicação, uma vez que o número de dispositivos conectados ao microcomputador também cresceu muito, tornando o número de portas de comunicação existentes nos micros de hoje insuficiente para as aplicações existentes. Outras características, como velocidade de transmissão/recepção destas portas e dificuldade de configuração para o usuário final, estimularam a criação de um novo protocolo, mais fácil, rápido e eficiente. Apesar da especificação do protocolo USB trazer inúmeros benefícios, é necessário prover uma forma de se manter a compatibilidade com os equipamentos já existentes. Considerando que o protocolo serial é um dos protocolos mais utilizados nos dias de hoje, o projeto de um conversor de interface serial para USB é de grande valor prático.

Page 3: Um agente embutido para conversão de uma interface serial em USB

ÍNDICE

RESUMO _________________________________________________________________________________ 2

1 INTRODUÇÃO _________________________________________________________________________ 4

2 USB – VISÃO GERAL ___________________________________________________________________ 5

2.1 CARACTERÍSTICAS FUNCIONAIS _________________________________________________________ 5 2.2 CLASSES USB ________________________________________________________________________ 7 2.2.1 HUMAN INTERFACE DEVICE CLASS (HID) _______________________________________________ 7 2.3 TIPOS DE TRANSFERÊNCIAS_____________________________________________________________ 8 2.4 REQUISIÇÕES ________________________________________________________________________ 9

3 ESPECIFICAÇÃO DO SISTEMA __________________________________________________________ 9

3.1 REQUERIMENTOS DO HOST _____________________________________________________________ 9 3.2 REQUERIMENTOS DO DISPOSITIVO ______________________________________________________ 10

4 DESCRIÇÃO DO HARDWARE ___________________________________________________________ 11

5 PROJETO E IMPLEMENTAÇÃO DO SOFTWARE _________________________________________ 12

5.1 DEFINIÇÃO DOS DESCRITORES__________________________________________________________ 12 5.2 ROTINAS DE INTERRUPÇÃO NOS ENDPOINTS ______________________________________________ 16 5.3 DETECÇÃO E ENUMERAÇÃO DO DISPOSITIVO______________________________________________ 17 5.4 O PROCESSO DE ENVIO E RECEPÇÃO DE DADOS ____________________________________________ 18

6 DRIVERS E APLICAÇÕES ______________________________________________________________ 18

7 CONCLUSÕES ________________________________________________________________________ 19

AGRADECIMENTOS _____________________________________________________________________ 19

REFERÊNCIAS BIBLIOGRÁFICAS_________________________________________________________ 19

ANEXO A ________________________________________________________________________________ 20

Page 4: Um agente embutido para conversão de uma interface serial em USB

1 Introdução Este documento descreve a especificação e implementação de um conversor de interface serial para interface USB – Universal Serial Bus. O conversor é responsável por receber dados de uma interface serial de um periférico (ou dispositivo) e repassá-los à interface USB de um microcomputador, permitindo, da mesma forma, o envio de dados do PC para o dispositivo. Em função da crescente demanda por interfaces de comunicação, provocada por um número cada vez maior de periféricos conectados ao computador, o número de portas de comunicação disponíveis nos micros de hoje tornou-se insuficiente para a gama de aplicações existentes. Além disso, características como velocidade de transmissão/recepção das portas existentes e dificuldades associadas ao custo, configuração e conexão de periféricos estimularam, ainda mais, a criação de um novo protocolo que simplificasse todo o processo do ponto de vista do usuário final e que fosse mais rápido e eficiente. USB surgiu como um novo padrão de conexão de periféricos desenvolvido por líderes da indústria de computadores e telecomunicações1 a partir da necessidade de se integrar de uma melhor forma estes dois ramos que cresceram separadamente e que, nos dias de hoje, convergem para um mesmo objetivo. O padrão USB utiliza a tecnologia Plug and Play, onde um dispositivo é conectado e automaticamente reconhecido. Permite ainda conexão dinâmica (hot attachment), onde um dispositivo pode ser conectado com o computador ligado, não sendo necessário reinicializar a máquina. Através de uma porta USB é possível conectar até 127 dispositivos simultaneamente em um computador, resolvendo diversos problemas de conflito de recursos (DMA’s, IRQ’s, jumpers, etc.). USB apresenta-se, portanto, como um protocolo que regulamenta meios físicos, software do host, firmware dos dispositivos, plugs e hubs para conexão de periféricos a computadores [8]. Implica, ainda, em aumento de performance com baixo consumo, resultando em baixo custo. Se adequam a essa nova tecnologia periféricos de baixa e média velocidades, como monitores, mouses, dispositivos de E/S de áudio, telefones, modems, teclados, impressoras, entre outros [2,3]. Essa tecnologia já está sendo implementada e comercializada amplamente, estando presente em PCs e periféricos. O projeto de um conversor de interface serial para USB permite que um dispositivo com interface serial se comunique através de uma interface USB, liberando a interface serial do micro para outras aplicações. Desta forma, não estaremos mais restritos à disponibilidade de uma porta de comunicação serial, além de podermos usufruir das vantagens do protocolo USB. A utilização de um conversor de interfaces permite ainda que mantenhamos o hardware e software do dispositivo utilizados inalterados, passando para o conversor a responsabilidade de lidar com as diferenças entre os protocolos de comunicação. O projeto foi desenvolvido baseado nos no-breaks Engetron2, que podem ser gerenciados através da interface serial para troca de dados com o PC. Esta comunicação com os no-breaks é feita, muitas vezes, periodicamente, uma vez que a utilização da porta de comunicação não pode ficar restrita a um único periférico. Para o desenvolvimento do projeto tornou-se necessário um estudo prévio do padrão USB, de sua interação com drivers e aplicativos, além de uma análise das arquiteturas de processadores com interface USB existentes no mercado. O desenvolvimento de um dispositivo USB envolve a implementação do periférico propriamente dito além do desenvolvimento de um software que execute no PC e que se comunique com o mesmo.

1 Empresas fundadoras do USB Forum: Compaq, IBM, Intel, Microsoft, DEC, NEC e Northern Telecom. 2 Engetron é uma empresa conceituada especializada no desenvolvimento de no-breaks inteligentes (smart UPSs)

Page 5: Um agente embutido para conversão de uma interface serial em USB

Para melhor entendimento do texto, serão introduzidos conceitos importantes do padrão USB, tornando assim mais claras as decisões de projeto. Uma breve descrição da família de microcontroladores utilizado permitirá ainda uma contextualização da estrutura exposta na especificação em relação ao hardware e firmware implementado. As estruturas de dados e decisões de projeto serão explicitadas no texto, assim como as etapas do desenvolvimento, possibilitando ao leitor uma fácil adaptação do código do conversor para outros periféricos. 2 USB – Visão Geral A especificação USB [1] descreve os atributos do barramento, define o protocolo, tipos de transações, gerenciamento do barramento e programação da interface, operações estas requeridas no processo de desenho e implementação de sistemas e periféricos compatíveis ao padrão USB. No protocolo USB, o barramento toma para si a responsabilidade de instalar drivers e reconfigurar automaticamente o sistema quando da inserção ou remoção de um dispositivo. Padroniza ainda dois tipos de conectores diferentes, permitindo a transmissão bidirecional e evitando confusões nas conexões. O primeiro tipo é utilizado para conexão ao microcomputador, e é único para todo tipo de periférico. O segundo tipo é utilizado para se conectar o cabo ao periférico, em casos onde a utilização de um cabo fixo é impraticável. A conexão de dois ou mais periféricos é conseguida através da utilização de hubs, que podem ser implementados como dispositivos independentes ou embutidos em periféricos como monitores, teclados, etc. Os dispositivos compartilham a largura de banda utilizando um protocolo baseado em tokens e comandado pelo host. 2.1 Características Funcionais USB se comporta como um barramento Master/Slave onde o Master é o USB Host, que toma conhecimento da inserção e remoção dos periféricos, inicia o processo de enumeração e comanda todas as transações subsequentes nele. É também de sua responsabilidade coletar o status e as estatísticas de cada periférico. Os periféricos são Slaves do barramento, podendo ser funcionais (teclado, mouse, joystick, etc.) ou hubs, utilizados para conectar outros dispositivos. A conexão de dispositivos pode ser feita em cascata ou em estrela. Os dispositivos USB não consomem recursos do sistema. Ao contrário dos dispositivos implementados seguindo padrões mais antigos, dispositivos USB não são mapeados em memória ou em endereços de I/O, nem utilizam IRQs e DMAs. Os únicos recursos de sistema utilizados por um sistema USB são as posições de memória utilizadas pelo software de sistema e as posições de memória e/ou endereços de I/O e IRQs utilizados pelo USB Host Controller. Os dispositivos contêm um número de registradores individuais, conhecidos como endpoints, que podem ser acessados indiretamente pelos device drivers. Quando uma transação é enviada pelo barramento, todos os dispositivos (exceto os de baixa velocidade) identificarão sua presença. Cada transação inicia com um pacote que determina o tipo de transação que será executada e o endereço do endpoint. Esse endereçamento é controlado pelo software USB. A Figura 1, extraída de [4], ilustra os elementos de hardware e software envolvidos em um sistema USB. Todas as transações são iniciadas pelo USB Client software. Esses acessos são tipicamente originados do USB device driver quando este necessita comunicar com seu respectivo dispositivo. O USB driver provê a interface entre o USB device driver e o USB host controller. É o responsável pela conversão da requisição do cliente em uma ou mais transações, que serão direcionadas do ou para o dispositivo alvo.

Page 6: Um agente embutido para conversão de uma interface serial em USB

Do ponto de vista do dispositivo, as mesmas funcionalidades estão refletidas em três estruturas: a primeira (Function) representa a interface funcional do dispositivo, que consiste de uma classe particular de dispositivos que podem ser manipulados por um mesmo driver; a segunda (Logical Device) pode ser vista como uma coleção de endpoints, sendo responsável por lidar com os mecanismos de transferência USB e as suas características; a terceira (Bus Interface), por sua vez, é responsável por receber e enviar sinais elétricos pelo cabo USB.

FUNCTION LAYER

USB DEVICE LAYER

USB BUS INTERFACE LAYER

Figura 1 - Fluxo de Comunicação em um sistema USB Os dispositivos USB (USB Devices) contêm um conjunto de descritores que especificam os atributos e características do dispositivo. Essa informação é necessária para que o host configure o dispositivo e localize seu respectivo driver, além de ser utilizada pelo device driver para acessar o dispositivo. Cada dispositivo possui um endpoint 0, que é reservado para configuração. É através desse endpoint que o software de sistema acessa os descritores do dispositivo. Os dispositivos USB podem ser implementados como de baixa ou alta velocidade. Os dispositivos de alta velocidade ‘enxergam’ todas as transações no barramento, enviando e recebendo dados à taxa de 12Mb/s. Os dispositivos de baixa velocidade estão limitados à taxa de 1,5Mb/s e só ‘enxergam’ as transações que seguem um pacote especial denominado preamble packet. As portas de baixa velocidade dos hubs ficam desabilitadas durante transações de alta velocidade, fazendo com que os dados que devem ser transmitidos à alta velocidade não transitem em cabos de baixa velocidade. O cliente USB requisita uma transferência e fornece um buffer de memória que será utilizado na mesma. O USB Host Controller Driver recebe a requisição e organiza a transferência em transações. O Host Controller gera a transação baseada no Descritor de Transferência, que foi construído pelo Host Controller Driver. Cada transação resulta na transferência de dados do buffer para o dispositivo, ou vice-versa. Quando a transação é completada, o software do sistema notifica o driver cliente. Um dispositivo deve se descrever ao host software através de descritores (Descriptors), que se relacionam utilizando uma estrutura do tipo ´Árvore’, mostrada na Figura 2. Os descritores contêm informações sobre o dispositivo, suas configurações, classes, utilização de energia e características dos endpoints. Suas funcionalidades são descritas a seguir: ��Device Descriptor: contém informações sobre o dispositivo, suas configurações e classes, além de

fornecer as características do barramento de comunicação padrão que será utilizado para configurar o dispositivo.

HOST SYSTEM

Client Software (Client Driver)

System Software (USB Drv + HC Drv)

Host Controller/Hub

USB DEVICE

Function

Logical Device

Bus Interface USB Cable

Page 7: Um agente embutido para conversão de uma interface serial em USB

��Configuration Descriptors: informa características e habilidades de cada uma das configurações possíveis para um dispositivo, como por exemplo, utilização de energia e número de interfaces suportadas

��Interface Descriptors: contém informações relacionadas a uma interface, como por exemplo, classe e subclasse, e aos endpoints utilizados por ela.

��Endpoint Descriptors: contêm informações relativas ao endereço do endpoint, informando o número e direção deste, tamanho máximo do pacote de dados e frequência na qual este deve ser consultado para transferência de dados.

Figura 2 – Descritores de Dispositivos Outros descritores podem ser utilizados conforme as características do periférico a ser implementado, tais como String Descriptors e Class Descriptors. String descriptors contêm uma descrição textual de determinadas características do periférico, tais como nome do fabricante, nome do produto, número serial, etc. Class Descriptors identificam o tamanho e o tipo dos descritores adicionais utilizados para descrever um determinado dispositivo pertencente a uma dada classe. 2.2 Classes USB Na especificação USB, dispositivos que possuem funções similares são agrupados em classes, de forma que se possa compartilhar funcionalidades comuns, além de utilizarem device drivers comuns. Cada classe de dispositivos pode definir descritores próprios (class-specific descriptors) e a inserção desses descritores na definição da estrutura geral do dispositivo é definida pelas próprias classes. Um dispositivo pode pertencer a uma única classe ou ser composto de várias classes. Por exemplo, um telefone possui elementos de áudio, interação humana (HID) e telefonia. Isso é possível em função da estrutura que descreve um dispositivo USB e a indicação desta característica é feita através da definição de várias interfaces. Para a implementação do conversor, foram estudadas duas classes em especial: a Communication Interface Device Class e a Human Interface Device Class. O conversor se enquadrou na classe HID e, por este motivo, esta é apresentada em maior grau de detalhes logo a seguir.

2.2.1 Human Interface Device Class (HID)

De forma geral, a classe HID [3] consiste de dispositivos que são utilizados por pessoas para controlar a operação de sistemas de computação. Fazem parte desta classe dispositivos como mouse, teclados, controles utilizados em jogos e simulações, entre outros dispositivos. Inclui também dispositivos que, apesar de não requererem interação humana, provêm dados em um formato similar, como leitores de códigos de barras, termômetros, etc [4].

Page 8: Um agente embutido para conversão de uma interface serial em USB

A classe HID define uma estrutura que descreve um dispositivo HID. Além dos descritores padrões definidos pela especificação de USB, esta utiliza um descritor de classe (Class Descriptor) através do qual podem ser definidos dois outros tipos de descritores: Report Descriptor e Physical Descriptor. Report Descriptors, diferentemente dos outros descritores, não consistem apenas de tabelas de valores. O tamanho e o conteúdo de um Report Descriptor varia dependendo do número de campos de dados necessários para descrever um dispositivo. É composto por itens que provêm informações sobre o dispositivo. Physical Descriptors descrevem parte ou partes do corpo utilizadas para se ativar um determinado controle. Neste trabalho não foram utilizados Physical Descriptors. A identificação do dispositivo como um HID é feita dentro do descritor de interface, sendo então definido um conjunto de descritores para cada Interface. A estrutura geral dos descritores para um dispositivo HID pode ser vista na Figura 2. Um dispositivo HID utiliza, normalmente, dois canais de comunicação, com transferências do tipo Interrupt e de Controle (endpoint 0), que são explicadas com maior detalhe na próxima seção. 2.3 Tipos de Transferências O barramento USB é um barramento compartilhado e que pode estar sendo utilizado, simultaneamente, por vários dispositivos. O driver cliente comunica ao driver USB que deseja efetuar uma transferência do/para o seu dispositivo correspondente. Algumas dessas transferências consistem de blocos maiores de dados, as quais precisam ser quebradas em várias transações. A transferência de dados é feita em intervalos regulares denominados frames. Um frame é composto de uma ou mais transações que devem ser executadas dentro de 1ms. Cada dispositivo USB é composto por uma coleção de registradores (endpoints) que podem ser acessados pelo driver cliente quando este necessita transferir dados ao seu dispositivo correspondente. Cada endpoint suport um determinado tipo de transferência. Estes são descritos a seguir: ��Isochronous: taxa de transmissão de dados constante O foco desse tipo de transferência é garantir a entrega dos dados dentro de um determinado tempo, sendo dispensável a verificação de erros. Não pode ocorrer distorção no envio dos dados e o sincronismo é o foco deste tipo de transferência. Portanto, só é suportada por dispositivos de alta velocidade (12Mb/s). É unidirecional e possui um payload de dados de 1023 bytes/frame. Exemplos de dispositivos que utilizam este tipo de transferência são microfones, som de uma maneira geral. ��Interrupt O objetivo desse tipo de transferência é verificar se algum dispositivo necessita de transferir algum dado para o host. Esse processo ocorre de tempos em tempos, e o intervalo é denominado Polling Interval. Dessa forma, o host ‘sonda’ os devices e caso seja necessário, a transferência é feita. É feita verificação de erros. Possui um payload de dados de 64 bytes/frame. Exemplo de dispositivos que utilizam este tipo de transferência são o teclado e o mouse. ��Bulk Esse tipo de transferência é utilizado para blocos maiores de dados, onde a taxa de transferência não é fator relevante. O que é importante nesse tipo de transferência é o grau de correção em que os dados chegarão ao destino, sendo indispensável a verificação de erros. Possui um payload de dados de 8, 16, 32

Page 9: Um agente embutido para conversão de uma interface serial em USB

ou 64 bytes por frame. A largura de banda disponível para esse tipo de transferência varia de acordo com a disponibilidade. Um bom exemplo de um dispositivo que utiliza esse tipo de transferência são as impressoras. ��Controle Tipo de transferência utilizado pelo host para fazer requisições ao dispositivo. É nesse tipo de transferência que é feita a leitura dos descritores, por exemplo. Para esse tipo de transferência acontece também a verificação de erros. Transferências do tipo Isochronous e Interrupt têm uma maior prioridade em relação às outras quando distribuídas em um frame. Transferências do tipo Bulk só serão executadas quando houver disponibilidade de espaço dentro de um frame. 2.4 Requisições O protocolo USB é baseado em requisições, que são enviadas pelo host e processadas pelos dispositivos. Essas requisições possuem um limite de tempo para serem processadas pelos dispositivos que as recebem e são respondidas através do Default Control Pipe de cada dispositivo (endpoint 0). Essas requisições são feitas utilizando transferências de controle e a requisição e seus parâmetros são enviados ao dispositivo através de um pacote de setup. Existem algumas requisições que são comuns a todo tipo de dispositivo, enquanto outras são específicas de cada classe. As requisições devem ser direcionadas ao dispositivo, a uma interface dentro de um dispositivo ou ainda a um endpoint específico dentro de um dispositivo. Exemplos de requisições são Get_Configuration e Get_Descriptor, que retornam o valor da configuração e o descritor especificado respectivamente. O detalhamento das requisições será omitido pois foge do escopo deste documento. 3 Especificação do sistema Para o desenvolvimento de um dispositivo USB são necessários: ��Um host que suporte USB ��Driver que execute no host e seja capaz de se comunicar com o periférico ��Aplicação que execute no host e permita o acesso ao periférico. ��Um microcontrolador com interface USB ��Implementação no microcontrolador do código responsável pela comunicação USB ��Implementação das demais funcionalidades do periférico no microcontrolador 3.1 Requerimentos do Host A escolha do sistema operacional a ser utilizado pelo host, feita em 1999, baseou-se no suporte à tecnologia USB. O sistema operacional deveria prover toda uma infra-estrutura de drivers e suporte a características do protocolo, como por exemplo Plug and Play. Na época, os únicos sistemas operacionais que ofereciam tal suporte eram Windows95 OSR2 e Windows98, sendo que o primeiro estava ainda restrito a algumas aplicações. Por este motivo, a escolha do Windows98 como sistema operacional tornou-se evidente, uma vez que este trazia um melhor suporte à USB.

Page 10: Um agente embutido para conversão de uma interface serial em USB

O host deve ser capaz de receber dados USB utilizando para isso device drivers e disponibilizá-los às aplicações quando solicitados. É indispensável que tenhamos executando no host um driver capaz de efetuar as transferências USB (reconhecer o dispositivo, receber e enviar dados, etc). É desejável que tenhamos um driver virtualizado que simule o funcionamento de uma porta serial. Este driver deve ter a capacidade de obter os dados do driver USB e permitir com que uma aplicação os receba como se estivesse acessando uma porta serial. A presença deste driver não é indispensável, uma vez que as aplicações podem receber dados USB diretamente. É desejável uma vez que não seriam necessárias mudanças nas aplicações. Do ponto de vista das aplicações, estas devem ser capazes de receber e enviar dados, o que pode ser feito através de uma porta serial padrão (virtualizada) ou ainda modificando as aplicações para que estas acessem uma porta USB diretamente. A Microsoft provê um driver denominado USB POS driver [12], que foi implementado com o objetivo de permitir que as aplicações “enxergassem” dispositivos USB como se estes estivessem conectados a uma porta serial padrão. A estrutura geral do projeto é mostrada na Figura 5.

Figura 5 – Estrutura geral do sistema 3.2 Requerimentos do dispositivo Foram definidos alguns requisitos a serem observados no processo de comunicação do dispositivo para a escolha do microcontrolador, como a velocidade de transmissão, a frequência em que estas ocorrem e o volume de dados a serem enviados e recebidos. Levando em consideração a velocidade de comunicação de dispositivos USB, verificou-se que um conversor de interface serial pode ser implementado como um dispositivo de baixa velocidade, com velocidade de transmissão variando de 10 a 100Kb/s. Em relação ao volume de dados transmitidos e frequência das transmissões, definiu-se a utilização de transferências do tipo Interrupt, na qual quantidades moderadas de dados devem ser transmitidas em períodos de tempo específicos. O host é responsável por verificar se o dispositivo possui dados a serem transmitidos em intervalos determinados de tempo. As transferências do tipo Interrupt podem ocorrer, não simultaneamente, nos dois sentidos, tanto para o envio de dados do PC ao conversor, quanto o contrário. Este tipo de interrupção é suportado pelo sistema operacional, que já disponibiliza drivers para a classe HID, a qual utiliza este tipo de transferência. O tamanho do pacote de dados para uma única transação, no caso de dispositivos de baixa velocidade, é de 8 bytes. Para envio de uma quantidade maior de dados, estes são subdivididos em múltiplas transações. Outra característica definida foi o número de endpoints que seriam necessários. Conforme explicado anteriormente, endpoints são estruturas capazes de armazenar múltiplos bytes sendo, tipicamente, blocos

Page 11: Um agente embutido para conversão de uma interface serial em USB

de dados na memória ou registradores de um microcontrolador. Cada endpoint possui um endereço único e uma direção pré-definida. Um caso especial trata-se do Endpoint 0, utilizado para controle, que permite transferência de dados bidirecional e é utilizado para configuração do dispositivo e troca de mensagens com o host. Foram definidos três endpoints, sendo um para o envio de dados ao host (IN) e outro para recepção (OUT), além do Endpoint 0 (controle), que está presente em todos os periféricos. A definição do número de endpoints foi feita em função da definição de POS driver da Microsoft, que traz como exigência a disponibilidade de um número par de endpoints além do Endpoint 0. Desta forma, o conversor de interface serial para USB pode ser implementado como um dispositivo da classe HID, que possui exatamente as características mencionadas acima. 4 Descrição do Hardware Com estes requisitos levantados, a escolha do microcontrolador a ser utilizado na implementação pôde ser feita, buscando aquele que tivesse, além das características especificadas, uma melhor documentação e outras aplicações já implementadas. A família CY7C634xx/5xx da Cypress é constituída de microcontroladores de 8 bits RISC, que utilizam arquitetura Harvard, possuem um número razoável de pinos de I/O, 256 bytes de RAM e de 4K a 8Kbytes de EPROM, variando de acordo com o microcontrolador. Esta família de microcontroladores é compatível com a versão 1.0 da Especificação USB [1]. Os microcontroladores desta família suportam três tipos de Reset: Power On Reset, WatchDog Reset e USB Bus Reset (non-hardware reset). Não possuem uma interface serial em hardware, a qual foi implementada utilizando-se pinos de I/O e um software que incorporasse as funções seriais. Todas as interrupções são mascaráveis através de dois registradores – Global Interrupt Enable Register e USB Endpoint Interrupt Enable Register. Cada interrupção está associada a um fragmento de código responsável por tratá-la. O conjunto de instruções foi otimizado para implementação de dispositivos USB, e todos os microcontroladores da família possuem um USB transceiver e uma USB Serial Interface Engine (SIE) (ver Figura 3 retirada de [6]). Apesar disto, estes microcontroladores podem ser utilizados para outras aplicação não-USB. A SIE permite que o microcontrolador se comunique com o host. Ela simplifica a interface entre o microcontrolador e o host, incorporando o hardware que lida com as atividade do barramento independentemente do controlador. Os microcontroladores da família CY7C634xx/5xx provêm um endereço para o dispositivo e três endpoints. O endereço é atribuído ao dispositivo e salvo em um registrador - USB Device Address Register (7 bits) - durante o processo de enumeração. O USB Controller comunica-se com o Host utilizando buffers dedicados, um por endpoint. Cada buffer é implementado como um conjunto de 8 bytes de memória SRAM e seu status e controle é feito utilizando os registradores Mode Register e Count Register. A organização da memória RAM pode ser vista na Figura 4, onde estão salientados os endereços de memória utilizados pelos endpoints.

Page 12: Um agente embutido para conversão de uma interface serial em USB

Figura 4 – Organização da memória RAM dos microcontroladores da família

CY7C634xx/5xx da Cypress Figura 3 – Diagrama de blocos lógicos

Foi utilizado o kit de desenvolvimento (CY3651) correspondente à família CY7C634xx/5xx, o que permitiu uma maior flexibilidade em relação à quantidade de memória necessária para o código. 5 Projeto e implementação do Software O desenvolvimento do conversor pode ser dividido em algumas fases: ��Definição dos descritores ��Implementação das rotinas para tratamento de interrupções ��Módulo de detecção e enumeração do dispositivo ��Módulo de troca de dados USB ��Módulo de troca de dados seriais ��Interseção dos módulos USB e Serial para que trabalhassem conjuntamente A definição de fases não implica que estas devam ser executadas de forma independente (disjunta). 5.1 Definição dos descritores A principal estrutura de dados a ser implementada é formada por um conjunto de descritores de dispositivo que, definidos na especificação USB [1], armazenam características do periférico e permitem que o host o conheça melhor. Cada um dos descritores contém informações sobre o dispositivo como um todo ou de suas partes.

Page 13: Um agente embutido para conversão de uma interface serial em USB

O Device Descriptor contém informações básicas sobre o dispositivo e é o primeiro a ser lido pelo host. É através dele que o host consegue acessar os demais descritores de forma a obter toda a informação necessária para a configuração do dispositivo. Os valores de seus campos foram definidos de acordo com as características do conversor [8]. Para a implementação de um novo dispositivo, estes valores devem ser reavaliados e modificados, caso necessário. A versão da especificação de dispositivos HID utilizada para a implementação foi a v1.1 [3]. As especificações da classe e subclasse do dispositivo são feitas no descritor de interface, pois o dispositivo foi implementado como um HID. O descritor do dispositivo foi definido utilizando o código de vendedor da Lakeview Research (0925h), por ter sido baseado em um exemplo fornecido juntamente com um livro desta editora [5]. Portanto, foi definido para o código do dispositivo o valor 0x1234h, por se tratar de uma aplicação exemplo. Não foi necessário obter um código próprio para a Engetron por este projeto se tratar apenas de um protótipo. Para a implementação efetiva seria necessário obter um Vendor ID através da associação ao USB Implementers Forum. Para um conversor genérico, os campos em destaque devem ser alterados de acordo com as requisições do dispositivo e da versão da especificação que estiver sendo utilizada. Device Descriptor (18 bytes) Campo Offset /

tamanho (bytes)

Descrição Valor Assumido

Blength 0/1 Tamanho deste descriptor (em bytes) 0x12 BDescriptorType 1/1 Tipo do descriptor (device descriptor) 0x01 BcdUSB 2/2 Versão da especificação de USB utilizada 0x0110 BDeviceClass 4/1 Classe do dispositivo3 0x00 BDeviceSubClass 5/1 Subclasse do dispositivo3 0x00 BDeviceProtocol 6/1 Código do protocolo qualificado pela subclasse3 0x00 BMaxPacketSize0 7/1 Tamanho máximo do pacote para o endpoint0 0x08 IdVendor 8/2 Identificação do vendedor 0x0925

(Lakeview Research) IdProduct 10/2 Identificação do dispositivo 0x3412 (exemplo) BcdDevice 12/2 Versão do dispositivo 0x01 IManufacturer 14/1 Índice para o string descriptor contendo a identificação do

fabricante 0x00 (nenhum)4

IProduct 15/1 Índice para o string descriptor contendo a identificação do produto

0x00 (nenhum) 4

ISerialNumber 16/1 Índice para o string descriptor contendo o número de série 0x00 (nenhum) 4 bNumConfigurations 17/1 Número de configurações possíveis 0x01

Tabela 1 – Definição do Device Descriptor Uma configuração descreve as características e capacidades do periférico e, de forma geral, uma única configuração é suficiente. Dispositivos com vários modos ou casos de uso podem ter várias configurações e, consequentemente, vários descritores de configuração. Para o conversor implementado, foi definida uma única configuração, que utiliza a energia fornecida pelo barramento (Bus Powered) até um limite máximo de 100mA.

3 A definição da classe, subclasse e protocolo para um dispositivo foi feita dentro do descritor de interface pois o dispositivo implementado possui apenas uma configuração e uma interface. 4 Podem ser definidos descritores para descrever melhor atributos como nome do fabricante, do dispositivo e número serial. Para o caso, não foi necessário.

Page 14: Um agente embutido para conversão de uma interface serial em USB

Configuration Descriptor (9 bytes) Campo Offset /

tamanho (bytes)

Descrição Valor Assumido

bLength 0/1 Tamanho deste descriptor (em bytes) 0x09 bDescriptorType 1/1 Tipo do descriptor (configuration descriptor) 0x02 wTotalLength 2/2 Tamanho total de todos descriptors

(configuration+interface+endpoint+HID) 0x22 (34 bytes)

bNumInterfaces 4/1 Número de interfaces (conversor) 0x01 bConfigurationValue 5/1 Valor utilizado para selecionar essa configuração 0x01 iConfiguration 6/1 Índice do string descriptor que descreve esta configuração 0x00 (nenhum)4

bmAttributes 7/1 Características da configuração (em relação à energia) Bus Powered

0x80

MaxPower 8/1 Quantidade máxima de energia consumida do barramento pelo dispositivo (expressa em 2mA)

0x32 (100 mA)

Tabela 2 – Definição do Configuration Descriptor Uma interface define, para um dispositivo, um conjunto de endpoints utilizados para uma característica ou funcionalidade. Um descritor de interface contém, assim, informações relacionadas aos endpoints. A utilização de múltiplas interface acontece quando um dispositivo possui várias formas de utilização, implicando em endpoints com características diferentes. Para o conversor, foi definida uma única interface com três endpoints, sendo um de controle, um Interrupt IN e outro Interrupt OUT. A utilização de um endpoint Interrupt OUT não foi possível, pois o suporte provido pelo Windows98 estava ainda restrito. Sendo assim, foram utilizados dois endpoints, um de controle e o outro Interrupt IN. Os dados são enviados ao dispositivo através do Endpoint 0 e ao PC através do Endpoint 1, utilizando as rotinas SET REPORT e GET REPORT respectivamente. Interface Descriptor (9 bytes) Campo Offset /

tamanho (bytes)

Descrição Valor Assumido

bLength 0/1 Tamanho deste descriptor (em bytes) 0x09 bDescriptorType 1/1 Tipo do descriptor (interface descriptor) 0x04 bInterfaceNumber 2/1 Número da interface que identifica o índice no array de

interfaces concorrentes para esta configuração 0x00

bAlternateSetting 3/1 Valor utilizado para selecionar configurações alternativas para esta interface

0x00

bNumEndpoints 4/1 Número de endpoints utilizados por esta interface (excluindo o endpoint0) - 1 Interrupt IN

0x01

bInterfaceClass 5/1 Código de classe para esta interface 0x03 (HID) bInterfaceSubClass 6/1 Código de subclasse 0x00 (nenhum)5

bInterfaceProtocol 7/1 Código do protocolo (0 = none) 0x00 (nenhum)5

Interface 8/1 Índice do string descriptor que descreve esta interface 0x00 (nenhum)5

Tabela 3 – Definição do Interface Descriptor Exceto para o Endpoint 0, cada endpoint especificado em uma interface está associado a um descritor de endpoint. O Endpoint 1 foi definido como sendo do tipo Interrupt, cuja direção de tráfego de dados é do dispositivo para o host, ou seja, IN. O tamanho dos pacotes de dados foi definido para o valor máximo em transferências do tipo Interrupt - 8 bytes – e o menor intervalo de pooling para dispositivos de baixa velocidade - 10ms., de forma a permitir a transferência de um volume maior de dados em cada transação. 5 Podem ser definidos descritores para descrever melhor atributos como nome do fabricante, do dispositivo e número serial. Para o caso, não foi necessário.

Page 15: Um agente embutido para conversão de uma interface serial em USB

Endpoint Descriptor (7 bytes) - Endpoint1 - Interrupt IN Endpoint Campo Offset /

tamanho (bytes)

Descrição Valor Assumido

bLength 0/1 Tamanho deste descriptor (em bytes) 0x07 bDescriptorType 1/1 Tipo do descriptor (endpoint descriptor) 0x05 bEndpointAddress 2/1 Endereço do endpoint no dispositivo USB

bit 0..3 número do endpoint bit 4..6 reservado bit 7 direção (0=OUT 1=IN)

0x81

bmAttributes 3/1 Atributos do endpoint quando configurado utilizando o bConfigurationValue bit 0..1 Tipo de transferência

0x03

wMaxPacketSize 4/1 Tamanho máximo de pacote que o endpoint é capaz de enviar e receber (de 8 a 64bytes)

0x0008

bInterval 6/1 Intervalo para verificação se o endpoint possui dado a ser transferido (em ms)

0x0A (10 ms)

Tabela 4 – Definição do Endpoint Descriptor String Descriptors não foram definidos por serem dispensáveis no caso do protótipo. O objetivo de descritores de classe é identificar informações adicionais a serem utilizadas na comunicação, podendo assim fazer um melhor uso das características do dispositivo. Podem possuir sete ou mais campos, dependendo do número de descritores adicionais utilizados. Para o conversor, foi utilizado apenas um descritor adicional, o Report Descriptor. HID (Class) Descriptor (9 bytes) Campo Offset /

tamanho (bytes)

Descrição Valor Assumido

bLength 0/1 Tamanho deste descriptor (em bytes) 0x09 bDescriptorType 1/1 Tipo do descriptor (hid (class) descriptor) 0x21 bcdHID 2/2 Versão da especificação da classe HID (em BCD) 0x0110 bCountryCode 4/1 País de origem do hardware

00 = não suportado 0x006

bNumDescriptors 5/1 Número de descriptors para a classe HID 0x01 bDescriptorType 6/1 Tipo do descritor adicional - Report descriptor 0x22 wDescriptorLength 7/2 Tamanho total do report descriptor end_hid_report_desc_table

– hid_report_desc_table

Tabela 5 – Definição do descritor de classe – HID Descriptor A definição de país de origem só é utilizada quando o hardware é desenvolvido para um país específico, o que não foi o caso. Report Descriptors definem o formato e utilização dos dados que implementam as funcionalidades do dispositivo. Para o conversor, foram definidas apenas duas funcionalidades, uma de envio e outra de recebimento de dados. A recepção de dados pelo conversor é feita através de Output Reports, estrutura que foi definida como sendo formada por 16 campos de 8 bits em função do tamanho dos comandos enviados ao no-break. O envio de dados do conversor ao host é feito diretamente, uma vez que estes são recebidos da interface serial e encaminhados à interface USB (Endpoint buffer), sendo lidos através de uma requisição. Neste caso, não foi definido um Input Report que implemente esta funcionalidade, pois não é feito o armazenamento da resposta recebida.

6 Caso se deseje implementar uma versão de dispositivo específica para um país, utilizar este campo para indicar o código do país.

Page 16: Um agente embutido para conversão de uma interface serial em USB

O código associado à implementação do Report Descriptor é mostrado abaixo. O código implementado para os demais descritores pode ser encontrado no Anexo A. ;The HID-report descriptor table hid_report_desc_table: db 06h, A0h, FFh ; Usage Page (vendor defined) FFA0 db 09h, 01h ; Usage (vendor defined) db A1h, 01h ; Collection (Application) db 09h, 02h ; Usage (vendor defined) db A1h, 00h ; Collection (Physical) db 06h, A1h, FFh ; Usage Page (vendor defined) ;The output report db 09h, 05h ; usage - vendor defined db 09h, 06h ; usage - vendor defined db 15h, 80h ; Logical Minimum (-128) db 25h, 7Fh ; Logical Maximum (127) db 35h, 00h ; Physical Minimum (0) db 45h, FFh ; Physical Maximum (255) db 75h, 08h ; Report Size (8) (bits) db 95h, 0Bh ; Report Count (11) (fields) db 91h, 02h ; Output (Data, Variable, Absolute) db C0h ; End Collection db C0h ; End Collection end_hid_report_desc_table:

5.2 Rotinas de interrupção nos Endpoints A implementação das rotinas que tratam da comunicação USB foi feita baseada em códigos de exemplo de teclado, entre outras aplicações USB, fornecidas pela Cypress [8,9] ou extraídas do livro USB Complete [10], muitas destas implementadas para a família CY7C6300x. As rotinas para tratamento de interrupção (ISR) são chamadas uma vez que o host ou o controlador enviem um pacote ao barramento. Estas checam o tipo de pacote enviado ou recebido pelo seu respectivo endpoint e provêm o tratamento da requisição. Existem ainda rotinas de interrupção para cada um dos endpoints. Estas acontecem após o host ou o controlador enviarem um pacote ao barramento. São responsáveis por checar qual o tipo do pacote que está sendo enviado/recebido pelo respectivo Endpoint. A rotina referente à interrupções no Endpoint 0 é mostrada a seguir. Ela recebe uma conjunto de dados do host e verifica sua validade (correção de erros, completeza e ordem). Basicamente, esta rotina espera até que um pacote de SETUP seja recebido, processando então a requisição e tomando a ação apropriada: enumeração, configuração ou troca de dados. Uma vez que uma requisição é recebida, ela desabilita as interrupções no Endpoint 0 a fim de poder processar o pacote recebido. As demais interrupções devem ser habilitadas, neste caso, de forma a permitir que o microcontrolador responda a elas. A parte do código referente ao recebimento e tratamento da requisição está destacado.

Page 17: Um agente embutido para conversão de uma interface serial em USB

USB_EP0_ISR: save accumulator on stack if we did not receive a SETUP token packet – done_EP0 if data is not valid - STALL_IN_OUT if we did not receive 10 data bytes (8 data bytes + 2 CRC bytes) – STALL_IN_OUT if data toggle is not 0 – STALL_IN_OUT disable just endpoint zero interrupt (other interrupts enabled) parse SETUP packet

test bmRequestType ; Find out whether the request is a standard device or HID request, the direction of data transfer, and ; whether the request is to a device, interface, or endpoint. (from Table 9.2 in the USB spec)

test bRequest ; Find out which request it is. process the request ; Process the request.

handshake with the host STALL_IN_OUT:

accept SETUP, stall IN/OUT enable endpoint zero interrupt

done_EP0: restore accumulator from stack and return

Conforme dito anteriormente, o Endpoint 1 é do tipo Interrupt IN, ou seja, é utilizado para envio de dados ao host. A rotina de interrupção é chamada toda vez que o host deseja obter dados do Endpoint 1. Os dados a serem enviados já devem estar disponíveis para leitura pela requisição de GET REPORT, ou seja, a rotina apenas prepara para o envio do próximo pacote. A interrupção acontece quando o host responde ao dispositivo afirmando o recebimento de um pacote de dados no Endpoint 1 (ACK). O bit que indica o tipo de pacote de dados é então modificado (Data Packet type - de 0 para 1 ou de 1 para 0), conforme a especificação. A rotina de interrupção do Endpoint 1 é mostrada a seguir. USB_EP1_ISR:

save accumulator on the stack test whether ACK bit is set to know that the last data transmission was successful if we don’t have an ACK bit – doneEP1 toggle the data 0/1 bit so it’s correct for the next transaction. clear endpoint 1 FIFO

doneEP1: restore accumulator from the stack

return from interrupt 5.3 Detecção e enumeração do dispositivo A terceira fase do projeto consiste na implementação do código que permite que o host detecte e enumere o dispositivo. A implementação destas rotinas foi baseada em códigos de exemplo [8,9,10]. Devemos ter implementado dentro do microcontrolador o código que permita o acesso aos descritores, o reconhecimento e tratamento das requisições que o host envia para enumeração do dispositivo. Do ponto de vista do host, deve ser criado um arquivo de extensão .INF [5], de forma que o Windows possa identificar o dispositivo e enumerá-lo, associando a ele o driver adequado. O sistema operacional provê arquivos .INF de exemplo, e por este motivo, é apresentado no Anexo A apenas as rotinas implementadas no microcontrolador.

Page 18: Um agente embutido para conversão de uma interface serial em USB

5.4 O processo de envio e recepção de dados O envio de dados ao no-break ocorre através de transferências de controle utilizando a requisição SET REPORT e o Endpoint 0. Estas transferências possuem uma fase de SETUP, uma ou mais fases de DADOS e uma fase de STATUS. O host deve, primeiramente, enviar uma requisição ao dispositivo, indicando que ele deseja enviar dados. Uma vez identificado o tipo da requisição, é chamada uma rotina que recebe os dados, armazenado-os no Endpoint 0. Tendo preenchido o buffer do Endpoint 0, os dados são copiados em outro buffer para que sejam enviados ao no-break através da interface serial. É chamada então a rotina que envia os dados (comando) byte a byte ao no-break, inserindo Start e Stop bits e atrasos necessários. Podem ser lidos 8 ou 16 bytes, valor este que é informado no cabeçalho da requisição, na fase de SETUP. O tamanho máximo do pacote de dados a ser enviado foi definido em função do maior comando a ser recebido pelo no-break. Para a implementação de um conversor mais genérico, esta função deve ser alterada de forma a permitir o recebimento de um número arbitrário de bytes. Uma vez enviado o comando, o controlador se prepara para receber a resposta do no-break, armazenando-a da mesma forma em um buffer de recepção. O buffer de recepção é gradativamente preenchido pelas rotinas da serial e lido pela rotina de GET REPORT. Caso todo o processo de envio de comandos e recepção da resposta ocorra com sucesso, é enviado ao host um pacote de ACK (acknowledge) através da rotina NO_DATA_CONTROL. Caso contrário, desconsidera-se o que foi enviado e/ou recebido. A leitura de dados do no-break ocorre através de transferências do tipo interrupt utilizando a requisição GET REPORT e o Endpoint 1. O host envia uma requisição ao dispositivo, indicando que ele deseja receber dados. Os dados são obtidos da serial e copiados para o buffer do Endpoint 1, para que estes sejam enviados ao host. As principais rotinas implementadas encontram-se no Anexo A. 6 Drivers e aplicações A comunicação com dispositivos USB pode ser implementada em qualquer linguagem que possibilite a chamada de funções da API do Windows [11]. Os drivers necessários para comunicação com dispositivos HID já são fornecidos juntamente com o sistema operacional. Para dispositivos que se enquadrem em outras classes ou em nenhuma delas pode ser necessário que se escreva o device driver correspondente. A comunicação com um dispositivo HID envolve operações mais complexas do que as existentes para portas baseadas em padrões mais antigos. Antes mesmo de executar as funções de “abrir” uma porta, setar parâmetros, ler e escrever dados, é necessário encontrar o dispositivo e obter informações a respeito do mesmo e seus Reports. Esse processo é feito através de uma série de chamadas à funções de API. Primeiramente, a aplicação procura por todos os dispositivos HID presentes no sistema, examinando então as informações de cada um até encontrar aquele com os atributos desejados. Após encontrar o dispositivo desejado, a aplicação pode trocar informações com este através dos Reports.

Page 19: Um agente embutido para conversão de uma interface serial em USB

7 Conclusões O conversor foi implementado e testado utilizando o kit de desenvolvimento da Cypress e ferramentas fornecidas pelo USB Implementers Forum. O código implementado responde corretamente a todos os tipos de requisições (enumeração e troca de dados). Todo o processo de troca de dados, incluindo a conversão de protocolos, foi validada utilizando uma aplicação que emulasse a serial do dispositivo (similar ao no-break) e uma outra aplicação, rodando no host, e que se comunica via USB. Este documento é um bom guia àqueles que desejam desenvolver dispositivos USB, uma vez que ele apresenta todas as etapas do desenvolvimento de forma clara e baseada no exemplo implementado. Os principais algoritmos implementados são mostrados no Anexo A. Como trabalho futuro, deseja-se utilizar drivers para a virtualização da porta serial. Isto permitirá que as aplicações possam acessar dados USB sem a necessidade de serem modificadas. Agradecimentos Este trabalho foi desenvolvido com o apoio financeiro provido por uma parceria entre a Engetron e o Departamento de Ciência da Computação. Referências Bibliográficas [1] USB Implementers Forum, “Universal Serial Bus Specification,” Revision 1.0, 1996. [2] USB Implementers Forum, “Universal Serial Bus Specification,” Revision 1.1, 1998. [3] USB Implementers Forum, “Device Class Definition for Human Interface Devices,” Revision 1.1, 1999 [4] D. Anderson, “Universal Serial Bus System Architecture ”. Addison-Wesley Developers Press, 1997. [5] J. Axelson, “USB Complete – Everything You Need to Develop Custom USB Peripherals”. Lakeview Research, 1999. [6] Cypress, “CY7C63411/12/13 and CY7C63511/12/13 Low-Speed, High I/O, 1.5 Mbps USB Controller”, 1998, available at http://www.cypress.com/ usb/datasheets.html (microcontrollers datasheet) [7] Cypress, “USB Development System CY3651 User’s Guide”, Revision 2.2, 1997. [8] Cypress, “Designing a Low-Cost USB Interface for an Uninterruptable Power Supply with the Cypress Semiconductor CY7C630001 USB Controller”, 1998, available at http://www.cypress.com/ usb/usbappnotes.html [9] Cypress, “CY3651 test firmware”, 1999 (Application notes) [10] J. Axelson, “Usbhidio - Universal Serial Bus Human Interface Device Input/Ouput (I/O) application” [11] Microsoft, Windows 98 DDK [12] Microsoft USB Point Of Sale, available at http://www.microsoft.com/HWDEV/usb/posusb.htm

Page 20: Um agente embutido para conversão de uma interface serial em USB

ANEXO A ;======================================================================;interrupt vectors;======================================================================ORG 00h

jmp Reset ; Reset vector; begin here after a reset.jmp USB_Bus_Reset_ISR ; USB bus resetjmp Serial_ISR ; Used to implement serial receive/transmit

; bit timing loops – declared on serial.asmjmp One_mSec_ISR ; 1024-millisecond interruptjmp USB_EP0_ISR ; endpoint 0 interruptjmp USB_EP1_ISR ; endpoint 1 interruptjmp DoNothing_ISR ; endpoint 2 interruptjmp Reset ; reserved interruptjmp DoNothing_ISR ; reserved interruptjmp DoNothing_ISR ; reserved interruptjmp DoNothing_ISR ; DAC interruptjmp GPIO_ISR ; GPIO interrupt – declared on serial.asmjmp DoNothing_ISR ; reserved interrupt

ORG 1Ah

include "serial.asm"

;======================================================================;Interrupt routines;======================================================================

DoNothing_ISR:reti ; return from interrupt

; When the part detects a single-ended zero from the upstream port,; an interrupt occurs that wakes up the CPU from suspend mode and; transfers control to this interrupt service routine.; clears watchdog timer - clears USB device address register; The USB Bus Reset interrupt service routine prepares for enumeration.USB_Bus_Reset_ISR:

push A ; save accumulator on stackiowr Watchdog ; clear watchdog timeriord Status_Control ; clear the Bus Reset bit in theand A, ~USBReset ; Status & Control registeriowr Status_Controlmov A, TIMER_MASK ; enable one msec timer interruptiowr Global_Interruptmov A, ENDPOINT_ZERO_ONLY ; enable endpoint zero interruptiowr Endpoint_Interrupt ; disable endpoint one&two interruptiord EP_A0_Mode ; unlock Mode registermov A, SETUP ; enable endpoint zero setupiowr EP_A0_Mode

mov A, ADDRESS_ENABLE_BIT ; enable endpoint zero responseiowr USB_Device_Addressmov A, DISABLEDiowr EP_A1_Mode ; disable endpoint oneiowr EP_A2_Mode ; disable endpoint two

iowr Watchdog ; clear Watchdog timercall Init ; perform initialization on variablesiowr Watchdog ; clear Watchdog timer again

Page 21: Um agente embutido para conversão de uma interface serial em USB

pop A ; restore accumulator from stackreti ; return from interrupt

;----------------------------------------------------------------------; 1-millisecond interrupt - Check to see if the chip is in suspend; mode and take appropriate action. Copy values to Endpoint 1's buffer for sending.;----------------------------------------------------------------------One_mSec_ISR:

push Aiowr Watchdog ;clear watchdog timer

;Find out if enumeration is complete.;If enumerating is in progress, loop_temp = 0.

mov A, [loop_temp]cmp A, 0h

;If enumeration is still in progress, jump.jz not_main

;Enumeration has ended, so decrement the loop counter;(so it no longer = 0).

dec [loop_temp]

mov A, [wakeup_flag] ;check if we are in a remotecmp A, 01h ;wakeup processjnz not_sending_wakeupinc [wakeup_counter] ;increment wakeup counterjmp One_mSec_end ;indicate if the 10msecs of wakeup

;have passednot_sending_wakeup:

dec [4ms_counter] ;decrement the 4msecs counterjnz not_main ;if not zero, then check bus activity statusmov A, 4 ;if zero, re-initialize to 4msecsmov [4ms_counter], Amov A, [idle_period_counter] ;since 4msecs has passed, increment

;the idle_period_countercmp A, FFh ;which has a 4msecs resolutionjz not_main ;if the idle_period_counter reaches FFh,

;don't incrementinc [idle_period_counter] ;to prevent rollover

not_main:;Check for bus activity.

iord USB_Status_Control ;check if there is no bus activityand A, 08h ;bit 3 = bus activitycmp A, 0h

;If no bus activity, increment the suspend counter.jz Inc_counter

;If bus activity detected, clear the bus-activity bit,iord USB_Status_Controland A, 0F7hiowr USB_Status_Control

;and clear the suspend counter.mov A, 0hmov [suspend_counter], Ajmp Suspend_end

Inc_counter:;Keep track of the amount of time with no bus activity.

inc [suspend_counter];Get the number of milliseconds the bus has been idle.

mov A, [suspend_counter];Has it been 3 milliseconds? = check if 3msecs of bus inactivity passed

cmp A, 03h;If no, there's nothing else to do. = less than 3msecs

jnz Suspend_end

Page 22: Um agente embutido para conversão de uma interface serial em USB

;If yes, put the chip in Suspend mode.;Clear the Suspend counter.

mov A, 0hmov [suspend_counter], A

mov A, [remote_wakeup_status] ; is remote wakeup feature enabled?cmp a, ENABLE_REMOTE_WAKEUP ;jnz Suspend_controller ; if not, just do a normal suspend

mov A, GPIO_ONLY_MASK ; enable GPIO interruptsiowr Global_Interruptei ; enable interrupts

Suspend_controller:mov A, RESISTIVE_NEG ; all ports resistive neg so thatiowr GPIO_Config ; we stay within suspend current budget

;In "Resistive" mode, a 7 Kohm pull-up resistor is conditionally enabled;for all pins of a GPIO port. The resistor is enabled for any pin that has been;written as a "1". The resistor is disabled on any pin that has been written;as a "0".

iord Status_Controlor A, 08hiowr Status_Control

;The chip is now in Suspend mode.;On exiting Suspend mode, the chip will begin;executing instructions here:

nop;Disable pullups on Port 1. Enable the output DAC.

mov A, NORMAL ; restore original GPIO configurationiowr GPIO_Config

Suspend_end:;Is endpoint 1 enabled?

iord EP_A1_Modeand A, 0Fhcmp A, 0

;If no, do nothing.jz Select

;If yes, is start_send = 1?;(Start_send adds a short delay after enumeration.)

mov A, [start_send]cmp A, 01h

;If no, do nothingjnz Select

;If yes, send data:jmp send_value

send_value:;Copies values from RAM into Endpoint 1's buffer;and enables sending the bytes on the next poll.;disable Endpoint 1 interrupts

iord Endpoint_Interrupt ; disable endpoint one interruptand A, ~ENDPOINT_ONEiowr Endpoint_Interrupt

;Copy values from RAM to Endpoint 1's buffer for transmitting to the host.mov A, [Data_Byte0]mov [Endpoint1_Byte0], Amov A, [Data_Byte1]mov [Endpoint1_Byte1], Amov A, [Data_Byte2]mov [Endpoint1_Byte2], Amov A, [Data_Byte3]mov [Endpoint1_Byte3], A

Page 23: Um agente embutido para conversão de uma interface serial em USB

mov A, [Data_Byte4]mov [Endpoint1_Byte4], Amov A, [Data_Byte5]mov [Endpoint1_Byte5], Amov A, [Data_Byte6]mov [Endpoint1_Byte6], Amov A, [Data_Byte7]mov [Endpoint1_Byte7], A

;Configure Endpoint 1's transmit register;so that the bytes will transmit on the next poll.

iord [EP_A1_Counter];Don't change the Data 0/1 bit.

and A, 80h;Set bits 4 and 7 to 1 enable transmitting.

or A, 08h ; sending 8 bytesiowr [EP_A1_Counter]

Select:;Enable Endpoint 1 interrupts.

iord Endpoint_Interruptor A, ENDPOINT_ONEiowr Endpoint_Interrupt

One_mSec_end:pop Areti ; return from interrupt

;======================================================================; The main program loop.;======================================================================

main:;Find out if the loop_temp delay has timed out.;Loop_temp =0 if not timed out, FFh if timed out.

mov A, [loop_temp]cmp A, 0Ah

;If no, don't enable transmitting.jnc no_set

;If yes, enable transmitting.mov A, 01hmov [start_send], A

no_set:;Clear the watchdog timer.;This has to be done at least once every 8 milliseconds!

iowr Watchdogiord Port0_Data

nochange:jmp main

;----------------------------------------------------------------------; This is the routine that is entered when a data item from the UPS is; requested and returns with the string resident in the receive buffer;----------------------------------------------------------------------putCommand:

mov A, 00h ; Clear the accumulator.mov [txBufPtr], A ; Reset tx buffer pointer.iowr Watchdog ; Clear watchdog timer.mov X, [txBufPtr] ; Set the index.mov A, [X + txBuf] ; Load Command Byte Zero.mov [txData], A ; Put it in the transmit register.inc [txBufPtr] ; Point to the next byte.iowr Watchdog ; Ping the watch dog timer.call txRoutine ; Go transmit the byte.

Page 24: Um agente embutido para conversão de uma interface serial em USB

iowr Watchdog ; Ping the watch dog timer.call delay1 ; Give the serial device some time.mov X, [txBufPtr] ; Set the index.mov A, [X + txBuf] ; Load Command Byte Onemov [txData], A ; Put it in the transmit register.inc [txBufPtr] ; Point to the next byte.iowr Watchdog ; clear watchdog timercall txRoutine ; Go transmit the byte.iowr Watchdog ; clear watchdog timercall delay1 ; Give the serial device some time.mov X, [txBufPtr] ; Set the index.mov A, [X + txBuf] ; Load Command Byte Twomov [txData], A ; Put it in the transmit register.inc [txBufPtr] ; Point to the next byte.iowr Watchdog ; clear watchdog timercall txRoutine ; Go transmit the byte.iowr Watchdog ; clear watchdog timercall delay1 ; Give the serial device some time.mov X, [txBufPtr] ; Set the index.mov A, [X + txBuf] ; Load Command Byte Threemov [txData], A ; Put it in the transmit register.inc [txBufPtr] ; Point to the next byte.iowr Watchdog ; clear watchdog timercall txRoutine ; Go transmit the byte.iowr Watchdog ; clear watchdog timercall delay1mov X, [txBufPtr] ; Set the index.mov A, [X + txBuf] ; Load Command Byte Four.mov [txData], A ; Put it in the transmit register.inc [txBufPtr] ; Point to the next byte.iowr Watchdog ; Ping the watch dog timer.call txRoutine ; Go transmit the byte.iowr Watchdog ; Ping the watch dog timer.call delay1 ; Give the serial device some time.mov X, [txBufPtr] ; Set the index.mov A, [X + txBuf] ; Load Command Byte Fivemov [txData], A ; Put it in the transmit register.inc [txBufPtr] ; Point to the next byte.iowr Watchdog ; clear watchdog timercall txRoutine ; Go transmit the byte.iowr Watchdog ; clear watchdog timercall delay1 ; Give the serial device some time.mov X, [txBufPtr] ; Set the index.mov A, [X + txBuf] ; Load Command Byte Sixmov [txData], A ; Put it in the transmit register.inc [txBufPtr] ; Point to the next byte.iowr Watchdog ; clear watchdog timercall txRoutine ; Go transmit the byte.iowr Watchdog ; clear watchdog timercall delay1 ; Give the serial device some time.mov X, [txBufPtr] ; Set the index.mov A, [X + txBuf] ; Load Command Byte Sevenmov [txData], A ; Put it in the transmit register.inc [txBufPtr] ; Point to the next byte.iowr Watchdog ; clear watchdog timercall txRoutine ; Go transmit the byte.iowr Watchdog ; clear watchdog timercall delay1 ; Give the serial device some timemov X, [txBufPtr] ; Set the index.mov A, [X + txBuf] ; Load Command Byte Eight.mov [txData], A ; Put it in the transmit register.inc [txBufPtr] ; Point to the next byte.iowr Watchdog ; Ping the watch dog timer.

Page 25: Um agente embutido para conversão de uma interface serial em USB

call txRoutine ; Go transmit the byte.iowr Watchdog ; Ping the watch dog timer.call delay1 ; Give the serial device some time.mov X, [txBufPtr] ; Set the index.mov A, [X + txBuf] ; Load Command Byte Ninemov [txData], A ; Put it in the transmit register.inc [txBufPtr] ; Point to the next byte.iowr Watchdog ; clear watchdog timercall txRoutine ; Go transmit the byte.iowr Watchdog ; clear watchdog timercall delay1 ; Give the serial device some time.mov X, [txBufPtr] ; Set the index.mov A, [X + txBuf] ; Load Command Byte Tenmov [txData], A ; Put it in the transmit register.inc [txBufPtr] ; Point to the next byte.iowr Watchdog ; clear watchdog timercall txRoutine ; Go transmit the byte.iowr Watchdog ; clear watchdog timercall delay1 ; Give the serial device some time.mov X, [txBufPtr] ; Set the index.mov A, [X + txBuf] ; Load Command Byte Elevenmov [txData], A ; Put it in the transmit register.inc [txBufPtr] ; Point to the next byte.iowr Watchdog ; clear watchdog timercall txRoutine ; Go transmit the byte.iowr Watchdog ; clear watchdog timercall delay1 ; Give the serial device some timemov X, [txBufPtr] ; Set the index.mov A, [X + txBuf] ; Load Command Byte Twelvemov [txData], A ; Put it in the transmit register.inc [txBufPtr] ; Point to the next byte.iowr Watchdog ; Ping the watch dog timer.call txRoutine ; Go transmit the byte.iowr Watchdog ; Ping the watch dog timer.call delay1 ; Give the serial device some time.mov X, [txBufPtr] ; Set the index.mov A, [X + txBuf] ; Load Command Byte Thirteenmov [txData], A ; Put it in the transmit register.inc [txBufPtr] ; Point to the next byte.iowr Watchdog ; clear watchdog timercall txRoutine ; Go transmit the byte.iowr Watchdog ; clear watchdog timercall delay1 ; Give the serial device some time.mov X, [txBufPtr] ; Set the index.mov A, [X + txBuf] ; Load Command Byte Fourteenmov [txData], A ; Put it in the transmit register.inc [txBufPtr] ; Point to the next byte.iowr Watchdog ; clear watchdog timercall txRoutine ; Go transmit the byte.iowr Watchdog ; clear watchdog timercall delay1 ; Give the serial device some time.mov X, [txBufPtr] ; Set the index.mov A, [X + txBuf] ; Load Command Byte Fifteenmov [txData], A ; Put it in the transmit register.inc [txBufPtr] ; Point to the next byte.call txRoutine ; Go transmit the byte.

mov A, 80h ; Enable the Port 0 Bit 7iowr Port0_Interrupt ; GPIO interrupt.iowr Watchdog ; clear watchdog timerret ; Return to sender.

;========================================================================

Page 26: Um agente embutido para conversão de uma interface serial em USB

; function: no_data_control; purpose: performs the no-data control operation; as defined by the USB specifications; (i.e. respond to Status IN token with 0 byte data);; premature_setup is set to 1 (true) if a SETUP is received during; the routine, otherwise it is set to 0 (false)no_data_control:

push Amov A, STATUSINONLY ; enable TX0 INcall set_ep0_modemov A, [premature_setup] ; return if we received a prematurecmp A, 0 ; SETUPjnz done_nodata_control

; wait for the zero-length transfer to completewait_nodata_sent:

iord EP_A0_Mode ; read mode registerand A, EP0_SETUP_RCV ; did we receive premature SETUP?jz check_nodata_ackmov A, 1 ; yes, then set flag and return

mov [premature_setup], Ajmp done_nodata_control

check_nodata_ack:iord EP_A0_Mode ; read mode registerand A, ACK_BIT ; wait for ACK bit highjz wait_nodata_sent

; clear ACK bitmov A, SETUP ; NAK IN and OUT packetscall set_ep0_mode

done_nodata_control:pop Aret ; return

;========================================================================; function: Control_write; purpose: performs the control_write operation; as defined by the USB specifications for 1 OUT byte:; SETUP - OUT - IN; This routine does not acknowledge the Status IN packet from the host.; The no_data_control routine will do this.; data_ok is set to 1 (true) if valid data is received,; otherwise it is set to 0 (false); premature_setup is set to 1 (true) if a SETUP is received during; the routine, otherwise it is set to 0 (false)Control_write:

mov A, ACKOUTSTATUSIN ; accept OUT, enable TX0 INcall set_ep0_modemov A, [premature_setup] ; return if we received a prematurecmp A, 0 ; SETUPjnz done_control_write

; wait until we get the OUT bytewait_control_write_OUT:

iord EP_A0_Mode ; read mode registerand A, EP0_OUT_RCV ; did we get the OUT packet?jnz control_write_check_OUTiord EP_A0_Mode ; read mode registerand A, EP0_SETUP_RCV ; did we receive premature SETUP?jz wait_control_write_OUT

Page 27: Um agente embutido para conversão de uma interface serial em USB

mov A, 1 ; yes, then set flag and returnmov [premature_setup], Ajmp done_control_write

; we received the OUT byte, make sure the data is validcontrol_write_check_OUT:

iord EP_A0_Counter ; read counter registerand A, DATAVALID ; is data valid?

; data is invalid, and the SIE did not send ACK the host will try to send data; again, so jump back to the beginning

jz Control_writeiord EP_A0_Counter ; read counter registerand A, COUNT_MASKcmp A, 03h ; is count 3 (1 OUT byte + 2 CRC)?jnz control_write_send_stall

mov A, NAKINOUT ; accept SETUP, NAK IN/OUTcall set_ep0_mode

mov A, [premature_setup] ; return if we received a prematurecmp A, 0 ; SETUPjnz done_control_write

; wait until host sends us Status INcontrol_write_wait_IN:

iord EP_A0_Mode ; read mode registerand A, EP0_OUT_RCV ; did we get an OUT?jnz Control_write ; if so, jump back to beginning

; to get new OUT byte

iord EP_A0_Mode ; read mode registerand A, EP0_IN_RCV ; did we get the Status IN?jnz control_write_status_in ; if so, return so that we can; process the OUT byte before no_data_control responds to the Status IN

iord EP_A0_Mode ; read mode registerand A, EP0_SETUP_RCV ; did we get a premature SETUP?jz control_write_wait_IN ; no, then wait for Status INmov A, 1 ; yes, set flag and returnmov [premature_setup], Ajmp done_control_write

control_write_send_stall:; set data_ok flag to false so that calling routine does not use the bad datamov A, 0mov [data_ok], Acall SendStalljmp done_control_write

control_write_status_in:; set data_ok flag to true so that calling routine knows data is goodmov A, 1mov [data_ok], A

done_control_write:ret ; return

;======================================================================;Lookup Tables;Contain the descriptors and the codes for status indicators.;The firmware accesses the information by referencing a specific;table's address as an offset from the control_read_table.;======================================================================

Page 28: Um agente embutido para conversão de uma interface serial em USB

control_read_table:

device_desc_table:db 12h ; Descriptor length (18 bytes)db 01h ; Descriptor type (Device)db 10h,01h ; Complies with USB Spec. Release (0110h = release 1.10)db 00h ; Class code (0)db 00h ; Subclass code (0)db 00h ; Protocol (No specific protocol)db 08h ; Max. packet size for EP0 (8 bytes)db 25h,09h ; Vendor ID (Lakeview Research, 0925h)db 34h,12h ; Product ID (1234)db 01h,00h ; Device release number (0001)

db 01h ; Mfr string descriptor indexdb 00h ; Mfr string descriptor index (None)db 00h ; Product string descriptor indexdb 00h ; Serial Number string descriptor index (None)db 01h ; Number of possible configurations (1)

end_device_desc_table:

config_desc_table:db 09h ; Descriptor length (9 bytes)db 02h ; Descriptor type (Configuration)db 22h,00h ; Total data length (34 bytes)db 01h ; Interface supported (1)db 01h ; Configuration value (1)db 00h ; Index of string descriptor (None)db 80h ; Configuration (Bus powered)db 32h ; Maximum power consumption (100mA)

Interface_Descriptor:db 09h ; Descriptor length (9 bytes)db 04h ; Descriptor type (Interface)db 00h ; Number of interface (0)db 00h ; Alternate setting (0)db 01h ; Number of interface endpoint (1)db 03h ; Class code ()db 00h ; Subclass code ()db 00h ; Protocol code ()db 00h ; Index of string()

Class_Descriptor:db 09h ; Descriptor length (9 bytes)db 21h ; Descriptor type (HID)db 00h,01h ; HID class release number (1.00)db 00h ; Localized country code (None)db 01h ; # of HID class dscrptr to follow (1)db 22h ; Report descriptor type (HID)

; Total length of report descriptordb (end_hid_report_desc_table - hid_report_desc_table),00h

Endpoint_Descriptor:db 07h ; Descriptor length (7 bytes)db 05h ; Descriptor type (Endpoint)db 81h ; Encoded address (Respond to IN, 1 endpoint)db 03h ; Endpoint attribute (Interrupt transfer)db 06h,00h ; Maximum packet size (6 bytes)db 0Ah ; Polling interval (10 ms)

end_config_desc_table:

Page 29: Um agente embutido para conversão de uma interface serial em USB

;----------------------------------------------------------------------;The HID-report descriptor table;----------------------------------------------------------------------

hid_report_desc_table:db 06h, A0h, FFh ; Usage Page (vendor defined) FFA0db 09h, 01h ; Usage (vendor defined)db A1h, 01h ; Collection (Application)db 09h, 02h ; Usage (vendor defined)db A1h, 00h ; Collection (Physical)db 06h, A1h, FFh ; Usage Page (vendor defined)

;The output reportdb 09h, 05h ; usage - vendor defineddb 09h, 06h ; usage - vendor defineddb 15h, 80h ; Logical Minimum (-128)db 25h, 7Fh ; Logical Maximum (127)db 35h, 00h ; Physical Minimum (0)db 45h, FFh ; Physical Maximum (255)db 75h, 08h ; Report Size (8) (bits)db 95h, 0Bh ; Report Count (11) (fields)db 91h, 02h ; Output (Data, Variable, Absolute)

db C0h ; End Collectiondb C0h ; End Collection

end_hid_report_desc_table: