TODOPIC
Bienvenido(a), Visitante. Por favor, ingresa o regístrate.
¿Perdiste tu email de activación?
03 de Septiembre de 2010, 05:25:53

Ingresar con nombre de usuario, contraseña y duración de la sesión
Buscar:     Búsqueda Avanzada
257111 Mensajes en 28437 Temas por 27916 Usuarios
Último usuario: zororyuzaki
* Inicio Ayuda Buscar Calendario Ingresar Registrarse
Buscar en TodoPIC
+  TODOPIC
|-+  Microcontroladores PIC
| |-+  Lenguaje C para microcontroladores PIC (Moderadores: Modulay, pikman, pocher, vszener, Suky)
| | |-+  Librería de Funciones "Útiles" en CCS C
0 Usuarios y 1 Visitante están viendo este tema. « anterior próximo »
Páginas: [1] 2 Marcar como favorito Imprimir
Autor Tema: Librería de Funciones "Útiles" en CCS C  (Leído 1767 veces)
RedPic
Administrador
DsPIC33
*******
Desconectado Desconectado

Sexo: Masculino
Tibet Tibet

Mensajes: 4876



WWW
« : 04 de Agosto de 2008, 09:21:27 »

Todos los que trabajamos en C hacemos programas y programas y programas. Muchas veces repetimos una y otra vez las mismas funciones "útiles" que nos van haciendo falta aquí y allá según las necesidades que en cada momento tengamos.

No es mala idea acostumbrarnos a meter todas esas funciones en un único fichero fuente y tenerlas siempre disponibles para cada nuevo proyecto. Ademas si descubrimos alguna forma de hacer lo mismo de forma mas rápida o eficiente o corta solo tenemos que modificar nuestra librería colección de funciones "útiles" y recompilar los proyectos, y no tener que modificar éstos uno a uno para hacer lo mismo en cada ocasión.

Cada uno de nosotros tendrá su conjunto de funciones "útiles" propias y con ellas vamos, poco a poco, construyendo nuestra librería.

Es inteligente dividir la librería en un par de ficheros, uno con la extensión .H en la que declararemos todas las funciones que lleva la librería y que nos servirá para poner en nuestro proyecto un #include al principio de nuestro proyecto y que así el compilador sepa qué funciones tenemos disponibles y poder así utilizarlas en cualquier sitio del mismo.

Y otro fichero con la extensión .C en el que pondremos el desarrollo de todas y cada una de nuestras funciones declaradas en el .H. Con un #include de este fichero en cualquier sitio de nuestro proyecto compilaremos solo aquellas de que hagamos uso en el mismo.

Inicio aquí una relación comentada de aquellas funciones "útiles" que yo personalmente uso muy a menudo en mis proyectos.
En línea

Contra la estupidez los propios dioses luchan en vano. Schiller
Mi Güeb : Picmania
RedPic
Administrador
DsPIC33
*******
Desconectado Desconectado

Sexo: Masculino
Tibet Tibet

Mensajes: 4876



WWW
« Respuesta #1 : 04 de Agosto de 2008, 09:23:56 »

Muy útil sobre todo para el control de Displays de 7 Segmentos en los que los Drivers del tipo 74LS48 ó DM9368 reciben la entrada en BCD (Binary Code Decimal).

Código
GeSHi (csharp):
  1. /** \brief Función auxiliar que convierte de \c BIN a \c BCD
  2.  *
  3.  * \param binary_value Valor binario a convertir.
  4.  * \return Valor BCD convertido.
  5.  */
  6. int8 bin_to_bcd(int8 binary_value){
  7.  
  8.  int8 temp;
  9.  int8 retval;
  10.  
  11.  temp = binary_value;
  12.  retval = 0;
  13.  while(1){
  14.    if(temp >= 10){
  15.      temp -= 10;
  16.      retval += 0x10;
  17.    }else{
  18.      retval += temp;
  19.      break;
  20.    }
  21.  }
  22.  return(retval);
  23. }
  24.  
  25. /** \brief Función auxiliar que convierte de \c BCD a \c BIN
  26.  *
  27.  * \param bcd_value Valor BCD a convertir.
  28.  * \return Valor Binario convertido.
  29.  */
  30. int8 bcd_to_bin(int8 bcd_value){
  31.  
  32.  int8 temp;
  33.  
  34.  temp = bcd_value;
  35.  temp >>= 1;
  36.  temp &= 0x78;
  37.  return(temp + (temp >> 2) + (bcd_value & 0x0f));
  38. }
  39.  
  40.  
En línea

Contra la estupidez los propios dioses luchan en vano. Schiller
Mi Güeb : Picmania
RedPic
Administrador
DsPIC33
*******
Desconectado Desconectado

Sexo: Masculino
Tibet Tibet

Mensajes: 4876



WWW
« Respuesta #2 : 04 de Agosto de 2008, 09:33:02 »

Cuando estamos monitorizando nuestro programas hay veces que deseamos ver qué bits están activados y cuales no en un Byte determinado. Podemos utilizar un printf y sacar el byte en hexadecimal por el puerto serie y después calcular los bits ... o podemos convertir los bits a un string de unos y ceros ASCII y usar el printf para enviar el string al canal serie y ver directamente los bits en nuestro monitor serie del PC.

O de forma complementaria podemos desear enviar al PIC una cadena de unos y ceros en ASCII y que el PIC componga el Byte correspondiente a los bits codificados de esta forma.

Las dos funciones que os pongo a continuación hacen exactamente esto: Convierten un Byte a una cadena de 8 carácteres ASCII '0' ó '1' terminados en '\0' o admite una cadena de este mismo tipo y lo convierte a un Byte correspondiente a estos bits.

