Índice:

AVR Assembler Tutorial 2: 4 etapas
AVR Assembler Tutorial 2: 4 etapas

Vídeo: AVR Assembler Tutorial 2: 4 etapas

Vídeo: AVR Assembler Tutorial 2: 4 etapas
Vídeo: Лекция 4. Архитектура AVR. Ассемблер 2024, Julho
Anonim
AVR Assembler Tutorial 2
AVR Assembler Tutorial 2

Este tutorial é uma continuação do "AVR Assembler Tutorial 1"

Se você não passou pelo Tutorial 1, deve parar agora e fazer este primeiro.

Neste tutorial, continuaremos nosso estudo de programação em linguagem assembly do atmega328p usado no Arduino.

Você vai precisar de:

  1. um Arduino breadboard ou apenas um Arduino normal como no Tutorial 1
  2. um LED
  3. um resistor de 220 ohms
  4. um botão de pressão
  5. conectando fios para fazer o circuito em sua placa de ensaio
  6. Manual de instalação: www.atmel.com/images/atmel-0856-avr-instruction-s…
  7. Folha de dados: www.atmel.com/images/Atmel-8271-8-bit-AVR-Microco…

A coleção completa de meus tutoriais pode ser encontrada aqui:

Etapa 1: Construindo o Circuito

Construindo o Circuito
Construindo o Circuito

Primeiro você precisa construir o circuito que estudaremos neste tutorial.

Esta é a maneira como ele está conectado:

PB0 (pino digital 8) - LED - R (220 ohm) - 5V

PD0 (pino digital 0) - botão de pressão - GND

Você pode verificar se o LED está orientado corretamente conectando-o ao GND em vez de ao PB0. Se nada acontecer, inverta a orientação e a luz deve acender. Em seguida, reconecte-o ao PB0 e continue. A imagem mostra como meu arduino breadboard está conectado.

Etapa 2: Escrevendo o Código de Montagem

Escrevendo o Código de Montagem
Escrevendo o Código de Montagem

Escreva o código a seguir em um arquivo de texto chamado pushbutton.asm e compile-o com o avra como fez no Tutorial 1.

Observe que neste código temos muitos comentários. Cada vez que o montador vê um ponto-e-vírgula, ele pula o resto da linha e passa para a próxima linha. É uma boa prática de programação (especialmente em linguagem assembly!) Comentar pesadamente seu código para que, quando você retornar a ele no futuro, saiba o que está fazendo. Vou comentar muitas coisas nos primeiros tutoriais para que possamos saber exatamente o que está acontecendo e por quê. Mais tarde, quando nos tornarmos um pouco melhores na codificação de assembly, irei comentar as coisas com um pouco menos de detalhes.

;************************************

; escrito por: 1o_o7; data: 23 de outubro de 2014; ************************************

.nolist

.include "m328Pdef.inc".list.def temp = r16; designar o registro de trabalho r16 como temp rjmp Init; primeira linha executada

Iniciar:

ser temp; defina todos os bits em temp para 1's. saída DDRB, temp; definir um bit como 1 no I / O de direção de dados; registrar para PortB, que é DDRB, define isso; pino como saída, um 0 definiria esse pino como entrada; portanto, aqui, todos os pinos do PortB são saídas (definido como 1) ldi temp, 0b11111110; carregue o número `imediato 'no registrador temporário; se fosse apenas ld, então o segundo argumento; teria que ser um local de memória em vez de DDRD, temp; mv temp para DDRD, o resultado é que PD0 é inserido; e o resto são saídas clr temp; todos os bits em temp são configurados para 0's out PortB, temp; definir todos os bits (ou seja, pinos) na porta B para 0V ldi temp, 0b00000001; carregar o número imediato para temp out PortD, temp; mover temp para PortD. PD0 tem um resistor pull up; (ou seja, definido para 5 V), uma vez que tem um 1 nesse bit; o resto são 0V desde 0's.

Principal:

em temp, PinD; PinD mantém o estado de PortD, copie para temp; se o botão estiver conectado ao PD0, será; 0 quando o botão é pressionado, 1 caso contrário, desde; PD0 tem um resistor pull up que está normalmente a 5 V da porta B, temp; envia os 0's e 1's lidos acima para o PortB; isso significa que queremos o LED conectado ao PB0,; quando PD0 é BAIXO, ele define PB0 para BAIXO e gira; no LED (visto que o outro lado do LED está; conectado a 5 V e isso definirá PB0 para 0 V; portanto, a corrente fluirá) rjmp Principal; volta ao início de Main

Observe que, desta vez, não apenas temos muito mais comentários em nosso código, mas também temos uma seção de cabeçalho que fornece algumas informações sobre quem o escreveu e quando foi escrito. O resto do código também é separado em seções.

