8
 Programação em C em micro-controlador es.  Neste conjunto de tutoriais, tentamos ensinar ao leitor como programar um micro-controlador AVR em C “low-level”. Para fazer isso, é especialmente necessário compreend er como controlar as várias funções do micro-controlador. Os restantes tutoriais concentram-se nisso.  No entanto, para compreender os exemplos dados, e poder aplicar o que é ensinado, o leitor necessita de compreender algumas coisas básicas primeiro, respectivame nte: Controlo da funcionalidade do micro-controlador – os registers. Pseudo-código/có digo esqueleto MACROS Variáveis volatile Operações bit-wise em C. É assumido que o leitor sabe programar em C e que domina os seguintes conceitos: comentários,  bibliotecas, variáve is, funções, po nteiros, ciclos, cond ições, lógica e bases numéricas. Controlo da funcionalidade do micro-controlador – os registers Os AVR têm várias funções: podem ser usados para comparar e ler diferenças de potencial, comunicar por serial, … T odas estas funções são controladas por registers … mas o que são registers? Todos os CPUs têm uma certa memória interna. Esta funciona quase como a memória ram, excepto no uso de ponteiros. O CPU tem acesso directo a esta memória, o que significa que em termos de performance é muito mais eficiente usar registers para armazenamento do que memória ram (o compilador em C optimiza automaticamente os programas, dando uso deste “boost” na performance sempre que  possível – daí a importância de usar variáveis volatile quando se usam interrupções, estudadas mais à frente). No entanto, estes não são só usados para armazenamento, mas também para controlar várias funções dos micro-controladores. Certos bits em certos registers podem controlar o estado de um pino, ligar e desligar o ADC, … Nos AVR todos os registers têm o tamanho de 8 bits. Logo, quando é necessário armazenar valores maiores que 255, usam-se mais do que um register. No entanto, este pormenor é abstraído pelo compilador, visto que podemos muitas vezes aceder a um conjunto de registers como se fosse um só (como por exemplo, o register TCNT1 do timer1 que corresponde a dois registers, visto que pode conter um valor de 16 bits). Agora que sabemos o que é um register, vamos aprender como usá-los. As bibliotecas do avr dão-nos um header muito útil que nos permite aceder directamente aos

bitwise microcontroladores

Embed Size (px)

Citation preview

Page 1: bitwise microcontroladores

5/17/2018 bitwise microcontroladores - slidepdf.com

http://slidepdf.com/reader/full/bitwise-microcontroladores 1/8

 

Programação em C em micro-controladores.

 Neste conjunto de tutoriais, tentamos ensinar ao leitor como programar um micro-controlador 

AVR em C “low-level”. Para fazer isso, é especialmente necessário compreender como controlar as

várias funções do micro-controlador. Os restantes tutoriais concentram-se nisso. No entanto, para compreender os exemplos dados, e poder aplicar o que é ensinado, o leitor 

necessita de compreender algumas coisas básicas primeiro, respectivamente:

Controlo da funcionalidade do micro-controlador – os registers.

Pseudo-código/código esqueleto

MACROS

Variáveis volatile

Operações bit-wise em C.

É assumido que o leitor sabe programar em C e que domina os seguintes conceitos: comentários,

 bibliotecas, variáveis, funções, ponteiros, ciclos, condições, lógica e bases numéricas.

Controlo da funcionalidade do micro-controlador – os registers

Os AVR têm várias funções: podem ser usados para comparar e ler diferenças de potencial,

comunicar por serial, …Todas estas funções são controladas por registers … mas o que são registers?

Todos os CPUs têm uma certa memória interna. Esta funciona quase como a memória ram,

excepto no uso de ponteiros.

O CPU tem acesso directo a esta memória, o que significa que em termos de performance é

muito mais eficiente usar registers para armazenamento do que memória ram (o compilador em C

optimiza automaticamente os programas, dando uso deste “boost” na performance sempre que

 possível – daí a importância de usar variáveis volatile quando se usam interrupções, estudadas maisà frente). No entanto, estes não são só usados para armazenamento, mas também para controlar 

várias funções dos micro-controladores. Certos bits em certos registers podem controlar o estado de

um pino, ligar e desligar o ADC, … Nos AVR todos os registers têm o tamanho de 8 bits. Logo,

quando é necessário armazenar valores maiores que 255, usam-se mais do que um register. No

entanto, este pormenor é abstraído pelo compilador, visto que podemos muitas vezes aceder a um

conjunto de registers como se fosse um só (como por exemplo, o register TCNT1 do timer1 que

corresponde a dois registers, visto que pode conter um valor de 16 bits).Agora que sabemos o que é um register, vamos aprender como usá-los.

As bibliotecas do avr dão-nos um header muito útil que nos permite aceder directamente aos

Page 2: bitwise microcontroladores