Código
GeSHi (csharp):
  1. /** \brief Función que convierte un Byte (de 8 bits) a bits string ASCII.
  2.  *
  3.  * Convierte un Byte (de 8 bits) a string ASCII terminado en NULL (\\0).
  4.  *
  5.  * \param val Valor del Byte a convertir.
  6.  * \param[out] pF  Puntero a string_null_ended.
  7.  *
  8.  * \return void
  9.  */
  10. void int8_to_bits_string(int8 val,char* pF){
  11.  
  12.   int8 i,j;
  13.  
  14.   for(i=0,j=7;i<8;i++,j--){
  15.      pF[i]=bit_test(val,j)+'0';
  16.   }
  17.   pF[8]='\0';
  18. }
  19.  
  20. /** \brief Función que convierte un bits string ASCII a un Byte (de 8 bits).
  21.  *
  22.  * Convierte un string ASCII terminado en NULL (\\0) a Byte (de 8 bits).
  23.  *
  24.  * \param pF Puntero a string_null_ended.
  25.  *
  26.  * \return int8 Valor convertido.
  27.  */
  28. int8 bits_string_to_int8(char* pF){
  29.  
  30.   int8 i,j,ret=0;
  31.  
  32.   for(i=0,j=7;i<8;i++,j--){
  33.      if(pF[i]=='1'){
  34.       bit_set(ret,j);
  35.      }
  36.   }
  37.  
  38.   return ret;
  39. }
  40.  
  41.  
En línea

Contra la estupidez los propios dioses luchan en vano. Schiller
Mi Güeb : Picmania
RedPic
Administrador
DsPIC33
*******
Desconectado Desconectado

Sexo: Masculino
Tibet Tibet

Mensajes: 4876



WWW
« Respuesta #3 : 04 de Agosto de 2008, 09:42:43 »

¿Quien no ha recibido un string de digitos decimales por el canal serie y tiene que convertirlo al correspondiente byte en la RAM? Aquí teneis mi función strdec_to_int8:

Código
GeSHi (csharp):
  1. /** \brief Función que convierte un string numérico decimal ASCII NULL-END a su valor entero (8 bits).
  2.  *
  3.  * \param pF  Puntero al buffer que contiene el string numérico ASCII.
  4.  *
  5.  * \return int8 Valor numérico.
  6.  */
  7. int8 strdec_to_int8(char* pF){
  8.  
  9.   int8 ret=0,i,l,m=1;
  10.  
  11.   l=strlen(pF)-1;
  12.   for(i=l;i<255;i--){
  13.      if(isdigit(pF[i])){
  14.         ret+= m * (pF[i]-'0');
  15.         m = m * 10;
  16.      } else break;
  17.   }
  18.   return ret;
  19. }
  20.  
En línea

Contra la estupidez los propios dioses luchan en vano. Schiller
Mi Güeb : Picmania
RedPic
Administrador
DsPIC33
*******
Desconectado Desconectado

Sexo: Masculino
Tibet Tibet

Mensajes: 4876



WWW
« Respuesta #4 : 04 de Agosto de 2008, 09:47:18 »

El "Timestamp" es un formato de fecha y hora muy utilizado sobre todos en entornos de SQL. Es un string o cadena del tipo "aaaa-mm-dd hh-mm-ss" o sea Año-Mes-Día Hora-Minuto-Segundo. Los que trabajamos con cacharros que calzan PIC's y RTC (del tipo de los DS1307) estamos constantemente manejando este formato. Aquí tenéis la forma de convertir las variables enteras componentes de este tipo de fechas en Timestamp y viceversa:

Código
GeSHi (csharp):
  1. /** \brief Función decodifica una variable TimeStamp extrayendo sus componentes.
  2.  *
  3.  * Acepta una variable TimeStamp y devuelve valores enteros de Año, Mes, Día, Hora, Minuto, y Segundo.
  4.  * \param pF Puntero a string TimeStamp
  5.  * \param[out] day Dia
  6.  * \param[out] mth Mes
  7.  * \param[out] year Año (4 dígitos, int16)
  8.  * \param[out] hr Hora
  9.  * \param[out] min Minuto
  10.  * \param[out] sec Segundo
  11.  * \see encode_timestamp()
  12.  */
  13. void decode_timestamp(char *pF, int8 &day, int8 &mth, int16 &year, int8 &hr, int8 &min, int8 &sec){
  14.  
  15.   char sval[]="00";
  16.  
  17.   sval[2]='\0';
  18.   sval[0]=pF[2];  sval[1]=pF[3];  year= 2000+str_to_int8(sval);
  19.   sval[0]=pF[5];  sval[1]=pF[6];  mth = str_to_int8(sval);
  20.   sval[0]=pF[8];  sval[1]=pF[9];  day = str_to_int8(sval);
  21.   sval[0]=pF[11]; sval[1]=pF[12]; hr  = str_to_int8(sval);
  22.   sval[0]=pF[14]; sval[1]=pF[15]; min = str_to_int8(sval);
  23.   sval[0]=pF[17]; sval[1]=pF[18]; sec = str_to_int8(sval);
  24. }
  25.  
  26. /** \brief Función codifica una variable TimeStamp dando sus componentes.
  27.  *
  28.  * Acepta variables de valores enteros de Año, Mes, Día, Hora, Minuto, y Segundo y devuelve puntero a variable de tipo Timestamp.
  29.  * \param[out] pF Puntero a string TimeStamp
  30.  * \param day Dia
  31.  * \param mth Mes
  32.  * \param year Año (4 dígitos, int16)
  33.  * \param hr Hora
  34.  * \param min Minuto
  35.  * \param sec Segundo
  36.  * \see encode_timestamp()
  37.  */
  38. void encode_timestamp(char *pF, int8 day, int8 mth, int16 year, int8 hr, int8 min, int8 sec){
  39.  
  40.   sprintf(pF,"%Lu-%2u-%2u_%2u:%2u:%2u\0",year,mth,day,hr,min,sec);
  41.   replace_char(' ','0',pF);
  42.   replace_char('_',' ',pF);
  43. }
  44.  
  45.  

