TODOPIC

Microcontroladores PIC => Lenguaje C para microcontroladores PIC => Mensaje iniciado por: Marttyn en 04 de Febrero de 2008, 14:35:40

Título: Para que sirven los ".h"???
Publicado por: Marttyn en 04 de Febrero de 2008, 14:35:40
hola! hace "bastante" que uso CCS y la verdad nunca me habia preguntado esto... pero, cada vez que creo un nuevo proyecto se crean varios archivos, entre ellos el ".c" donde va el programa, y un ".h" donde especifica que tipo de micro es, los fuses y alguna otra cosilla mas...
lo que hago siempre es eliminar ese archivo y poner el codigo en el programa, porque no se que utilidad tiene que este en un archivo separado.
hoy empezando un nuevo proyecto me plantee la pregunta, y asocie el tema con los includes de librerias especificas. por ejemplo, para controlar una memoria serial hay una libreria para eso, y esta bien que este "sola" dentro de un archivo. supongo que el ".h" que se crea podria ser para algo similar, como poder usar el mismo codigo con otro micro solo cambiando la cabecera... pero eso no lo veo posible, ya que en el main se configuran todos los modulos del pic, y estos varian de uno a otro...
entonces solo se que no se nada...  :mrgreen:
alguno podria explicarme un poquillo para que sirve esto o remitirme a donde considere oportuno?
salu2 y Kgracias (mil gracias  :D :D :D :D)!
Título: Re: Para que sirven los ".h"???
Publicado por: MGLSOFT en 04 de Febrero de 2008, 17:04:15
El archivo .h es un archivo de cabecera (header) y es parte de la anatomia del lenguaje C.
Veras que es muy util al momento de depurar, cuando tienes un proyecto grande con muchos archivos y librerias, el archivo .h te ayuda a declarar alli las variables que son globales (tienen alcance desde cualquier funcion, no solo del main() ), recien alli le sacas el jugo a tu archivo de cabecera.

Es exactamente lo mismo que no aprovechar los archivos .inc del assembler y declarar directamente dentro del programa todas tus variables, en el momento de depurar o cambiar de PIC arrancan todos los problemas... :mrgreen: :mrgreen:
Título: Re: Para que sirven los ".h"???
Publicado por: PalitroqueZ en 06 de Febrero de 2008, 14:56:13
yo creo que es como dice MGLSOFT, que es por cuestiones de convención, porque da igual que le pongas cualquier otra extensión.





Título: Re: Para que sirven los ".h"???
Publicado por: RICHI777 en 06 de Febrero de 2008, 17:53:06
Hola, la función principal del los archivos "H" es instruir al compilador sobre tipos de datos definidos por el usuario ( typedefs ) y el prototipado de funcionesy definicion de macros. En el caso del prototipado, por un lado el compilador puede hacer una verificacion de los tipos parametros que son pasados a una funcion y tambien el retorno de la misma. Es bueno recordar que en versiones primitivas de compilador ( ANSI C ) los prototipos no eran obligatorios y si el compilador no encontraba el prototipo asumia que la funcion era del tipo int Function( void ), si la función no retorna un tipo entero se produce un bug, por demas muy comun. Por eso versiones actuales de compiladores presentan un warning feo cuando falta un prototipo y es mas en C++ ( evolucion de C ) es totalmente obligatorio.
Con respecto al comentario de incluir declaracion de variables globales no es para nada recomendable, ya que si tenemos un H con declaracion tipo "int Pepe", y tenemos dos fuentes distintos que incluyen ese H, la variable Pepe se incluira dos veces, con la cual en el momento del linkeo se producira un error, en C los nombres de las variables deben ser unico, el unico caso es utilizar la palabra static...pero eso es tema para otro post...
Espero haber aportado algo.
Salu2!
Título: Re: Para que sirven los ".h"???
Publicado por: RedPic en 06 de Febrero de 2008, 18:22:30
Uf. Marttyn, voy a ver si puedo ponerte algún caso práctico para que veas su utilidad.

Por ejemplo declarar una función independiente de donde coloques su código, y así poder utilizarla aunque el compilador no ha llegado aún a ella y la haya compilado.

Este primer ejemplo te funcionará porque el compilador encuentra la función antes de llamarla en main()
Código: C#
  1. void mifuncion(void){
  2.    printf("Hola mundo");
  3. }
  4.  
  5. void main(void){
  6.    mifuncion();
  7. }

Pero este te dará error porque el compilador cuando llega a la llamada de mifuncion() no tiene ni idea de que es ni donde está ni que tipo de llamada necesita:
Código: C#
  1. void main(void){
  2.    mifuncion();
  3. }
  4.  
  5. void mifuncion(void){
  6.    printf("Hola mundo");
  7. }

Esto se soluciona declarando la función antes de usarla, así este nuevo ejemplo sí funcionaría:
Código: C#
  1. void mifuncion(void);
  2.  
  3. void main(void){
  4.    mifuncion();
  5. }
  6.  
  7. void mifuncion(void){
  8.    printf("Hola mundo");
  9. }

Ahora imagina que mifuncion() la quieres utilizar, llamar desde muchas otras funciones, o incluso en otros programas y quieres asegurarte que todas tus funciones y programas "conozcan" que mifuncion() existe y cómo se llama.

Entonces guardas la declaración void mifuncion(void); un fichero .h, por ejemplo mifuncion.h y en nuestro ejemplo lo que hacemos es poner el correspondiente include en lugar de la declaración directamente:
Código: C#
  1. #include "mifuncion.h";
  2.  
  3. void main(void){
  4.    mifuncion();
  5. }
  6.  
  7. void mifuncion(void){
  8.    printf("Hola mundo");
  9. }

Estaremos haciendo lo mismo pero tenemos la declaración disponible para cualquier otro programa con tan solo poner el include. Si además colocamos el desarrollo de la función en un fichero .c, por ejemplo mifuncion.c, entonces tendremos el ejemplo absolutamente igual de operativo pero preparado para poder usar mifuncion() donde haga falta:

Código: C#
  1. #include "mifuncion.h";
  2.  
  3. void main(void){
  4.    mifuncion();
  5. }
  6.  
  7. #include "mifuncion.c";

Parece trivial pero exactamente esto es lo que hacemos cuando escribimos una librería, por ejemplo para manejar el LCD o para la EEPROM o ... En el .h ponemos las declaraciones de las funciones que después desarrollaremos en el .c e incluimos ese .h al principio de cada programa donde vayamos a utilizar alguna de las funciones que contiene, y mas abajo podemos poner el include del .c donde se desarrolla su contenido.

Te voy a poner ahora un ejemplo real como la vida misma. Mi librería de utils.h y utils.c donde pongo todo aquello que no se donde poner.

