Aula 9: Estudo Guiado

Objetivos

  1. Programar um contador microprocessado.

Contextualização

Até agora, fizemos o projeto do hardware do nosso computador. Agora, vamos iniciar a confecção do programa do contador.

Para organizar o trabalho, podemos dividir o programa em duas etapas distintas:

Não é a toa que os dois procedimentos encontrados na interface de programação do Arduino tenham os nomes de Setup e Loop.

IDE do Arduino
IDE do Arduino



Breve Parênteses

O mapa de memória é uma referência para a função e característica das posições, ou blocos, de memória. Ele é útil durante a programação para nos lembrar os locais onde estão localizados os nossos periféricos.

Mapa de Memória
Endereço em Decimal Periférico Largura dos Dados Tipo de Acesso Bloco (Página) de Memória
0 ~ 63 RAM 8 bits Leitura/Escrita 0
64 ~ 127 Reservado 1
128 ~ 191 Reservado 2
192 ~ 255 Reservado 3
256 LEDR0 ~ LEDR7 8 bits Escrita 4
257 LEDR8 1 bit Escrita 4
258 LEDR9 1 bit Escrita 4
259 ~ 287 Reservado 4
288 HEX0 4 bits Escrita 4
289 HEX1 4 bits Escrita 4
290 HEX2 4 bits Escrita 4
291 HEX3 4 bits Escrita 4
292 HEX4 4 bits Escrita 4
293 HEX5 4 bits Escrita 4
294 ~ 319 Reservado 4
320 SW0 ~ SW7 8 bits Leitura 5
321 SW8 1 bit Leitura 5
322 SW9 1 bit Leitura 5
323 ~ 351 Reservado 5
352 KEY0 1 bit Leitura 5
353 KEY1 1 bit Leitura 5
354 KEY2 1 bit Leitura 5
355 KEY3 1 bit Leitura 5
356 FPGA_RESET 1 bit Leitura 5
357 ~ 383 Reservado 5
384 ~ 447 Reservado 6
448 ~ 509 Reservado 7
510 Limpa Leitura KEY1 Escrita 7
511 Limpa Leitura KEY0 Escrita 7

O mapa de memória é uma fonte valiosa de consulta, pois resume todo o endereçamento implementado no computador.


A Inicialização do Computador (ou Setup)

Ao ligar o contador, precisaremos apagar os LEDs e escrever o valor zero nos displays de sete segmentos. Isso pode ser feito através de uma sequência de carga de imediato seguida do armazenamento nas devidas posições de memória.

Zerando os displays de sete segmentos
Linha Instrução Observação
0 LDI $0 Carrega o acumulador com o valor 0
1 STA @288 Armazena o valor do acumulador em HEX0
2 STA @289 Armazena o valor do acumulador em HEX1
STA @293 Armazena o valor do acumulador em HEX5
Apagando os LEDs
Linha Instrução Observação
0 LDI $0 Carrega o acumulador com o valor 0
1 STA @256 Armazena o valor do bit0 do acumulador no LDR0 ~ LEDR7
2 STA @257 Armazena o valor do bit0 do acumulador no LDR8
3 STA @258 Armazena o valor do bit0 do acumulador no LDR9

Além disso, devemos inicializar as posições de memória de todas as variáveis que utilizaremos. Para tanto, precisamos definir as variáveis necessárias para o funcionamento do programa. Como exemplo, utilizaremos os valores a serem escritos nos displays de sete segmentos. Para simplificar o exemplo, consideraremos que a contagem está limitada a 999. Nesse caso, precisaríamos de três variáveis, que estariam alocadas nos seguintes endereços:

Inicializando as Variáveis
Linha Instrução Observação
0 LDI $0 Carrega o acumulador com o valor 0
1 STA @0 Armazena o valor do acumulador em MEM[0] (unidades)
2 STA @1 Armazena o valor do acumulador em MEM[1] (dezenas)
3 STA @2 Armazena o valor do acumulador em MEM[2] (centenas)

Existem outras variáveis que não estão listadas de vem ser alocadas na memória, como por exemplo: o flag de inibir contagem; os valores dos limites de contagem; etc…

O mesmo deve ser feito para as constantes necessárias, como por exemplo:

Para evitar problemas (que ocorrem somente em algumas placas) com os flip flops dos botões, é recomendável fazer o reset deles durante a inicialização.