En línea

Contra la estupidez los propios dioses luchan en vano. Schiller
Mi Güeb : Picmania
RedPic
Administrador
DsPIC33
*******
Desconectado Desconectado

Sexo: Masculino
Tibet Tibet

Mensajes: 4876



WWW
« Respuesta #5 : 04 de Agosto de 2008, 09:50:24 »

En las funciones anteriores sobre el Timestamp hacemos uso de una función str_to_int8 que ya hemos visto, pero también de otra que aún no os he puesto. Veamos aquí unas cuantas funciones útiles para el manejo de strings o cadenas de caracteres:

Función para reemplazar un caracter por otro dentro de un string:

Código
GeSHi (csharp):
  1. /** \brief Reemplaza carácter en string null-terminated.
  2.  *
  3.  * \param c Carácter a sustituir.
  4.  * \param p Carácter nuevo a insertar.
  5.  * \param[out] pF  Puntero a string null-terminated.
  6.  *
  7.  * \return void
  8.  */
  9. void replace_char(char c, char p, char* pF){
  10.  
  11.   int8 i;
  12.   char x;
  13.  
  14.   x=pF[0];
  15.   for(i=0;i<max_len_buffer,x!='\0';i++){
  16.      x=pf[i];
  17.      if(x==c){
  18.         pF[i]=p;
  19.      }
  20.   }
  21. }
  22.  
  23.  

Función que descarta los espacios en blanco a la derecha de un string o sea que mueve el '\0' hasta la posición siguiente al último caracter por la derecha distinto a espacio. Es similar a lo que conocemos con Right Trim:

Código
GeSHi (csharp):
  1. /** \brief Función que descarta los espacios en blanco por la derecha de una cadena.
  2.  *
  3.  * \param[out] pF  Puntero a string_null_ended a procesar.
  4.  *
  5.  * \return void
  6.  */
  7. void right_trim(char* pF){
  8.  
  9.   int8 i,L;
  10.  
  11.   for(i=0;i<len_string;i++){
  12.      if(pF[i]=='\0'){
  13.            L=i-1;
  14.            break;
  15.      }
  16.   }
  17.   for(i=L;i>0;i--){
  18.      if(pF[i]==' '){
  19.            pF[i]='\0';
  20.      }
  21.      else{
  22.         break;
  23.      }
  24.   }
  25. }
  26.  

« Última modificación: 04 de Agosto de 2008, 09:58:30 por RedPic » En línea

Contra la estupidez los propios dioses luchan en vano. Schiller
Mi Güeb : Picmania
reiniertl
Moderadores
PIC24H
*****
Desconectado Desconectado

Sexo: Masculino
España España

Mensajes: 1011



« Respuesta #6 : 04 de Agosto de 2008, 10:03:23 »

Olé al PicRojo-SuperHéroe

Señores el foro se viste de gala con estas librerías
En línea
Cryn
Colaborador
DsPIC30
*****
Desconectado Desconectado

Sexo: Masculino
Bolivia Bolivia

Mensajes: 3914


ahora con C18 C30 C32


« Respuesta #7 : 04 de Agosto de 2008, 11:34:29 »

buenísima idea señor Rojo rebotando

que se llene de funciones!! Mr. Green
En línea

El peor día, cuando te fuiste.
Cuando estabas a nuestro lado, los mejores
Nocturno
Administrador
DsPIC33
*******
Desconectado Desconectado

Sexo: Masculino
España España

Mensajes: 12825



WWW
« Respuesta #8 : 04 de Agosto de 2008, 12:22:00 »

Yo añadiría al hilo todas las que ya incluiste en Ejemplitos en C: Mis Funciones favoritas en CCS C.

De hecho, quizás podrían fundirse los dos hilos en uno; ¿cuál es su diferencia?

Gracias por ilustrarnos, querido Don Diego.
En línea

Un saludo desde Sevilla, España.
Visita MicroPIC                                                                                        ɔ!doɹɔ!ɯ ɐʇ!s!ʌ
RedPic
Administrador
DsPIC33
*******
Desconectado Desconectado

Sexo: Masculino
Tibet Tibet

Mensajes: 4876



WWW
« Respuesta #9 : 04 de Agosto de 2008, 05:23:54 »

Yo añadiría al hilo todas las que ya incluiste en Ejemplitos en C: Mis Funciones favoritas en CCS C.

De hecho, quizás podrían fundirse los dos hilos en uno; ¿cuál es su diferencia?

Gracias por ilustrarnos, querido Don Diego.

No recordaba ese hilo.

Edito: He estado dando una vuelta por Mis Funciones favoritas en CCS C y efectivamente es aproximadamente lo mismo que estoy haciendo aquí, salvo que aquí empecé ha hacerlo de forma un poco mas ordenada, con un post por "tipo" u objetivo de función y editando correspondientemente el título para que sea fácilmente encontrable en el buscador. Pero sí, en esencia es lo mismo.
 