utils.h
Código: C#
  1. /** \file lib_Utils.h
  2.  *  \brief Este fichero contiene las cabeceras necesarias para el manejo de la Libreria de Utilidades
  3.  *
  4.  *
  5.  * \author Diego Márquez
  6.  */
  7.  
  8.  
  9. // Prototipos de funciones
  10.  
  11. int8 bin_to_bcd(int8 binary_value);
  12. int8 bcd_to_bin(int8 bcd_value);
  13. void decode_timestamp(char *pF, int8 &day, int8 &mth, int16 &year, int8 &hr, int8 &min, int8 &sec);
  14. void encode_timestamp(char *pF, int8 day, int8 mth, int16 year, int8 hr, int8 min, int8 sec);
  15. void give_me_greater_timestamp(char *pF);
  16. void timestamp_to_DateTimeShort(char *pF, struct tDateTimeShort * pDTS);
  17. void fill_end_with_char(char c, int8 maxlen,char* pF);
  18. void int8_to_bits_string(int8 val, char* pF);
  19. int8 bits_string_to_int8(char* pF);
  20. int8 str_to_int8(char* pF);
  21. void replace_char(char c, char p, char* pF);
  22. void init_buffer(int16 maxlen, char *buffer);
  23. int8 ascii_to_hex(char d);
  24. int8 hex_to_int8(char* pF);
  25. int16 hex_to_int16(char* pF);
  26. int32 hex_to_int32(char* pF);
  27. int1 are_all_chars_equal_ASCII(char *pF, char Eq);
  28. int1 are_all_chars_equal_ASCII_zero(char *pF);
  29. int32 tDateTimeShort_to_int32(struct tDateTimeShort t);
  30. void right_trim(char* pF);
  31.  
  32. ///////////////////////////////////////////////////////////////////////////////////////////////////
  33. //
  34. // end of lib_Utils.h
  35. //
  36. ///////////////////////////////////////////////////////////////////////////////////////////////////

