PicManía by RedRaven
 

Búsqueda personalizada

 

TÉCNICAS  EN C

 
 


Tareas Comunes Realizadas en C

 
 

Esta serie de contenidos titulada Técnicas en C pretende mostrar con ejemplos comentados la forma de implementar ciertos procesos muy usuales durante el uso de los Microcontroladores PIC de las series 16F y 18F.

 


  Librería de Funciones "Útiles"


 
A.- Un poco de Teoría:
 

Utilizando una Librería de Funciones "Útiles" en CCS C

Los conceptos fundamentales a tener en cuenta son varios y todos importantes, voy a intentar haceros una descripción simple y a ser posible completa.

1º.- Se hace uso una directiva de compilación que permite insertar cualquier código fuente allá donde la utilicemos. Se trata de la directiva #include que hace exactamente eso: Incluye en esa posición el contenido del fichero descrito en la directiva.

Así #include "lib_utils.c" coloca, a partir de la línea donde aparece la directiva, el contenido completo del fichero lib_utils.c.

Esta directiva tiene dos formas de delimitar el nombre del fichero a incluir:

Delimitado el nombre con dobles comillas "", como en el ejemplo anterior que hemos puesto, y que hace que se busque el fichero en su dirección absoluta, podemos entonces escribir #include "c:\proyectos\librerias\lib_utils.c" y entonces se buscará el fichero en esa dirección. Si omitimos alguna parte de su dirección se buscará entonces partiendo del directorio actual en uso, así poniendo solo "lib_utils.c" se entenderá que el directorio donde se encuentra es el mismo en el que está el programa que estamos compilando y que incluye a aquél.

Delimitado con los símbolos mayor y menor <> que hace que el fichero a incluir sea buscado en el/los directorio/s predefinido/s en el compilador para la inclusión de fuentes. En CCS C esta opción se encuentra en el menú Options/Include Dirs...
 


 
Por lo demás ambas formas de citar al fichero a incluir son exactamente iguales.

2º.- Otro concepto que tenemos que aplicar es el de la precedencia en C. En este lenguaje, como en todos los estructurados, es fundamental el hecho de tener que declarar cualquier cosa antes de poder usarla. De esta forma solo podremos usar un función en nuestro programa si, y solo si, el compilador conoce la función antes de usarla.

Hay que tener en cuenta que el compilador va a recorrer, compilando, nuestro programa de arriba a abajo, siendo la parte superior la que primero se compila y la inferior la última en serlo.

Así si en nuestro main() utilizamos una función ésta debe estar colocada antes del main() para que pueda ser compilada, en otro caso el compilador nos lanzará un error indicándonos que dicha función no está declarada.

Este caso compilaría perfectamente ya que al utilizar en main() la función miFuncion() ha sido desarrollada antes de llegar allí el compilador y por lo tanto "la conoce" antes de usarla:
 
     
  int8 miFuncion(void){
   return 100
}

void main(void){
   int8 n;

   n = miFuncion();
}
 
     
 
Y sin embargo éste no lo haría ya que al compilar main() el compilador no sabría qué es miFuncion() al estar desarrollada después de ser usada:
 
     
  void main(void){
   int8 n;

   n = miFuncion();
}

int8 miFuncion(void){
   return 100
}
 
     
 
La solución a éste último caso sería o mover miFunción() para colocarla antes del main(), que es lo que teníamos en el ejemplo anterior, o mejor aún: Declararla, colocando su prototipo antes del mismo y podríamos así dejar su desarrollo donde quisiésemos. Esto lo haríamos de la siguiente forma:
 
     
  int8 miFuncion(void);

void main(void){
   int8 n;

   n = miFuncion();
}

int8 miFuncion(void){
   return 100
}
 
     

 

 

Fijaos en que solo he puesto una línea exactamente igual que la que describe mi función pero sin desarrollarla, sin poner qué hace la función por dentro, solo qué parámetros recibe la función y qué devuelve la misma.

Así el compilador sabe qué es lo que debe hacer con ella y su llamada desde el main() es correcta, aunque aún no sepa qué debe hacer dentro de la función, que viene mas abajo y aún no ha sido compilada.

Notad que tenemos en este último ejemplo tres partes perfectamente diferenciadas: Cabecera, Main y Desarrollo. Vamos a escribir de nuevo el ejemplo pero diferenciando sus partes:

 

 

 

     
   // Cabecera //////////////
int8 miFuncion(void);
// Fin de Cabecera ////////

// Main ///////////////////
void main(void){
   int8 n;

   n = miFuncion();
}
// Fin de Main ////////////

