Entendendo o Vídeo Composto B&P
O sinal de vídeo composto utiliza três níveis de tensão: 1V corresponde a branco, 0,3V corresponde a preto e 0V indica um sinal de sincronismo. Tons de cinza podem ser representados por tensões entre 0,3 e 1V.A coisa complica um pouco quando começamos a falar nas temporizações, já que existem vários padrões. O Brasil segue as temporizações de sincronismo do padrão americano, o NTSC. No caso de vídeo branco e preto, o nosso sistema é igual ao americano. Para vídeo colorido o Brasil fez uma mescla entre o padrão NTSC (para ficar compatível com as TV b&p já existentes) e o padrão PAL (que possui uma codificação melhor para cores), criando o PAL-M.Como vamos ficar no b&p, podemos usar informações sobre o sistema americano, como aqui e aqui.
Uma tela é dividida em 525 linhas, que são enviadas em dois frames de 262,5 linhas a uma taxa de 60 frames por segundo. Na TV temos o chamado vídeo entrelaçado, onde um frame envia as linhas pares e o seguinte as linhas ímpares. Vamos trabalhar com vídeo não entrelaçado, enviando sempre as mesmas 262 linhas em cada frame.Destas 262 linhas, apenas as 20 últimas correspondem ao retraço vertical (quando o feixe está se movimentando rapidamente de baixo para cima).
O sinal para as primeiras 242 linhas começa com um pulso de sincronismo horizontal (sinal em 0V) com duração de 4,7 uS. Em seguida temos o "back porch", um período de 5,9 uS onde o sinal deve ser mantido em 0,3V. Chegamos então à parte visível da linha, com 51.5 uS, onde o sinal pode variar ente 0,3 e 1V. Fechando a linha temos o "front porch" onde o sinal deve ser mantido em 0,3V por 1.4uS. Somando todos estes tempos obtemos 63,5 uS. Na prática a nossa imagem deve ocupar da linha 30 até a linha 230, deixando margens apagadas em cima e em baixo.
Durante o retraço vertical o sinal de vídeo corresponde a linhas apagadas "inventidas": 4,7 uS no nível 0,3V seguindo de 58,8 uS no nível 0V.
Hardware
Para obter um sinal preciso é recomendado usar um clock de 20 MHz dividido por um timer de 16 bits. Com isto podemos gerar uma base de tempo de 63,55 dividindo o clock por 1271 (este tempo multiplicado por 262 ficará extremamente próximo dos 60 frames por segundo.A minha ideia inicial era usar um ATtinyx5 (de 8 pinos), porém ele não tem o timer de 16 bits. Optei então por um ATtiny44.
O sinal de vídeo é gerado por duas saídas digitais (PA0 e PA1) ligadas através de diodos a um divisor resistivo. Com isto conseguimos gerar os três níveis de interesse:
- 0V: PA0 e PA em 0
- 0,3V: PA0 em 1 e PA1 em 0
- 1,0V: PA0 e PA em 1
Software
A ideia básica é programar o timer para interromper a cada 63,5 uS; o sinal de vídeo será controlado dentro da rotina de interrupção. O primeiro passo da rotina é gerar o pulso de sincronismo horizontal. Se estivermos fora da região visível podemos encerrar a interrupção, caso contrário vamos gerar o sinal de vídeo conforme os pixels a apresentar (o que será feito em assembler para ter a velocidade e precisão necessárias).Para que o timing seja preciso e a imagem não tenha interferências, as interrupções durante as linhas visíveis deve acontecer quando o processador estiver dormindo. Com isto sobra só uma parte do tempo do "front porch" nas primeiras 242 linhas. Durante o retraço vertical podemos relaxar um pouco; teremos um pouco menos de 20 * (63,5 - 4,7) para gerar o próximo frame (ou seja, cerca de 1,1 ms a cada 16,7 ms).
O ATtiny44 não tem Ram suficiente para guardar uma imagem da tela. O exemplo da Cornell implementa uma resolução de 144 x 200 pontos, o que exige 3600 bytes. Para ficar no simples, vamos construir nossa imagem utilizando somente duas linhas repetidas várias vezes.
Para gerar um pixel, vamos manter PA0 em 1 e posicionar PA1 em 0 (pixel apagado) ou em 1 (pixel aceso). A sequência de instruções assembler gera o sinal correspondente ao bit b do byte contido no registrador R4, supondo que R30 contenha 1:
BST R4,b BLD R30,1 OUT 0x1B,R30 NOP NOP
A primeira instrução (bit store) move o bit b para o flag T. A segunda (bit load) move o flag T para o vbit de R30. A instrução OUT escreve o valor de R30 no registrador PORTA. Os dois NOPs finais são para dar o tempo exato de um pixel.
A macro videobits contem oito vezes esta sequência, para gerar o video correspondente a os oito bits de R4. A rotina byteblast utiliza 18 vezes a macro videobits para gerar o sinal de uma linha. Desta forma é obtida a velocidade e precisão necessárias, ao custo de um código longo (o meu programa ocupou 3262 bytes dos 4K disponíveis).
A rotina de interrupção ficou assim:
- ISR (TIM1_COMPA_vect)
- {
- static uint8_t syncON = VID_0V;
- static uint8_t syncOFF = VID_03V;
- // Gera o pulso de sync
- VID_PORT = syncON;
- linhaAtual++;
- if (linhaAtual == iniSyncV)
- {
- // Sync vertical é invertido
- syncON = VID_03V;
- syncOFF = VID_0V;
- }
- if (linhaAtual == fimSyncV)
- {
- // Voltar ao sync normal
- syncON = VID_0V;
- syncOFF = VID_03V;
- }
- if (linhaAtual == fimFrame)
- {
- // Fim do frame
- linhaAtual = 1;
- }
- _delay_us(2); // Aguarda fim do tempo do pulso
- VID_PORT = syncOFF;
- // Gerar a imagem
- if ((linhaAtual <= ultLinha) && (linhaAtual >= primLinha))
- {
- pontLinha = linhas[(linhaAtual-primLinha) & 3];
- _delay_us(12); // tempinho para centrar a linha
- byteblast();
- }
- }
- int main(void)
- {
- uint8_t i;
- // inicia as e/s digitais
- VID_DDR = VID_PINOS;
- VID_PORT = VID_0V;
- // começar na linha 1
- linhaAtual = 1;
- // prepara as nossas duas linhas
- for (i = 0; i < nBytesLinha; i++)
- {
- linha1[i] = 0xFF;
- linha2[i] = 0x81;
- }
- // inicia o timer1 para interromper a cada linha
- // clock máximo, zera contador e interrompe quando
- // atinge a contagem em OCR1A
- OCR1A = tempoLinha;
- TCCR1B = 0x09;
- TCCR1A = 0x00;
- TIMSK1 = 0x02;
- // Habilitar interrupções e preparar para dormir
- sei();
- set_sleep_mode(SLEEP_MODE_IDLE);
- sleep_enable();
- // O loop abaixo executa uma vez para cada linha
- for(;;)
- {
- // Precisa estar dormindo quando chegar a interrupção
- sleep_cpu();
- if (linhaAtual == (ultLinha+2))
- {
- // Retraço vertical
- // aqui temos um tempinho para gerar a próxima tela
- }
- }
- }
Um comentário:
Ainda nem li o artigo, mas curti o tv :)
Postar um comentário