« Última modificación: 04 de Agosto de 2008, 05:36:47 por RedPic » En línea

Contra la estupidez los propios dioses luchan en vano. Schiller
Mi Güeb : Picmania
vasconinicolas
PIC16
***
Desconectado Desconectado

Sexo: Masculino
Argentina Argentina

Mensajes: 121



« Respuesta #10 : 05 de Agosto de 2008, 09:59:08 »

Hola muchachos. Red, espectacular tu idea pues nos ayuda con el orden. Una consulta, podrías poner un ejemplo de archivo .h y archivo .c llamando algunas funciones? Me gusto eso de tener las funciones todas, dentro de un solo archivo y que se compilen solo aquellas que utilizamos. Gracias  Smile
En línea

"No hacen ciencia los países ricos,
Son ricos los países por hacer ciencia"
Cryn
Colaborador
DsPIC30
*****
Desconectado Desconectado

Sexo: Masculino
Bolivia Bolivia

Mensajes: 3914


ahora con C18 C30 C32


« Respuesta #11 : 06 de Agosto de 2008, 11:56:14 »

Me gusto eso de tener las funciones todas, dentro de un solo archivo y que se compilen solo aquellas que utilizamos. Gracias  Smile

eso si qeu esta muy interesante, no lo sabia, espero ver algo sobre eso

gracias, un saludo
En línea

El peor día, cuando te fuiste.
Cuando estabas a nuestro lado, los mejores
RedPic
Administrador
DsPIC33
*******
Desconectado Desconectado

Sexo: Masculino
Tibet Tibet

Mensajes: 4876



WWW
« Respuesta #12 : 11 de Agosto de 2008, 06:29:52 »

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:

Código
GeSHi (csharp):
  1. int8 miFuncion(void){
  2.  return 100
  3. }
  4.  
  5. void main(void){
  6.   int8 n;
  7.  
  8.   n = miFuncion();
  9. }
  10.  


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:

Código
GeSHi (csharp):
  1. void main(void){
  2.   int8 n;
  3.  
  4.   n = miFuncion();
  5. }
  6.  
  7. int8 miFuncion(void){
  8.  return 100
  9. }
  10.  


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:

Código
GeSHi (csharp):
  1. int8 miFuncion(void);
  2.  
  3. void main(void){
  4.   int8 n;
  5.  
  6.   n = miFuncion();
  7. }
  8.  
  9. int8 miFuncion(void){
  10.  return 100
  11. }
  12.  


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:

Código
GeSHi (csharp):
  1. // Cabecera ///////////////////
  2. int8 miFuncion(void);
  3. // Fin de Cabecera /////////////
  4.  
  5. // Main ////////////////////////
  6. void main(void){
  7.   int8 n;
  8.  
  9.   n = miFuncion();
  10. }
  11. // Fin de Main /////////////////
  12.  
  13. // Desarrollo //////////////////
  14. int8 miFuncion(void){
  15.  return 100
  16. }
  17. // Fin de Desarrollo ///////////
  18.  


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:

Código
GeSHi (csharp):
  1. #include "Lib_utils.h"
  2.  
  3. // Main ////////////////////////
  4. void main(void){
  5.   int8 n;
  6.  
  7.   n = miFuncion();
  8. }
  9. // Fin de Main /////////////////
  10.  
  11. #include "Lib_utils.c"
  12.  


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
Código
GeSHi (csharp):
  1. /** \file lib_Utils.h
  2. *  \brief Este fichero contiene las cabeceras necesarias para el manejo de la Librería de Utilidades
  3. *
  4. *
  5. * \author (c) 08.2008 by RedPic
  6. */
  7.  
  8. // Prototipos de funciones
  9.  
  10. int8 bin_to_bcd(int8 binary_value);
  11. int8 bcd_to_bin(int8 bcd_value);
  12. void int8_to_bits_string(int8 val,char* pF);
  13. int8 bits_string_to_int8(char* pF);
  14. int1 get_bit_in_byte(int8 byte_for_bits, int8 pbit);
  15. int8 set_bit_in_byte(int8 byte_for_bits, int1 vbit, int8 pbit);
  16. void fill_end_with_char(char c, int8 maxlen,char* pF);
  17. int8 str_to_int8(char* pF);
  18. void decode_timestamp(char *pF, int8 &day, int8 &mth, int16 &year, int8 &hr, int8 &min, int8 &sec);
  19. void encode_timestamp(char *pF, int8 day, int8 mth, int16 year, int8 hr, int8 min, int8 sec);
  20. void replace_char(char c, char p, char* pF);
  21. int8 ascii_to_hex(char d);
  22. int8 hex_to_int8(char* pF);
  23. int16 hex_to_int16(char* pF);
  24. int32 hex_to_int32(char* pF);
  25. void right_trim(char* pF);
  26. int1 are_all_char_code_printable(char *pF);
  27. void insert_char_in_string_in_position(char *pF, char c, int8 position, int8 len);
  28.  
  29. ///////////////////////////////////////////////////////////////////////////////////////////////////
  30. //
  31. // end of lib_Utils.h
  32. //
  33. ///////////////////////////////////////////////////////////////////////////////////////////////////
  34.  