5/17/2018 bitwise microcontroladores - slidepdf.com

http://slidepdf.com/reader/full/bitwise-microcontroladores 2/8

 

registers e bits dos mesmos através dos seus nomes: avr/io.h

Um exemplo:

Para alterar o estado de um pino, alteramos o bit correspondente no register DDRx (em que x

corresponde à porta. Por exemplo, o pino PB1 está na porta B, logo para alterar o seu estado,

alteramos o bit PB1 no register DDRB). Logo, utilizamos o código seguinte:

#include <avr/io.h>

int main(void) {

DDRB |= (1<<PB1); }

(quando alteramos o bit para 1, estamos a colocar o pino em output)

Se não compreende exactamente como alterámos um bit no register, não se preocupe, pois as

operações bit-wise serão explicadas de seguida.

Pseudo-código/código esqueleto

O pseudo-código é basicamente uma representação abstracta do código, em linguagem natural.

Muitas vezes começa-se por escrever o pseudo-código, e depois vai-se substituindo por linhas de

código (muitas vezes o pseudo-código transforma-se nos comentários). Irei usar isto nos meus

tutoriais para ir construindo os programas passo-a-passo.

Por exemplo, o famoso programa “Hello World”, feito passo-a-passo:

// Iniciar o programa

  // Escrever “Hello World no Ecrã”

// Terminar o programa

Primeiro, fazemos o mais simples: iniciar e terminar o programa. Como vamos precisar de

funções Input/Output, parte da inicialização é incluir o header stdio.h, o resto é começar a função

main(), e terminamos com return 0 (sair do programa com sucesso – visto que nos AVRs não existesistema operativo, a função main nunca fará um return, apenas acabará num loop infinito):

// Iniciar o programa:

#include <stdio.h>

int main(void) {

  // Escrever “Hello World” no Ecrã

  return 0; } // Terminar o programa

Agora falta a parte funcional do programa: escrever o Hello World no ecrã:

Page 3: bitwise microcontroladores

5/17/2018 bitwise microcontroladores - slidepdf.com

http://slidepdf.com/reader/full/bitwise-microcontroladores 3/8

 

// Iniciar o programa:

#include <stdio.h>

int main(void) {

  printf(“Hello World”); // Escrever “Hello World” no Ecrã

  return 0; } // Terminar o programa

MACROS 

Em quase todos os programas de C, temos instruções começadas por '#'. Estas não são instruções

em C, mas sim instruções interpretadas apenas pelo pré-processador, antes da compilação. Por 

exemplo, quando fazemos “#include <qualquercoisa.h>”, estamos a indicar ao pré-processador para

incluir o conteúdo do ficheiro qualquercoisa.h no nosso programa.

Uma MACRO é uma instrução deste tipo, que se comporta como uma função. São úteis quando

queremos realizar certas tarefas repetidamente, mas não se justifica o custo em performance de

chamar uma função (para quem programa em C++, isto é equivalente ao inline).

Por exemplo, duas macros que costumo usar são as seguintes:

#define max(I,J) ((I)>(J)?(I):(J))

#define min(I,J) ((I)<(J)?(I):(J))

Antes da compilação, o pré-processador substitui todas as declarações de max(x,y) e min(x,y)

 pelo código correspondente, sem ser assim necessário chamar uma função (as macros são úteis para

substituir principalmente funções com só uma linha de código). Há vários pormenores envolvidos

na criação de macro (como por exemplo, abusar das parêntesis para proteger o código), mas não

interessam para este tutorial. No entanto, visto que são muito úteis, aconselho os interessados a

 pesquisar sobre elas.

Variáveis volatile

Quando declaramos variáveis, podemos controlar certos aspectos de como o código deve acedê-las. Uma declaração importante quando se programa AVRs, devido à existência de interrupções, é a

volatile. Mais à frente explicarei a importância disto, por agora é apenas importante reter que

quando se declara uma variável como volatile, estamos a informar que o seu valor pode ser alterado

de formas inesperadas, logo deve sempre ir buscar o seu valor actualizado.

Operações bit-wise em C 

Muita da programação em micro-controladores consiste principalmente em manipular bits de

certos registers. Para fazer isso, usamos as operações bit-wise que manipulam valores ao nível dos

 bits.

Page 4: bitwise microcontroladores

5/17/2018 bitwise microcontroladores - slidepdf.com

http://slidepdf.com/reader/full/bitwise-microcontroladores 4/8

 

Para quem não compreende bases numéricas, e não sabe o que significa manipular bits,

aconselho a lerem algum livro/tutorial que trate deste assunto. No entanto, explicado de uma forma

 breve, é o seguinte:

 Normalmente usamos a base decimal. Isto significa que usamos 10 dígitos diferentes (do 0 ao 9).