utils.c
Código: C#
  1. /** \file lib_Utils.c
  2.  * \brief Este fichero contiene un compendio de funciones de utilidad varia.
  3.  *
  4.  * \author Diego Márquez García-Cuervo \n <diego@indalosecurity.com> \n <micropic@garcia-cuervo.com>
  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 obtiene el nombre del día de la semana.
  47.   *
  48.   * Convierte el orden del día de la semana a su correspondiente nombre Lunes=1, Martes=2 ... Domingo=7.
  49.   *
  50.   * \param dow Orden del Día de la semana.
  51.   * \param[out] *pF Puntero a string donde cargar el nombre del día de la semana.
  52.   * \return void
  53.   */
  54. void get_name_day_of_week(int8 dow, char *pF){
  55.  
  56.    strcpy(pF,"\0");
  57.    switch(dow){
  58.       case 1: strcpy(pF,"Lunes");     break;
  59.       case 2: strcpy(pF,"Martes");    break;
  60.       case 3: strcpy(pF,"Miércoles"); break;
  61.       case 4: strcpy(pF,"Jueves");    break;
  62.       case 5: strcpy(pF,"Viernes");   break;
  63.       case 6: strcpy(pF,"Sábado");    break;
  64.       case 7: strcpy(pF,"Domingo");   break;
  65.    }
  66.  
  67. }
  68.  
  69. /** \brief Función que convierte un Byte (de 8 bits) a bits string ASCII.
  70.   *
  71.   * Convierte un Byte (de 8 bits) a string ASCII terminado en NULL (\\0).
  72.   *
  73.   * \param val Valor del Byte a convertir.
  74.   * \param[out] pF  Puntero a string_null_ended.
  75.   *
  76.   * \return void
  77.   */
  78. void int8_to_bits_string(int8 val,char* pF){
  79.  
  80.    int8 i,j;
  81.  
  82.    for(i=0,j=7;i<8;i++,j--){
  83.       pF[i]=bit_test(val,j)+'0';
  84.    }
  85.    pF[8]='\0';
  86. }
  87.  
  88. /** \brief Función que convierte un bits string ASCII a un Byte (de 8 bits).
  89.   *
  90.   * Convierte un string ASCII terminado en NULL (\\0) a Byte (de 8 bits).
  91.   *
  92.   * \param pF Puntero a string_null_ended.
  93.   *
  94.   * \return int8 Valor convertido.
  95.   */
  96. int8 bits_string_to_int8(char* pF){
  97.  
  98.    int8 i,j,ret=0;
  99.  
  100.    for(i=0,j=7;i<8;i++,j--){
  101.       if(pF[i]=='1'){
  102.        bit_set(ret,j);
  103.       }
  104.    }
  105.  
  106.    return ret;
  107. }
  108.  
  109. /** \brief Función que rellena con un caracter dado un string a partir de NULL hasta una longitud dada.
  110.   *
  111.   * \param c Carácter de relleno.
  112.   * \param maxlen Longitud máxima del string.
  113.   * \param[out] pF  Puntero a string_null_ended a rellenar.
  114.   *
  115.   * \return void
  116.   */
  117. void fill_end_with_char(char c, int8 maxlen,char* pF){
  118.  
  119.    int8 i;
  120.    int1 start=0;
  121.  
  122.    for(i=0;i<maxlen;i++){
  123.       if(start==0){
  124.          if(pF[i]=='\0'){
  125.             start=1;
  126.          }
  127.       }
  128.       if(start==1){
  129.          pf[i]=c;
  130.       }
  131.    }
  132.    pF[maxlen]='\0';
  133.  
  134. }
  135.  
  136. /** \brief Función que convierte un string numérico decimal ASCII NULL-END a su valor entero (8 bits).
  137.   *
  138.   * \param pF  Puntero al buffer que contiene el string numérico ASCII.
  139.   *
  140.   * \return int8 Valor numérico.
  141.   */
  142. int8 str_to_int8(char* pF){
  143.  
  144.    int8 ret=0,i,l,m=1;
  145.  
  146.    l=strlen(pF)-1;
  147.    for(i=l;i<255;i--){
  148.       if(isdigit(pF[i])){
  149.          ret+= m * (pF[i]-'0');
  150.          m = m * 10;
  151.       } else break;
  152.    }
  153.    //printf("StrToInt %s (%u)\r\n",pF,ret);
  154.    return ret;
  155. }
  156.  
  157. /** \brief Función decodifica una variable TimeStamp extrayendo sus componentes.
  158.   *
  159.   * Acepta una variable TimeStamp y devuelve valores enteros de Año, Mes, Día, Hora, Minuto, y Segundo.
  160.   * \param pF Puntero a string TimeStamp
  161.   * \param[out] day Dia
  162.   * \param[out] mth Mes
  163.   * \param[out] year Año (4 dígitos, int16)
  164.   * \param[out] hr Hora
  165.   * \param[out] min Minuto
  166.   * \param[out] sec Segundo
  167.   * \see encode_timestamp()
  168.   */
  169. void decode_timestamp(char *pF, int8 &day, int8 &mth, int16 &year, int8 &hr, int8 &min, int8 &sec){
  170.  
  171.    char sval[]="00";
  172.  
  173.    sval[2]='\0';
  174.    sval[0]=pF[2];  sval[1]=pF[3];  year= 2000+str_to_int8(sval);
  175.    sval[0]=pF[5];  sval[1]=pF[6];  mth = str_to_int8(sval);
  176.    sval[0]=pF[8];  sval[1]=pF[9];  day = str_to_int8(sval);
  177.    sval[0]=pF[11]; sval[1]=pF[12]; hr  = str_to_int8(sval);
  178.    sval[0]=pF[14]; sval[1]=pF[15]; min = str_to_int8(sval);
  179.    sval[0]=pF[17]; sval[1]=pF[18]; sec = str_to_int8(sval);
  180. }
  181.  
  182. /** \brief Función codifica una variable TimeStamp dando sus componentes.
  183.   *
  184.   * Acepta variables de valores enteros de Año, Mes, Día, Hora, Minuto, y Segundo y devuelve puntero a variable de tipo Timestamp.
  185.   * \param[out] pF Puntero a string TimeStamp
  186.   * \param day Dia
  187.   * \param mth Mes
  188.   * \param year Año (4 dígitos, int16)
  189.   * \param hr Hora
  190.   * \param min Minuto
  191.   * \param sec Segundo
  192.   * \see encode_timestamp()
  193.   */
  194. void encode_timestamp(char *pF, int8 day, int8 mth, int16 year, int8 hr, int8 min, int8 sec){
  195.  
  196.    sprintf(pF,"%Lu-%2u-%2u_%2u:%2u:%2u\0",year,mth,day,hr,min,sec);
  197.    replace_char(' ','0',pF);
  198.    replace_char('_',' ',pF);
  199. }
  200.  
  201. /** \brief Función que devuelve la mayor fecha posible en formato Timestamp.
  202.   *
  203.   * \param[out] pF Puntero a string TimeStamp
  204.   */
  205. void give_me_greater_timestamp(char *pF){
  206.  
  207.    strcpy(pF,greater_Timestamp);
  208. }
  209.  
  210. /** \brief Función que convierte una variable TimeStamp en una tDateTimeShort.
  211.   *
  212.   * Acepta una variable TimeStamp y devuelve los valores correspondientes de la estructura tDateTimeShort (int8) de Año, Mes, Día, Hora y Minuto.
  213.   * \param pF Puntero a string TimeStamp
  214.   * \param[out] pDTS Puntero a una variable tDateTimeShort
  215.   */
  216. void timestamp_to_DateTimeShort(char *pF, struct tDateTimeShort * pDTS){
  217.  
  218.    char sval[]="00";
  219.  
  220.    // Info Debug
  221.    //printf("\r\ntimestamp_to_DateTimeShort recibe: %s",pf);
  222.  
  223.    sval[2]='\0';
  224.    sval[0]=pF[2];  sval[1]=pF[3];  pDTS->year= str_to_int8(sval);
  225.    sval[0]=pF[5];  sval[1]=pF[6];  pDTS->mth = str_to_int8(sval);
  226.    sval[0]=pF[8];  sval[1]=pF[9];  pDTS->day = str_to_int8(sval);
  227.    sval[0]=pF[11]; sval[1]=pF[12]; pDTS->hr  = str_to_int8(sval);
  228.    sval[0]=pF[14]; sval[1]=pF[15]; pDTS->min = str_to_int8(sval);
  229.  
  230.    // Info Debug
  231.    //printf("\r\ntimestamp_to_DateTimeShort devuelve: %u %u %u %u %u\r\n",pDTS->year,pDTS->mth,pDTS->day,pDTS->hr,pDTS->min);
  232. }
  233.  
  234. /** \brief Reemplaza carácter en string null-terminated.
  235.   *
  236.   * \param c Carácter a sustituir.
  237.   * \param p Carácter nuevo a insertar.
  238.   * \param[out] pF  Puntero a string null-terminated.
  239.   *
  240.   * \return void
  241.   */
  242. void replace_char(char c, char p, char* pF){
  243.  
  244.    int8 i;
  245.    char x;
  246.  
  247.    x=pF[0];
  248.    for(i=0;i<max_len_buffer,x!='\0';i++){
  249.       x=pf[i];
  250.       if(x==c){
  251.          pF[i]=p;
  252.       }
  253.    }
  254. }
  255.  
  256. /** \brief Inicializa un Buffer
  257.   *
  258.   * Esta función inicializa a "\0" todos los bytes de un Buffer.\n
  259.   *
  260.   * \return void
  261.   */
  262. void init_buffer(int16 maxlen, char *buffer){
  263.  
  264.    int16 i;
  265.    for(i=0;i<maxlen;i++){
  266.       buffer[i]='\0';
  267.    }
  268. }
  269.  
  270. /** \brief Función que convierte un carácter Hexadecimal ASCII NULL-END a su valor entero de 8 bits.
  271.   *
  272.   * \param d Caracter Hexadecimal a convertir.
  273.   *
  274.   * \return int8 Valor numérico.
  275.   */
  276. int8 ascii_to_hex(char d){
  277.  
  278.    int r=0x00;
  279.  
  280.    if(isxdigit(d)){
  281.       if(isdigit(d)){
  282.          r=d-'0';
  283.       }
  284.       if(isalpha(d)){
  285.          d=toupper(d);
  286.          r=10+(d-'A');
  287.       }
  288.    }
  289.    return(r);
  290. }
  291.  
  292. /** \brief Función que convierte un string numérico Hexadecimal ASCII NULL-END a su valor entero de 8 bits.
  293.   *
  294.   * \param pF Puntero al buffer que contiene el string numérico ASCII Hexadecimal de 2 digitos (00h a FFh).
  295.   *
  296.   * \return int8 Valor numérico.
  297.   */
  298. int8 hex_to_int8(char* pF){
  299.  
  300.    int8 i,ret;
  301.  
  302.    ret=0;
  303.    for(i=1;i!=255;i--){
  304.       ret+=ascii_to_hex(pF[i])*((15*(1-i))+1);
  305.    }
  306.    //printf("hex_to_int8 recibe: %s valor: %X (%u)\r\n",pF,ret,ret);
  307.    return ret;
  308. }
  309.  
  310. /** \brief Función que convierte un string numérico Hexadecimal ASCII NULL-END a su valor entero de 16 bits.
  311.   *
  312.   * \param pF Puntero al buffer que contiene el string numérico ASCII Hexadecimal de 4 digitos (0000h a FFFFh).
  313.   *
  314.   * \return int16 Valor numérico.
  315.   */
  316. int16 hex_to_int16(char* pF){
  317.  
  318.    int8  i, p2, p1;
  319.    int16 ret;
  320.  
  321.    p1=hex_to_int8(&pF[2]);
  322.    p2=hex_to_int8(&pF[0]);
  323.    ret=make16(p2,p1);
  324.    //printf("hex_to_int16 recibe: %s p2: %LX p1 %LX valor: %LX (%Lu)\r\n",pF,p2,p1,ret,ret);
  325.    return ret;
  326. }
  327. /** \brief Función que convierte un string numérico Hexadecimal ASCII NULL-END a su valor entero de 32 bits.
  328.   *
  329.   * \param pF Puntero al buffer que contiene el string numérico ASCII.
  330.   *
  331.   * \return int32 Valor numérico.
  332.   */
  333. int32 hex_to_int32(char* pF){
  334.  
  335.    int32 ret;
  336.    int16 p1,p2;
  337.  
  338.    p1=hex_to_int16(&pF[4]);
  339.    p2=hex_to_int16(&pF[0]);
  340.    ret = make32(p2, p1);
  341.    //printf("hex_to_int32 recibe: %s p2: %LX p1 %LX valor: %LX (%Lu)\r\n",pF,p2,p1,ret,ret);
  342.    return ret;
  343. }
  344.  
  345. /** \brief Función que comprueba si todos los caracteres del string son iguales a uno dado.
  346.   *
  347.   * \param pF Puntero al string a investigar.
  348.   * \param Eq Caracter a comprobar.
  349.   *
  350.   * \return int1 0 si al menos un caracter es distinto del dado, 1 si todos los caracteres son iguales al dado.
  351.   */
  352. int1 are_all_chars_equal_ASCII(char *pF, char Eq){
  353.  
  354.    int8 ret;
  355.    int8 i,l;
  356.  
  357.    ret=1;
  358.    l=strLen(pF);
  359.    for(i=0;i<l;i++){
  360.       if(pf[i]!=Eq){
  361.          ret=0;
  362.       }
  363.    }
  364.  
  365.    return ret;
  366. }
  367.  
  368. /** \brief Función que comprueba si todos los caracteres del string son '0' (ASCII Cero).
  369.   *
  370.   * \param pF Puntero al string a investigar.
  371.   *
  372.   * \return int1 0 si al menos un caracter es distinto de cero, 1 si todos los caracteres son cero.
  373.   */
  374. int1 are_all_chars_equal_ASCII_zero(char *pF){
  375.  
  376.    int8 ret;
  377.  
  378.    ret = are_all_chars_equal_ASCII(pF,'0');
  379.  
  380.    return ret;
  381. }
  382.  
  383. /** \brief Función que crea un int32 con el contenido de un tDateTimeShort (Se desprecian el componente struct .min)
  384.   *
  385.   * \param t Fecha a convertir.
  386.   *
  387.   * \return int32 valor truncado y convertido.
  388.   */
  389. int32 tDateTimeShort_to_int32(struct tDateTimeShort t){
  390.  
  391.    int32 ret=0;
  392.  
  393.    t.hr = (t.hr * 60) + t.min;
  394.  
  395.    ret=make32(t.year,t.mth,t.day,t.hr);
  396.  
  397.    return ret;
  398. }
  399.  
  400. /** \brief Función que descarta los espacios en blanco por la derecha de una cadena.
  401.   *
  402.   * \param[out] pF  Puntero a string_null_ended a procesar.
  403.   *
  404.   * \return void
  405.   */
  406. void right_trim(char* pF){
  407.  
  408.    int8 i,L;
  409.  
  410.    for(i=0;i<len_string;i++){
  411.       if(pF[i]=='\0'){
  412.             L=i-1;
  413.             break;
  414.       }
  415.    }
  416.    for(i=L;i>0;i--){
  417.       if(pF[i]==' '){
  418.             pF[i]='\0';
  419.       }
  420.       else{
  421.          break;
  422.       }
  423.    }
  424. }
  425.  
  426. ///////////////////////////////////////////////////////////////////////////////////////////////////
  427. //
  428. // End of lib_utils
  429. //
  430. ///////////////////////////////////////////////////////////////////////////////////////////////////

