Temporizador
Contadores i8254, palavra de controlo, read-back, divisor e intervalos em ticks.
Uma aplicação que desenha a cada dois ticks precisa de uma fonte regular de eventos. No PC, a saída do contador 0 do i8254 está ligada à IRQ0. Os três contadores de 16 bits recebem impulsos de um relógio de entrada; o divisor e o modo determinam a saída de cada um.
Relógio, divisor e modos
Nos exemplos usamos a frequência nominal de entrada . No código do laboratório usa a constante TIMER_FREQ do cabeçalho de apoio, sem a repetir em várias funções.
Nos modos periódicos, um divisor determina a frequência:
Para 1000 Hz, a política de quociente inteiro escolhe . Dividir o relógio por 1193 produz cerca de 1000,15 Hz. O pedido não é a frequência exata da saída. Em contagem binária, o valor carregado 0x0000 representa 65536, não um divisor zero. Os modos 2 e 3 exigem um divisor de pelo menos 2. Em BCD, o contador representa quatro algarismos decimais e o intervalo é diferente; não interpretes um valor BCD como um contador binário de 16 bits.
| Modo | Comportamento da saída |
|---|---|
| 0, terminal count | A saída sobe quando a contagem termina e conserva esse nível |
| 1, one-shot | Um disparo externo inicia um impulso, que pode ser novamente disparado |
| 2, rate generator | Um impulso baixo de um ciclo por período, com recarga automática |
| 3, square wave | Saída alta e baixa durante partes aproximadamente iguais do período |
| 4, software strobe | Impulso no fim da contagem iniciada por software |
| 5, hardware strobe | Impulso no fim da contagem disparada externamente |
No modo 3, um divisor par dá metades iguais. Com divisor ímpar, a fase alta dura um ciclo de entrada mais do que a baixa, por isso não contes os dois flancos da onda como duas interrupções por período.
Palavra de controlo
As portas dos contadores são 0x40, 0x41 e 0x42; a porta de controlo é 0x43. A palavra de controlo normal tem estes campos:
| Bits | Campo |
|---|---|
| 7 a 6 | Contador: 00 para 0, 01 para 1, 10 para 2 |
| 5 a 4 | Acesso: 01 só LSB, 10 só MSB, 11 LSB seguido de MSB |
| 3 a 1 | Modo de contagem |
| 0 | Base: 0 binária, 1 BCD |
Por exemplo, contador 2, LSB seguido de MSB, modo 3, binário:
Para carregar :
| Passo | Porta | Valor | Efeito |
|---|---|---|---|
| 1 | 0x43 | 0xB6 | Selecionar contador 2, LSB/MSB, modo 3 binário |
| 2 | 0x42 | 0xD2 | Escrever os oito bits inferiores |
| 3 | 0x42 | 0x04 | Escrever os oito bits superiores |
As duas escritas usam a mesma porta, mas carregam partes diferentes. Inverter os bytes daria 0xD204, ou 53764. Se esse divisor fosse usado no contador 0, produziria cerca de 22,19 Hz em vez da frequência esperada para 1234.
Read-back e estado
Para conhecer a configuração, escreve um read-back command em 0x43 e lê o estado na porta do contador escolhido. Os bits 7 e 6 são 11 para distinguir este comando de uma palavra de controlo normal.
| Bit | Valor relevante no read-back |
|---|---|
| 5 | 0 para capturar a contagem, 1 para não a capturar |
| 4 | 0 para capturar o estado, 1 para não o capturar |
| 3 | 1 seleciona o contador 2 |
| 2 | 1 seleciona o contador 1 |
| 1 | 1 seleciona o contador 0 |
| 0 | Reservado, escreve 0 |
COUNT e STATUS são seleções ativas a 0. Para obter só o estado do contador 0, a palavra é 0xE2: 0xC0 identifica read-back, 0x20 evita a contagem e 0x02 seleciona o contador. Depois, lê 0x40 uma vez.
No byte de estado, o bit 7 representa OUT e o bit 6 é Null Count, que indica se o novo valor ainda não foi carregado no elemento de contagem. Os bits 5 a 0 repetem acesso, modo e base. Não interpretes os bits 7 e 6 do estado como seleção do contador.
Extrai o modo com (status >> 1) & 7. Os códigos 6 e 7 são alternativas de 2 e 3; normaliza-os subtraindo 4. Quando o comando captura estado e contagem, a leitura da porta do contador devolve primeiro o byte de estado e depois os bytes da contagem, pela ordem de acesso programada. O latch permite obter um valor coerente sem juntar bytes de contagens diferentes.
Escolher a frequência
Para pedir 60 Hz, o divisor ideal é:
Como o divisor é inteiro, uma política habitual no laboratório usa o quociente inteiro, . A frequência real é:
Carregam-se LSB 0xAE e MSB 0x4D. O período é cerca de 16,66636 ms. Uma contagem de 216000 ticks, interpretada como uma hora a 60 Hz, termina cerca de 0,0664 s antes de uma hora real neste modelo de relógio ideal.
A escolha por quociente inteiro e a escolha por arredondamento ao inteiro mais próximo são políticas diferentes. Com 100 Hz, o quociente dá 11931 e cerca de 100,006873 Hz; o arredondamento dá 11932 e cerca de 99,998491 Hz. Declara qual estás a usar antes de comparar resultados.
A simulação usa quociente inteiro e contagem binária em modo 3. Altera a frequência para observar o divisor, os bytes e a diferença entre pedido e resultado. Não modela tolerância do oscilador, atrasos do Minix nem perda de notificações.
Antes de converter para uint16_t, verifica a frequência e o quociente. Um pedido de 18 Hz exige um divisor maior do que 65536 e não cabe no intervalo binário. Converter primeiro pode produzir um divisor pequeno e uma frequência inesperada. Para usar 65536, conserva esse valor num tipo mais largo durante as contas e codifica-o como dois bytes zero.
Preservar a configuração
Se a função só altera a frequência, conserva os quatro bits inferiores do estado, que contêm modo e base. A palavra normal reúne seleção do contador, acesso LSB/MSB e status & 0x0F:
uint8_t control = (uint8_t)((timer << 6) | 0x30 | (status & 0x0F));
Este é um excerto de construção da palavra, com timer previamente validado entre 0 e 2. Ler a configuração, construir a palavra e carregar o divisor são operações com possíveis falhas de I/O. Se a base for BCD, uma rotina que só calcula divisores binários deve rejeitá-la ou tratá-la explicitamente.
Alterar timer 0 também afeta o Minix: o sistema assume a frequência que programou para medir tempo. Se passares de 60 para 30 Hz sem mudar esse pressuposto, o relógio do sistema avança cerca de metade do tempo real. Repõe a configuração exigida pelo ambiente ao terminar a experiência.
Ticks, intervalos e overflow
Com frequência , ticks representam aproximadamente segundos. Entre os contadores 120 e 453, passaram 333 ticks, ou 5,55 s a 60 Hz. A resolução é um tick; o resultado não mede frações de tick.
Para um contador sem sinal de 32 bits, a subtração também é feita módulo . Se start = 0xFFFFFFFE e now = 3, now - start dá 5 em uint32_t, apesar da passagem por zero. O cálculo é útil desde que o intervalo real tenha menos de uma volta completa do contador. Não funciona se já passaram uma ou mais voltas sem que as tenhas contabilizado.
Para decidir timeouts por comparação de diferenças, mantém todos os valores com o mesmo tipo sem sinal e uma duração dentro do intervalo suportado. A 60 Hz, uma volta de 32 bits dura cerca de 828,5 dias. Um contador com sinal não oferece a mesma regra: o overflow com sinal é comportamento indefinido.
Finalmente, distinguir frequência do hardware e frequência de desenho evita erros de animação. Um timer a 60 Hz pode gerar um desenho a cada dois ticks, ou 30 frames/s. A lógica pode receber teclas entre frames; não a bloqueies à espera de um desenho.
Exercícios
Construir uma palavra de controlo
Configura contador 1, LSB seguido de MSB, modo 2 e contagem binária. Que palavra escreves em 0x43? Responde em decimal.
Pista
Solução
0x40 | 0x30 | 0x04 | 0x00 = 0x74, ou 116. O bit de BCD permanece a 0. Esta é uma palavra normal, não um comando read-back.
Erros frequentes
Colocar o modo diretamente sem o deslocar ou escolher acesso MSB seguido de LSB, que não é o código 11.
Calcular os bytes do divisor
Com relógio de 1193182 Hz, pede 100 Hz pela política de quociente inteiro. Calcula divisor, LSB e MSB e diz se a frequência real é acima ou abaixo de 100 Hz.
Pista
Solução
O divisor é floor(1193182/100) = 11931. Escreve 0x9B e depois 0x2E no contador. Como 11931 é menor do que o divisor ideal 11931,82, o quociente real é maior: cerca de 100,006873 Hz.
Confere a tua resposta:
- Usei quociente inteiro 11931.
- Separei LSB 0x9B e MSB 0x2E.
- Distingui frequência pedida e real.
Erros frequentes
Arredondar para 11932 quando o enunciado pede quociente inteiro ou inverter a ordem dos bytes.
Medir através da passagem por zero
start é uint32_t com valor 0xFFFFFFFC e now é
uint32_t com valor 3. Houve menos de uma volta completa do
contador. Quanto vale now - start em
uint32_t?
Pista
Solução
Há sete incrementos do início ao fim. A diferença calculada módulo 2 elevado a 32 é 7. O resultado exige que o intervalo real não inclua uma volta adicional inteira.
Erros frequentes
Concluir que toda a passagem por zero invalida a subtração ou aplicar a mesma regra ao overflow de inteiros com sinal.
Separar comando e byte de estado
Queres ler só o estado do contador 2. Dá o comando read-back, a porta que lês e interpreta acesso, modo e base se receberes 0xBE. Normaliza o modo.
Pista
Selecionar contador 2 usa bit 3 no read-back; COUNT e STATUS são ativos a zero.
Mais uma pista
0xBE = 10111110 em binário. Os bits superiores do estado são OUT e Null Count.
Solução
O comando é 0xC0 | 0x20 | 0x08 = 0xE8, escrito em 0x43. Lês 0x42. O estado 0xBE tem acesso 11, LSB seguido de MSB; modo 111, código 7 que normaliza para modo 3; base binária, bit 0 a zero. OUT está a 1 e Null Count a 0. Os bits 7 e 6 não selecionam o contador neste byte.
Confere a tua resposta:
- Construí 0xE8 e li 0x42.
- Extraí acesso 11 e base binária.
- Normalizei modo 7 para 3.
Erros frequentes
Usar 0xA0 como seleção num read-back ou interpretar código 7 como um sétimo modo independente.
Rejeitar uma frequência impossível
Pedem 18 Hz ao temporizador binário. O quociente inteiro é 66287. Que deve
fazer a função antes de converter para uint16_t?
Pista
Solução
Deve rejeitar o pedido. 66287 não cabe em 16 bits nem como 65536 codificado
em dois bytes zero, porque excede o intervalo. Converter para
uint16_t antes de validar daria um divisor pequeno e uma
frequência inesperada.
Confere a tua resposta:
- Calculei o divisor antes de converter.
- Rejeitei 18 Hz por exceder 65536.
- Não truncuei para um divisor pequeno.
Erros frequentes
Truncar o divisor ou programar dois bytes zero para um valor que não é 65536.