Lib_Utils.c
Código
GeSHi (csharp):
  1. /** \file lib_Utils.c
  2. * \brief Este fichero contiene un compendio de funciones de utilidad varia.
  3. *
  4. * \author (c) 08.2008 by RedPic
  5. */
  6.  
  7. /** \brief Función auxiliar que convierte de \c BIN a \c BCD
  8.  *
  9.  * \param binary_value Valor binario a convertir.
  10.  * \return Valor BCD convertido.
  11.  */
  12. int8 bin_to_bcd(int8 binary_value){
  13.  
  14.  int8 temp;
  15.  int8 retval;
  16.  
  17.  temp = binary_value;
  18.  retval = 0;
  19.  while(1){
  20.    if(temp >= 10){
  21.      temp -= 10;
  22.      retval += 0x10;
  23.    }else{
  24.      retval += temp;
  25.      break;
  26.    }
  27.  }
  28.  return(retval);
  29. }
  30.  
  31. /** \brief Función auxiliar que convierte de \c BCD a \c BIN
  32.  *
  33.  * \param bcd_value Valor BCD a convertir.
  34.  * \return Valor Binario convertido.
  35.  */
  36. int8 bcd_to_bin(int8 bcd_value){
  37.  
  38.  int8 temp;
  39.  
  40.  temp = bcd_value;
  41.  temp >>= 1;
  42.  temp &= 0x78;
  43.  return(temp + (temp >> 2) + (bcd_value & 0x0f));
  44. }
  45.  
  46. /** \brief Función que convierte un Byte (de 8 bits) a bits string ASCII.
  47.  *
  48.  * Convierte un Byte (de 8 bits) a string ASCII terminado en NULL (\\0).
  49.  *
  50.  * \param val Valor del Byte a convertir.
  51.  * \param[out] pF  Puntero a string_null_ended.
  52.  *
  53.  * \return void
  54.  */
  55. void int8_to_bits_string(int8 val,char* pF){
  56.  
  57.   int8 i,j;
  58.  
  59.   for(i=0,j=7;i<8;i++,j--){
  60.      pF[i]=bit_test(val,j)+'0';
  61.   }
  62.   pF[8]='\0';
  63. }
  64.  
  65. /** \brief Función que convierte un bits string ASCII a un Byte (de 8 bits).
  66.  *
  67.  * Convierte un string ASCII terminado en NULL (\\0) a Byte (de 8 bits).
  68.  *
  69.  * \param pF Puntero a string_null_ended.
  70.  *
  71.  * \return int8 Valor convertido.
  72.  */
  73. int8 bits_string_to_int8(char* pF){
  74.  
  75.   int8 i,j,ret=0;
  76.  
  77.   for(i=0,j=7;i<8;i++,j--){
  78.      if(pF[i]=='1'){
  79.       bit_set(ret,j);
  80.      }
  81.   }
  82.  
  83.   return ret;
  84. }
  85.  
  86.  
  87. /** \brief Función que lee el valor de un bit del byte en la posición dada \c pbit.
  88.  *
  89.  * Lee un Bit en la posición \c pbit del Byte.
  90.  * \param byte_for_bits Byte contenedor de los bits.
  91.  * \param pbit Posición en el Byte del bit a leer.
  92.  * \return bit leído.
  93.  */
  94. int1 get_bit_in_byte(int8 byte_for_bits, int8 pbit){
  95.  
  96.   int1 r;
  97.  
  98.   r=bit_test(byte_for_bits,pbit);
  99.   return r;
  100.  
  101. }
  102.  
  103. /** \brief Función que guarda un bit de valor \c vbit en el Byte en la posición dada \c pbit.
  104.  *
  105.  * Guarda un bit de valor \c vbit en la posición \c pbit del Byte dado.
  106.  * \param byte_for_bits Puntero al Byte contenedor de bits.
  107.  * \param vbit Valor del bit a escribir.
  108.  * \param pbit Posición en el Byte del bit a escribir.
  109.  * \return byte con el bit cambiado.
  110.  */
  111. int8 set_bit_in_byte(int8 byte_for_bits, int1 vbit, int8 pbit){
  112.  
  113.   bit_clear(byte_for_bits,pbit);
  114.   if(vbit==1) bit_set(byte_for_bits,pbit);
  115.   return byte_for_bits;
  116. }
  117.  
  118. /** \brief Función que rellena con un caracter dado un string a partir de NULL hasta una longitud dada.
  119.  *
  120.  * \param c Carácter de relleno.
  121.  * \param maxlen Longitud máxima del string.
  122.  * \param[out] pF  Puntero a string_null_ended a rellenar.
  123.  *
  124.  * \return void
  125.  */
  126. void fill_end_with_char(char c, int8 maxlen,char* pF){
  127.  
  128.   int8 i;
  129.   int1 start=0;
  130.  
  131.   for(i=0;i<maxlen;i++){
  132.      if(start==0){
  133.         if(pF[i]=='\0'){
  134.            start=1;
  135.         }
  136.      }
  137.      if(start==1){
  138.         pf[i]=c;
  139.      }
  140.   }
  141.   pF[maxlen]='\0';
  142.  
  143. }
  144.  
  145. /** \brief Función que convierte un string numérico decimal ASCII NULL-END a su valor entero (8 bits).
  146.  *
  147.  * \param pF  Puntero al buffer que contiene el string numérico ASCII.
  148.  *
  149.  * \return int8 Valor numérico.
  150.  */
  151. int8 str_to_int8(char* pF){
  152.  
  153.   int8 ret=0,i,l,m=1;
  154.  
  155.   l=strlen(pF)-1;
  156.   for(i=l;i<255;i--){
  157.      if(isdigit(pF[i])){
  158.         ret+= m * (pF[i]-'0');
  159.         m = m * 10;
  160.      } else break;
  161.   }
  162.   //printf("StrToInt %s (%u)\r\n",pF,ret);
  163.   return ret;
  164. }
  165.  
  166. /** \brief Función decodifica una variable TimeStamp extrayendo sus componentes.
  167.  *
  168.  * Acepta una variable TimeStamp y devuelve valores enteros de Año, Mes, Día, Hora, Minuto, y Segundo.
  169.  * \param pF Puntero a string TimeStamp
  170.  * \param[out] day Dia
  171.  * \param[out] mth Mes
  172.  * \param[out] year Año (4 dígitos, int16)
  173.  * \param[out] hr Hora
  174.  * \param[out] min Minuto
  175.  * \param[out] sec Segundo
  176.  * \see encode_timestamp()
  177.  */
  178. void decode_timestamp(char *pF, int8 &day, int8 &mth, int16 &year, int8 &hr, int8 &min, int8 &sec){
  179.  
  180.   char sval[]="00";
  181.  
  182.   sval[2]='\0';
  183.   sval[0]=pF[2];  sval[1]=pF[3];  year= 2000+str_to_int8(sval);
  184.   sval[0]=pF[5];  sval[1]=pF[6];  mth = str_to_int8(sval);
  185.   sval[0]=pF[8];  sval[1]=pF[9];  day = str_to_int8(sval);
  186.   sval[0]=pF[11]; sval[1]=pF[12]; hr  = str_to_int8(sval);
  187.   sval[0]=pF[14]; sval[1]=pF[15]; min = str_to_int8(sval);
  188.   sval[0]=pF[17]; sval[1]=pF[18]; sec = str_to_int8(sval);
  189. }
  190.  
  191. /** \brief Función codifica una variable TimeStamp dando sus componentes.
  192.  *
  193.  * Acepta variables de valores enteros de Año, Mes, Día, Hora, Minuto, y Segundo y devuelve puntero a variable de tipo Timestamp.
  194.  * \param[out] pF Puntero a string TimeStamp
  195.  * \param day Dia
  196.  * \param mth Mes
  197.  * \param year Año (4 dígitos, int16)
  198.  * \param hr Hora
  199.  * \param min Minuto
  200.  * \param sec Segundo
  201.  * \see encode_timestamp()
  202.  */
  203. void encode_timestamp(char *pF, int8 day, int8 mth, int16 year, int8 hr, int8 min, int8 sec){
  204.  
  205.   sprintf(pF,"%Lu-%2u-%2u_%2u:%2u:%2u\0",year,mth,day,hr,min,sec);
  206.   replace_char(' ','0',pF);
  207.   replace_char('_',' ',pF);
  208. }
  209.  
  210. /** \brief Reemplaza carácter en string null-terminated.
  211.  *
  212.  * \param c Carácter a sustituir.
  213.  * \param p Carácter nuevo a insertar.
  214.  * \param[out] pF  Puntero a string null-terminated.
  215.  *
  216.  * \return void
  217.  */
  218. void replace_char(char c, char p, char* pF){
  219.  
  220.   int8 i;
  221.   char x;
  222.  
  223.   x=pF[0];
  224.   for(i=0;i<255,x!='\0';i++){
  225.      x=pf[i];
  226.      if(x==c){
  227.         pF[i]=p;
  228.      }
  229.   }
  230. }
  231.  
  232. /** \brief Función que convierte un carácter Hexadecimal ASCII NULL-END a su valor entero de 8 bits.
  233.  *
  234.  * \param d Caracter Hexadecimal a convertir.
  235.  *
  236.  * \return int8 Valor numérico.
  237.  */
  238. int8 ascii_to_hex(char d){
  239.  
  240.   int r=0x00;
  241.  
  242.   if(isxdigit(d)){
  243.      if(isdigit(d)){
  244.         r=d-'0';
  245.      }
  246.      if(isalpha(d)){
  247.         d=toupper(d);
  248.         r=10+(d-'A');
  249.      }
  250.   }
  251.   return(r);
  252. }
  253.  
  254. /** \brief Función que convierte un string numérico Hexadecimal ASCII NULL-END a su valor entero de 8 bits.
  255.  *
  256.  * \param pF Puntero al buffer que contiene el string numérico ASCII Hexadecimal de 2 digitos (00h a FFh).
  257.  *
  258.  * \return int8 Valor numérico.
  259.  */
  260. int8 hex_to_int8(char* pF){
  261.  
  262.   int8 i,ret;
  263.  
  264.   ret=0;
  265.   for(i=1;i!=255;i--){
  266.      ret+=ascii_to_hex(pF[i])*((15*(1-i))+1);
  267.   }
  268.   return ret;
  269. }
  270.  
  271. /** \brief Función que convierte un string numérico Hexadecimal ASCII NULL-END a su valor entero de 16 bits.
  272.  *
  273.  * \param pF Puntero al buffer que contiene el string numérico ASCII Hexadecimal de 4 digitos (0000h a FFFFh).
  274.  *
  275.  * \return int16 Valor numérico.
  276.  */
  277. int16 hex_to_int16(char* pF){
  278.  
  279.   int8  i, p2, p1;
  280.   int16 ret;
  281.  
  282.   p1=hex_to_int8(&pF[2]);
  283.   p2=hex_to_int8(&pF[0]);
  284.   ret=make16(p2,p1);
  285.   //printf("hex_to_int16 recibe: %s p2: %LX p1 %LX valor: %LX (%Lu)\r\n",pF,p2,p1,ret,ret);
  286.   return ret;
  287. }
  288. /** \brief Función que convierte un string numérico Hexadecimal ASCII NULL-END a su valor entero de 32 bits.
  289.  *
  290.  * \param pF Puntero al buffer que contiene el string numérico ASCII.
  291.  *
  292.  * \return int32 Valor numérico.
  293.  */
  294. int32 hex_to_int32(char* pF){
  295.  
  296.   int32 ret;
  297.   int16 p1,p2;
  298.  
  299.   p1=hex_to_int16(&pF[4]);
  300.   p2=hex_to_int16(&pF[0]);
  301.   ret = make32(p2, p1);
  302.   //printf("hex_to_int32 recibe: %s p2: %LX p1 %LX valor: %LX (%Lu)\r\n",pF,p2,p1,ret,ret);
  303.   return ret;
  304. }
  305.  
  306. /** \brief Función que descarta los espacios en blanco por la derecha de una cadena.
  307.  *
  308.  * \param[out] pF  Puntero a string_null_ended a procesar.
  309.  *
  310.  * \return void
  311.  */
  312. void right_trim(char* pF){
  313.  
  314.   int8 i,L;
  315.  
  316.   for(i=0;i<255;i++){
  317.      if(pF[i]=='\0'){
  318.            L=i-1;
  319.            break;
  320.      }
  321.   }
  322.   for(i=L;i>0;i--){
  323.      if(pF[i]==' '){
  324.            pF[i]='\0';
  325.      }
  326.      else{
  327.         break;
  328.      }
  329.   }
  330. }
  331.  
  332. /** \brief Comprueba si todos los caracteres del string NULL-Terminated son imprimibles ('0'..'9','A'..'Z' ó ' ') y hay al menos uno.
  333.  *
  334.  * \param pF Puntero al string a investigar.
  335.  *
  336.  * \return int1 1 si todos los carácteres son válidos, 0 en caso contrario.
  337.  */
  338. int1 are_all_char_code_printable(char *pF){
  339.  
  340.   int1 ret=1;
  341.   int8 i;
  342.  
  343.   for(i=0;i<255;i++){
  344.      if(pF[i]=='\0'){ break;}
  345.      if(!isdigit(pF[i]) && !isupper(pF[i]) && pF[i]!=' ') ret=0;
  346.   }
  347.   if(i==0) ret=0;
  348.  
  349.   return ret;
  350. }
  351.  
  352. /** \brief Función que inserta un caracter en un string.
  353.  *
  354.  */
  355. void insert_char_in_string_in_position(char *pF, char c, int8 position, int8 len){
  356.  
  357.   int8 i;
  358.   char tmp1,tmp2;
  359.  
  360.   for(i=0;i<len;i++){
  361.      if(i>position){
  362.         tmp2=pF[i];
  363.         pf[i]=tmp1;
  364.         tmp1=tmp2;
  365.      }
  366.      if(i==position){
  367.         tmp1=pF[i];
  368.         pF[i]=c;
  369.      }
  370.  
  371.   }
  372.  
  373. }
  374.  
  375. ///////////////////////////////////////////////////////////////////////////////////////////////////
  376. //
  377. // End of lib_utils
  378. //
  379. ///////////////////////////////////////////////////////////////////////////////////////////////////
  380.  

