terça-feira, 8 de abril de 2014

Introdução ao compilador MPLAB C18 da Microchip

Conforme dito no primeiro artigo sobre sistemas embarcados, irei abordar exemplos de código para processadores ARM e também PIC. E para ter um melhor entendimento é importante que se saiba alguns detalhes específicos do compilador MPLAB C18 da Microchip. Irei falar em outros artigos sobre o compilador do ARM indicado no artigo anterior, o gcc-arm-embedded. Lembrando novamente que existem vários compiladores para ARM.

Compiladores C para sistemas embarcados


Quando se trabalha com sistemas embarcados a um certo tempo, percebe-se que cada compilador é diferente na forma de programação, embora todos suportem a "mesma" linguagem C. Especialmente quando se trata de coisas especificas de microcontroladores como interrupções, manipulação de pinos de entrada e saída, etc. Mesmo quando comparamos compiladores para uma mesma arquitetura como PIC, por exemplo, eles ainda são diferentes na forma de programar! Depois de muito tempo fazendo projetos com PIC, considero o MPLAB C18 um dos melhores compiladores. E quando se trata de compiladores C para microcontroladores ARM a diferença é ainda maior! Você entenderá o porque em artigos posteriores.

MPLAB C18



O compilador MPLAB C18 é projetado para gerar executáveis para microcontroladores PIC18, com suporte ao padrão ANSI C, porém otimizado, e trabalha em conjunto com o MPLINK. O MPLINK já vem embutido na IDE do MPLAB. Para quem não sabe o MPLAB é um ambiente integrado de desenvolvimento para microcontroladores PIC em geral. Porém, nativamente ele só suporta assembly para PIC. Por isso existe a necessidade do compilador MPLAB C18. 
O compilador MPLAB C18 ou simplesmente MC18, tem os seguintes atributos:
  • Compatibilidade com o ANSI '89
  • Integração com a IDE MPLAB para gerenciamento de projeto e debug
  • Geração de módulos de objetos realocáveis para reuso de código
  • Compatibilidade com módulos de objetos gerados pelo montador MPASM, permitindo a liberdade para misturar código C com código assembly
  • Gerador de código eficiente com otimizações em multi nível
  • Biblioteca para manipulação de hardware, incluindo PWM, SPI, I2C, UART, manipulação de string e funções matemáticas

Invocando o compilador


O compilador MC18 pode ser usado em conjunto com a IDE MPLAB ou pode ser invocado da linha de comando. A forma de usar via linha de comando é:
mcc18 [options] file [options]
A maioria dos projetistas usam o compilador MC18 integrado na IDE MPLAB. Porque é mais fácil de criar e manipular projetos de código fonte. Principalmente quando o código fonte é grande.

Tipos de dados e limites


O compilador MC18 suporta o padrão ANSI definido para tipos inteiros. O intervalo dos tipos inteiros são mostrados na tabela abaixo. Em adição, o compilador MC18 suporta um tipo inteiro de 24 bits, em duas variações, com sinal e sem sinal.

Também é possível usar variáveis de ponto flutuante, mas em geral, seu uso não é recomendado em sistemas embarcados, porque é preciso alocar muito espaço. O uso de variáveis de ponto flutuante precisa ser moderado.

Qualificadores de Acesso (Importante)


Em adição aos qualificadores de acesso do padrão ANSI C (const, volatile), o compilador MPLAB C18 introduz qualificadores de acesso far, near, rom e ram. Estes qualificadores são muitos importantes em programação de sistemas embarcados. Sintaticamente, a forma de uso desses novos qualificadores é parecida com o uso de const e volatile. Estes qualificadores definem o local onde as variáveis serão armazenadas. Os qualificadores padrões para um objeto caso não seja especificado nenhum é far e ram.


Pragmas (Muito importante)


#pragma tipo-de-seção
A declaração de uma seção de pragma muda a forma como o compilador MC18 alocará a informação do tipo associado. Uma seção é uma porção de uma aplicação localizada em um local especifico de memoria. Porções podem conter códigos ou dados. Uma seção pode estar localizada na memoria de programa ou na memoria de dados. Existem dois tipos de seções para cada tipo de memoria.
  • memoria de programa
code: contem instruções executáveis
romdata: contem variáveis e constantes
  • memoria de dados
udata: contem variáveis não inicializadas estaticamente alocadas
idata: contem variáveis inicializadas estaticamente alocadas

Seções de pragmas são especialmente uteis para definir interrupções do microcontrolador associada a algum periférico. Exemplo, código de tratamento para interrupção do timer, interrupção da interface UART, interrupção externa de um pino, etc.
/* A declaração seguinte muda a
* seção do codigo para o endereço absoluto 0x08
*/
#pragma code high_vector=0x08
Isso é necessário porque o código de tratamento de uma interrupção precisa estar em um endereço de memoria especifico.

Modelo de rotina de tratamento de interrupção para prioridade baixa
#include <p18cxxx.h>

void low_isr(void);
void high_isr(void);

/* For PIC18 devices the low interrupt vector is found at
* 00000018h. The following code will branch to the
* low_interrupt_service_routine function to handle
* interrupts that occur at the low vector.
*/
#pragma code low_vector=0x18
void interrupt_at_low_vector(void)
{
_asm GOTO low_isr _endasm
}
#pragma code /* return to the default code section */

#pragma interruptlow low_isr
void low_isr (void)
{
/* ... */
}
Quando uma interrupção ocorre o processador pula para função de tratamento de interrupção interrupt_at_low_vector() e esta função chama a função low_isr(). Perceba que a função está localizada em um endereço especifico, 0x18, usado para interrupções de baixa prioridade. Em microcontroladores PIC18 há interrupções de baixa prioridade e interrupções de alta prioridade.

Exemplo de um pisca LED

#include <p18cxxx.h>
#include <timers.h>

#define NUMBER_OF_LEDS 8

void timer_isr (void);
static unsigned char s_count = 0;

#pragma code low_vector=0x18
void low_interrupt (void)
{
    _asm GOTO timer_isr _endasm
}
#pragma code

#pragma interruptlow timer_isr save=PROD
void timer_isr (void)
{
    static unsigned char led_display = 0;
    INTCONbits.TMR0IF = 0;
    s_count = s_count % (NUMBER_OF_LEDS + 1);
    led_display = (1 << s_count++) - 1;
    PORTB = led_display;
}

void main (void)
{
    TRISB = 0;
    PORTB = 0;
    OpenTimer0 (TIMER_INT_ON & T0_SOURCE_INT & T0_16BIT);
    INTCONbits.GIE = 1;
    while (1)
   {
   }
}
Salve esse exemplo como led.c e compile da seguinte forma:
mcc18 -p 18c452 -I c:\mcc18\h leds.c
onde c:\mcc18\h é o diretório em que o compilador foi instalado e -p indica o modelo de microcontrolador usado. Você pode simular um microcontrolador PIC18 rodando esse código com um programa chamado Proteus.


Nenhum comentário:

Postar um comentário