A ordem de programação dos microcontroladores avr. programação de microcontroladores avr

Bom dia queridos radioamadores!
Seja bem-vindo ao site ""

O que é um microcontrolador e por que ele é necessário. Vejamos sua definição:

- um microcircuito projetado para controlar dispositivos eletrônicos, ou de outra forma - um computador simples (microcomputador) capaz de realizar tarefas simples.

Ou seja, na verdade, um microcontrolador é um dispositivo que nos permite realizar nossas ideias (mesmo loucas), mas, claro, dentro de suas capacidades. E o mais importante, a realização de uma ideia é alcançada não pela criação de estruturas eletrônicas sofisticadas, mas apenas, basicamente, pelo poder do nosso pensamento (você gostaria de se tornar um mago?).
Os mais populares entre os radioamadores são dois tipos de microcontroladores:
FOTO- Tecnologia de microchips
AVR- por Atmel

Gostaria de fazer uma pequena digressão e esclarecer uma de minhas posições. Não vou discutir os méritos deste ou daquele tipo de microcontroladores, deste ou daquele software, e em geral tudo relacionado a microcontroladores, para aconselhar algo, e mais ainda, impor aos leitores. É tudo uma questão de gosto, preferência pessoal e seus objetivos finais no aprendizado de microcontroladores. Bem, já que “a imensidão não pode ser abraçada”, farei toda a minha narração posterior em relação aos microcontroladores AVR e, não muito comum, mas meu favorito, o programa “Algorithm Builder”. Diferentes tipos de microcontroladores, programas, é claro, têm diferenças, mas também têm muito em comum. E aprenderemos o mundo dos microcontroladores de tal forma que, posteriormente, o conhecimento adquirido poderá ser aplicado a PICs e a qualquer software sem problemas. E deixe-me lembrá-lo mais uma vez que esta série de artigos é minha tentativa de ajudar aqueles que ouviram falar pela primeira vez sobre a existência de microcontroladores e querem entender como trabalhar com eles.

O que você precisa aprender a trabalhar com microcontroladores? Destaco algumas, na minha opinião, as principais condições:
1. Desejo e perseverança .
Tudo é muito simples aqui: há um desejo - tudo vai dar certo. E desejo com perseverança é, em geral, uma super coisa.
2. Conhecimento do dispositivo microcontrolador.
O conhecimento profundo não é importante aqui (e talvez nem seja necessário), mas é necessário saber o que está “a bordo” do microcontrolador. Apenas sabendo em que consiste o microcontrolador, quais dispositivos ele possui, seus recursos, como eles funcionam - só assim poderemos usar ao máximo os recursos do microcontrolador.
3. Conhecimento da linguagem de programação e comandos de controle do microcontrolador.
Como o microcontrolador funcionará, quais tarefas você atribui a ele e como ele as executará, é determinado pelo programa embutido nele - o programa que você mesmo compõe para o microcontrolador. E vamos nos debruçar sobre esse ponto com mais detalhes para considerar questões que possam surgir no futuro.

Programa(na tradução, esta palavra significa “prescrição”) - uma descrição preliminar dos próximos eventos ou ações.

Por exemplo, queremos que o microcontrolador pisque um LED. Uma tarefa simples, mas, no entanto, para que o microcontrolador complete essa tarefa, devemos primeiro, passo a passo, descrever todas as ações do microcontrolador, escrever um programa que ele deve executar para obter o resultado que precisamos - um LED piscando . Algo assim:
♦ Acenda o LED:
- configurar a saída à qual o LED está conectado para trabalhar na saída de informações
- aplique um nível lógico a este pino, o que permitirá acender o LED
♦ Aguarde um pouco:
- vá para a sub-rotina que forma uma pausa (que também precisa ser “mastigada”)
- ao término da sub-rotina de pausa, retornar ao programa principal
♦ Desligue o LED:
- aplicar um nível lógico à saída, apagando o LED
e assim por diante.
com o termo Programa outro termo está inextricavelmente ligado - Algoritmo(como o Lobo e a Lebre, Tom e Jerry).

Algoritmo- um conjunto de instruções que descrevem o procedimento para alcançar o resultado desejado.

Se no programa estivermos da forma mais detalhada prescrever ações microcontrolador, então no algoritmo nós determinar o curso de ação microcontrolador, com base no qual criaremos um programa. Semelhante ao exemplo acima:
♦ Acenda o LED
♦ Espere um pouco
♦ Desligue o LED
e assim por diante.
Nesse caminho, o algoritmo é o precursor do programa. E quanto mais cuidadosa e cuidadosamente um algoritmo for criado, mais fácil será criar um programa.

No total, o programa para o microcontrolador é uma sequência de ações do microcontrolador na forma de um conjunto de comandos e instruções que ele deve executar para atingir nossos objetivos.

Os comandos para o microcontrolador parecem um conjunto de uns e zeros:
00110101 011000100
assim chamado - códigos de comando, e os códigos de comando são a linguagem que o microcontrolador entende. E para traduzir nosso algoritmo do russo para o idioma do microcontrolador - nesses mesmos conjuntos de zeros e uns, existem programas especiais.
Esses programas nos permitem descrever a ordem de trabalho do microcontrolador em uma linguagem que é mais ou menos compreensível para nós, e então traduzir essa ordem para uma linguagem compreensível para o microcontrolador, resultando no chamado Código da máquina- uma sequência de comandos e instruções (os próprios zeros e uns) que o microcontrolador entende apenas. O texto de um programa escrito por um programador é chamado Código fonte. O programa é traduzido da linguagem de programação (código-fonte) para a linguagem do microcontrolador (código de máquina) tradutores. O tradutor converte o texto do programa em códigos de máquina, que são então escritos na memória do microcontrolador.
Nesses programas, a ordem de operação do microcontrolador é descrita por uma linguagem especial - a linguagem de programação. A linguagem de programação é diferente da nossa linguagem humana. Se nossa linguagem de comunicação é principalmente para troca de informações, então:

Linguagem de programação - esta é uma maneira de transmitir comandos, instruções, um guia claro de ação para o microcontrolador.

Existem muitas linguagens de programação e elas podem ser divididas em dois tipos:
linguagens de programação de baixo nível
linguagens de programação de alto nível
Qual é a diferença. E eles diferem em sua proximidade com o microcontrolador.
No início do surgimento da tecnologia de microprocessadores, os programas eram escritos em códigos de máquina, ou seja, todo o algoritmo de trabalho era escrito sequencialmente na forma de zeros e uns. Assim ficou o programa:

01000110
10010011
01010010

