O Compilador C da CCS (Custom Computer Services) talvez seja um dos mais fáceis de ser usado para gerar código c embarcado (ou embutido) para microcontroladores PIC. Talvez, também, seja um dos mais odiados por profissionais que o acusam de gerar código grande, lento, frente à outros como o HI-tech C, IAR ou Microchip C18, este para linha PIC18. Bem, o fato é a CCS tem melhorado seu produto. Então, resolvi postar aqui alguns recursos que a CCS diz serem exclusivos de seu compilador, conforme menciona a página do produto no site da empresa (Features Exclusive to CCS Compiler). Quem sabe depois disso, o compilador CCS deixe, ao menos um pouco, de ser tão criticado.
Recursos Exclusivos do Compilador C CCS (para PIC) (Exclusive Features to CCS C Compiler)
Constantes strings de comprimento variável
Examine esta ineficiente estrutura de constantes strings:
const char strings[3][15] =
{
"HELLO",
"WORLD",
"EXTRALONGERWORD"
};
{
"HELLO",
"WORLD",
"EXTRALONGERWORD"
};
No exemplo acima, foi deixado o comprimento máximo de cada string em 15 caracteres, isto devido à string "EXTRALONGERWORD". Mas, um vez que as strings "HELLO" e "WORLD" possuem somente 6 caracteres cada (não devemos esquecer do caractere de terminação null), 9 bytes são perdidos para cada um destas strings.
Para amenizar este problema, use o método para constantes strings de comprimento variável:
const char strings[][*] =
{
"HELLO",
"WORLD",
"EXTRALONGERWORD"
};
{
"HELLO",
"WORLD",
"EXTRALONGERWORD"
};
Obs.: Isto é possível devido à adição de inteligência na indexação de tabela de dados constantes. Devido à isto, não é possível criar ponteiros para strings constantes de comprimento variável.
Maior flexibilidade na manipulação de constantes
O compilador adiciona ponteiros para constantes:
/*
um simples exemplo mostrando a atribuição de ponteiro para constante com o endereço de uma string constante:
*/
const char version[] = "PRODUCT ID V1.01";
const char *ptr;
ptr = &version[0];
um simples exemplo mostrando a atribuição de ponteiro para constante com o endereço de uma string constante:
*/
const char version[] = "PRODUCT ID V1.01";
const char *ptr;
ptr = &version[0];
/*
Um exemplo mais complexo que cria um estrutura de ponteiros para strings constantes:
*/
const char *strings[] =
{
"HELLO",
"WORLD",
"CONST",
"STRINGS"
};
/*
Acessando os ponteiros para constantes acima:
*/
const char *ptr;
while (i = 0; i < (sizeof(strings) / sizeof(const char *)); i++)
{
ptr = strings[i];
printf("%s", ptr);
}
Um exemplo mais complexo que cria um estrutura de ponteiros para strings constantes:
*/
const char *strings[] =
{
"HELLO",
"WORLD",
"CONST",
"STRINGS"
};
/*
Acessando os ponteiros para constantes acima:
*/
const char *ptr;
while (i = 0; i < (sizeof(strings) / sizeof(const char *)); i++)
{
ptr = strings[i];
printf("%s", ptr);
}
Em adição, strings constantes podem ser passadas para funções que normalmente recebem ponteiros para caracteres:
/*
A diretiva a seguir habilita o recurso do compilador para copiar strings constantes dentro da RAM quando estas são passadas como parâmetros a uma função:
*/
#device PASS_STRINGS=IN_RAM
/*
um exemplo do uso deste novo recurso:
*/
if (stricmp(buffer,"ATDT\r")==0)
{
//faz alguma coisa
}
A diretiva a seguir habilita o recurso do compilador para copiar strings constantes dentro da RAM quando estas são passadas como parâmetros a uma função:
*/
#device PASS_STRINGS=IN_RAM
/*
um exemplo do uso deste novo recurso:
*/
if (stricmp(buffer,"ATDT\r")==0)
{
//faz alguma coisa
}
Note que o qualificador const sempre significa que o dado será colocado na memória de programa e, portanto, este dado é "somente leitura". Isto não segue a definição ANSI que estabelece simplesmente que o qualificador const apenas significa ser "somente leitura".
Experimente a poderosa biblioteca I²C (Inter-Integrated Circuit - Circuito Inter-Integrado) incluída no compilador. Primeiro, você pode atribuir à suas portas I²C diferentes identificadores de stream (fluxo de dados). Atribuindo diferentes identificadores de stream aos canais I²C, fica fácil diferenciar no código qual porta está sendo usada.
Segundo, a função i2c_start() pode enviar um sinal de início (I²C start) ou um sinal de reinício (I²C restart). O parâmetro de reinício para a função i2c_start() pode ser configurado para um valor "2" para forçar um reinício ao invés de um início. Um valor "1" fará um início normal. Se o reinício não é especificado ou é "0", então o reinício será feito somente se o compilador encontrou por último um i2c_start() e não um i2c_stop().
/*
Isto configura duas portas I²C, cada qual com seu identificador de stream diferente.
*/
#use i2c(sda=PIN_C4, scl=PIN_C3, stream=I2C_HW)
#use i2c(sda=PIN_B1, scl=PIN_B2, stream=I2C_SW)
/*
A função seguinte lê um byte de dado da EEPROM I²C 24LC16 da Microchip, usando a porta identificada como I2C_HW:
*/
int Read2416(int address)
{
i2c_start(I2C_HW, 1); //perform a start
i2c_write(I2C_HW, 0xA0);
i2c_write(I2C_HW, address);
i2c_start(I2C_HW, 2); //perform a restart
i2c_write(I2C_HW, 0xA1);
data=i2c_read(IC2_HW, 0);
i2c_stop(I2C_HW);
return(data);
}
Isto configura duas portas I²C, cada qual com seu identificador de stream diferente.
*/
#use i2c(sda=PIN_C4, scl=PIN_C3, stream=I2C_HW)
#use i2c(sda=PIN_B1, scl=PIN_B2, stream=I2C_SW)
/*
A função seguinte lê um byte de dado da EEPROM I²C 24LC16 da Microchip, usando a porta identificada como I2C_HW:
*/
int Read2416(int address)
{
i2c_start(I2C_HW, 1); //perform a start
i2c_write(I2C_HW, 0xA0);
i2c_write(I2C_HW, address);
i2c_start(I2C_HW, 2); //perform a restart
i2c_write(I2C_HW, 0xA1);
data=i2c_read(IC2_HW, 0);
i2c_stop(I2C_HW);
return(data);
}
Diretiva #USE SPI()
Algumas das bibliotecas mais poderosas do CCS são as que dão suporte à comunicação serial com protocolos RS-232 e I²C, as quais dão ao programador a flexibilidade de usar múltiplas portas RS-232 e I²C ao mesmo tempo que estiver usando qualquer conjunto de pinos de I/O de uso geral, sem restringir o uso destes pinos somente aos periféricos. Bibliotecas SPI (Serial Peripheral Interface - Interface Serial para Periféricos) estão incluídas no CCS e permitem ao usuário o uso de qualquer pino I/O de uso geral, configuração de clock, qualquer número de bits de dados, streams, freqüência de clock e mais.
/*
O #use SPI configura a porta SPI. Aqui tem-se um configuração simples:
*/
#use SPI(
DO = PIN_B0,
DI = PIN_B1,
CLK = PIN_B2,
baud = 100000,
BITS = 8,
LSB_FIRST,
SAMPLE_RISE,
stream = SPI_PORT0
)
/*
Lendo um byte de dados, no caso, de uma memória EEPROM 9356 usando o novo stream SPI:
*/
void Read9356(long address, int data)
{
output_high(EEPROM_9356_SELECT);
SPI_XFER(SPI_PORT0, 0x18);
SPI_XFER(SPI_PORT0, address);
data=SPI_XFER(SPI_PORT0, 0);
output_low(EEPROM_9356_SELECT);
}
O #use SPI configura a porta SPI. Aqui tem-se um configuração simples:
*/
#use SPI(
DO = PIN_B0,
DI = PIN_B1,
CLK = PIN_B2,
baud = 100000,
BITS = 8,
LSB_FIRST,
SAMPLE_RISE,
stream = SPI_PORT0
)
/*
Lendo um byte de dados, no caso, de uma memória EEPROM 9356 usando o novo stream SPI:
*/
void Read9356(long address, int data)
{
output_high(EEPROM_9356_SELECT);
SPI_XFER(SPI_PORT0, 0x18);
SPI_XFER(SPI_PORT0, address);
data=SPI_XFER(SPI_PORT0, 0);
output_low(EEPROM_9356_SELECT);
}
Nenhum comentário:
Postar um comentário