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:
A configuração, que é a primeira etapa e só é executada uma vez.
Ela trata da inicialização dos valores de memória;
Da configuração dos periféricos (quando existe);
Da inicialização dos periféricos (por exemplo: apagar os LEDs);
E outros eventuais procedimentos que são executados uma única vez e no início do programa.
O laço principal, que contém, normalmente, a maior parte do programa.
Ele trata das entradas de dados (por exemplo: leitura das chaves e botões);
Da manipulação, ou tranformação, desses dados de acordo com os objetivos do programa;
E da saída dos resultados dessas manipulações dos dados (por exemplo: escrita nos Displays e LEDs).
Não é a toa que os dois procedimentos encontrados na interface de programação do Arduino tenham os nomes de Setup e Loop.
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.
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.
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.
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 |
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:
Valor das unidades: Memória[0] ;
Valor das dezenas: Memória[1];
Valor das centenas: Memória[2].
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:
O valor zero, para poder fazer comparações;
O valor um, para fazer incrementos;
O valor dez, para poder fazer comparações que definem o limite da faixa a ser exibida no display.
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.
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 |
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:
Incremento de contagem;
Reinício de contagem;
Configuração de valores.
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**.
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.
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.
Caso o limite de contagem seja atingido, devemos:
Parar a contagem;
Indicar o estado de limite atingido.
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.
Para reiniciar a contagem, devemos limpar os valores de contagem, limpar os indicadores de estado e reabilitar a contagem.