É improvável que alguém consiga descobrir esse conjunto de combinações de dois números, e o trabalho dos primeiros programadores foi muito trabalhoso. Para facilitar suas vidas, os programadores começaram a criar as primeiras linguagens de programação. Assim, quanto mais próxima a linguagem de programação estiver desse conjunto de zeros e uns, mais “baixo nível” ela é, e quanto mais longe deles, mais “alto nível”.
As linguagens de programação mais comuns para microcontroladores:
- linguagem de baixo nível - montador
– linguagem de alto nível – C (Ci)
Vejamos um exemplo de sua diferença (esses exemplos são abstratos).
Digamos que precisamos adicionar dois números: 25 e 35.
No código nativo, esse comando pode ficar assim:
00000101 1101001
Em linguagem de baixo nível:
ADICIONAR Rd, Rr
Em linguagem de alto nível:
25+35
A diferença entre linguagens de baixo e alto nível é visível a olho nu, comentários, como se costuma dizer, são supérfluos.
Mas vamos nos aprofundar nesses exemplos. Não analisaremos o exemplo de código de máquina, pois é idêntico ao exemplo em assembler. Em sua essência, as instruções de montagem são os mesmos códigos de máquina (comandos) que são simplesmente atribuídos a abreviações de letras para não se perder em zeros e uns. Com a instrução do montador ADD Rd, Rr, configuramos o microcontrolador para somar os dois números encontrados (e para isso devemos primeiro escrevê-los lá) - o primeiro em Rd, o segundo em Rr, e colocar o resultado da adição na Rua Como você pode ver, definimos uma tarefa muito específica para o microcontrolador: onde obtê-lo, o que fazer com ele e onde colocar o resultado. Neste caso, trabalhamos diretamente com o microcontrolador.
Comando em linguagem de alto nível: 25+35 , uma notação matemática familiar para nós, agradável aos nossos olhos. Mas neste caso, não trabalhamos diretamente com o microcontrolador, simplesmente configuramos a tarefa de somar dois números. O resultado e a sequência de ações neste caso serão os mesmos da execução do comando assembler: primeiro, esses dois números serão escritos em algum lugar, depois somados e o resultado colocado em algum lugar.
E aqui está a principal diferença entre linguagens de alto e baixo nível. Se no Assembler controlamos todo o processo (quer gostemos ou não): sabemos onde esses dois números estão escritos e sabemos onde estará o resultado, então em uma linguagem de alto nível não controlamos o processo. O próprio programa decide onde pré-escrever os números e onde colocar o resultado. Na maioria dos casos, não precisamos saber disso, pois para nós o resultado principal é o número 60 na saída. Como resultado, os programas em linguagens de alto nível são mais legíveis, agradáveis ​​aos olhos e menores em tamanho - afinal, não precisamos “entrar em todos os buracos” e pintar cada passo do microcontrolador, o programa faz isso mais tarde para nós quando compila - traduz em códigos de máquina. Mas também há uma desvantagem. Dois algoritmos idênticos escritos em Assembler e em C, depois de convertidos em códigos de máquina, terão um tamanho diferente: um programa escrito em Assembler será 20-40% mais curto que um programa escrito em C - o diabo sabe para que lado C vai alcançar o resultado que precisamos. E há casos em que não há confiança em uma linguagem de alto nível e em um programa C eles inserem código escrito em Assembler.
Programadores profissionais, via de regra, conhecem várias linguagens de programação (ou trabalham em uma equipe que inclui especialistas em diferentes linguagens), combinando de forma criativa seus recursos e benefícios em um só programa. Bem, nós, amadores, precisamos conhecer pelo menos uma linguagem (para iniciantes), e precisamos começar (e estou firmemente convencido disso, e ninguém me convencerá) de uma linguagem de baixo nível - Assembly.

Bem, eu acho, e aqui tudo está claro para nós - você precisa aprender uma linguagem de programação, de uma maneira diferente - de jeito nenhum.

Comandos e instruções para controlar o microcontrolador.
Os microcontroladores AVR possuem mais de 130 comandos diferentes que permitem realizar todas as possibilidades inerentes a ele. Mas direi imediatamente que poucos amadores conhecem todos eles, quanto mais usá-los todos. Normalmente, na prática amadora, há conhecimento suficiente e metade das equipes, ou até menos. Mas você precisa aprender comandos. Quanto mais comandos você souber, mais sofisticados (no bom sentido da palavra) e mais elegantes serão os programas.

Unidade lógica aritmética e organização de memória - memória de programa, memória de dados, memória não volátil



Neste tutorial sobre avr, tentei descrever todas as coisas mais básicas para iniciantes na programação de microcontroladores. avr. Todos os exemplos são construídos em um microcontrolador atmega8. Isso significa que para repetir todas as lições você precisará de apenas um MK. Como emulador de circuito eletrônico, o Proteus é usado - na minha opinião, a melhor opção para iniciantes. Os programas em todos os exemplos são escritos no compilador C para avr CodeVision AVR. Por que não em alguma montadora? Porque o iniciante já está carregado de informações, e o programa que multiplica dois números leva cerca de cem linhas em assembler, e eles usam C em projetos complexos e ousados. O compilador CodeVision AVR é ​​afiado para microcontroladores atmel, possui um gerador de código conveniente, boa interface e diretamente a partir dele pode ser flashed pelo microcontrolador.

Este tutorial irá mostrar e explicar com exemplos simples como:

  • Comece a programar microcontroladores, por onde começar, o que você precisa para isso.
  • Quais programas usar para escrever firmware para avr, simular e depurar código em um PC,
  • Quais dispositivos periféricos estão dentro do MK, como controlá-los usando seu programa
  • Como gravar o firmware finalizado no microcontrolador e como depurá-lo
  • Como fazer um PCB para o seu dispositivo
Para dar os primeiros passos na programação MK, você só precisa de dois programas:
  • Proteus é um programa emulador (você pode desenvolver um circuito nele sem recorrer a solda real e depois testar nosso programa neste circuito). Primeiro vamos lançar todos os projetos no Proteus, e depois já podemos soldar um aparelho real.
  • CodeVisionAVR é ​​um compilador de linguagem de programação C para AVR. Nele, desenvolveremos programas para o microcontrolador, e será possível fazer o flash de um MK real diretamente dele.
Depois de instalar o Proteus, execute-o
Ele nos oferece para ver os projetos que vão com ele, recusamos educadamente. Agora vamos criar o circuito mais simples nele. Para fazer isso, clique no ícone visualmente nada acontece. Agora você precisa clicar na letra pequena R (selecione da biblioteca) no painel da lista de componentes, a janela de seleção de componentes será aberta
no campo de máscara, digite o nome do componente que queremos encontrar na biblioteca. Por exemplo, precisamos adicionar um microcontrolador mega8
na lista de resultados, cutuque o mega8 e aperte o botão OK. Temos um microcontrolador mega8 na lista de componentes
Assim, adicionamos outro resistor à lista de componentes digitando a palavra no campo de máscara res e LED conduziu

Para colocar peças no diagrama, clique na peça, depois clique no campo do diagrama, selecione a localização do componente e clique novamente. Para adicionar um terra ou um menos comum ao circuito à esquerda, clique em "Terminal" e selecione Ground. Assim, somando todos os componentes e conectando-os, obtemos um circuito tão simples
Tudo, agora nosso primeiro esquema está pronto! Mas você pode estar se perguntando, o que ela pode fazer? Mas nada. Nada, porque para que o microcontrolador funcione, você precisa escrever um programa para ele. Um programa é uma lista de instruções que o microcontrolador irá executar. Precisamos do microcontrolador para instalar em uma perna PC0 lógica 0 (0 volts) e lógica 1 (5 volts).