Que es lo que queríamos conseguir, una librería de funciones útiles lista para usar.  Mr. Green
« Última modificación: 12 de Agosto de 2008, 05:22:20 por RedPic » En línea

Contra la estupidez los propios dioses luchan en vano. Schiller
Mi Güeb : Picmania
RedPic
Administrador
DsPIC33
*******
Desconectado Desconectado

Sexo: Masculino
Tibet Tibet

Mensajes: 4876



WWW
« Respuesta #13 : 12 de Agosto de 2008, 05:31:11 »

Utilizando una Librería de Funciones "Útiles" en CCS C, 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:

Código
GeSHi (csharp):
  1. void main() {
  2.   printf("\r\nLibrerias de Funciones Main 1\r\n");
  3.   printf("By Redpic para Foro TODOPIC\r\n");
  4.   printf("Recibe CHAR y convierte a BINARIO\r\n\n");
  5.  
  6.   recFlag=0;                    // Desactivo el Flag de Recepción
  7.   enable_interrupts(int_rda);   // Habilito la interrpción INT_RDA
  8.   enable_interrupts(global);    // Habilito las interrupciones
  9.  
  10.   do {
  11.      if(recFlag==1){
  12.         recFlag=0;
  13.         int8_to_bits_string(rec,respuesta);
  14.         printf("Recibido : %c En bits : %s\r\n",rec,respuesta);
  15.      }
  16.   } while (TRUE);
  17. }
  18.  