// Desarrollo /////////////
int8 miFuncion(void){
   return 100
}
// Fin de Desarrollo //////
 
 
     

 

 

 

 

 

 

Nos vamos acercando a nuestro objetivo ...

3º.- Por último vamos a cortar nuestro código y dividirlo en tres ficheros distintos. A la parte que hemos llamado Cabecera la vamos a poner en un fichero que se llame "Lib_Utils.h", la extensión .h viene de la palabra inglesa header o sea cabecera en español. La parte que hemos llamado Desarrollo la vamos a colocar en un fichero que se llame "lib_utils.c" ya que contiene el código C que desarrolla nuestras funciones. Y a la parte Main la vamos a colocar en su propia "main.c" que es la que nosotros compilamos.

Como es necesario compilar todo el código fuente y ahora lo tenemos dividido en tres ficheros distintos es necesario decirle al compilador que incluya los ficheros correspondientes en el que estamos compilando, así nuestro main.c debería incluir a los otros dos:

 

 

 

     
  #include "Lib_utils.h"

// Main ////////////////////////
void main(void){
int8 n;

n = miFuncion();
}
// Fin de Main /////////////////

#include "lib_utils.c"
 
     

 

 

 

 

Esta forma de escribirlo es exactamente igual que la anterior, el compilador va a recorrer las mismas líneas de código en el mismo orden generando exactamente el mismo código objeto compilado. La única diferencia entre ellos es que el compilador ha tenido que unir las distintas líneas de código contenidas en los distintos ficheros mediante los #include antes de compilar.

Y 4º.- Hacemos uso por fin de una característica muy útil del compilador y que es que éste solo compila efectivamente, y traslada al código objeto compilado, aquellas funciones que realmente son llamadas desde el main(), o que son llamadas desde una función que a su vez es llamada desde el main(), o que son llamadas desde una función que a su vez es llamada desde una función que a su vez es llamada desde el main() ....

Esta característica del compilador nos posibilita para escribir cien funciones en nuestro "lib_utils.c" y declarar esas mismas cien funciones en el "Lib_Utils.h", incluirlas todas en el "main.c" pero que al ser compiladas sólo exclusivamente aquella o aquellas que usemos en el main sean las que realmente se compilen, independientemente de cuantas funciones contengan nuestro ficheros de utilidades.

Finalizando.

Si unimos todas las funciones "útiles" que tengamos a mano en nuestro único fichero "lib_utils.c" y colocamos todas sus descripciones o prototipos en "Lib_Utils.h" tendremos nuestra libería completa y lista para su uso:

Lib_Utils.h

 
     
   /** \file lib_Utils.h
* \brief Este fichero contiene las cabeceras necesarias para
* el manejo de la Librería de Utilidades
*
*
* \author (c) 08.2008 by RedPic
*/

// Prototipos de funciones

int8 bin_to_bcd(int8 binary_value);
int8 bcd_to_bin(int8 bcd_value);
void int8_to_bits_string(int8 val,char* pF);
int8 bits_string_to_int8(char* pF);
int1 get_bit_in_byte(int8 byte_for_bits, int8 pbit);
int8 set_bit_in_byte(int8 byte_for_bits, int1 vbit, int8 pbit);
void fill_end_with_char(char c, int8 maxlen,char* pF);
int8 str_to_int8(char* pF);
void decode_timestamp(char *pF, int8 &day, int8 &mth, int16 &year, int8 &hr, int8 &min, int8 &sec);
void encode_timestamp(char *pF, int8 day, int8 mth, int16 year, int8 hr, int8 min, int8 sec);
void replace_char(char c, char p, char* pF);
int8 ascii_to_hex(char d);
int8 hex_to_int8(char* pF);
int16 hex_to_int16(char* pF);
int32 hex_to_int32(char* pF);
void right_trim(char* pF);
int1 are_all_char_code_printable(char *pF);
void insert_char_in_string_in_position(char *pF, char c, int8 position, int8 len);
///////////////////////////////////////////////////////////////////////////////////////////////////
//
// end of lib_Utils.h
//
///////////////////////////////////////////////////////////////////////////////////////////////////
 
 
     
 
lib_utils.c
 
     
  /** \file lib_utils.c
* \brief Este fichero contiene un compendio de funciones de utilidad varia.
*
* \author (c) 08.2008 by RedPic
*/

/** \brief Función auxiliar que convierte de \c BIN a \c BCD
*
* \param binary_value Valor binario a convertir.
* \return Valor BCD convertido.
*/
int8 bin_to_bcd(int8 binary_value){

int8 temp;
int8 retval;

temp = binary_value;
retval = 0;
while(1){
if(temp >= 10){
temp -= 10;
retval += 0x10;
}else{
retval += temp;
break;
}
}
return(retval);
}