Escrevendo um programa para um microcontrolador

Vamos escrever o programa em linguagem C usando o compilador CodeVisionAVR. Depois de lançar o CV, ele nos pergunta o que queremos criar: Fonte ou Projeto Selecionamos o último e pressionamos o botão OK. Em seguida, seremos solicitados a executar o CVAVR CodeWizard (esta é uma ferramenta inestimável para um iniciante, pois pode gerar o esqueleto principal do programa) escolher Sim
O assistente começa com a aba Chip ativa, aqui podemos selecionar o modelo do nosso MK - este é mega8, e a frequência em que o MK irá operar (mega8 está configurado para 1 megahertz por padrão), então configuramos tudo conforme mostrado em a captura de tela acima. Vá para a guia Portas
O microcontrolador atmega8 possui 3 portas: Porta C, Porta D, Porta B. Cada porta possui 8 pinos. Os pinos da porta podem estar em dois estados:
  • Saída
Com a ajuda do registrador DDRx.y, podemos definir o pino como entrada ou saída. Se em
  • DDRx.y = 0 - a saída funciona como ENTRADA
  • DDRx.y = 1 pino funciona SAÍDA
Quando o pino está configurado como saída, podemos configurá-lo para lógica 1 (+5 volts) e lógica 0 (0 volts). Isso é feito escrevendo no registrador PORTx.y. A seguir será discutido em detalhes sobre as portas de entrada-saída. E agora configuramos tudo como mostrado na captura de tela e clicamos em Arquivo->Gerar, Salvar e Sair. Em seguida, o CodeWizard nos oferecerá para salvar o projeto, salvamos e olhamos o código:

#incluir //biblioteca para criar atrasos de tempo void main(void) ( PORTB=0x00; DDRB=0x00; PORTC=0x00; DDRC=0x01; // faz a saída da perna PC0 PORTD=0x00; DDRD=0x00; // Inicialização do Timer/Counter 0 TCCR0=0x00; TCNT0=0x00; // Inicialização do temporizador/contador 1 TCCR1A=0x00; TCCR1B=0x00; TCNT1H=0x00; TCNT1L=0x00; ICR1H=0x00; ICR1L=0x00; OCR1AH=0x00; OCR1AL=0x00; OCR1BH=0x00 ; OCR1BL=0x00; // Inicialização do temporizador/contador 2 ASSR=0x00; TCCR2=0x00; TCNT2=0x00; OCR2=0x00; // Inicialização da(s) interrupção(ões) externa(s) MCUCR=0x00; // Temporizador(es)/contador(es) ) Inicialização de interrupção(ões) TIMSK=0x00; // Inicialização do comparador analógico ACSR=0x80; SFIOR=0x00; while (1) ( ); )


Tudo aqui pode parecer assustador e desconhecido para você, mas na realidade nem tudo é assim. O código pode ser simplificado descartando a inicialização dos periféricos MK que não usamos. Após a simplificação fica assim:

#incluir //biblioteca para trabalhar com microcontrolador mega8 #include //biblioteca para criar atrasos de tempo void main(void) ( DDRC=0x01; /* torna a perna PC0 a entrada de saída 0x01 pode parecer desconhecida para você, e este é apenas o número 1 em hexadecimal, esta linha será equivalente a 0b00000001 em binário, então vou escrever exatamente assim. */ while (1) ( ); )


Tudo está bem. Mas para que o LED pisque, precisamos alterar o nível lógico na perna PC0. Para fazer isso, adicione algumas linhas ao loop principal:

#incluir //biblioteca para trabalhar com microcontrolador mega8 #include //biblioteca para criar atrasos de tempo void main(void) ( DDRC=0x01; /* torna a perna PC0 a entrada de saída 0x01 pode parecer desconhecida para você, e este é apenas o número 1 em hexadecimal, esta linha será equivalente a 0b00000001 em binário, então eu vou escrever exatamente assim.*/ while (1)//o loop do programa principal (// o suporte do operador do loop do programa principal abre PORTC.0=1; // define a porta C 1 para pin 0 delay_ms(500); //demora 500 milissegundos PORTC.0=0; // configura a porta C 0 para pino 0 delay_ms(500); // faz um atraso de 500 milissegundos );// fecha o suporte do operador do loop principal do programa)


Tudo, agora o código está pronto. Clicamos no ícone Build all Project files para compilar (traduzir para instruções do processador MK) nosso programa. Na pasta Exe, que está localizada em nosso projeto, deve aparecer um arquivo com a extensão hexadecimal, este é o nosso arquivo de firmware para o MK. Para alimentar nosso firmware para o microcontrolador virtual no Proteus, você precisa clicar duas vezes na imagem do microcontrolador no Proteus. Uma janela como esta aparecerá
clique no ícone da pasta no campo Program File, selecione hex - o arquivo do nosso firmware e pressione o botão OK. Agora podemos executar a simulação do nosso circuito. Para fazer isso, clique no botão "Play" no canto inferior esquerdo da janela do Proteus.

Já disse mais de uma ou duas vezes que o estudo do MK deve começar com o montador. Um curso inteiro no site foi dedicado a isso (embora não seja muito consistente, mas aos poucos vou penteando-o para uma aparência adequada). Sim, é difícil, o resultado não será no primeiro dia, mas você aprenderá a entender o que está acontecendo no seu controlador. Você saberá como funciona, e não copiará as fontes de outras pessoas como um macaco e tentará entender por que de repente parou de funcionar. Além disso, é muito mais fácil para C mexer em código caipira que sairá com forcado no momento mais inoportuno.

Infelizmente, todo mundo quer resultados imediatamente. Por isso, decidi ir para o outro lado - fazer um tutorial sobre C, mas com uma demonstração de sua cueca. Um bom programador embutido sempre segura seu pedaço de ferro firmemente pela greva, evitando que ele dê um único passo sem permissão. Então, qual será o código C primeiro, depois o que o compilador deu à luz e como tudo funciona na realidade :)

Por outro lado, o ponto forte do C é a portabilidade do código. Se, é claro, para escrever tudo corretamente. Separando algoritmos de trabalho e suas implementações de ferro em diferentes partes do projeto. Então, para transferir o algoritmo para outro MK, bastará reescrever apenas a camada de interface, onde está escrito todo o acesso ao hardware, e deixar todo o código de trabalho como está. E, claro, legibilidade. O código-fonte do Sish é mais fácil de entender de relance (embora .. por exemplo, eu não me importo com o que acender - pelo menos si, pelo menos asm :)), mas, novamente, se tudo estiver escrito corretamente. Vou também prestar atenção a estes pontos.

Como uma peça de ferro experimental na qual a maior parte de todos os exemplos será colocada, minha placa de depuração será.

Primeiro programa C para AVR

Escolhendo um compilador e instalando um ambiente
Existem muitos compiladores C diferentes para o AVR:
Em primeiro lugar, este IAR AVR C- quase inequivocamente reconhecido como o melhor compilador para AVR, tk. o próprio controlador foi criado em estreita colaboração entre a Atmel e especialistas do IAR. Mas você tem que pagar por tudo. E esse compilador não é apenas um software comercial caro, mas também possui uma infinidade de configurações que você só precisa trabalhar duro para compilá-lo. Eu realmente não tinha amizade com ele, o projeto apodreceu devido a erros estranhos na fase de vinculação (mais tarde descobri que era um crack torto).