Depois de compilar o código acima, você deve carregá-lo no microcontrolador e ver se funciona. O LED deve acender enquanto você pressiona o botão e desligar novamente quando você o solta. Eu mostrei o que parece na foto.

Etapa 3: Análise do Código linha a linha

Vou pular as linhas que são meramente comentários, pois seu propósito é evidente.

.nolist

.include "m328Pdef.inc".list

Essas três linhas incluem o arquivo contendo as definições de Registro e Bit para o ATmega328P que estamos programando. O comando.nolist diz ao montador para não incluir esse arquivo no arquivo pushbutton.lst que ele produz ao montá-lo. Desativa a opção de listagem. Depois de incluir o arquivo, ativamos a opção de listagem novamente com o comando.list. A razão de fazermos isso é porque o arquivo m328Pdef.inc é muito longo e não precisamos vê-lo no arquivo de lista. Nosso montador, avra, não gera automaticamente um arquivo de lista e, se quisermos, montaríamos usando o seguinte comando:

avra -l pushbutton.lst pushbutton.asm

Se você fizer isso, um arquivo chamado pushbutton.lst será gerado e, se você examinar esse arquivo, verá que ele mostra o código do programa junto com informações extras. Se você observar as informações extras, verá que as linhas começam com C: seguido pelo endereço relativo em hexadecimal de onde o código é colocado na memória. Essencialmente, ele começa em 000000 com o primeiro comando e aumenta a partir daí com cada comando subsequente. A segunda coluna após o lugar relativo na memória é o código hexadecimal para o comando seguido pelo código hexadecimal para o argumento do comando. Discutiremos os arquivos de lista mais adiante em tutoriais futuros.

.def temp = r16; designar registro de trabalho r16 como temp

Nesta linha, usamos a diretiva assembler ".def" para definir a variável "temp" como igual ao r16 "registro de trabalho". Usaremos o registrador r16 como aquele que armazena os números que queremos copiar para várias portas e registradores (que não podem ser gravados diretamente).

Exercício 1: tente copiar um número binário diretamente em uma porta ou registro especial como DDRB e veja o que acontece quando você tenta montar o código.

Um registro contém um byte (8 bits) de informação. Essencialmente, é geralmente uma coleção de SR-Latches, cada um é um "bit" e contém um 1 ou um 0. Podemos discutir isso (e até mesmo construir um!) Posteriormente nesta série. Você pode estar se perguntando o que é um "registro operacional" e por que escolhemos o r16. Discutiremos isso em um tutorial futuro, quando mergulharmos no atoleiro da parte interna do chip. Por enquanto, quero que você entenda como fazer coisas como escrever código e programar hardware físico. Então você terá um quadro de referência dessa experiência que tornará as propriedades de memória e registro do microcontrolador mais fáceis de entender. Eu percebo que a maioria dos livros didáticos introdutórios e discussões fazem isso ao contrário, mas descobri que jogar um videogame por um tempo primeiro para obter uma perspectiva global antes de ler o manual de instruções é muito mais fácil do que ler o manual primeiro.

rjmp Init; primeira linha executada

Esta linha é um "salto relativo" para o rótulo "Init" e não é realmente necessário aqui, pois o próximo comando já está em Init, mas o incluímos para uso futuro.

Iniciar:

ser temp; defina todos os bits em temp para 1's.

Após o rótulo de inicialização, executamos um comando "set register". Isso define todos os 8 bits no registro "temp" (que você lembra é r16) para 1's. Portanto, a temperatura agora contém 0b11111111.

saída DDRB, temp; definir um bit como 1 no registro de E / S de direção de dados

; para PortB, que é DDRB, define esse pino como saída; um 0 definiria esse pino como entrada; então aqui, todos os pinos do PortB são saídas (definido como 1)

O registro DDRB (Registro de Direção de Dados para Porta B) informa quais pinos na Porta B (ou seja, PB0 a PB7) são designados como entrada e quais são designados como saída. Uma vez que temos o pino PB0 conectado ao nosso LED e o resto não conectado a nada, definiremos todos os bits para 1, o que significa que são todas saídas.