Com combinações deles, fazemos diferentes números. Quando queremos um valor acima do dígito

maior, transportamos mais um para a posição seguinte (se contarmos desde a direita). Assim

 podemos dar valores a cada posição no número.

Por exemplo, o número 29 tem um 9 na posição 0 e um 2 na posição 1. A posição 0 corresponde

ao valor 10 (1), e a 1 ao valor 10¹. Assim, podemos chegar ao número através da conta: 2*10¹ +⁰  

9*10 .⁰

 Números de base binária funcionam da mesma forma que os de base decimal, com a

 particularidade de apenas utilizarmos dois algarismos: o 0 e o 1. Assim, cada posição tem um valor 

diferente. Por convenção, chamam-se às posições de um número em base binária de bit. Assim,

quando falamos em manipular bits, estamos a falar em manipular o valor (0 ou 1) de certas

 posições. Por exemplo, o número 1001 (para facilitar a leitura, costumam-se ler os dígitos

separados. Assim, em vez de se ler “mil e um”, lê-se “um zero zero um”) corresponde ao número

em decimal 9. Isto porque o bit 0 tem o valor de 1 (2 ) e o bit 3 tem o valor de 8 (2³). Logo, como⁰  

esses são os únicos bits com dígitos lá, chegamos ao 9 através da conta: 1*2 +1*2³.⁰

Agora que já conhecemos a base binária, e o que significa manipular bits, vamos ver como

 podemos manipulá-los.

Isto é feito através de operações bit-wise.

Em C, existem cinco operações bit-wise:

| – or 

& – and 

~ – not 

^ – xor 

<< – shift left 

>> – shift right 

As duas primeiras operações funcionam como as operações lógicas ||, &&. No entanto, em vez

de testarem a variável como um todo lógico, testam bit a bit, e o resultado corresponde a essacomparação bit a bit. Em termos de valor lógico, os pares de operações ||/| e &&/& dão exactamente

Page 5: bitwise microcontroladores

5/17/2018 bitwise microcontroladores - slidepdf.com

http://slidepdf.com/reader/full/bitwise-microcontroladores 5/8

 

o mesmo resultado. No entanto, enquanto temos resultados bem definidos com as operações | e &,

as operações || e && podem dar um valor aleatório para verdadeiro. Assim, quando se necessitam

de valores lógicos, devem-se usar as operações || e &&, e para manipulação bit a bit, devem-se usar 

as operações | e &.

Vamos então começar por estudar essas duas operações:

O or retorna 0 quando ambos os bits são 0, e 1 quando pelo menos um dos bits é 1. Olhemos para

um exemplo:

111000 | 001110 = 111110

Vamos analisar isto bit a bit. Em ambos os números, o bit 0 tem o valor 0. 0 ou 0 = 0. Logo, o bit

0 do resultado será um 0. No bit 1, o primeiro número tem um 0, mas o segundo tem um 1. 0 ou 1 =

1. Logo, o resultado terá um 1 no bit 1. A mesma coisa ocorre com o bit 2. No bit 3, ambos osnúmeros têm um 1. 1 ou 1 = 1. Logo, o resultado terá um 1 no bit 3. Nos restantes bits, o primeiro

número tem um 1, e o segundo tem um 0. 1 ou 0 = 1. Logo os restantes bits (bits 4 e 5) terão um 1

no resultado. Assim, chegamos ao número 111110.

Podemos usar isto para colocar o valor 1 numa certa posição num número.

Por exemplo, temos o número 1101 (em decimal é o número 13), e queremos preencher aquele 0

com um 1. Se fizermos um ou com o número 0010 (em decimal é o número 2), preenchemo-lo.

Vejamos um exemplo:

#include <stdio.h>

int main(void) {

  int i = 13;

i = i|2; // Equivalente a fazer i |= 2

  printf(“%d\n”, i); // Imprime o número 15 – em binário

1111.

  return 0; }

Vamos agora observar a operação &.

O and retorna 0 quando pelo menos um dos bits é 0, e 1 quando os dois bits são 1.

Por exemplo:1101 & 0111 = 0101

Page 6: bitwise microcontroladores

5/17/2018 bitwise microcontroladores - slidepdf.com

http://slidepdf.com/reader/full/bitwise-microcontroladores 6/8

 

A análise deste exemplo será deixada como um desafio ao leitor.

O & é muitas vezes usado para colocar a 0 um certo bit.

Por exemplo: se tivermos o número 10111 (em decimal 23), e quisermos a partir dele obter o

número 10101 (em decimal 21), podemos fazer a seguinte operação: 10111 & 1101 = 10101:

#include <stdio.h>

int main(void) {

  int i = 23;

i = i&13; // 13 – em binário 1101; equivalente a i &= 13;

  printf(“%d\n”, i); // Imprime 21

  return 0; }