O segundo vai WinAVR GCCé um poderoso compilador de otimização. Full open source, multiplataforma, em geral, todas as alegrias da vida. Ele também se integra perfeitamente ao AVR Studio, permitindo que você depure ali mesmo, o que é extremamente conveniente. Em geral, eu escolhi.

Também tem CodeVision AVR Cé um compilador muito popular. Tornou-se popular devido à sua simplicidade. Você pode obter um programa funcional em poucos minutos - o assistente de código inicial contribui muito para isso, carimbando os padrões para inicializar qualquer uarts. Para ser honesto, de alguma forma eu o trato com suspeita - uma vez que tive que desmontar um programa escrito por este compilador, algum tipo de mingau e não código acabou. Uma quantidade terrível de gestos e operações desnecessárias, o que resultou em uma quantidade bastante grande de código e desempenho lento. No entanto, talvez tenha havido um erro no DNA do gravador de firmware original. Além disso, ele quer dinheiro. Não tanto quanto IAR, mas perceptível. E no modo de demonstração permite que você escreva não mais que 2kb de código.
Claro que há um crack, mas se você roubar, então um milhão, no sentido de IAR :)

Há também Image Craft AVR C e MicroC da microeletrônica. Também não precisei usar, mas... SWG muitos elogios micropascal, eles dizem, um ambiente de programação e bibliotecas terrivelmente convenientes. Acho que MicroC não será pior, mas também pago.

Como eu disse, eu escolhi WinAVR por três razões: gratuito, ele se integra ao AVR Studio e apenas um monte de código pronto é escrito para todas as ocasiões.

Então baixe WinAVR com e AVR Studio. Em seguida, o estúdio é instalado primeiro e, de cima, o WinAVR se enrola e se apega ao estúdio na forma de um plug-in. Eu recomendo fortemente colocar o WinAVR em um caminho curto, algo como C:\WinAVR, assim você evitará muitos problemas com caminhos.

Criação do projeto
Então, o estúdio está montado, C está ferrado, é hora de tentar programar alguma coisa. Vamos começar com o simples, o mais simples. Execute o estúdio, selecione um novo projeto como o compilador AVR GCC e digite o nome do projeto.

A área de trabalho é aberta com um arquivo *.c vazio.

Agora não custa configurar a exibição de caminhos nos favoritos do estúdio. Para fazer isso, acesse:
Ferramentas de menu - Opções - Geral - FileTabs e selecione "Apenas nome de arquivo" na lista suspensa. Caso contrário, será impossível trabalhar - a guia conterá o caminho completo do arquivo e não haverá mais de duas ou três guias na tela.

Configuração do projeto
Em geral, é considerado clássico criar um arquivo make no qual todas as dependências seriam descritas. E isso provavelmente está correto. Mas para mim, que cresci com IDEs totalmente integrados como uVision ou Estúdio AVR essa abordagem é profundamente estranha. Portanto, farei do meu jeito, por todos os meios do estúdio.

Clique no botão de engrenagem.


Essas são as configurações do seu projeto, ou melhor, as configurações para geração automática do arquivo make. Na primeira página, você só precisa informar a frequência em que seu MK irá funcionar. Depende dos bits do fusível, então assumimos que a frequência é 8000000Hz.
Preste atenção também na linha de otimização. Agora há -Os é otimização de tamanho. Deixe como está por enquanto, então você pode tentar brincar com este parâmetro. -O0 não é nenhuma otimização.

O próximo passo é configurar os caminhos. Antes de tudo, adicione o diretório do seu projeto lá - você colocará bibliotecas de terceiros lá. O caminho ".\" aparecerá na lista

O arquivo make é gerado, você pode visualizá-lo na pasta padrão do seu projeto, basta dar uma olhada, ver o que está lá.


É tudo por agora. Clique em OK em todos os lugares e vá para a fonte.

Formulação do problema
Uma lousa em branco é tentadora para incorporar algum tipo de ideia astuta, já que o lampejo banal de um diodo não se insere mais. Vamos imediatamente pegar o touro pelos chifres e implementar uma conexão com o computador - essa é a primeira coisa que faço.

Vai funcionar assim:
Quando uma unidade chega na porta COM (código 0x31), ligamos o diodo, e quando chega zero (código 0x30), o apagamos. Além disso, tudo será feito em interrupções, e a tarefa em segundo plano será o piscar de outro diodo. Simples e significativo.

Montando o esquema
Precisamos conectar o módulo conversor USB-USART aos pinos USART do microcontrolador. Para fazer isso, pegamos um jumper de dois fios e os colocamos nos pinos transversalmente. Ou seja, conectamos o Rx do controlador com o Tx do conversor e o Tx do conversor com o Rx do controlador.

Acontece que, no final, este é o esquema:


Eu não considero conectar as saídas restantes, fonte de alimentação, reset, é padrão

Nós escrevemos o código

Farei logo uma ressalva de que não me aprofundarei especificamente na descrição da própria linguagem C. Para fazer isso, há simplesmente uma quantidade colossal de material, que vai desde a clássica "Linguagem de Programação C" de K&R até vários manuais.

Um desses métodos foi encontrado no meu estoque, uma vez estudei essa linguagem usando-a. Tudo é curto, claro e direto ao ponto. Estou gradualmente digitando e arrastando para o meu site.

É verdade que nem todos os capítulos foram movidos para lá ainda, mas acho que não é por muito tempo.

É improvável que eu o descreva melhor, portanto, a partir do curso de treinamento, em vez de uma explicação detalhada dos meandros do Cish, simplesmente darei links diretos para páginas individuais deste manual.

Adicionando bibliotecas.
Em primeiro lugar, adicionamos as bibliotecas e cabeçalhos necessários com definições. Afinal, C é uma linguagem universal e precisa ser explicado que estamos trabalhando com AVR, então insira a linha no código-fonte:

1 #incluir

#incluir

Este arquivo está localizado na pasta WinAVR e contém uma descrição de todos os registradores e portas do controlador. E tudo lá é complicado, com referência a um controlador específico, que é transmitido pelo compilador por meio de faço arquivo no parâmetro MCU e com base nesta variável, um arquivo de cabeçalho com uma descrição dos endereços de todas as portas e registros para este controlador específico é conectado ao seu projeto. Quão! Sem ele, você também pode, mas não poderá usar nomes de registro simbólicos como SREG ou UDR, e terá que lembrar o endereço de cada um como "0xC1", e isso está quebrando sua cabeça.

A mesma equipe #incluir<имя файла> permite adicionar o conteúdo de qualquer arquivo de texto ao seu projeto, por exemplo, um arquivo com uma descrição de funções ou um pedaço de outro código. E para que a diretiva encontrasse esse arquivo, indicamos os caminhos para nosso projeto (o diretório WinAVR já está registrado lá por padrão).