temp ldi, 0b11111110; carregue o número `imediato 'no registro temporário

; se fosse apenas ld, o segundo argumento seria; tem que ser um local de memória

Esta linha carrega o número binário 0b11111110 no registrador temporário.

saída DDRD, temp; mv temp para DDRD, o resultado é que PD0 é inserido e

; o resto são saídas

Agora definimos o Registrador de Direção de Dados para PortD de temp, já que temp ainda contém 0b11111110, vemos que PD0 será designado como um pino de entrada (já que há um 0 no ponto mais à direita) e o resto são designados como saídas, pois há 1 está nessas manchas.

clr temp; todos os bits em temp são definidos como 0's

porta B, temp; defina todos os bits (ou seja, pinos) na Porta B para 0V

Primeiro, "apagamos" o registrador temp, o que significa definir todos os bits para zero. Em seguida, copiamos isso para o registrador PortB, que define 0V em todos os pinos. Um zero em um bit da Porta B significa que o processador manterá aquele pino em 0 V, um um em um bit fará com que esse pino seja definido como 5 V.

Exercício 2: Use um multímetro para verificar se todos os pinos na porta B são realmente zero. Algo estranho está acontecendo com PB1? Alguma ideia de por que isso pode ser? (semelhante ao Exercício 4 abaixo, siga o código …) Exercício 3: Remova as duas linhas acima de seu código. O programa ainda funciona corretamente? Porque?

temp ldi, 0b00000001; carregar número imediato para temp

saída PortD, temp; mover temp para PortD. PD0 está em 5 V (tem um resistor pullup); já que tem 1 nesse bit, o resto é 0V. Exercício 4: Remova as duas linhas acima de seu código. O programa ainda funciona corretamente? Porque? (Isso é diferente do Exercício 3 acima. Veja o diagrama de pinagem. Qual é a configuração DDRD padrão para PD0? (Veja a página 90 da folha de dados

Primeiro, "carregamos imediatamente" o número 0b00000001 para temp. A parte "imediata" está lá, pois estamos carregando um número direto para a temperatura ao invés de um ponteiro para um local da memória contendo o número a ser carregado. Nesse caso, simplesmente usaríamos "ld" em vez de "ldi". Em seguida, enviamos esse número para o PortD, que define PD0 em 5V e o restante em 0V.

Agora definimos os pinos como entrada ou saída e configuramos seus estados iniciais como 0V ou 5V (LOW ou HIGH) e, portanto, entramos em nosso "loop" de programa.

Principal: em temp, PinD; PinD mantém o estado de PortD, copie para temp

; se o botão estiver conectado ao PD0, então será; um 0 quando o botão é pressionado, 1 caso contrário, desde; PD0 tem um resistor pull up que normalmente está em 5V

O registro PinD contém o estado atual dos pinos do PortD. Por exemplo, se você conectou um fio de 5 V ao PD3, no próximo ciclo de clock (que acontece 16 milhões de vezes por segundo, já que temos o microcontrolador conectado a um sinal de clock de 16 MHz), o bit PinD3 (do estado atual de PD3) se tornaria 1 em vez de 0. Portanto, nesta linha, copiamos o estado atual dos pinos para temp.

porta B, temp; envia os 0's e 1's lidos acima para o PortB

; isso significa que queremos o LED conectado ao PB0, então; quando PD0 é BAIXO, ele definirá PB0 para BAIXO e girará; no LED (o outro lado do LED está conectado; em 5 V e isso definirá PB0 em 0 V para que a corrente flua)

Agora enviamos o estado dos pinos no PinD para a saída do PortB. Efetivamente, isso significa que PD0 enviará 1 para PortD0 a menos que o botão seja pressionado. Nesse caso, como o botão está conectado ao aterramento, esse pino estará em 0 V e enviará um 0 para a porta B0. Agora, se você olhar o diagrama do circuito, 0 V no PB0 significa que o LED acenderá, pois o outro lado dele está a 5 V. Se não estivermos pressionando o botão, de modo que um 1 seja enviado para PB0, isso significaria que temos 5V no PB0 e também 5V do outro lado do LED e, portanto, não há diferença de potencial e nenhuma corrente fluirá e assim o O LED não acenderá (neste caso, é um LED que é um diodo e, portanto, a corrente flui apenas em uma direção, independentemente, mas em qualquer).

rjmp Main; volta ao início

Este salto relativo nos leva de volta ao nosso rótulo Main: e verificamos o PinD novamente e assim por diante. Verificando a cada 16 milionésimos de segundo se o botão está sendo pressionado e configurando PB0 de acordo.

Exercício 5: Modifique seu código para que seu LED seja conectado a PB3 em vez de PB0 e veja se ele funciona. Exercício 6: Conecte seu LED em GND em vez de 5V e modifique seu código de acordo.

Etapa 4: Conclusão

Neste tutorial, investigamos a linguagem assembly para o ATmega328p e aprendemos como controlar um LED com um botão de pressão. Em particular, aprendemos os seguintes comandos:

registrador ser define todos os bits de um registrador para 1's

O registro clr define todos os bits de um registro para 0's

no registro, o registro i / o copia o número de um registro i / o para um registro operacional

No próximo tutorial, examinaremos a estrutura do ATmega328p e os vários registros, operações e recursos nele contidos.

Antes de continuar com esses tutoriais, vou esperar para ver o nível de interesse. Se houver várias pessoas que estão realmente gostando de aprender como codificar programas para este microprocessador em linguagem assembly, irei continuar e construir circuitos mais complicados e usar um código mais robusto.

Recomendado: