- Criação de projeto/edição de programas: basta escolher na janela inicial "AVR GCC"
em vez de "Avr Atmel Assembler"; os passos seguintes são idênticos aos da criação
de um projeto em assembler; o sistema automaticamente coloca o sufixo .c no programa
sendo editado.
- Configuração: em Project → Configuration Options escolha:
Optmization: -Os e marque a caixa Generate List File (gera o arquivo .lss contendo o código em C junto
com o código gerado em assembler).
- Includes: o comando #include <avr/io.h> equivale à diretiva
.include "m88def.inc" do assembler: inclui as definições de constantes,
registradores de E/S, etc do modelo do AVR selecionado; deve constar em todos os
programas em C; outros includes poderão ser usados dependendo do recurso
da biblioteca requerido, conforme mostram os exemplos a seguir.
- Programando os registradores de E/S
Comandos de atribuição (*) usando
o nome de portas de E/S automaticamente geram a instrução out nome-porta, ... ou in ...,nome-porta
(caso a otimização -Os tenha sido configurada, conforme o item Configuração, acima,
caso contrário o compilador gera instruções de 32 bits para acesso aos registradores de E/S
via memória, bastante ineficientes)
Exemplo:
#include <avr/io.h>
. . .
DDRB= 0xff; // programa todos os pinos da porta B para saida
PORTB |= curled; // liga curled na PORTA B (*)
byte= PORTD // lê a PORTA D; equivale a: in rx, PORTD
Macros adicionais para manipular e testar bits dos registradores de E/S, etc, estão
disponiveis via comando #include<avr/sfr_defs.h> (avr-libc manual 6.17 p. 133-135); abaixo
uma lista das macros extraída do manual:
Bit manipulation:
#define _BV(bit) (1 << (bit)) //0=< bit <7
Obs: o compilador gera uma máscara com apenas um bit ligado, correspondente ao numero do bit bit
IO register bit manipulation:
#define bit_is_set(sfr, bit) (_SFR_BYTE(sfr) & _BV(bit))
#define bit_is_clear(sfr, bit) (!(_SFR_BYTE(sfr) & _BV(bit)))
#define loop_until_bit_is_set(sfr, bit) do { } while (bit_is_clear(sfr, bit))
#define loop_until_bit_is_clear(sfr, bit) do { } while (bit_is_set(sfr, bit))
Um exemplo de uso:
#include <avr/io.h>
#include<avr/sfr_defs.h>
...
PORTB |= _BV(PB7); // coloca em 1 o bit 7 da PORTA B (máscara = 128 = 0x80)
if(bit_is_set(PORTB,1)) // testa o bit 1 da PORTA B,
temp=1; // deve entrar aqui,
else
temp=0; // mas não aqui
- Gravando e acessando dados na memória de programa: (avr libc manual 6.14 p. 116).
Exemplo:
#include <avr/io.h>
#include <avr/pgmspace.h>
char mystring[] PROGMEM= "ABCDEFGHI"; //esta cadeia vai ser gerada na área de programa
char byte, char buffer[32]; // buffer na RAM para onde vamos copiar a cadeia
...
strcpy_P(buffer, mystring); //copia a cadeia C (terminada por 0) para a RAM, ou então,
byte= pgm_read_byte(&mystring[i]); //se quisermos obter apenas o i-ésimo byte de mystring
- Rotinas de interrupção: (avr libc manual 6.13 p. 97)
Para instalar um vetor e a rotina correspondente basta declarar a função:
ISR(Nome_do_tipo_vetor) (ver tabela p. 100-114, longa, pois o nome varia com o modelo do AVR).
O código gerado pelo compilador salva o contexto do programa interrompido e
ao retornar da rotina de interrupção gera automaticamente reti. Observe
que a programação das máscaras de interrupção, registradores de contrôle, etc, deve
ser feita externamente no programa principal.
Exemplo:
#include <avr/io.h>
#include <avr/interrupt.h>
ISR(TIMER0_OVF_vect) // rotina de interrupção de overflow do Timer0
{ // veja um exemplo completo em ctimer0.c
....
....
} // retorna ao programa interrompido via reti
ISR(INT0_vect) //rotina de interrupção externa INT0
//veja um exemplo completo aqui
{
...
} // retorna ao programa interrompido via reti
- Funções para os modos de sleep (avr libc manual 6.18 p. 136-138)
void set_sleep_mode (uint8_t mode)
void sleep_mode (void)
void sleep_enable (void)
void sleep_disable (void)
void sleep_cpu (void)
Exemplo:
#include <avr/interrupt.h>
#include <avr/sleep.h>
...
cli();
if (some_condition) {
set_sleep_mode(SLEEP_MODE_IDLE); // configura o modo sleep no registrador SMCR
sleep_enable(); //liga o bit SE (p/ habilitar o sleep) no registrador SMCR
sei(); // gera a instrução sei
sleep_cpu(); // gera a instrução sleep
sleep_disable(); // desliga o bit SE no registrador SMCR
}
- Tamanhos de inteiros suportados pelo Avr-gcc
O Avr-gcc suporta inteiros com e sem sinal de 8, 16, 32 e 64 bits, através
das declarações: com sinal: int8_t, int ou int16_t, int32_t, int64_t e
sem sinal: uint8_t, unsigned int ou uint16_t, uint32_t, uint64_t.
Observe que as declarações padrão
int e unsigned int são para inteiros com 16 bits (ao contrário do
Linux convencional para processadores Pentium que são de 32 bits); a declaração char
é implementada como um inteiro de 8 bits sem sinal (ao contrário do gcc do Linux).
Isto não é um bug do Avr-gcc pois a especificação do ANSI/ISO C coloca ambos como
dependentes da implementação. Essas diferenças podem afetar a portabilidade de
programas do ambiente Linux para o Avr-gcc.
Exemplos:
int n; // inteiro com sinal de 16 bits
uint_8 nsmall; // inteiro sem sinal de 8 bits
unsigned int n; // inteiro sem sinal de 16 bits
uint32_t p; // inteiro sem sinal de 32bits
- Rotinas para fazer atraso por software (busy-wait): (avr libc manual 6.22 p. 147)
Exemplo:
#include <util/delay.h>
_delay_loop_2(0); // atrasas 262 ms numa CPU com relógio de 1 MHZ
- Embutindo instruções em assembler dentro de programas em C:
Veja sintaxe e exemplos neste documento da biblioteca
avr-libc.
O tema é importante mas não trivial devido ao problema da comunicação do assembler com variáveis
do programa em C (também chamado de impedance mismatch).
- Manipulação de bits na linguagem C
Os operadores binários a seguir fazem parte do padrão ANSI/ISO C
(ou seja valem para qualquer compilador aderente ao padrão):
& bitwise AND
| bitwise OR
^ bitwise Exclusive OR
<< shift left
>> shift right
~ complemento de 1
Exemplos:
int n;
n = n << 2; // desloca n 2 bits para a esquerda (i. é multiplica n por 4)
n = n & 0xfe; // desliga o bit - significativo de n
n = n & 1; // obtém o bit - significativo de n
n = n | 1; // liga o bit menos significativo de n
Operador módulo (obtém o resto da divisão de dois inteiros:
m = n % 3;