função principal.
Um programa em C tem tudo a ver com funções. Eles podem ser aninhados e chamados uns dos outros em qualquer ordem e de muitas maneiras diferentes. Cada função tem três parâmetros obrigatórios:

  • Valor de retorno, por exemplo, pecado(x) retorna o valor do seno de x. Como na matemática, em suma.
  • Os parâmetros transferidos, o mesmo x.
  • Corpo da função.

Todos os valores passados ​​e retornados devem ser de algum tipo, dependendo dos dados.

Todo programa C deve conter uma função a Principal como um ponto de entrada para o programa principal, caso contrário, não é C :). Pela presença de main na fonte de um milhão de arquivos de outra pessoa, você pode entender que essa é a parte principal do programa de onde tudo começa. Aqui vamos definir:

1 2 3 4 5 int main(void) (retorno 0;)

int main(void) ( return 0; )

É isso, o primeiro programa mais simples foi escrito, não importa que não faça nada, apenas começamos.

Vamos ver o que fizemos.
inté o tipo de dados que a função principal retorna.

Claro que no microcontrolador a Principal em princípio, nada pode ser devolvido e, em teoria, deve ser void principal (void), mas o GCC é originalmente aprimorado no PC e lá o programa pode retornar o valor ao sistema operacional após a conclusão. Portanto, o GCC em void principal (void) jura por Aviso.

Isso não é um erro, vai funcionar, mas eu não gosto de avisos.

vazio este é o tipo de dados que estamos passando para a função, neste caso a Principal também não pode aceitar nada de fora, o poeta vazio- vazio. Um stub é usado quando nada precisa ser passado ou retornado.

Aqui estão estes { } chaves é um bloco de programa, neste caso o corpo da função a Principal, o código estará localizado lá.

Retorna- esse é o valor de retorno que a função main dará ao final, já que temos um int, ou seja, um número, então devemos retornar um número. Embora ainda não faça sentido, porque. no microcontrolador do main, só podemos ir a lugar nenhum. Eu devolvo null. Para nefig. E o compilador geralmente é inteligente e não gera código para este caso.
Embora, se pervertido, então de a Principal você pode ir para o MK - por exemplo, cair na seção de bootloader e executá-lo, mas aqui você já precisará de seleção de baixo nível do firmware para corrigir os endereços de transição. Abaixo você verá e entenderá como fazê-lo. Pelo que? Agora essa é outra pergunta, em 99,999% dos casos isso não é necessário :)

Pronto, siga em frente. Vamos adicionar uma variável, não precisamos dela e não devemos introduzir variáveis ​​sem ela, mas estamos aprendendo. Se as variáveis ​​forem adicionadas dentro do corpo da função, elas serão locais e existirão apenas nesta função. Quando você sai da função, essas variáveis ​​são excluídas e a memória RAM é fornecida para necessidades mais importantes. .

1 2 3 4 5 6 int main(void) ( unsigned char i; return 0 ; )

int main(void) ( unsigned char i; return 0; )

não assinado significa não assinado. O fato é que na representação binária, o bit mais significativo é atribuído ao sinal, o que significa que o número +127/-128 cabe em um byte (char), mas se o sinal for descartado, caberá de 0 a 255. Geralmente o sinal não é necessário. De modo a não assinado.
eué apenas um nome de variável. Não mais.

Agora precisamos inicializar as portas e UART. Claro, você pode pegar e conectar a biblioteca e chamar algum tipo de UartInit (9600); mas então você não saberá o que realmente aconteceu.

Nós fazemos isso:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 int main(void) ( unsigned char i; #define XTAL 8000000L #define baudrate 9600L #define bauddivider (XTAL/(16*taxa de transmissão)-1)#define HI(x) ((x)>>8) #define LO(x) ((x)& 0xFF) UBRRL = LO(divisor de bauds) ; UBRRH = HI(divisor de transmissão); UCSRA = 0 ; UCSRB=1<< RXEN| 1 << TXEN| 1 << RXCIE| 0 << TXCIE; UCSRC = 1 << URSEL| 1 << UCSZ0| 1 << UCSZ1; }

int main(void) ( unsigned char i; #define XTAL 8000000L #define baudrate 9600L #define bauddivider (XTAL/(16*baudrate)-1) #define HI(x) ((x)>>8) #define LO( x) ((x)& 0xFF) UBRRL = LO(divisor de transmissão); UBRRH = HI(divisor de transmissão); UCSRA = 0; UCSRB = 1<

Apavorante? Na verdade, existem apenas cinco últimas linhas de código real. Tudo que #definiré uma linguagem de macro de pré-processador. Quase os mesmos tops do Assembler, mas a sintaxe é um pouco diferente.

Eles facilitarão suas operações de rotina para calcular os coeficientes necessários. Na primeira linha dizemos que em vez de XTAL você pode substituir com segurança 8000000, e eu- indicação de tipo, eles dizem que long é a frequência de clock do processador. Mesmo taxa de transmissão- frequência de transferência de dados via UART.

divisor de transmissão já mais complicado, em vez disso será substituída a expressão calculada pela fórmula das duas anteriores.
Bem e OA e OI a partir deste resultado, os bytes baixos e altos serão tomados, pois obviamente não pode caber em um byte. NO OI x é deslocado (parâmetro de entrada da macro) oito vezes para a direita, como resultado, apenas o byte alto permanecerá dele. E em OA fazemos um AND bit a bit com o número 00FF, deixando apenas o byte baixo como resultado.

Então tudo o que é feito é como #definir você pode jogá-lo fora com segurança e calcular os números necessários na calculadora e inseri-los imediatamente nas linhas UBBRL = .... e UBBRH=…..

Posso. Mas! Fazem isto ESTRITAMENTE IMPOSSÍVEL!

Funcionará desta e daquela maneira, mas você terá os chamados números mágicos- valores retirados do nada e não está claro por que, e se você abrir um projeto desses em alguns anos, será muito difícil entender quais são esses valores. E agora, se você quiser mudar a velocidade, ou mudar a frequência do quartzo e você terá que recalcular tudo, então você mudou alguns números no código e pronto. Em geral, se você não quer ser considerado um codificador ruim, faça o código de forma que seja fácil de ler, compreensível e facilmente modificável.

Então tudo é simples:
Todos esses "UBRLL and Co" são os registros de configuração do transmissor UART com o qual nos comunicaremos com o mundo. E agora atribuímos a eles os valores necessários, configurando-os para a velocidade desejada e o modo desejado.

Ver entrada 1< Significa o seguinte: pegue 1 e coloque-o em seu lugar RXEN em um byte. RXEN este é o 4º bit do registro UCSRB, assim 1< forma o número binário 00010000, TXENé o 3º bit, e 1< dará 00001000. Um único "|" é bit a bit OU, então 00010000 | 00001000 = 00011000. Da mesma forma, os restantes bits de configuração necessários são definidos e adicionados ao heap comum. Como resultado, o número coletado é gravado no UCSRB. Ele é descrito com mais detalhes na folha de dados do MK na seção USART. Portanto, não se distraia com os detalhes técnicos.

Pronto, é hora de ver o que acontece. Clique na compilação e inicie a emulação (Ctrl+F7).

Depuração
Todos os tipos de barras de progresso passaram, o estúdio mudou e uma seta amarela apareceu perto da entrada da função principal. Este é o local onde o processador está atualmente e a simulação é pausada.

O fato é que inicialmente, na verdade, estava na linha UBRRL = LO(bauddivider); Afinal, o que temos em definição não é código, mas apenas cálculos preliminares, então o simulador é um pouco maçante. Mas agora ele percebeu que a primeira instrução foi cumprida e se você subir em uma árvore Visualização de E/S, para a seção USART e olhe o byte UBBRL lá, você verá que já existe um valor lá! 0x33.

Dê mais um passo. Veja como o conteúdo de outro registrador irá mudar. Então passe por todos eles, preste atenção ao fato de que todos os bits especificados estão configurados como eu lhe disse, e eles são configurados simultaneamente para todo o byte. As coisas não vão além do Retorno - o programa acabou.

Abertura
Agora redefina a simulação para zero. Clique lá Redefinir (Shift+F5). Abra a listagem desmontada, agora você verá o que realmente está acontecendo no controlador. Visualizar -> Desmontador. E não AAAA!!! Montador!!! HORRÍVEL!!! MAS VOCÊ DEVE. Para que mais tarde, quando algo der errado, você não fique estúpido no código e não faça perguntas idiotas nos fóruns, mas imediatamente entre nos miúdos e veja onde você tem um plugue. Não há nada terrível lá.

Primeiro haverá tops da série:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 +00000000: 940C002A JMP 0x0000002A Jump +00000002: 940C0034 JMP 0x00000034 Jump +00000004: 940C0034 JMP 0x00000034 Jump +00000006: 940C0034 JMP 0x00000034 Jump +00000008: 940C0034 JMP 0x00000034 Jump +0000000A: 940C0034 JMP 0x00000034 Jump +0000000C: 940C0034 JMP 0x00000034 Jump + 0000000E: 940C0034 JMP 0x00000034 Jump +00000010: 940C0034 JMP 0x00000034 Jump +00000012: 940C0034 JMP 0x00000034 Jump +00000014: 940C0034 JMP 0x00000034 Jump +00000016: 940C0034 JMP 0x00000034 Jump +00000018: 940C0034 JMP 0x00000034 Jump +0000001A: 940C0034 JMP 0x00000034 Jump +0000001C : 940C0034 JMP 0x00000034 Jump +0000001E: 940C0034 JMP 0x00000034 Jump +00000020: 940C0034 JMP 0x00000034 Jump +00000022: 940C0034 JMP 0x00000034 Jump +00000024: 940C0034 JMP 0x00000034 Jump +00000026: 940C0034 JMP 0x00000034 Jump +00000028: 940C0034 JMP 0x00000034 Jump

00000000: 940C002A JMP 0x0000002A Jump +00000002: 940C0034 JMP 0x00000034 Jump +00000004: 940C0034 JMP 0x00000034 Jump +00000006: 940C0034 JMP 0x00000034 Jump +00000008: 940C0034 JMP 0x00000034 Jump +0000000A: 940C0034 JMP 0x00000034 Jump +0000000C: 940C0034 JMP 0x00000034 Jump +0000000E : 940C0034 JMP 0x00000034 Jump +00000010: 940C0034 JMP 0x00000034 Jump +00000012: 940C0034 JMP 0x00000034 Jump +00000014: 940C0034 JMP 0x00000034 Jump +00000016: 940C0034 JMP 0x00000034 Jump +00000018: 940C0034 JMP 0x00000034 Jump +0000001A: 940C0034 JMP 0x00000034 Jump +0000001C: 940C0034 JMP 0x00000034 Jump +0000001E: 940C0034 JMP 0x00000034 Jump +00000020: 940C0034 JMP 0x00000034 Jump +00000022: 940C0034 JMP 0x00000034 Jump +00000024: 940C0034 JMP 0x00000034 Jump +00000026: 940C0034 JMP 0x00000034 Jump +00000028: 940C0034 JMP 0x00000034 Jump

Esta é a tabela de vetores de interrupção. Voltaremos a ela mais tarde, por enquanto, basta olhar e lembrar que ela está lá. A primeira coluna é o endereço da célula flash em que o comando está localizado, a segunda é o código de comando, a terceira mnemônica de comando, a mesma instrução do assembler, os terceiros operandos do comando. Ah, e comentário automático.
Então, se você olhar, há transições contínuas. E o código de comando JMP é de quatro bytes, ele contém o endereço de salto escrito para trás - o byte baixo no endereço baixo e o código de comando de salto 940C

0000002B: BE1F OUT 0x3F,R1 Out to I/O location

Escreva este zero no endereço 0x3F.Se você olhar na coluna de visualização de E/S, verá que o endereço 0x3F é o endereço do registrador SREG - o registrador de flag do controlador. Aqueles. redefinimos o SREG para executar o programa em condições zero.

1 2 3 4 +0000002C: E5CF LDI R28,0x5F Carregar imediato +0000002D: E0D4 LDI R29,0x04 Carregar imediatamente +0000002E: BFDE OUT 0x3E,R29 Out to I/O location +0000002F: BFCD OUT 0x3D,R28 Out to I/O location

0000002C: E5CF LDI R28,0x5F Carregar imediato +0000002D: E0D4 LDI R29,0x04 Carregar imediatamente +0000002E: BFDE OUT 0x3E,R29 Out to I/O location +0000002F: BFCD OUT 0x3D,R28 Out to I/O location

Isso está carregando o ponteiro de pilha. Você não pode carregar registradores diretamente em I/O, apenas através de um registrador intermediário. Portanto, primeiro LDI para intermediário, e depois OUT para I/O. Também vou falar mais sobre a pilha. Enquanto isso, saiba que essa é uma área de memória tão dinâmica, que fica travada no final da RAM e armazena endereços e variáveis ​​intermediárias nela mesma. Agora indicamos de onde a pilha começará.

00000032: 940C0041 JMP 0x00000041 Saltar

Um salto para o saaaaamy final do programa, e lá temos interrupções desabilitadas e em loop firmemente em si mesmo:

1 2 +00000041: 94F8 CLI Global Interrupção Desativar +00000042: CFFF RJMP PC-0x0000 Salto relativo

00000041: 94F8 CLI Global Interrupção Desativar +00000042: CFFF RJMP PC-0x0000 Salto relativo

Isso ocorre em caso de circunstâncias imprevistas, como uma saída da função principal. O controlador pode ser retirado de tal loop por uma reinicialização de hardware ou, mais provavelmente, por uma reinicialização de um watchdog. Bem, ou, como eu disse acima, conserte esses lugares no editor hexadecimal e ande para onde quisermos. Observe também que existem dois tipos de saltos JMP e RJMP o primeiro é um salto direto para um endereço. Ocupa quatro bytes e pode fazer um salto direto sobre toda a área de memória. O segundo tipo de transição - RJMP - é relativo. Seu comando leva dois bytes, mas ele salta da posição atual (endereço) 1024 passos para frente ou para trás. E seus parâmetros indicam o deslocamento do ponto atual. Usado com mais frequência, tk. ocupa metade do espaço no flash e raramente são necessárias transições longas.

1 +00000034: 940C0000 JMP 0x00000000 Saltar

00000034: 940C0000 JMP 0x00000000 Saltar

E este é um salto para o início do código. Uma espécie de reinicialização. Você pode verificar se todos os vetores saltam aqui. A partir desta conclusão - se você agora habilitar as interrupções (elas estão desabilitadas por padrão) e você tiver uma interrupção, mas não houver um manipulador, haverá uma reinicialização do software - o programa será lançado para o início.

função principal. Tudo é o mesmo, você não pode nem descrever. Procure apenas nos registros o número já calculado é inserido. Pré-processador do compilador arrasa!!! Portanto, nada de números "mágicos"!

1 2 3 4 5 6 7 8 9 10 11 12 <

00000036: E383 LDI R24,0x33 Carregar imediato +00000037: B989 OUT 0x09,R24 Out to I/O location 15: UBRRH = HI(bauddivider); +00000038: BC10 OUT 0x20,R1 Out to I/O location 16: UCSRA = 0; +00000039: B81B OUT 0x0B,R1 Out to I/O location 17: UCSRB = 1<

E aqui está o batente:

1 2 3 +0000003E: E080 LDI R24,0x00 Carregar imediato +0000003F: E090 LDI R25,0x00 Carregar imediato +00000040: 9508 RET Retorno de subrotina

0000003E: E080 LDI R24,0x00 Carregar imediato +0000003F: E090 LDI R25,0x00 Carregar imediatamente +00000040: 9508 RET Retorno de subrotina

A questão é: por que o compilador adiciona esses tops? E isso nada mais é do que Return 0, então definimos a função como int main (void) então fodemos mais quatro bytes, não entendi o quê :) E se você fizer void main (void) então apenas RET permanecerá, mas aparecerá um aviso, que eles dizem que nossa função principal não retorna nada. Em geral, faça o que quiser :)