A terceira operação, ~ (not), também tem um comportamento semelhante ao seu equivalente

lógico, o !. No entanto, foi separado das outras duas operações, pois esta não pode ser usada como

uma operação lógica, visto que ~(true) pode dar um valor verdadeiro à mesma (interessantemente,

devido à forma como a aritmética dos CPUs funcionam, fazer o ~ de qualquer número positivo dá

um número negativo e vice-versa, sendo a única excepção o -1, já que ~(-1) = 0. Nãoaprofundaremos mais isto, visto que não interessa muito para programar micro-controladores).

Vejamos como funciona:

~1101 = 0010

Cada bit do número original é invertido, logo a partir de um número positivo (true), podemos

não obter 0 (false), que é exactamente o que o ! lógico faz.

O ~ é muitas vezes utilizado em conjunção com o & para pôr um valor 0 num bit. Vejamos porquê:

11101 & 10111 = 10101

Sabendo a posição do bit que queremos pôr a 0 (neste caso a posição 4), como chegamos ao seu

inverso, de forma a manter o resto do número intacto.

Usando o ~, claro!

 Neste caso, fazer:11101 & 10111 = 10101

Page 7: bitwise microcontroladores

5/17/2018 bitwise microcontroladores - slidepdf.com

http://slidepdf.com/reader/full/bitwise-microcontroladores 7/8

 

é igual a fazer:

11101 & (~01000) = 11101 & 10111 = 10101

(mais à frente iremos estudar como criar um número com apenas um 1 na posição pretendida,

sabendo apenas essa posição).

Por exemplo, com código agora (reformulação do exemplo do &):

#include <stdio.h>

int main(void) {

  int i = 23;

i &= ~(8); // 8 – 01000

  printf(“%d\n”, i); // Imprime 21.

  return 0; }

O “exclusive or”, ou como é melhor conhecido, o xor, não tem um equivalente lógico óbvio. É o

mesmo que !=. O seu comportamento é o seguinte: retorna 0 quando ambos os números são iguais,

e 1 quando são diferentes. Como o & e o |, quando se procura um resultado lógico, é equivalente

usar o ^ e o !=.

Vejamos então um exemplo

1101 ^ 0101 = 1001

O xor é muitas vezes usado para fazer “toggle” (alterar o valor de 0 para 1 e vice-versa) de um

certo bit. Por exemplo, se tivermos um número 11x1, e quisermos alterar o estado do bit 1, sem

conhecermos o seu valor, basta fazer a seguinte operação:

11x1 ^ 0010

Isto porque quando fazermos um xor com 0, o resultado é sempre igual ao do outro número (1^0

= 1; 0^0 = 0), e quando fazemos um xor com 1, altera sempre (1^1 = 0; 1^0 = 1).

(visto que o código de exemplo seria semelhante aos anteriores, iremos passar à frente desse

 passo).

Agora só nos falta estudar os operadores de shift.

Estes são muito úteis porque nos permitem pôr um valor em qualquer posição do número, ouseja, fazer shift para cima ou para baixo desse mesmo valor.

Page 8: bitwise microcontroladores

5/17/2018 bitwise microcontroladores - slidepdf.com

http://slidepdf.com/reader/full/bitwise-microcontroladores 8/8

 

Vamos utilizar o exemplo do ~ e do &. Sabendo apenas a posição em que queremos pôr o 0, e o

número que tem essa posição a 1, e as restantes a 0, já sabemos que operação utilizar.

Mas ainda nos falta uma coisa: como chegamos ao número que tem a posição desejada a 1?

Para isso usam-se os operadores de shift.

Por exemplo, se quisermos colocar o 1 na posição 3, fazemos o seguinte:

1<<3 = 1000

#include <stdio.h>

int main(void) {

  int i = 23;

i &= ~(1<<3); // 1<<3 = 8 – 01000

  printf(“%d\n”, i); // Imprime 21.

  return 0; }

Esta técnica também é utilizada para chegar aos valores utilizador com o or e o xor, sabendo

apenas os bits que queremos, respectivamente, colocar a 1, ou alterar o valor.

Também existe o operador de shift >>, que faz o contrário do <<. Por exemplo:

111>>2 = 1

Mas é menos usado quando se programa micro-controladores.

É de notar que qualquer overflow é completamente esquecido.

Por exemplo, se considerarmos um limite de 5 bits:

10111<<3 = 11000

10111>>3 = 00010

Uma pequena curiosidade: dadas as características das bases numéricas, fazer <<x, é equivalente

a multiplicar por 2^x, e fazer >>x é equivalente a dividir por 2^x.

E assim terminamos o nosso tutorial acerca das bases de programação necessárias para

 programar micro-controladores. Esperamos que o leitor esteja agora preparado para se aventurar no

mundo da programação “low-level” dos mesmos!