/** \brief Función auxiliar que convierte de \c BCD a \c BIN
*
* \param bcd_value Valor BCD a convertir.
* \return Valor Binario convertido.
*/
int8 bcd_to_bin(int8 bcd_value){

int8 temp;

temp = bcd_value;
temp >>= 1;
temp &= 0x78;
return(temp + (temp >> 2) + (bcd_value & 0x0f));
}

/** \brief Función que convierte un Byte (de 8 bits) a bits string ASCII.
*
* Convierte un Byte (de 8 bits) a string ASCII terminado en NULL (\\0).
*
* \param val Valor del Byte a convertir.
* \param[out] pF Puntero a string_null_ended.
*
* \return void
*/
void int8_to_bits_string(int8 val,char* pF){

int8 i,j;

for(i=0,j=7;i<8;i++,j--){
pF[i]=bit_test(val,j)+'0';
}
pF[8]='\0';
}

/** \brief Función que convierte un bits string ASCII a un Byte (de 8 bits).
*
* Convierte un string ASCII terminado en NULL (\\0) a Byte (de 8 bits).
*
* \param pF Puntero a string_null_ended.
*
* \return int8 Valor convertido.
*/
int8 bits_string_to_int8(char* pF){

int8 i,j,ret=0;

for(i=0,j=7;i<8;i++,j--){
if(pF[i]=='1'){
bit_set(ret,j);
}
}

return ret;
}


/** \brief Función que lee el valor de un bit del byte en la posición dada \c pbit.
*
* Lee un Bit en la posición \c pbit del Byte.
* \param byte_for_bits Byte contenedor de los bits.
* \param pbit Posición en el Byte del bit a leer.
* \return bit leído.
*/
int1 get_bit_in_byte(int8 byte_for_bits, int8 pbit){

int1 r;

r=bit_test(byte_for_bits,pbit);
return r;

}

/** \brief Función que guarda un bit de valor \c vbit en el Byte en la posición dada \c pbit.
*
* Guarda un bit de valor \c vbit en la posición \c pbit del Byte dado.
* \param byte_for_bits Puntero al Byte contenedor de bits.
* \param vbit Valor del bit a escribir.
* \param pbit Posición en el Byte del bit a escribir.
* \return byte con el bit cambiado.
*/
int8 set_bit_in_byte(int8 byte_for_bits, int1 vbit, int8 pbit){

bit_clear(byte_for_bits,pbit);
if(vbit==1) bit_set(byte_for_bits,pbit);
return byte_for_bits;
}

/** \brief Función que rellena con un caracter dado un string a partir de NULL hasta una longitud dada.
*
* \param c Carácter de relleno.
* \param maxlen Longitud máxima del string.
* \param[out] pF Puntero a string_null_ended a rellenar.
*
* \return void
*/
void fill_end_with_char(char c, int8 maxlen,char* pF){

int8 i;
int1 start=0;

for(i=0;i<maxlen;i++){
if(start==0){
if(pF[i]=='\0'){
start=1;
}
}
if(start==1){
pf[i]=c;
}
}
pF[maxlen]='\0';

}

/** \brief Función que convierte un string numérico decimal ASCII NULL-END a su valor entero (8 bits).
*
* \param pF Puntero al buffer que contiene el string numérico ASCII.
*
* \return int8 Valor numérico.
*/
int8 str_to_int8(char* pF){

int8 ret=0,i,l,m=1;

l=strlen(pF)-1;
for(i=l;i<255;i--){
if(isdigit(pF[i])){
ret+= m * (pF[i]-'0');
m = m * 10;
} else break;
}
//printf("StrToInt %s (%u)\r\n",pF,ret);
return ret;
}

/** \brief Función decodifica una variable TimeStamp extrayendo sus componentes.
*
* Acepta una variable TimeStamp y devuelve valores enteros de Año, Mes, Día, Hora, Minuto, y Segundo.
* \param pF Puntero a string TimeStamp
* \param[out] day Dia
* \param[out] mth Mes
* \param[out] year Año (4 dígitos, int16)
* \param[out] hr Hora
* \param[out] min Minuto
* \param[out] sec Segundo
* \see encode_timestamp()
*/
void decode_timestamp(char *pF, int8 &day, int8 &mth, int16 &year, int8 &hr, int8 &min, int8 &sec){

char sval[]="00";

sval[2]='\0';
sval[0]=pF[2]; sval[1]=pF[3]; year= 2000+str_to_int8(sval);
sval[0]=pF[5]; sval[1]=pF[6]; mth = str_to_int8(sval);
sval[0]=pF[8]; sval[1]=pF[9]; day = str_to_int8(sval);
sval[0]=pF[11]; sval[1]=pF[12]; hr = str_to_int8(sval);
sval[0]=pF[14]; sval[1]=pF[15]; min = str_to_int8(sval);
sval[0]=pF[17]; sval[1]=pF[18]; sec = str_to_int8(sval);
}