Difícil? Parece que não. Clique em execução passo a passo no modo desmontador e veja como o processador executa instruções individuais, o que acontece com os registradores. Como é a movimentação pelos comandos e o loop final.

Continua em alguns dias...

Fora do topo:
Alexei78 Eu fiz um plugin para o firefox que facilita a navegação no meu site e fórum.
Discussão e download,

Existem várias linguagens de programação para microcontroladores AVR, mas assembler e C são talvez as mais indicadas, pois essas linguagens fornecem a melhor implementação de todos os recursos necessários para controlar o hardware do microcontrolador.

Assembler é uma linguagem de programação de baixo nível que usa o conjunto de instruções diretas do microcontrolador. Criar um programa nesta linguagem requer um bom conhecimento do sistema de comando do chip programável e tempo suficiente para desenvolver o programa. Assembler perde para C em termos de velocidade e conveniência de desenvolvimento do programa, mas tem vantagens notáveis ​​no tamanho do código executável final e, consequentemente, na velocidade de sua execução.

C permite criar programas com muito mais conforto, dando ao desenvolvedor todos os benefícios de uma linguagem de alto nível.
Cabe ressaltar mais uma vez que a arquitetura e o sistema de comando do AVR foi criado com a participação direta dos desenvolvedores do compilador C e leva em consideração as características desta linguagem. Compilar o código-fonte escrito em C é rápido e produz um código compacto e eficiente.

As principais vantagens do C sobre o montador são: alta velocidade de desenvolvimento do programa; versatilidade que dispensa um estudo aprofundado da arquitetura do microcontrolador; melhor documentabilidade e legibilidade do algoritmo; disponibilidade de bibliotecas de funções; suporte para cálculos de ponto flutuante.

A linguagem C combina harmoniosamente as capacidades da programação de baixo nível com os recursos de uma linguagem de alto nível. A capacidade de programação de baixo nível facilita a operação direta no hardware, e as propriedades de linguagem de alto nível permitem a criação de código de programa facilmente legível e modificável. Além disso, quase todos os compiladores C têm a capacidade de usar inserções de montador para escrever seções críticas do programa em termos de tempo de execução e recursos.

Em uma palavra, C é a linguagem mais conveniente tanto para iniciantes se familiarizarem com microcontroladores AVR quanto para desenvolvedores sérios.

Os compiladores são usados ​​para converter o código-fonte do programa em um arquivo de firmware do microcontrolador.

A Atmel fornece um compilador montador poderoso que está incluído no ambiente de desenvolvimento Atmel Studio baseado em Windows. Junto com o compilador, o ambiente de desenvolvimento contém um depurador e um emulador.
O Atmel Studio é totalmente gratuito e está disponível no site da Atmel.

Existem alguns compiladores C para o AVR atualmente. O mais poderoso deles é o compilador de IAR Systems de Estocolmo. Foram seus funcionários que, em meados dos anos 90, participaram do desenvolvimento do sistema de comando AVR. O IAR C Compiler possui amplos recursos de otimização de código e vem como parte do ambiente de desenvolvimento integrado IAR Embedded Workbench (EWB), que também inclui um compilador assembler, um linker, um gerenciador de projeto e biblioteca e um depurador. O preço da versão completa do pacote é 2820 EUR. No site da empresa, você pode baixar uma versão de avaliação gratuita por 30 dias ou uma versão ilimitada com limite de tamanho de código de 4 KB.

A empresa americana Image Craft de Palo Alto, Califórnia, produz um compilador para a linguagem C, que ganhou bastante popularidade. JumpStart C para AVR tem otimização de código aceitável e não é muito caro (de $ 50 a $ 499 dependendo da versão). A versão demo do JumpStart C para AVR é ​​totalmente funcional por 45 dias.

O romeno Code Vision AVR C Compiler não ganhou menos popularidade, o preço da versão completa deste compilador é relativamente baixo e chega a 150 EUR. O compilador vem com um ambiente de desenvolvimento integrado, que, além dos recursos padrão, inclui um recurso bastante interessante - CodeWizardAVR Automatic Program Generator. A presença de um terminal serial no ambiente de desenvolvimento permite depurar programas usando a porta serial do microcontrolador. Os desenvolvedores podem baixar uma versão de avaliação gratuita com um limite de tamanho de código de 4 KB e o salvamento desabilitado do código-fonte C gerado.

A MikroElektronika, localizada na cidade sérvia de Belgrado, produz uma família inteira de compiladores para microcontroladores AVR. Um compilador C chamado mikroC PRO para AVR custa $ 249. Há também mikroBasic e mikroPascal pelo mesmo preço. Existem demos no site do desenvolvedor com um limite de tamanho de código de 4096 bytes. A vantagem desta família de compiladores é uma única plataforma e uma única ideologia, que pode proporcionar uma transição fácil não só entre linguagens, mas também entre microcontroladores (existem versões de compiladores para PIC, STM32, 8051...).