Cualquier programa mio que necesite alguna de las funciones incluidas en utils se podría ver como:

Código: C#
  1. #include <18f1320.h>
  2. #fuses HS,MCLR,PUT,NOWDT,NOPROTECT,NOBROWNOUT,NOLVP,NOCPD,NODEBUG,NOWRT
  3. #use delay(clock=20000000)
  4. #use rs232(baud=115200, xmit=PIN_B1, rcv=PIN_B4)
  5.  
  6. #include "utils.h"
  7.  
  8. #int_rda
  9. void serial_isr() {
  10.  
  11.    if(kbhit()){
  12.       putc(getc());
  13.    }
  14. }
  15.  
  16. void main() {
  17.  
  18.    enable_interrupts(int_rda);
  19.    enable_interrupts(global);
  20.  
  21.    do {
  22.  
  23.    } while (TRUE);
  24. }
  25.  
  26. #include "utils.c"

Ea, esa es una forma de darle uso a los .h

Espero que te sirva.  :mrgreen: :mrgreen: :mrgreen: :mrgreen:
Título: Re: Para que sirven los ".h"???
Publicado por: MGLSOFT en 06 de Febrero de 2008, 19:32:12
Bien hecho!!

Incluso CCS incluye dentro del archivo de libreria .c el llamado al de encabezados .h
Veamos un ejemplo:

Código: [Seleccionar]
#include <16F876.h>
#fuses HS,NOPROTECT,NOLVP,NOWDT
#use delay(clock=2500000)
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7)

#define CAN_DO_DEBUG TRUE

#include <can-mcp2510.c>

#define PIN_LED1  PIN_A1
#define PIN_LED2  PIN_A2
#define PIN_LED3  PIN_A3

Luego veamos una parte del archivo .c

Código: [Seleccionar]
#include <can-mcp2510.h>

//IO pins connected to MCP2510
#ifndef EXT_CAN_CS
   #define EXT_CAN_CS   PIN_B1
   #define EXT_CAN_SI   PIN_C1
   #define EXT_CAN_SO   PIN_C0
   #define EXT_CAN_SCK  PIN_C3
//   #define EXT_CAN_RESET   PIN_B5 //CCS library does not use this pin by default
//   #define EXT_CAN_TX0RTS  PIN_C4 //CCS library does not use this pin by default
//   #define EXT_CAN_TX1RTS  PIN_B4 //CCS library does not use this pin by default
//   #define EXT_CAN_TX2RTS  PIN_C2 //CCS library does not use this pin by default
#endif