/** \brief Función codifica una variable TimeStamp dando sus componentes.
*
* Acepta variables de valores enteros de Año, Mes, Día, Hora, Minuto, y Segundo y devuelve puntero a variable de tipo Timestamp.
* \param[out] pF Puntero a string TimeStamp
* \param day Dia
* \param mth Mes
* \param year Año (4 dígitos, int16)
* \param hr Hora
* \param min Minuto
* \param sec Segundo
* \see encode_timestamp()
*/
void encode_timestamp(char *pF, int8 day, int8 mth, int16 year, int8 hr, int8 min, int8 sec){

sprintf(pF,"%Lu-%2u-%2u_%2u:%2u:%2u\0",year,mth,day,hr,min,sec);
replace_char(' ','0',pF);
replace_char('_',' ',pF);
}

/** \brief Reemplaza carácter en string null-terminated.
*
* \param c Carácter a sustituir.
* \param p Carácter nuevo a insertar.
* \param[out] pF Puntero a string null-terminated.
*
* \return void
*/
void replace_char(char c, char p, char* pF){

int8 i;
char x;

x=pF[0];
for(i=0;i<255,x!='\0';i++){
x=pf[i];
if(x==c){
pF[i]=p;
}
}
}

/** \brief Función que convierte un carácter Hexadecimal ASCII NULL-END a su valor entero de 8 bits.
*
* \param d Caracter Hexadecimal a convertir.
*
* \return int8 Valor numérico.
*/
int8 ascii_to_hex(char d){

int r=0x00;

if(isxdigit(d)){
if(isdigit(d)){
r=d-'0';
}
if(isalpha(d)){
d=toupper(d);
r=10+(d-'A');
}
}
return(r);
}

/** \brief Función que convierte un string numérico Hexadecimal ASCII NULL-END a su valor entero de 8 bits.
*
* \param pF Puntero al buffer que contiene el string numérico ASCII Hexadecimal de 2 digitos (00h a FFh).
*
* \return int8 Valor numérico.
*/
int8 hex_to_int8(char* pF){

int8 i,ret;

ret=0;
for(i=1;i!=255;i--){
ret+=ascii_to_hex(pF[i])*((15*(1-i))+1);
}
return ret;
}

/** \brief Función que convierte un string numérico Hexadecimal ASCII NULL-END a su valor entero de 16 bits.
*
* \param pF Puntero al buffer que contiene el string numérico ASCII Hexadecimal de 4 digitos (0000h a FFFFh).
*
* \return int16 Valor numérico.
*/
int16 hex_to_int16(char* pF){

int8 i, p2, p1;
int16 ret;

p1=hex_to_int8(&pF[2]);
p2=hex_to_int8(&pF[0]);
ret=make16(p2,p1);
//printf("hex_to_int16 recibe: %s p2: %LX p1 %LX valor: %LX (%Lu)\r\n",pF,p2,p1,ret,ret);
return ret;
}
/** \brief Función que convierte un string numérico Hexadecimal ASCII NULL-END a su valor entero de 32 bits.
*
* \param pF Puntero al buffer que contiene el string numérico ASCII.
*
* \return int32 Valor numérico.
*/
int32 hex_to_int32(char* pF){

int32 ret;
int16 p1,p2;

p1=hex_to_int16(&pF[4]);
p2=hex_to_int16(&pF[0]);
ret = make32(p2, p1);
//printf("hex_to_int32 recibe: %s p2: %LX p1 %LX valor: %LX (%Lu)\r\n",pF,p2,p1,ret,ret);
return ret;
}

/** \brief Función que descarta los espacios en blanco por la derecha de una cadena.
*
* \param[out] pF Puntero a string_null_ended a procesar.
*
* \return void
*/
void right_trim(char* pF){

int8 i,L;

for(i=0;i<255;i++){
if(pF[i]=='\0'){
L=i-1;
break;
}
}
for(i=L;i>0;i--){
if(pF[i]==' '){
pF[i]='\0';
}
else{
break;
}
}
}