Y el otro recibe los mismos caracteres pero los trata como hexadecimales convirtiéndolos a decimal:

Código
GeSHi (csharp):
  1. void main() {
  2.   printf("\r\nLibrerias de Funciones Main 2\r\n");
  3.   printf("By Redpic para Foro TODOPIC\r\n");
  4.   printf("Recibe HEX y convierte a INT8\r\n\n");
  5.  
  6.   recFlag=0;                    // Desactivo el Flag de Recepción
  7.   enable_interrupts(int_rda);   // Habilito la interrpción INT_RDA
  8.   enable_interrupts(global);    // Habilito las interrupciones
  9.  
  10.   do {
  11.  
  12.      if(recFlag==1){
  13.         recFlag=0;
  14.         tmp[1]=toupper(rec);
  15.         respuesta = hex_to_int8(tmp);
  16.         printf("Recibido HEX : %c En Decimal : %u\r\n",rec,respuesta);
  17.      }
  18.   } while (TRUE);
  19. }
  20.  



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 Tecnicas Básicas en C : 1 Usando librerías de Funciones.zip

 Mr. Green

« Última modificación: 12 de Agosto de 2008, 05:26:00 por RedPic » En línea

Contra la estupidez los propios dioses luchan en vano. Schiller
Mi Güeb : Picmania
RICHI777
Colaborador
PIC24F
*****
Desconectado Desconectado