#if CAN_DO_DEBUG
 #define can_debug printf
#else
 #define can_debug
#endif

////////////////////////////////////////////////////////////////////////
//
// can_init()
//
// Initializes MCP2510 CAN peripheral.  Sets the RX filter and masks so the
// CAN peripheral will receive all incoming IDs.  Configures both RX buffers
// to only accept valid valid messages (as opposed to all messages, or all
// extended message, or all standard messages).
//
// The constants (CAN_USE_RX_DOUBLE_BUFFER, CAN_ENABLE_DRIVE_HIGH,
// CAN_ENABLE_CAN_CAPTURE, etc) are given a default define in the can-mcp2510.h file.
// These default values can be overwritten in the main code, but most
// applications will be fine with these defaults.
//
//////////////////////////////////////////////////////////////////////////////
void can_init(void) {
   struct struct_RXB0CTRL b_rxb0ctrl;

   mcp2510_init();

   can_set_mode(CAN_OP_CONFIG);   //must be in config mode before params can be set
   can_set_baud();

   b_rxb0ctrl=0;
   b_rxb0ctrl.rxm=CAN_RX_VALID;
   b_rxb0ctrl.bukt=CAN_USE_RX_DOUBLE_BUFFER;
   mcp2510_write(RXB0CTRL, (int)b_rxb0ctrl);
   mcp2510_write(RXB1CTRL, (int)b_rxb0ctrl);

   //if you want to configure the TXnRTS pins, do it here.  default is off

   can_set_id(RX0MASK, CAN_MASK_ACCEPT_ALL, CAN_USE_EXTENDED_ID);  //set mask 0 (RX BUFFER 0)
   can_set_id(RX0FILTER0, 0, CAN_USE_EXTENDED_ID);  //set filter 0 of mask 0 (RX BUFFER 0)
   can_set_id(RX0FILTER1, 0, CAN_USE_EXTENDED_ID);  //set filter 1 of mask 0 (RX BUFFER 0)

   can_set_id(RX1MASK, CAN_MASK_ACCEPT_ALL, CAN_USE_EXTENDED_ID);  //set mask 1 (RX BUFFER 1)
   can_set_id(RX1FILTER2, 0, CAN_USE_EXTENDED_ID);  //set filter 0 of mask 1 (RX BUFFER 1)
   can_set_id(RX1FILTER3, 0, CAN_USE_EXTENDED_ID);  //set filter 1 of mask 1 (RX BUFFER 1)
   can_set_id(RX1FILTER4, 0, CAN_USE_EXTENDED_ID);  //set filter 2 of mask 1 (RX BUFFER 1)
   can_set_id(RX1FILTER5, 0, CAN_USE_EXTENDED_ID);  //set filter 3 of mask 1 (RX BUFFER 1)

   can_set_mode(CAN_OP_NORMAL);
}

////////////////////////////////////////////////////////////////////////
//
// can_set_baud()
//
// Configures the baud rate control registers.  All the defines here
// are defaulted in the can-mcp2510.h file.  These defaults can, and
// probably should, be overwritten in the main code.
//
// Current defaults are set to work with CCS's CAN Prototype board and
// Microchip's MCP250xxx CAN Developers Kit if this PIC is running at 20Mhz.
//
////////////////////////////////////////////////////////////////////////
void can_set_baud(void) {
   struct struct_CNF1 new_CNF1;
   struct struct_CNF2 new_CNF2;
   struct struct_CNF3 new_CNF3;


   new_CNF1.brp=CAN_BRG_PRESCALAR;
   new_CNF1.sjw=CAN_BRG_SYNCH_JUMP_WIDTH;

   new_CNF2.prseg=CAN_BRG_PROPAGATION_TIME;
   new_CNF2.phseg1=CAN_BRG_PHASE_SEGMENT_1;
   new_CNF2.sam=CAN_BRG_SAM;
   new_CNF2.btlmode=CAN_BRG_SEG_2_PHASE_TS;

   new_CNF3.phseg2=CAN_BRG_PHASE_SEGMENT_2;
   new_CNF3.wakfil=CAN_BRG_WAKE_FILTER;

   mcp2510_write(CNF1, (int)new_CNF1);
   mcp2510_write(CNF2, (int)new_CNF2);
   mcp2510_write(CNF3, (int)new_CNF3);
}

void can_set_mode(CAN_OP_MODE mode) {
   struct struct_CANCTRL old_CANCTRL;

   old_CANCTRL=mcp2510_read(CANCTRL);

   old_CANCTRL.reqop=mode;

   mcp2510_write(CANCTRL, (int)old_CANCTRL);

   do {
      old_CANCTRL=mcp2510_read(CANCTRL);
   } while (old_CANCTRL.reqop != mode);
}

Y aqui el archivo .h

Código: [Seleccionar]
#ifndef CAN_ENABLE_CAN_CAPTURE
 #define CAN_ENABLE_CAN_CAPTURE 0
#endif

enum CAN_OP_MODE {CAN_OP_CONFIG=4, CAN_OP_LISTEN=3, CAN_OP_LOOPBACK=2, CAN_OP_SLEEP=1, CAN_OP_NORMAL=0};

//can control
struct struct_CANCTRL {
   int  clkpre:2; //0:1 //clkout pin prescalar
   int1 clken; //2   //clkout pin enable
   int1 void3; //3
   int1 abat;  //4   //abort all pending transmissions
   CAN_OP_MODE reqop:3; //5:7 //request operation mode
};
#define CANCTRL   0x0F  //or 1f, or 2f, or 3f ... or 7f

enum CAN_INT_CODE {CAN_INT_RX1=7, CAN_INT_RX0=6, CAN_INT_TX2=5, CAN_INT_TX1=4, CAN_INT_TX0=3, CAN_INT_WAKEUP=2, CAN_INT_ERROR=1, CAN_INT_NO=0};

//can status register READ-ONLY
struct struct_CANSTAT {
   int1 void0;   //0
   CAN_INT_CODE icode:3;   //1:3   //interrupt code
   int1 void4;   //4
   CAN_OP_MODE opmode:3;   //5:7   //operation mode status
};
#define CANSTAT 0x0E //or 1e, or 2e ... or 7e

sigue pero es muy largo.....

Hasta llegar a los prototipos de funciones...

Código: [Seleccionar]
void  can_init(void);
void  can_set_baud(void);
void  can_set_mode(CAN_OP_MODE mode);
void can_set_id(int addr, int32 id, int1 ext);
int32 can_get_id(int addr, int1 ext);
int   can_putd(int32 id, int * data, int len, int priority, int1 ext, int1 rtr);
int1  can_getd(int32 & id, int * data, int & len, struct rx_stat & stat);