/** \brief Comprueba si todos los caracteres del string NULL-Terminated son imprimibles ('0'..'9','A'..'Z' ó ' ') y hay al menos uno.
*
* \param pF Puntero al string a investigar.
*
* \return int1 1 si todos los carácteres son válidos, 0 en caso contrario.
*/
int1 are_all_char_code_printable(char *pF){

int1 ret=1;
int8 i;

for(i=0;i<255;i++){
if(pF[i]=='\0'){ break;}
if(!isdigit(pF[i]) && !isupper(pF[i]) && pF[i]!=' ') ret=0;
}
if(i==0) ret=0;

return ret;
}

/** \brief Función que inserta un caracter en un string.
*
*/
void insert_char_in_string_in_position(char *pF, char c, int8 position, int8 len){

int8 i;
char tmp1,tmp2;

for(i=0;i<len;i++){
if(i>position){
tmp2=pF[i];
pf[i]=tmp1;
tmp1=tmp2;
}
if(i==position){
tmp1=pF[i];
pF[i]=c;
}

}

}

///////////////////////////////////////////////////////////////////////////////////////////////////
//
// End of lib_utils
//
///////////////////////////////////////////////////////////////////////////////////////////////////
 
 
     
 
Que es lo que queríamos conseguir, una librería de funciones útiles lista para usar.
 
 
A.- Un poco de Práctica: Un ejemplo.
 

Con todo lo descrito anteriormente he realizado un par de programas main absolutamente idénticos salvo que cada uno de ellos llama a una función distinta de nuestra librería de utilidades.

El uno recibe caracteres por la USART, los convierte a binario y devuelve el resultado:

 
     
  void main() {
   printf("\r\nLibrerias de Funciones Main 1\r\n");
   printf("By Redpic para Foro TODOPIC\r\n");
   printf("Recibe CHAR y convierte a BINARIO\r\n\n");

   recFlag=0; // Desactivo el Flag de Recepción
   enable_interrupts(int_rda); // Habilito la interrpción INT_RDA
   enable_interrupts(global); // Habilito las interrupciones

   do {
      if(recFlag==1){
         recFlag=0;
         int8_to_bits_string(rec,respuesta);
         printf("Recibido : %c En bits : %s\r\n",rec,respuesta);
      }
   } while (TRUE);
}
 
     
 

 
 
Y el otro recibe los mismos caracteres pero los trata como hexadecimales convirtiéndolos a decimal:
 
     
  void main() {
   printf("\r\nLibrerias de Funciones Main 2\r\n");
   printf("By Redpic para Foro TODOPIC\r\n");
   printf("Recibe HEX y convierte a INT8\r\n\n");

   recFlag=0; // Desactivo el Flag de Recepción
   enable_interrupts(int_rda); // Habilito la interrpción INT_RDA
   enable_interrupts(global); // Habilito las interrupciones

   do {

      if(recFlag==1){
         recFlag=0;
         tmp[1]=toupper(rec);
         respuesta = hex_to_int8(tmp);
         printf("Recibido HEX : %c En Decimal : %u\r\n",rec,respuesta);
      }
   } while (TRUE);
}
 
     
 
 
La diferencia entre ellos se muestra claramente al comparar entre si los dos códigos ASM generados por el compilador, en el uno solo ha sido compilada la función void int8_to_bits_string(int8 val,char* pF); y en el otro por el contrario solo lo ha sido la función int8 ascii_to_hex(char d);

En el siguiente fichero ZIP tenéis ambos main, la librería completa y los ASM generados para que podáis ver la diferencia entre ambos: Descargar Técnicas en C : Usando librerías de funciones.zip
 
 

 

Esta página se modificó el 27/12/2008


Esta página usa la letra Ñ

Nota pública importante sobre las consultas al Webmaster de PicManía.


Sugerencias a Picmanía... (que serán leídas pero seguramente no podrán ser contestadas)

Esta página pertenece al grupo de páginas de Diego RedRaven

 

 



Nota: Esta página Web esta repleta de imágenes, textos, logotipos y demás material extraídos de los mas variados medios de los que no soy ni autor ni depositario de los correspondientes derechos de autor, uso y/o reproducción. Si Ud. es depositario de dichos derechos y desea que el material correspondiente sea eliminado de esta Web no dude en ponerse en contacto conmigo mediante e-mail y será inmediatamente retirado. Gracias.
 
Visitas
Totales : 17537 Hoy: 1 Activas: 1 Vistas: 17537

Esta página fue modificada el 07-08-2010 15:42:21

           
 DmSoft WAMP Escribir Unreal