O ambiente de desenvolvimento integrado tornou-se verdadeiramente icônico. Inclui poderosos compiladores C e assembler, um programador AVRDUDE, um depurador, um simulador e muitos outros programas e utilitários de suporte. O WinAVR integra-se perfeitamente ao ambiente de desenvolvimento AVR Studio da Atmel. O montador é idêntico no código de entrada ao montador do AVR Studio. Os compiladores C e assembler têm a capacidade de criar arquivos de depuração no formato COFF, o que permite usar não apenas as ferramentas internas, mas também o poderoso simulador AVR Studio. Outra vantagem importante é que o WinAVR é ​​distribuído gratuitamente sem restrições (os fabricantes suportam a GNU General Public License).

Resumindo, o WinAVR é ​​a escolha ideal para quem está começando a dominar os microcontroladores AVR. É este ambiente de desenvolvimento que é considerado como o principal neste curso.

As operações bit a bit são baseadas em operações lógicas, que já abordamos anteriormente. Eles desempenham um papel fundamental na programação de microcontroladores AVR e outros tipos. Quase nenhum programa pode fazer sem o uso de operações bit a bit. Até agora, nós os evitamos deliberadamente para facilitar o aprendizado da programação MK.

Em todos os artigos anteriores, apenas programamos portas de E/S e não utilizamos nós embutidos adicionais, como temporizadores, conversores analógico-digitais, interrupções e outros dispositivos internos sem os quais o MK perde toda a sua potência.

Antes de passar a dominar os dispositivos MK integrados, você precisa aprender a controlar ou verificar bits individuais dos registradores AVR MK. Anteriormente, realizamos uma verificação ou definimos os bits de todo o registro de uma só vez. Vamos ver qual é a diferença, e depois continuar.

Operações bit a bit

Na maioria das vezes, ao programar microcontroladores AVR, nós o usamos, pois tem maior clareza em comparação e é bem compreendido por programadores MK iniciantes. Por exemplo, precisamos definir apenas o 3º bit da porta D. Para isso, como já sabemos, podemos usar o seguinte código binário:

PORTD = 0b00001000;

No entanto, com este comando, definimos o 3º bit para um e zeramos todos os outros (0, 1, 2, 4, 5, 6 e 7º). E agora vamos imaginar a situação que os 6º e 7º dígitos são usados ​​como entradas do ADC e neste momento um sinal de algum dispositivo chega nas saídas correspondentes do MK, e nós resetamos esses sinais usando o comando acima. Como resultado, o microcontrolador não os vê e acredita que os sinais não vieram. Portanto, em vez de tal comando, devemos usar outro que definiria apenas o 3º bit para um, sem afetar o restante dos bits. Para isso, geralmente é usada a seguinte operação bit a bit:

PORTO |= (1<<3);

Analisaremos sua sintaxe em detalhes a seguir. E agora outro exemplo. Digamos que precisamos verificar o estado do 3º bit do registrador PIND, verificando assim o estado do botão. Se este bit for zerado, então sabemos que o botão foi pressionado e então o código de comando é executado, que corresponde ao estado do botão pressionado. Anteriormente, teríamos usado a seguinte notação:

if (pind == 0b00000000)

(qualquer código)

No entanto, com a ajuda dele, verificamos não um único, - o 3º, mas todos os bits do registro PIND de uma só vez. Portanto, mesmo se o botão for pressionado e o bit desejado for resetado, mas nesse momento um sinal for recebido em qualquer outro pino da porta D, o bit correspondente será definido como um, e a condição entre parênteses será falsa. Como resultado, o código entre chaves não será executado mesmo quando o botão for pressionado. Portanto, para verificar o status de um 3º bit individual do registrador PIND, uma operação bit a bit deve ser usada:

se (~PIND & (1<<3))

(qualquer código)

Para trabalhar com bits individuais de microcontroladores, a linguagem de programação C tem em seu arsenal, com a qual é possível alterar ou verificar o estado de um ou mais bits individuais de uma só vez.

Configurando um único bit

Para definir um único bit, como a porta D, é usada uma operação OR bit a bit. Foi o que usamos no início do artigo.

PORTD = 0b00011100; // valor inicial

PORTD = PORTD | (1<<0); применяем побитовую ИЛИ

PORTO |= (1<<0); // сокращенная форма записи

PORTD == 0b00011101; // resultado

Este comando define o bit para zero e deixa o resto inalterado.

Por exemplo, vamos definir o 6º bit da porta D.

PORTD = 0b00011100; // estado inicial da porta

PORTO |= (1<<6); //

PORTD == 0b01011100; // resultado

Para escrever um ou vários bits separados de uma só vez, por exemplo, zero, sexta e sétima porta B aplica-se a seguinte notação.

PORTB = 0b00011100; // valor inicial

PORTB |= (1<<0) | (1<<6) | (1<<7); //

PORTB == 0b1011101; // resultado

Redefinindo (zerando) bits individuais

Para redefinir um único bit, três comandos discutidos anteriormente são usados ​​de uma só vez: .

Vamos resetar o 3º bit do registrador PORTC e deixar o resto inalterado.

PORTC = 0b00011100;

PORTA &= ~(1<<3);

PORTC == 0b00010100;

Vamos realizar ações semelhantes para o 2º e 4º dígitos:

PORTC = 0b00111110;

PORTC &= ~((1<<2) | (1<<4));

PORTC == 0b00101010;

Mudando a batida

Além de definir e redefinir, também é usado um comando útil que alterna um único bit para o estado oposto: um para zero e vice-versa. Essa operação lógica é amplamente utilizada na construção de vários efeitos de iluminação, por exemplo, como uma guirlanda de ano novo. Considere o exemplo do PORTA

PORTA = 0b00011111;

PORTA ^= (1<<2);

PORTA == 0b00011011;

Altere o estado dos bits zero, segundo e sexto:

PORTA = 0b00011111;

PORTA ^= (1<<0) | (1<<2) | (1<<6);

PORTA == 0b01011010;

Verificando o status de um bit individual. Deixe-me lembrá-lo que a verificação (ao invés de escrita) de uma porta de E/S é realizada através da leitura de dados do registro PIN.

O teste mais comum é realizado por uma das duas instruções de loop: if e while. Já estamos familiarizados com esses operadores anteriormente.

Verificando a descarga quanto à presença de um zero lógico (reset) com E se

if (0==(PIND & (1<<3)))

Se o terceiro bit da porta D for desenergizado, o Code1 será executado. Caso contrário, Code2 é executado.

Ações semelhantes são realizadas com e nesta forma de gravação:

se (~PIND & (1<<3))

Verificação da descarga quanto à presença de uma unidade lógica (configuração) com E se

if (0 != (PIND & (1<<3)))

se (PIND & (1<<3))

Os dois loops acima funcionam de forma semelhante, mas devido à flexibilidade da linguagem de programação C, eles podem ser escritos de forma diferente. A operação != significa diferente. Se o terceiro bit da porta PD I/O estiver definido (um), então o Code1 é executado, caso contrário, o Code2.

Aguardando um pouco de reset com enquanto

enquanto (PIND & (1<<5))

Code1 será executado enquanto o 5º bit do registrador PIND estiver definido. A redefinição começará a executar o Code2.

Aguardando o bit ser definido enquanto

Aqui, a sintaxe da linguagem C permite escrever código de duas das maneiras mais comuns. Na prática, ambos os tipos de gravação são usados.