void mcp2510_init();
void mcp2510_command(int command);
void mcp2510_write(int address, int data);
int mcp2510_status(void);
int mcp2510_read(int address);

#endif
Título: Re: Para que sirven los ".h"???
Publicado por: Geo en 08 de Enero de 2009, 22:13:30
Precisamente en este momento me había encontrado con el problema de compilar diferentes unidades, desconocía que CCS no permite enlazar unidades ya compiladas :?.

En su lugar, se incluye el código en el mismo archivo principal con #include archivo.c
Título: Re: Para que sirven los ".h"???
Publicado por: Marttyn en 08 de Enero de 2009, 22:29:08
Gracias a todos por las respuestas!
Un poco tarde lo se... pero no recibi ningun aviso de que me habian respondido  :(
Recibi un aviso ahora y veo que hay un monton de respuestas y ayuda!
Gracias de nuevo, cada vez que entro en el foro aprendo un monton!  :-/

PS: que son los prototipos?
Título: Re:Para que sirven los ".h"???
Publicado por: micro_pepe en 19 de Enero de 2017, 19:04:31
Tengo una duda a cerca de esto, si tengo dos librerías .h, y quiero que una vea a la otra y la otra a la una, ¿que hay que hacer?

pongamos este ejemplo:
Código: C
  1. #include "libreria_1.h"
  2. #include "libreria_2.h"

Si libreria_2 usa funciones de la 1, no hay problema, pero si la 1 usa funciones de la 2, no las ve, podría darse el caso que se usen librerías en los dos sentidos.

Gracias!!! Un saludo.
Título: Re:Para que sirven los ".h"???
Publicado por: KILLERJC en 19 de Enero de 2017, 19:41:31
Es dificil ver la situacion sin un ejemplo un poco mas practico.

Lo unico que se me ocurre sin ver el caso practico es que: Si libreria_2 usa funciones de la 1, entonces en libreria_2.c deberias tener el include a libreria_1.h y viceversa.
En si esos includes son la puerta de los demas archivos para acceder al .c. Por lo cual si estas en libreria_1.c, entonces podrias llegar a tener algo asi:

Código: C
  1. #include "libreria_1.h"
  2. #include "libreria_2.h"

Si estas en libreria_2.c tendrias algo asi:

Código: C
  1. #include "libreria_2.h"
  2. #include "libreria_1.h"

Ambos tienen acceso a la otra.
Si en realidad esos includes estan en otro archivo, digamos main.c es imposible que puedas juntarlos ahi. Ya que incluir 2 include en un archivo externo a los archivos fuentes de las librerias (.c ) No va a hacer que libreria_1.c pueda acceder a libreria_2.c.

Realmente no tengo idea de como puede llegar a ocurrir lo que estas planteando.
Título: Re:Para que sirven los ".h"???
Publicado por: micro_pepe en 20 de Enero de 2017, 13:12:07
Realmente no tengo idea de como puede llegar a ocurrir lo que estas planteando.

Es algo que me pasó hace tiempo, pero creo que por una mala organización de los ficheros, encontré este hilo y recordé aquel problema.

Lo que no tengo muy claro, es si en el fichero que hace uso del otro hay que incluir las definiciones de funciones con extern:

fichero_1.h

Código: C
  1. void funcion_1(void);

fichero_2.h
Código: C
  1. extern void funcion_1(void);
  2. void funcion_2(void);

fichero_2.c
Código: C
  1. #include "fichero_2.h"
  2.  
  3. void funcion_2(void){
  4.      ------
  5.      ------
  6.      funcion_1();
  7.      ------
  8. }

Eso me está dando problemas, y lo he resuelto poniendo la cabecera de la función con extern. He probado lo que dices    KILLERJC, de poner el include de la otra librería en el .c, pero da un error. Es una librería con un retardo que hace uso del __delay_ms(), y creo que por ahí vienen los problemas.

No he dicho nada  :5] estaba poniendo el include de la segunda librería en el .h, cuando debe ir en el .c:

Citar
Si estas en libreria_2.c tendrias algo asi:

Código: C

    #include "libreria_2.h"
    #include "libreria_1.h"

Salí a darme una vuelta, a que me diese un poco el fresco, y caí en ello. A veces es mejor cambiar de actividad que emperrarse en encontrar la solución, y luego volver a ello.

Gracias!!! un saludo.
Título: Re:Para que sirven los ".h"???
Publicado por: Marttyn en 24 de Enero de 2017, 10:14:27
Bueno, a mi me paso algo similar, que una libreria se "necesitaba a si misma".
En mi caso es una libreria para leer el voltaje de la bateria segun el voltaje Vdd.
Mi libreria necesita saber el tipo de bateria que se va a usar para los calculos, y los DEFINE de esa libreria estan en la misma libreria.
Por ejemplo:
leer_bateria.h
Código: C
  1. #define BAT_LIPO    3.7
  2. #define BAT_1AA    1.5
  3. #define BAT_2AA    3
  4. #define BAT_3AA    4.5
  5.  
  6. //calculos con la bateria, usando BAT_TYPE
  7. ...
  8. ...

Desde el header de mi proyecto tengo que declarar BAT_TYPE como el tipo de bateria a usar:
Código: C
  1. #define BAT_TYPE    BAT_LIPO

A su vez, la libreria leer_bateria.h buscaba BAT_TYPE para hacer ciertos calculos...
Por lo que si defino BAT_TYPE antes de llamar a la libreria, no encuentra los define de tipos de bateria
Y si defino BAT_TYPE despues de llamar a la libreria, lo que ocurre es que salta un error por que BAT_TYPE no esta declarado.

La solucion fue crear un header solo con los tipos de bateria, e incluirlo en el proyecto al principio, luego puedo crear el define BAT_TYPE, y luego incluir la libreria.
Título: Re:Para que sirven los ".h"???
Publicado por: micro_pepe en 26 de Enero de 2017, 09:54:11
Citar
La solucion fue crear un header solo con los tipos de bateria, e incluirlo en el proyecto al principio, luego puedo crear el define BAT_TYPE, y luego incluir la libreria.

Sí, es una solución posible, gracias!!!
Título: Re:Para que sirven los ".h"???
Publicado por: Picuino en 26 de Enero de 2017, 11:20:40
Muy sencillo:
Si quieres que la librería 1 vea la librería 2, añades en el header de la librería 1,  la librería 2.
Para eso sirve añadir headers.

Ahora el problema que tienes es que la librería 2 puede aparecer en varios sitios y estar "Redefinida".
Para evitar eso se suele añadir un pequeño código a las librerías que asegura que sólo se defina una vez:

Código: C
  1. #ifndef  LIBRERIA_2_H
  2. #define LIBRERIA_2_H
  3.  
  4. ... aquí pones todo el código de la librería 2, que sólo se leerá la primera vez.
  5.  
  6. #endif

Un saludo.
Título: Re:Para que sirven los ".h"???
Publicado por: Picuino en 26 de Enero de 2017, 12:16:45
Marttyn, creo que lo mejor en tu caso es utilizar macros para hacer los cálculos. Una macro es como una función y puedes pasarle el parámetro que quieras después de estar definida. Es más elegante y no cometes errores si cambias el orden de las líneas.

De todas formas debería ver el código para estar seguro.

Un saludo.
Título: Re:Para que sirven los ".h"???
Publicado por: Marttyn en 27 de Enero de 2017, 08:42:02
Muy sencillo:
Si quieres que la librería 1 vea la librería 2, añades en el header de la librería 1,  la librería 2.
Para eso sirve añadir headers.

Eso que comentas es muy util, incluido el IFNDEF, pero no quita que tengas que incluir un header extra para poder hacer los DEFINE correspondietes.
Si mi libreria necesita unos DEFINE, como por ejemplo el tipo de bateria para hacer los calculos. Y las constantes del tipo de bateria estan dentro de la misma libreria, entonces si inserto la libreria antes de los DEFINE la libreria me tira un error de que no estan definidos ciertos valores, y si la inserto despues de los DEFINE, el error me lo tira diciendo que esas constantes no existen.

Marttyn, creo que lo mejor en tu caso es utilizar macros para hacer los cálculos. Una macro es como una función y puedes pasarle el parámetro que quieras después de estar definida. Es más elegante y no cometes errores si cambias el orden de las líneas.

En principio no he encontrado utilidad para los macros, ya que quiero que todos estos calculos se hagan antes de compilar para liberar instrucciones en el PIC. Con macros entiendo que la funcion te queda creada y cuando le pasas los valores el PIC tendra que hacer los calculos.
Esta es una pequeña porcion de la libreria:

Código: C
  1. #ifndef BAT_ADC_CH
  2. #ERROR "Hay que declarar el canal del ADC que se usa para leer el voltaje"
  3. #endif
  4.  
  5. #ifndef BAT_PIC_VREF
  6. #ERROR "Hay que declarar el voltaje de referencia o el voltaje al que funciona el PIC"
  7. #endif
  8.  
  9. #ifndef BAT_R1
  10. #ERROR "Hay que declarar R1 del divisor de tension"
  11. #endif
  12.  
  13. #ifndef BAT_R2
  14. #ERROR "Hay que declarar R2 del divisor de tension"
  15. #endif
  16.  
  17. #define BAT_PROPORCION_IN_OUT   ((float)(BAT_R1 + BAT_R2) / BAT_R2)             // Vin / Vout
  18. #define BAT_PROPORCION_OUT_IN   ((float)BAT_R2 / (BAT_R1 + BAT_R2))             // Vout / Vin
  19. #define BAT_ADCxVOLT                    ((float)ADC_MAX_VAL / BAT_PIC_VREF)
  20. #define BAT_VOLTxADC                    ((float)BAT_PIC_VREF / ADC_MAX_VAL)
  21. #define BAT_VIN_MAX                             (BAT_PROPORCION_IN_OUT * BAT_PIC_VREF)
  22.  
  23. //Vin * (ADCMaxVal / Vref) * (R2 / (R1 + R2))
  24. #define ADC_BAT_BAJA    (long)(V_BAT_BAJA * BAT_ADCxVOLT * BAT_PROPORCION_OUT_IN)
  25. #define ADC_BAT_MEDIA   (long)(V_BAT_MEDIA * BAT_ADCxVOLT * BAT_PROPORCION_OUT_IN)
  26. #define ADC_BAT_ALTA    (long)(V_BAT_ALTA * BAT_ADCxVOLT * BAT_PROPORCION_OUT_IN)
  27.  
  28. #define RANGO_ADC_BAT   (ADC_BAT_MAX - ADC_BAT_MIN)

Tengo que decir que habiendo aprendido a usar Arduino, el CCS se me queda bastante pobre a la hora de desarrollar librerias. Arduino usa un lenguaje C mucho mas "estandar" que permite cosas muy interesantes. No hay mas que ver librerias de Adafruit para ver con que complejidad y "encapsulamiento" se pueden programar las librerias, para que luego tu codigo sea mucho mas limpio y legible.
Título: Re:Para que sirven los ".h"???
Publicado por: KILLERJC en 27 de Enero de 2017, 12:47:08
Citar
Tengo que decir que habiendo aprendido a usar Arduino, el CCS se me queda bastante pobre a la hora de desarrollar librerias. Arduino usa un lenguaje C mucho mas "estandar" que permite cosas muy interesantes. No hay mas que ver librerias de Adafruit para ver con que complejidad y "encapsulamiento" se pueden programar las librerias, para que luego tu codigo sea mucho mas limpio y legible.

Concuerdo que CCS no es estandar para nada, y por eso es una de las cosas que aborrezco del mismo. Arduino usa C++ lo cual incorpora unas cuaaantas cosas mas. Aunque el fuerte de C++ es su STL, RTTI y manejo de memoria dinamica, pero que no creo que sean usados en la programacion de micros como arduino especialmente este ultimo, Creo que unicamente toman de C++ el tema de las clases con sus inheritance / cast de c++ / nullptr y tal ves algo mas que me olvide.

En C podes realizar lo mismo que haces con Arduino, si es que correctamente creas tu codigo para que no dependa de nada del hardware (incluso con CCS), luego creas otro archivo para el hardware, de esta forma si necesitas cambiar el micro o de fabricante el codigo anterior todavia sirve, porque es puro C. Algunos rebusques son crear headers internos ( solo para el archivo de la libreria ) y headers externos, como el para el port a cada micro que te guste y... interfaz de funciones para que sea usado desde otro archivo (los cuales no incluyen nada que no necesiten ej. struct/union/defines/etc) asi no se contamina el espacio de nombres de variables.
Título: Re:Para que sirven los ".h"???
Publicado por: Picuino en 28 de Enero de 2017, 06:53:27
A lo que me refería es a que se pueden utilizar los define con macros:

Código: C
  1. #define BAT_PROPORCION_IN_OUT(R1, R2)   ((float)(R1 + R2) / R2)

Así puedes definir la macro sin que estén definidas R1 y R2. Luego utilizas la macro con dos valores cualquiera o con otras definiciones:


Código: C
  1. #include "cabecera.h"
  2.  
  3. int v = BAT_PROPORCION_IN_OUT(2200, 1000)
  4.  
  5. #define R1 2200
  6. #define R2 1000
  7.  
  8. int v = BAT_PROPORCION_IN_OUT(R1, R2)