Sexo: Masculino
Argentina Argentina

Mensajes: 812


« Respuesta #14 : 12 de Agosto de 2008, 10:58:59 »

Hola Red, muy buenos los ejemplos !, un comentario de mi parte, no es muy prolijo incluir dentro de la directiva include un archivo c, como el caso que pusiste.
Código:
#include "lib_utils.c"

Normalmente el problema se presenta al debuggear, ya que el debugger abre el archivo fuente y no la salida preprocesada del mismo, entonces le costaria o directamente no podria encontrar las lineas al código pegado atravez de la directiva include.
Mi humilde propuesta seria agrupar todas las funciones útiles en una libreria creada por nosotros y definidas en su correspondiente archivo header que es el único que se incluye en los fuentes que lo requieran.
Con respecto a este punto se debe tener en cuenta la tegnologia del linkers, si el mismo no esta dotado de cierta inteligencia cuando enlaza una lib externa incluye todo el módulo, ejemplo nuestra libreria tiene 10 funciones implementadas pero solo utilizamos 1, bueno hay linker que en la imagen final te coloca el código de las 9 restantes tambien aunque no las uses  Shocked
Para este caso no queda otra que ifdefear....
Normalmente los últimos linker que vi y sobre todos para embebidos el linker se da cuenta de este problema y solo incluye lo que se usa....

Espero que el aporte sume un poquito a tu excelente trabajo !!!!

Saludos !




En línea
Cryn
Colaborador
DsPIC30
*****
Desconectado Desconectado

Sexo: Masculino
Bolivia Bolivia

Mensajes: 3914


ahora con C18 C30 C32


« Respuesta #15 : 12 de Agosto de 2008, 12:41:17 »

totalmente entendido maestro rojo, había muchas cosas que no conocía, muy interesante esa forma de trabajo con el compilador

una pregunta, como se utiliza el Serial Input/Output Monitor? porque me parece que fue todo a nivel simulación verdad? o se molesto en armar todo el circuito?

un saludo
En línea

El peor día, cuando te fuiste.
Cuando estabas a nuestro lado, los mejores
RedPic
Administrador
DsPIC33
*******
Desconectado Desconectado

Sexo: Masculino
Tibet Tibet

Mensajes: 4876



WWW
« Respuesta #16 : 12 de Agosto de 2008, 05:29:21 »

No Cryn, no hay nada simulado.

Tome la primera placa que tenia por la mesa, monté los dos main.c y los programé uno tras otro conectando los PIC con un MAX232 y monitorizándolos en el PC para poder copiar las pantallas y verificar que los ejemplos que pongo funcionan realmente.

Estas pruebas están realizadas con un 18F4550 pero funcionarían igual con cualquier otro PIC.

Es mi forma normal de hacer las cosas.  Mr. Green
En línea

Contra la estupidez los propios dioses luchan en vano. Schiller
Mi Güeb : Picmania
Cryn
Colaborador
DsPIC30
*****
Desconectado Desconectado

Sexo: Masculino
Bolivia Bolivia

Mensajes: 3914


ahora con C18 C30 C32


« Respuesta #17 : 12 de Agosto de 2008, 05:45:56 »

caramba, te sale muy bien Redpic

me parece que voy a copiar tus pasos, lo que me faltaría es tener placas ya hechas, todavía me sigue gustando mi proto lol
En línea

El peor día, cuando te fuiste.
Cuando estabas a nuestro lado, los mejores
vasconinicolas
PIC16
***
Desconectado Desconectado

Sexo: Masculino
Argentina Argentina

Mensajes: 121



« Respuesta #18 : 17 de Agosto de 2008, 10:41:40 »

Hola RedPIC, se nota tu alma de docente jeje. Muchas gracias por tu tiempo puesto a nuestro servicio. La explicación es fantástica y ya mismo la pondré en práctica pues me interesa ir incorporando conocimientos tendientes a optimizar el código, su visualización y porque no, la mejorar nuestra manera de programar.
Particularmente me parece una idea perfecta y espero se siga enriqueciendo este hilo.
Saludos y hasta pronto.  Smile
N.
En línea

"No hacen ciencia los países ricos,
Son ricos los países por hacer ciencia"
Nocturno
Administrador
DsPIC33
*******
Desconectado Desconectado

Sexo: Masculino
España España

Mensajes: 12825



WWW
« Respuesta #19 : 30 de Agosto de 2008, 02:09:11 »

Tengo una duda respecto a la mejor ubicación para declarar las variables globales.

Si las pongo en el main y alguna de ellas es usada por alguna función de otro fichero .C, me da error porque no la reconoce.
Si las pongo en el fichero de la función y alguna de ellas es usada por el main, me da error porque no la reconoce.
Si las pongo en los dos sitios me dice que la definición está duplicada.

La única solución que he encontrado es ponerlas en el primer fichero que se compile, pero me parece una chapuza.

En línea

Un saludo desde Sevilla, España.
Visita MicroPIC                                                                                        ɔ!doɹɔ!ɯ ɐʇ!s!ʌ
TODOPIC
   

 En línea
Páginas: [1] 2 Imprimir 
« anterior próximo »
Ir a:  

Impulsado por MySQL Impulsado por PHP Powered by SMF 1.1.11 | SMF © 2006-2008, Simple Machines LLC XHTML 1.0 válido! CSS válido!
Página creada en 0.388 segundos con 22 consultas.