UFMG - ICEx
DEPARTAMENTO DE CIÊNCIA DA
UNIVERSIDADE FEDERAL DE MINAS GERAIS
C O M P U T A Ç Ã O
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
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.
ÍNDICE
RESUMO _________________________________________________________________________________ 2
1 INTRODUÇÃO _________________________________________________________________________ 4
2 USB – VISÃO GERAL ___________________________________________________________________ 5
2.1
2.2
2.2.1
2.3
2.4
CARACTERÍSTICAS FUNCIONAIS _________________________________________________________
CLASSES USB ________________________________________________________________________
HUMAN INTERFACE DEVICE CLASS (HID) _______________________________________________
TIPOS DE TRANSFERÊNCIAS _____________________________________________________________
REQUISIÇÕES ________________________________________________________________________
5
7
7
8
9
3 ESPECIFICAÇÃO DO SISTEMA __________________________________________________________ 9
3.1
3.2
REQUERIMENTOS DO HOST _____________________________________________________________ 9
REQUERIMENTOS DO DISPOSITIVO ______________________________________________________ 10
4 DESCRIÇÃO DO HARDWARE ___________________________________________________________ 11
5 PROJETO E IMPLEMENTAÇÃO DO SOFTWARE _________________________________________ 12
5.1
5.2
5.3
5.4
DEFINIÇÃO DOS DESCRITORES __________________________________________________________
ROTINAS DE INTERRUPÇÃO NOS ENDPOINTS ______________________________________________
DETECÇÃO E ENUMERAÇÃO DO DISPOSITIVO ______________________________________________
O PROCESSO DE ENVIO E RECEPÇÃO DE DADOS ____________________________________________
12
16
17
18
6 DRIVERS E APLICAÇÕES ______________________________________________________________ 18
7 CONCLUSÕES ________________________________________________________________________ 19
AGRADECIMENTOS _____________________________________________________________________ 19
REFERÊNCIAS BIBLIOGRÁFICAS_________________________________________________________ 19
ANEXO A ________________________________________________________________________________ 20
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
2
Empresas fundadoras do USB Forum: Compaq, IBM, Intel, Microsoft, DEC, NEC e Northern Telecom.
Engetron é uma empresa conceituada especializada no desenvolvimento de no-breaks inteligentes (smart UPSs)
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.
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.
HOST SYSTEM
USB DEVICE
Client Software
(Client Driver)
Function
System Software
Logical Device
(USB Drv + HC Drv)
Host
Controller/Hub
USB Cable
Bus Interface
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 viceversa. 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.
§
§
§
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].
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
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:
§
§
§
§
§
§
3.1
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
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.
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
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.
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.
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
Blength
BDescriptorType
BcdUSB
BDeviceClass
BDeviceSubClass
BDeviceProtocol
BMaxPacketSize0
IdVendor
Offset /
tamanho
(bytes)
0/1
1/1
2/2
4/1
5/1
6/1
7/1
8/2
IdProduct
BcdDevice
IManufacturer
10/2
12/2
14/1
IProduct
15/1
ISerialNumber
bNumConfigurations
16/1
17/1
Descrição
Valor Assumido
Tamanho deste descriptor (em bytes)
Tipo do descriptor (device descriptor)
Versão da especificação de USB utilizada
Classe do dispositivo3
Subclasse do dispositivo3
Código do protocolo qualificado pela subclasse3
Tamanho máximo do pacote para o endpoint0
Identificação do vendedor
0x12
0x01
0x0110
0x00
0x00
0x00
0x08
0x0925
(Lakeview Research)
Identificação do dispositivo
0x3412 (exemplo)
Versão do dispositivo
0x01
Índice para o string descriptor contendo a identificação do 0x00 (nenhum)4
fabricante
Índice para o string descriptor contendo a identificação do 0x00 (nenhum) 4
produto
Índice para o string descriptor contendo o número de série 0x00 (nenhum) 4
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.
Configuration Descriptor (9 bytes)
Campo
bLength
bDescriptorType
wTotalLength
Offset /
tamanho
(bytes)
0/1
1/1
2/2
bNumInterfaces
bConfigurationValue
iConfiguration
bmAttributes
4/1
5/1
6/1
7/1
MaxPower
8/1
Descrição
Valor Assumido
Tamanho deste descriptor (em bytes)
Tipo do descriptor (configuration descriptor)
Tamanho
total
de
todos
descriptors
(configuration+interface+endpoint+HID)
Número de interfaces (conversor)
Valor utilizado para selecionar essa configuração
Índice do string descriptor que descreve esta configuração
Características da configuração (em relação à energia) Bus
Powered
Quantidade máxima de energia consumida do barramento
pelo dispositivo (expressa em 2mA)
0x09
0x02
0x22 (34 bytes)
0x01
0x01
0x00 (nenhum)4
0x80
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
bLength
bDescriptorType
bInterfaceNumber
Offset /
tamanho
(bytes)
0/1
1/1
2/1
bAlternateSetting
3/1
bNumEndpoints
4/1
bInterfaceClass
bInterfaceSubClass
bInterfaceProtocol
Interface
5/1
6/1
7/1
8/1
Descrição
Valor Assumido
Tamanho deste descriptor (em bytes)
Tipo do descriptor (interface descriptor)
Número da interface que identifica o índice no array de
interfaces concorrentes para esta configuração
Valor utilizado para selecionar configurações alternativas
para esta interface
Número de endpoints utilizados por esta interface
(excluindo o endpoint0) - 1 Interrupt IN
Código de classe para esta interface
Código de subclasse
Código do protocolo (0 = none)
Índice do string descriptor que descreve esta interface
0x09
0x04
0x00
0x00
0x01
0x03 (HID)
0x00 (nenhum)5
0x00 (nenhum)5
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.
Endpoint Descriptor (7 bytes) - Endpoint1 - Interrupt IN Endpoint
Campo
bLength
bDescriptorType
bEndpointAddress
Offset /
tamanho
(bytes)
0/1
1/1
2/1
bmAttributes
3/1
wMaxPacketSize
4/1
bInterval
6/1
Descrição
Valor Assumido
Tamanho deste descriptor (em bytes)
Tipo do descriptor (endpoint descriptor)
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)
Atributos do endpoint quando configurado utilizando o
bConfigurationValue
bit 0..1 Tipo de transferência
Tamanho máximo de pacote que o endpoint é capaz de
enviar e receber (de 8 a 64bytes)
Intervalo para verificação se o endpoint possui dado a ser
transferido (em ms)
0x07
0x05
0x81
0x03
0x0008
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
bLength
bDescriptorType
bcdHID
bCountryCode
Offset /
tamanho
(bytes)
0/1
1/1
2/2
4/1
bNumDescriptors
bDescriptorType
wDescriptorLength
5/1
6/1
7/2
Descrição
Valor Assumido
Tamanho deste descriptor (em bytes)
Tipo do descriptor (hid (class) descriptor)
Versão da especificação da classe HID (em BCD)
País de origem do hardware
00 = não suportado
Número de descriptors para a classe HID
Tipo do descritor adicional - Report descriptor
Tamanho total do report descriptor
0x09
0x21
0x0110
0x006
0x01
0x22
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.
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
db 09h, 01h
db A1h, 01h
db 09h, 02h
db A1h, 00h
db 06h, A1h, FFh
;
;
;
;
;
;
;The output report
db 09h, 05h
db 09h, 06h
db 15h, 80h
db 25h, 7Fh
db 35h, 00h
db 45h, FFh
db 75h, 08h
db 95h, 0Bh
db 91h, 02h
;
;
;
;
;
;
;
;
;
db C0h
;
db C0h
;
Usage Page (vendor defined) FFA0
Usage (vendor defined)
Collection (Application)
Usage (vendor defined)
Collection (Physical)
Usage Page (vendor defined)
usage - vendor defined
usage - vendor defined
Logical Minimum (-128)
Logical Maximum (127)
Physical Minimum (0)
Physical Maximum (255)
Report Size (8) (bits)
Report Count (11) (fields)
Output (Data, Variable, Absolute)
End Collection
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.
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.
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.
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
ANEXO A
;======================================================================
;interrupt vectors
;======================================================================
ORG 00h
jmp
jmp
jmp
Reset
USB_Bus_Reset_ISR
Serial_ISR
jmp
jmp
jmp
jmp
jmp
jmp
jmp
jmp
jmp
jmp
One_mSec_ISR
USB_EP0_ISR
USB_EP1_ISR
DoNothing_ISR
Reset
DoNothing_ISR
DoNothing_ISR
DoNothing_ISR
GPIO_ISR
DoNothing_ISR
ORG
;
;
;
;
;
;
;
;
;
;
;
;
;
;
Reset vector; begin here after a reset.
USB bus reset
Used to implement serial receive/transmit
bit timing loops – declared on serial.asm
1024-millisecond interrupt
endpoint 0 interrupt
endpoint 1 interrupt
endpoint 2 interrupt
reserved interrupt
reserved interrupt
reserved interrupt
DAC interrupt
GPIO interrupt – declared on serial.asm
reserved interrupt
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 stack
iowr Watchdog
; clear watchdog timer
iord Status_Control
; clear the Bus Reset bit in the
and A, ~USBReset
; Status & Control register
iowr Status_Control
mov A, TIMER_MASK
; enable one msec timer interrupt
iowr Global_Interrupt
mov A, ENDPOINT_ZERO_ONLY
; enable endpoint zero interrupt
iowr Endpoint_Interrupt
; disable endpoint one&two interrupt
iord EP_A0_Mode
; unlock Mode register
mov A, SETUP
; enable endpoint zero setup
iowr EP_A0_Mode
mov A, ADDRESS_ENABLE_BIT
iowr USB_Device_Address
mov A, DISABLED
iowr EP_A1_Mode
iowr EP_A2_Mode
; enable endpoint zero response
iowr Watchdog
call Init
iowr Watchdog
; clear Watchdog timer
; perform initialization on variables
; clear Watchdog timer again
; disable endpoint one
; disable endpoint two
pop A
reti
; restore accumulator from stack
; 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 A
iowr 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
cmp
jnz
inc
jmp
A, [wakeup_flag]
A, 01h
not_sending_wakeup
[wakeup_counter]
One_mSec_end
not_sending_wakeup:
dec [4ms_counter]
jnz not_main
mov A, 4
mov [4ms_counter], A
mov A, [idle_period_counter]
cmp A, FFh
jz not_main
inc [idle_period_counter]
;check if we are in a remote
;wakeup process
;increment wakeup counter
;indicate if the 10msecs of wakeup
;have passed
;decrement the 4msecs counter
;if not zero, then check bus activity status
;if zero, re-initialize to 4msecs
;since 4msecs has passed, increment
;the idle_period_counter
;which has a 4msecs resolution
;if the idle_period_counter reaches FFh,
;don't increment
;to prevent rollover
not_main:
;Check for bus activity.
iord USB_Status_Control
;check if there is no bus activity
and A, 08h
;bit 3 = bus activity
cmp 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_Control
and A, 0F7h
iowr USB_Status_Control
;and clear the suspend counter.
mov A, 0h
mov [suspend_counter], A
jmp 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
;If yes, put the chip in Suspend mode.
;Clear the Suspend counter.
mov A, 0h
mov [suspend_counter], A
mov A, [remote_wakeup_status]
cmp a, ENABLE_REMOTE_WAKEUP
jnz Suspend_controller
; is remote wakeup feature enabled?
;
; if not, just do a normal suspend
mov A, GPIO_ONLY_MASK
iowr Global_Interrupt
ei
; enable GPIO interrupts
; enable interrupts
Suspend_controller:
mov A, RESISTIVE_NEG
; all ports resistive neg so that
iowr 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_Control
or A, 08h
iowr 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 configuration
iowr GPIO_Config
Suspend_end:
;Is endpoint 1 enabled?
iord EP_A1_Mode
and A, 0Fh
cmp 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 nothing
jnz 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 interrupt
and A, ~ENDPOINT_ONE
iowr Endpoint_Interrupt
;Copy values from RAM to Endpoint 1's buffer for transmitting to the host.
mov A, [Data_Byte0]
mov [Endpoint1_Byte0], A
mov A, [Data_Byte1]
mov [Endpoint1_Byte1], A
mov A, [Data_Byte2]
mov [Endpoint1_Byte2], A
mov A, [Data_Byte3]
mov [Endpoint1_Byte3], A
mov
mov
mov
mov
mov
mov
mov
mov
A, [Data_Byte4]
[Endpoint1_Byte4],
A, [Data_Byte5]
[Endpoint1_Byte5],
A, [Data_Byte6]
[Endpoint1_Byte6],
A, [Data_Byte7]
[Endpoint1_Byte7],
A
A
A
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 bytes
iowr [EP_A1_Counter]
Select:
;Enable Endpoint 1 interrupts.
iord Endpoint_Interrupt
or A, ENDPOINT_ONE
iowr Endpoint_Interrupt
One_mSec_end:
pop A
reti
; 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, 01h
mov [start_send], A
no_set:
;Clear the watchdog timer.
;This has to be done at least once every 8 milliseconds!
iowr Watchdog
iord 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.
iowr
call
mov
mov
mov
inc
iowr
call
iowr
call
mov
mov
mov
inc
iowr
call
iowr
call
mov
mov
mov
inc
iowr
call
iowr
call
mov
mov
mov
inc
iowr
call
iowr
call
mov
mov
mov
inc
iowr
call
iowr
call
mov
mov
mov
inc
iowr
call
iowr
call
mov
mov
mov
inc
iowr
call
iowr
call
mov
mov
mov
inc
iowr
Watchdog
delay1
X, [txBufPtr]
A, [X + txBuf]
[txData], A
[txBufPtr]
Watchdog
txRoutine
Watchdog
delay1
X, [txBufPtr]
A, [X + txBuf]
[txData], A
[txBufPtr]
Watchdog
txRoutine
Watchdog
delay1
X, [txBufPtr]
A, [X + txBuf]
[txData], A
[txBufPtr]
Watchdog
txRoutine
Watchdog
delay1
X, [txBufPtr]
A, [X + txBuf]
[txData], A
[txBufPtr]
Watchdog
txRoutine
Watchdog
delay1
X, [txBufPtr]
A, [X + txBuf]
[txData], A
[txBufPtr]
Watchdog
txRoutine
Watchdog
delay1
X, [txBufPtr]
A, [X + txBuf]
[txData], A
[txBufPtr]
Watchdog
txRoutine
Watchdog
delay1
X, [txBufPtr]
A, [X + txBuf]
[txData], A
[txBufPtr]
Watchdog
txRoutine
Watchdog
delay1
X, [txBufPtr]
A, [X + txBuf]
[txData], A
[txBufPtr]
Watchdog
; Ping the watch dog timer.
; Give the serial device some time.
; Set the index.
; Load Command Byte One
; Put it in the transmit register.
; Point to the next byte.
; clear watchdog timer
; Go transmit the byte.
; clear watchdog timer
; Give the serial device some time.
; Set the index.
; Load Command Byte Two
; Put it in the transmit register.
; Point to the next byte.
; clear watchdog timer
; Go transmit the byte.
; clear watchdog timer
; Give the serial device some time.
; Set the index.
; Load Command Byte Three
; Put it in the transmit register.
; Point to the next byte.
; clear watchdog timer
; Go transmit the byte.
; clear watchdog timer
; Set the index.
; Load Command Byte Four.
; Put it in the transmit register.
; Point to the next byte.
; Ping the watch dog timer.
; Go transmit the byte.
; Ping the watch dog timer.
; Give the serial device some time.
; Set the index.
; Load Command Byte Five
; Put it in the transmit register.
; Point to the next byte.
; clear watchdog timer
; Go transmit the byte.
; clear watchdog timer
; Give the serial device some time.
; Set the index.
; Load Command Byte Six
; Put it in the transmit register.
; Point to the next byte.
; clear watchdog timer
; Go transmit the byte.
; clear watchdog timer
; Give the serial device some time.
; Set the index.
; Load Command Byte Seven
; Put it in the transmit register.
; Point to the next byte.
; clear watchdog timer
; Go transmit the byte.
; clear watchdog timer
; Give the serial device some time
; Set the index.
; Load Command Byte Eight.
; Put it in the transmit register.
; Point to the next byte.
; Ping the watch dog timer.
call
iowr
call
mov
mov
mov
inc
iowr
call
iowr
call
mov
mov
mov
inc
iowr
call
iowr
call
mov
mov
mov
inc
iowr
call
iowr
call
mov
mov
mov
inc
iowr
call
iowr
call
mov
mov
mov
inc
iowr
call
iowr
call
mov
mov
mov
inc
iowr
call
iowr
call
mov
mov
mov
inc
call
txRoutine
Watchdog
delay1
X, [txBufPtr]
A, [X + txBuf]
[txData], A
[txBufPtr]
Watchdog
txRoutine
Watchdog
delay1
X, [txBufPtr]
A, [X + txBuf]
[txData], A
[txBufPtr]
Watchdog
txRoutine
Watchdog
delay1
X, [txBufPtr]
A, [X + txBuf]
[txData], A
[txBufPtr]
Watchdog
txRoutine
Watchdog
delay1
X, [txBufPtr]
A, [X + txBuf]
[txData], A
[txBufPtr]
Watchdog
txRoutine
Watchdog
delay1
X, [txBufPtr]
A, [X + txBuf]
[txData], A
[txBufPtr]
Watchdog
txRoutine
Watchdog
delay1
X, [txBufPtr]
A, [X + txBuf]
[txData], A
[txBufPtr]
Watchdog
txRoutine
Watchdog
delay1
X, [txBufPtr]
A, [X + txBuf]
[txData], A
[txBufPtr]
txRoutine
mov
iowr
iowr
ret
A, 80h
Port0_Interrupt
Watchdog
; Go transmit the byte.
; Ping the watch dog timer.
; Give the serial device some time.
; Set the index.
; Load Command Byte Nine
; Put it in the transmit register.
; Point to the next byte.
; clear watchdog timer
; Go transmit the byte.
; clear watchdog timer
; Give the serial device some time.
; Set the index.
; Load Command Byte Ten
; Put it in the transmit register.
; Point to the next byte.
; clear watchdog timer
; Go transmit the byte.
; clear watchdog timer
; Give the serial device some time.
; Set the index.
; Load Command Byte Eleven
; Put it in the transmit register.
; Point to the next byte.
; clear watchdog timer
; Go transmit the byte.
; clear watchdog timer
; Give the serial device some time
; Set the index.
; Load Command Byte Twelve
; Put it in the transmit register.
; Point to the next byte.
; Ping the watch dog timer.
; Go transmit the byte.
; Ping the watch dog timer.
; Give the serial device some time.
; Set the index.
; Load Command Byte Thirteen
; Put it in the transmit register.
; Point to the next byte.
; clear watchdog timer
; Go transmit the byte.
; clear watchdog timer
; Give the serial device some time.
; Set the index.
; Load Command Byte Fourteen
; Put it in the transmit register.
; Point to the next byte.
; clear watchdog timer
; Go transmit the byte.
; clear watchdog timer
; Give the serial device some time.
; Set the index.
; Load Command Byte Fifteen
; Put it in the transmit register.
; Point to the next byte.
; Go transmit the byte.
;
;
;
;
Enable the Port 0 Bit 7
GPIO interrupt.
clear watchdog timer
Return to sender.
;========================================================================
;
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 A
mov A, STATUSINONLY
; enable TX0 IN
call set_ep0_mode
mov A, [premature_setup]
; return if we received a premature
cmp A, 0
; SETUP
jnz done_nodata_control
; wait for the zero-length transfer to complete
wait_nodata_sent:
iord EP_A0_Mode
; read mode register
and A, EP0_SETUP_RCV
; did we receive premature SETUP?
jz check_nodata_ack
mov A, 1
; yes, then set flag and return
mov [premature_setup], A
jmp done_nodata_control
check_nodata_ack:
iord EP_A0_Mode
and A, ACK_BIT
jz wait_nodata_sent
; read mode register
; wait for ACK bit high
; clear ACK bit
mov A, SETUP
call set_ep0_mode
; NAK IN and OUT packets
done_nodata_control:
pop A
ret
; 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
call set_ep0_mode
mov A, [premature_setup]
cmp A, 0
jnz done_control_write
; wait until we get the OUT byte
wait_control_write_OUT:
iord EP_A0_Mode
and A, EP0_OUT_RCV
jnz control_write_check_OUT
iord EP_A0_Mode
and A, EP0_SETUP_RCV
jz wait_control_write_OUT
; accept OUT, enable TX0 IN
; return if we received a premature
; SETUP
; read mode register
; did we get the OUT packet?
; read mode register
; did we receive premature SETUP?
mov A, 1
mov [premature_setup], A
jmp done_control_write
; yes, then set flag and return
; we received the OUT byte, make sure the data is valid
control_write_check_OUT:
iord EP_A0_Counter
; read counter register
and 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_write
iord EP_A0_Counter
; read counter register
and A, COUNT_MASK
cmp A, 03h
; is count 3 (1 OUT byte + 2 CRC)?
jnz control_write_send_stall
mov A, NAKINOUT
call set_ep0_mode
; accept SETUP, NAK IN/OUT
mov A, [premature_setup]
cmp A, 0
jnz done_control_write
; return if we received a premature
; SETUP
; wait until host sends us Status IN
control_write_wait_IN:
iord EP_A0_Mode
and A, EP0_OUT_RCV
jnz Control_write
;
;
;
;
read mode register
did we get an OUT?
if so, jump back to beginning
to get new OUT byte
iord EP_A0_Mode
; read mode register
and 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
and A, EP0_SETUP_RCV
jz control_write_wait_IN
mov A, 1
mov [premature_setup], A
jmp done_control_write
;
;
;
;
read mode register
did we get a premature SETUP?
no, then wait for Status IN
yes, set flag and return
control_write_send_stall:
; set data_ok flag to false so that calling routine does not use the bad data
mov A, 0
mov [data_ok], A
call SendStall
jmp done_control_write
control_write_status_in:
; set data_ok flag to true so that calling routine knows data is good
mov A, 1
mov [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.
;======================================================================
control_read_table:
device_desc_table:
db 12h
db 01h
db 10h,01h
db 00h
db 00h
db 00h
db 08h
db 25h,09h
db 34h,12h
db 01h,00h
;
;
;
;
;
;
;
;
;
;
db 01h
;
db 00h
;
db 00h
;
db 00h
;
db 01h
;
end_device_desc_table:
config_desc_table:
db 09h
db 02h
db 22h,00h
db 01h
db 01h
db 00h
db 80h
db 32h
;
;
;
;
;
;
;
;
Interface_Descriptor:
db 09h
;
db 04h
;
db 00h
;
db 00h
;
db 01h
;
db 03h
;
db 00h
;
db 00h
;
db 00h
;
Descriptor length (18 bytes)
Descriptor type (Device)
Complies with USB Spec. Release (0110h = release 1.10)
Class code (0)
Subclass code (0)
Protocol (No specific protocol)
Max. packet size for EP0 (8 bytes)
Vendor ID (Lakeview Research, 0925h)
Product ID (1234)
Device release number (0001)
Mfr string descriptor index
Mfr string descriptor index (None)
Product string descriptor index
Serial Number string descriptor index (None)
Number of possible configurations (1)
Descriptor length (9 bytes)
Descriptor type (Configuration)
Total data length (34 bytes)
Interface supported (1)
Configuration value (1)
Index of string descriptor (None)
Configuration (Bus powered)
Maximum power consumption (100mA)
Descriptor length (9 bytes)
Descriptor type (Interface)
Number of interface (0)
Alternate setting (0)
Number of interface endpoint (1)
Class code ()
Subclass code ()
Protocol code ()
Index of string()
Class_Descriptor:
db 09h
db 21h
db 00h,01h
db 00h
db 01h
db 22h
; Descriptor length (9 bytes)
; Descriptor type (HID)
; HID class release number (1.00)
; Localized country code (None)
; # of HID class dscrptr to follow (1)
; Report descriptor type (HID)
; Total length of report descriptor
db (end_hid_report_desc_table - hid_report_desc_table),00h
Endpoint_Descriptor:
db 07h
db 05h
db 81h
db 03h
db 06h,00h
db 0Ah
;
;
;
;
;
;
end_config_desc_table:
Descriptor length (7 bytes)
Descriptor type (Endpoint)
Encoded address (Respond to IN, 1 endpoint)
Endpoint attribute (Interrupt transfer)
Maximum packet size (6 bytes)
Polling interval (10 ms)
;---------------------------------------------------------------------;The HID-report descriptor table
;---------------------------------------------------------------------hid_report_desc_table:
db 06h, A0h, FFh
db 09h, 01h
db A1h, 01h
db 09h, 02h
db A1h, 00h
db 06h, A1h, FFh
;
;
;
;
;
;
;The output report
db 09h, 05h
db 09h, 06h
db 15h, 80h
db 25h, 7Fh
db 35h, 00h
db 45h, FFh
db 75h, 08h
db 95h, 0Bh
db 91h, 02h
;
;
;
;
;
;
;
;
;
db C0h
db C0h
end_hid_report_desc_table:
;
;
Usage Page (vendor defined) FFA0
Usage (vendor defined)
Collection (Application)
Usage (vendor defined)
Collection (Physical)
Usage Page (vendor defined)
usage - vendor defined
usage - vendor defined
Logical Minimum (-128)
Logical Maximum (127)
Physical Minimum (0)
Physical Maximum (255)
Report Size (8) (bits)
Report Count (11) (fields)
Output (Data, Variable, Absolute)
End Collection
End Collection
Download

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