Pero tu solución también vale.

Un saludo.
Título: Re:Para que sirven los ".h"???
Publicado por: Marttyn en 30 de Enero de 2017, 15:42:39
Gracias Picuino!
Pero al hacer como dices tu, los macros se calculan antes de compilar? O utiliza memoria de programa para ello?
Título: Re:Para que sirven los ".h"???
Publicado por: Picuino en 30 de Enero de 2017, 17:07:54
Las macros se convierten en constantes antes de compilar. No ocupan memoria ni tiempo de ejecución.
Esa es la parte buena de las macros.
Un saludo.
Título: Re:Para que sirven los ".h"???
Publicado por: Marttyn en 09 de Febrero de 2017, 12:44:39
Las macros se convierten en constantes antes de compilar. No ocupan memoria ni tiempo de ejecución.
Esa es la parte buena de las macros.
Un saludo.

No entiendo como... un macro utiliza "variables", por lo que no se puede calcular antes de compilar. A menos que sea un macro utilizado solo para constantes definidas.
Título: Re:Para que sirven los ".h"???
Publicado por: Picuino en 09 de Febrero de 2017, 13:15:10
Eso es. Sólo se utilizan constantes.
Título: Re:Para que sirven los ".h"???
Publicado por: colotron en 09 de Febrero de 2017, 14:07:21
Las macros se convierten en constantes antes de compilar. No ocupan memoria ni tiempo de ejecución.
Esa es la parte buena de las macros.
Un saludo.

No entiendo como... un macro utiliza "variables", por lo que no se puede calcular antes de compilar. A menos que sea un macro utilizado solo para constantes definidas.

No, no son variables, es texto, el preprocesador trabaja con texto.

#define UNA_MACRO_CON_ARGUMENTO(ARG) ((ARG)*5 - 12)

Hace de cuenta que cuando invocas
algunaVariable = UNA_MACRO_CON_ARGUMENTO(40);

El pre-procesador de C hace un buscar/reemplazar, busca primero UNA_MACRO_CON_ARGUMENTO, y donde decía ARG ahora reemplaza con 40
algunaVariable = ((40)*5 - 12);
El pre-procesador se ejecuta antes del compilador, hace esos reemplazos, y luego se llama al compilador.
Luego si yo la uso mal y escribo:
algunaVariable = UNA_MACRO_CON_ARGUMENTO("prrrffff");

el pre-procesador va a poner
algunaVariable = (("prrrffff")*5 - 12);
y el compilador no va a entender nada y quejarse en la línea de código anterior.

Pero si defino mal la macro, por ejemplo cometo un error de tipeo:
#define UNA_MACRO_CON_ARGUMENTO(ARG) ((ATG)*5 - 12)
algunaVariable = UNA_MACRO_CON_ARGUMENTO(40);

ahora se convierte en:
algunaVariable = ((ATG)*5 - 12);
el compilador se va a preguntar donde está ATG y va a marcar error en la línea de algunaVariable, y NO en la definición de la macro.
Muchas veces me pasó que uso alguna macro, el compilador informa de error en la línea tal (que claro, la línea es una llamada a una función que toma un argumento que se saca de una macro que a su vez llama a otra función que hace un typecasting...) y uno pierde horas buscando errores en funciones cuando el problema era una macro mal definida.

NO hay chequeo de error de macros, es decir, a nivel pre-procesador. Y por eso se dice que una macro es "parecida" a una función, pero no lo es porque como ya mencionaron no genera código, ni tampoco hay chequeo de errores.
Y es solo eso, un reemplazo textual (un buscar-reemplazar), que puede ser mucho más complicado con varias líneas y varias pasadas.

Con pasadas me refiero a:

#define ECUACION_RECTA(pendiente, offset, valor) (pendiente * valor + offset)
#define ADC_PENDIENTE(ref_mv, resolucionBits) (ref_mv/ (2^resolucionBits-1) )

#define CUENTAS_ADC_A_MV(valorADC) ECUACION_RECTA(ADC_PENDIENTE(2500,10), 0, valorADC)

Esta última línea "diría" que uso un conversor de 10 bits con una referencia de 2,5V (2500mV).

Entonces si escribo

variable = CUENTAS_ADC_A_MV(registroADC) ;

primero el preprocesador hace (pasada 1):
variable = ECUACION_RECTA(ADC_PENDIENTE(2500,10), 0, registroADC);
luego (pasada 2):
variable = ECUACION_RECTA((2500/ (2^10-1) ), 0, registroADC);

y por último (pasada 3):
variable = ( (2500/ (2^10-1) ) * registroADC + 0);

Bueno, seguro hay errores pero yo hago como el preprocesador y no chequeo nada :P
¿Pero creo que la idea se ve?
Título: Re:Para que sirven los ".h"???
Publicado por: Marttyn en 09 de Febrero de 2017, 14:58:06
Pero con macros no puedes usar variables como argumento?
Algo asi:

#define UNA_MACRO_CON_ARGUMENTO(ARG) ((ARG)*5 - 12)
int x;

for(x = 0; x<10; x++){
   algunaVariable = UNA_MACRO_CON_ARGUMENTO(x);
}

Como hace el preprocesador con esto?
Título: Re:Para que sirven los ".h"???
Publicado por: colotron en 09 de Febrero de 2017, 15:58:54
Pero con macros no puedes usar variables como argumento?
Algo asi:

#define UNA_MACRO_CON_ARGUMENTO(ARG) ((ARG)*5 - 12)
int x;

for(x = 0; x<10; x++){
   algunaVariable = UNA_MACRO_CON_ARGUMENTO(x);
}

Como hace el preprocesador con esto?

Bueno, para el preprocesador tanto x como el valor 40 es solo texto, que reemplazará si hay algun #define que así lo indique:

int x;
for(x = 0; x<10; x++){
   algunaVariable = UNA_MACRO_CON_ARGUMENTO(x);
}

Al inspeccionar ese código va a encontrarse con que UNA_MACRO_CON_ARGUMENTO es una macro, luego en donde aparezca su nombre reemplazará su nombre por la definición especificada en el #define, resultando:

for(x = 0; x<10; x++){
   algunaVariable = ((x)*5 - 12);
}

Para el pre-procesador el código fuente que escribimos es un montón de texto, no distingue variables, constantes, funciones... todo texto. Lo que sí entiende es que se pueden definir reglas de edición con #define, y la directivas #if, #else y todo lo que empiece con #; que justamente son las instrucciones para el pre-procesador que realice los reemplazos (#define), incrustaciones (#include), e inclusión condicional que les indiquemos (#if, #ifdef).