Fazendo o reset dos flip flops
Linha Instrução Observação
0 STA @511 Limpa a leitura do botão zero
1 STA @510 Limpa a leitura do botão um

Visão Geral do Laço Principal

Basicamente, temos a leitura e verificação do estado dos botões que definem as funções principais do nosso contador. Conforme o estado desses botões, devemos, ou não, executar as rotinas de:

Um exemplo do pseudocódigo para o laço principal (ou Loop) do programa:

### INÍCIO:

Ler o botão de incremento de contagem (KEY0):

-   Caso tenha sido pressionado, desviar para a sub-rotina de incremento de valor.

Escrever os valores das variáveis nos respectivos displays (pode ser uma sub-rotina).

Ler o botão de configuração do limite de incremento (KEY1):

-   Caso esteja pressionado, desviar para a sub-rotina de configuração do limite de incremento.

Verificar se o limite de contagem foi alcançado (pode ser uma sub-rotina).

Ler o botão de reiniciar contagem (FPGA_RESET):

-   Caso esteja pressionado, desviar para a sub-rotina de reiniciar contagem.

Desviar para o **INÍCIO**.

Rotinas de Apoio

Limite de Incremento

Digamos que o contador deve contar até um determinado valor limite. Para que isso ocorra, é necessário que armazenemos esse limite na memória e durante a contagem, verifiquemos se o mesmo foi alcançado. Esta rotina trata do armazenamento desse limite na posições adequadas da memória.

Ler o valor das chaves que definem o limite de contagem para as unidades:

-   Escrever o valor na posição de memória desse limite.

Aguardar o pressionamento de KEY1:

-   Ler o valor das chaves que definem o limite de contagem para as dezenas:

-   Escrever o valor na posição de memória desse limite.

Aguardar o pressionamento de KEY1:

-   Ler o valor das chaves que definem o limite de contagem para as centenas:

-   Escrever o valor na posição de memória desse limite.

Retornar da sub-rotina.

Incrementando a Contagem

Quando o incremento da contagem é executado, devemos verificar se não ocorre o “vai um” (carry out) entre os dígitos que representam o valor incrementado.

Limpar o _flag_ de botão 0 apertado (acessar endereço 511).

Verificar _flag_ de inibir a contagem:

-   Caso esteja setado, deve retornar da sub-rotina.

Incrementar o valor de unidades da contagem:

-   Comparar o resultado do incremento das unidades com dez:

    -   Se for igual, escrever zero nas unidades e incrementar a dezena.

    -   Se não for igual, escrever o valor incrementado nas unidades e retornar da sub-rotina.

-   Comparar o resultado do incremento das dezenas com dez:

    -   Se for igual, escrever zero nas dezenas e incrementar a centena.

    -   Se não for igual, escrever o valor incrementado nas dezenas e retornar da sub-rotina.

-   Comparar o resultado do incremento das centenas com dez:

    -   Se for igual, escrever zero nas centenas e incrementar os milhares.

    -   Se não for igual, escrever o valor incrementado nas centenas e retornar da sub-rotina.

Continuar esse procedimento até chegar ao último valor a ser comparado, por exemplo: as centenas de milhares.

-   Comparar o resultado do incremento das centenas de milhares com dez:

    -   Se for igual, acender o LED indicador de _Overflow_ e ativar o _flag_ de inibir contagem.

    -   Se não for igual, escrever o valor incrementado nas centenas de milhares e retornar da sub-rotina.

Retornar da sub-rotina.

Verificação do Limite de Contagem

Caso o limite de contagem seja atingido, devemos:

Deve comparar cada posição de memória do limite com a posição de memória da contagem atual:

-   Para as unidades;

-   Para as dezenas;

-   Para as centenas;

-   Para as unidades de milhares;

-   Para as dezenas de milhares;

-   Para as centenas de milhares.

Caso todas comparações resultem iguais, devemos:

-   Ativar o _flag_ de inibir contagem;

-   Acender o LED de **Limite Atingido**.

Retornar da sub-rotina.

Reinício de Contagem

Para reiniciar a contagem, devemos limpar os valores de contagem, limpar os indicadores de estado e reabilitar a contagem.

Escrever zero nas variáveis de contagem.

Limpar o _flag_ de inibir contagem.

Apagar o LED de _Overflow_.

Apagar o LED de **Limite Atingido**.

Retornar da sub-rotina.

Ferramentas