Está en la página 1de 31

Generando un pulso. 1 Parte.

Onda cuadrada
simtrica mediante INTRTCC

A.- Conceptos involucrados:

Decamos en Midiendo un pulso. 4 Parte. El pulso


completo. El Periodo y la Frecuencia que ... dadle un
vistazo porque vamos a tratar con los mismos elementos,
o mejor an: los vamos a repetir en este mismo hilo.

Decamos all que el Tiempo T que transcurre ente dos


flancos sucesivos de subida (o de bajada) es lo que
conocemos como Periodo del Pulso (Period), o sea: lo
que dura un pulso completo.

Cada T segundos ( milisegundos, microsegundos)


tenemos un nuevo pulso completo.

Como vemos, cada pulso tiene un Tiempo en Alto (High


Time) y un Tiempo en estado Bajo (Low Time). La suma
de los dos es el Periodo T del pulso.

Como vemos en la imagen superior un pulso completo


comienza con un flanco de subida en T1, sigue con uno de
bajada en T2 y concluye en el nuevo flanco de subida T3.

Entre T1 y T2 permanece el pulso en Alto, Wh, mientras


que entre T2 y T3 lo hace en bajo, Wl.

El tiempo transcurrido entre T1 y T3 es el Periodo del


Pulso. Si este periodo lo expresamos en Segundos
entonces su inverso, 1 / Periodo, es la Frecuencia del tren
de pulsos.

Estos dos valores son los que vamos a generar con


nuestro PIC: el Periodo T y la frecuencia F.

B.- Tcnica a Aplicar:

Para generar el periodo T de nuestro pulso vamos a


utilizar dos recursos de los que disponen la gran mayora
de los PIC's, tanto los de la serie 18F como los 16F:

El TIMER0 y la Interrupcin Por Desbordamiento de


TIMER.

Sobre el TMR0 podis leer lo escrito anteriormente en los


hilos que preceden a ste hablando del TMR1 ...

Aqu solo recordar que el TMR0 es un contador


que incrementa su valor cada FOSC*4 segundos y que
cuando alcanza su valor mximo, 255 en TMR0 de bits
65535 en los PIC en que TMR0 es de 16 bits, dispara una
Peticin de Servicio por Interrupcin llamada RTCC.

Esta interrupcin se produce


cada 256*FOSC*4 segundos si el TMR0 es de 8 bits o
cada 65536*FOSC*4 si es de 16 bits.

Recordad tambin que podemos aplicar distintos divisores


a estas velocidades, Preescaler, de tal forma que
podemos ralentizar su cronometraje a 1:2, 1:4, 1:8,
1:16, 1:32, 1:64, 1:128 1:256 con lo que la formula
anterior queda automticamente multiplicada por el
divisor que usemos. El mnimo es 1:2.

Adems de todo esto tenemos tambin otro grado de


libertad con estos recursos: Podemos asignar un valor
determinado al TMR0 distinto de 0 cada vez que se nos
dispare la interrupcin RTCC. Con ello conseguimos que el
contador al no empezar en 0 sino en un valor mayor n no
deba completar su recorrido para disparar de nuevo la
interrupcin, sino que solo deba contar 255-n 65535n segn el tipo de TMR0 que estemos usando.

As entre FOSC (el cristal que oscila a una determinada


frecuencia), el divisor de frecuencia aplicado (Preescaler)
y el valor de TMR0 asignado cada RTCC (offset) tenemos
una gran capacidad de maniobra para poder contar
tiempos bastante precisos.

Como en las Tcnicas anteriores medamos una onda de


entrada cuya frecuencia era aproximadamente de 2 Khz
(0.5 ms de periodo) vamos en esta tcnica a generar este
mismo tipo de onda.

As lo que vamos a hacer es generar una RTCC cada


semiperiodo de onda conmutando el estado del Pin de alto
a bajo y viceversa: la mitad del periodo con el Pin en Alto
y la otra mitad con el Pin en bajo.

Cada RTCC volvemos a poner TMR0 a 177 para que


recorra slo 255 - 177 = 78 Ticks de TMR0.

C.- Implementacin en C:

Para implementar nuestro Cdigo en C vamos a


centrarnos en los puntos que hemos descrito en la seccin

anterior.

Para configurar la interrpcin RTCC utilizaremos lo


siguiente:

setup_timer_0(RTCC_DIV_16 | RTCC_8_BIT);// Ajusto divisor


de TMR1 para RTC cada 0.25 ms
enable_interrupts(int_rtcc);
enable_interrupts(global);
// Habilito las
interrupciones necesarias

Con esta configuracin inicial podemos ya escribir


nuestra rutina ISR que es extremadamente simple:

#int_rtcc
void handle_rtcc_int(){

Output_Toggle(OUT); // Conmuto PIN de Salida;


Set_timer0(177);

Fijaos que en la rutina ISR anterior lo nico que hacemos


es conmutar el Pin de Salida y poner el offset del TMR0 a
177 para obtener los 255-177 Ticks de TMR1 que
necesitamos para hacer saltar RTCC aproximadamente
cada 0.25 ms.

Con todo esto el programa completo para


nuestro Generar una Onda Cuadrada Simtrica de 2
Khz de Frecuencia queda as:

/////////////////////////////////////////////////////////////////////////////////////

//
// generando_un_pulso_5_intrtcc.c
//
// SERIE: "Tcnicas en C" para el Foro TODOPIC
//
// (c) 10.2006 by RedPic
//
// Propsito: Generar un pulso de 2 Khz (0.5 ms de periodo)
//
// Tcnica Empleada: Habilitar INTRTC para conmutar un PIN cada
semiperiodo
//
////////////////////////////////////////////////////////////////////////////////////
#include <18f4550.h>
#fuses
HS,MCLR,CCP2B3,NOWDT,NOPROTECT,NOPUT,NOBROWNOUT,NOPB
ADEN,NOLVP,NOCPD,NODEBUG,NOWRT,NOVREGEN
#use delay(clock=20000000)
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7)
////////////////////////////////////////////////////////////////////////////////////
//
// Defines y Constantes
//
////////////////////////////////////////////////////////////////////////////////////
#define LED PIN_E0 // Defino el Pin del Led
#define OUT PIN_B3
////////////////////////////////////////////////////////////////////////////////////
//
// Interrupcin por Externa por Cambio de Flanco en RB0
//
////////////////////////////////////////////////////////////////////////////////////
#int_rtcc
void handle_rtcc_int(){
Output_Toggle(OUT); // Conmuto PIN de Salida;
Set_timer0(177);
Output_Toggle(LED); // Conmuto PIN de Led;
}
////////////////////////////////////////////////////////////////////////////////////
//
// Main
//
////////////////////////////////////////////////////////////////////////////////////
void main() {

////////////////////////////////////////// INICIALIZACIONES GENERALES


delay_ms(333);
// Espero a que todo se estabilice e ...
disable_interrupts(global);
// Inicializo el Micro y ...
disable_interrupts(int_timer1); // deshabilitando todo lo no
necesario ...
disable_interrupts(int_rda);
disable_interrupts(int_ext);
disable_interrupts(int_ext1);
disable_interrupts(int_ext2);
setup_adc_ports(NO_ANALOGS);
setup_adc(ADC_OFF);
setup_spi(FALSE);
setup_psp(PSP_DISABLED);
setup_counters(RTCC_INTERNAL,RTCC_DIV_2);
setup_timer_0(RTCC_OFF);
setup_timer_1(T1_INTERNAL | T1_DIV_BY_1);
setup_timer_2(T2_DISABLED,0,1);
setup_timer_3(T3_DISABLED);
setup_comparator(NC_NC_NC_NC);
setup_vref(FALSE);
port_b_pullups(FALSE);
delay_ms(333);
/////////////////////////////////////////// INICIALIZACIONES PERTINENTES A LA
APLICACION
setup_timer_0(RTCC_DIV_16 | RTCC_8_BIT); // Ajusto divisor de
TMR1 para RTC cada 0.25 ms
enable_interrupts(int_rtcc);
enable_interrupts(global);
// Habilito las interrupciones
necesarias
printf("\r\nGenerando un pulso : RTCC\r\n");
printf("By Redpic para Foro TODOPIC\r\n\n");
do {
}

} while (TRUE);

D.- Ejemplo funcionando:

Generando un pulso. 2 Parte. Onda cuadrada


simtrica mediante INTCCP

A.- Conceptos involucrados:

Seguimos dndole vueltas al mismo concepto que en


todas las Tcnicas en C anteriores. Si lo deseis podis
repasar esto mismo en cualquiera de ellas.

Volvemos a generar un pulso de Periodo T con dos


semiperiodos iguales, simtrico, y de unos 2 Khz de
frecuencia: unos 0.25 ms en alto y otros 0.25 ms en bajo,
total 0.5 ms de periodo.

B.- Tcnica a Aplicar:

Para generar el periodo T de nuestro pulso vamos a


utilizar de nuevo el mdulo CCP del que disponen la gran
mayora de los PIC's, tanto los de la serie 18F como los
16F.

Recordemos: En la Tcnica Midiendo un pulso. 2 Parte.


Tiempo en Alto con INTCCP hacamos uso de este mismo

mdulo utilizando su modo Capture en el que cuando


detectbamos un flanco externo se nos capturaba el
valor en ese momento del TMR1. Jugando con los
sucesivos valores obtenamos la duracin del pulso.

En sta tcnica que ahora describimos vamos a hacer lo


diametralmente opuesto: Vamos a utilizar el CCP en
modo Compare para que el CCP
vaya comparando automticamente el valor
de TMR1 con un valor de referencia que le
damos, CCP_x, y en cuanto lo alcance se realice
la conmutacin del Pin asociado a l y nos dispare la
Peticin de Servicio de Interrupcin
(ISR) INT_CCPxcorrespondiente.

En el cdigo de esta interrupcin volvemos a hacer lo


mismo pero configurando el Compare para que cuando
TMR1 alcance el valor de referencia conmute nuestro Pin
CCP al estado contrario.

Si ambos valores de TMR1 de referencia, para el


Compare a Alto y el Compare a bajo, son
iguales tendremos un tren de pulsos simtricos, estn
el mismo tiempo en alto que en bajo, o sea que tienen un
Duty Cycle del 50%. En caso contrario estarn mas
tiempo en un estado que en otro y ser una onda
asimtrica, o sea que tendr un Duty Cicle distinto del
50%.

El periodo ser la suma de ambos tiempos, el que


permanece en estado alto mas el que est en estado
bajo.

Calculando cuidadosamente dichos tiempos podremos


generar una onda del periodo que deseamos. Pondremos
a 0 siempre TRM1 y cargaremos CCP_x con el nmero
de Ticks de TMR1 en que deseamos que se dispare
nuestra conmutacin/interrupcin.

C.- Implementacin en C:

Para implementar nuestro Cdigo en C vamos a


centrarnos en los puntos que hemos descrito en la seccin
anterior.

Para configurar la interrupcin CCP utilizaremos lo


siguiente:

setup_ccp2(CCP_COMPARE_SET_ON_MATCH); // Configura
CCP2 en modo COMPARE cambiando a Alto
enable_interrupts(int_ccp2);
// Habilito la interrpcin
INT_CCP2
enable_interrupts(global);
// Habilito las
interrupciones necesarias

Con esta configuracin inicial podemos ya escribir


nuestra rutina ISR que es muy simple ya que
nicamente selecciona mediante un flag si debe el
siguiente cambio es a Alto o a Bajo y colocar el tiempo
en que deseamos en que esto se produzca:

#int_ccp2
void handle_ccp2_int(){
if(++flagConmuta==1){
setup_ccp2(CCP_COMPARE_CLR_ON_MATCH); //
Configura CCP2 modo COMPARE cambiando a Bajo
} else{
setup_ccp2(CCP_COMPARE_SET_ON_MATCH); //
Configura CCP2 modo COMPARE cambiando a Alto
}
CCP_2=0;
// Pongo a cero el valor a
comparar ...
set_timer1(0);
// y el TMR1.
CCP_2 = 1250;
// Quiero que se dispare
cada 0.25 ms ...
// luego pongo los Ticks de
TMR1 para ese tiempo.
}

Notad que estoy trabajando con una configuracin


particular a la que me sujeta el Hardware especfico del
que dispongo: El mdulo CCP2 y un cuarzo de 20 Mhz.

Con un Cristal de 20 Mhz los clculos me indican que el


nmero de Ticks de TMR1 necesarios para realizar una
conmutacin cada 0.25 ms es de 1250 (0.00025 / (1/
FOSC)*4)

Con todo esto el programa completo para


nuestro Generar una Onda Cuadrada Simtrica de 2
Khz de Frecuencia queda as:

/////////////////////////////////////////////////////////////////////////////////////
//
// generando_un_pulso_6_intccp.c
//
// SERIE: "Tcnicas en C" para el Foro TODOPIC
//
// (c) 11.2006 by RedPic
//
// Propsito: Generar un pulso de 2 Khz (0.5 ms de periodo)
//
// Tcnica Empleada: Habilitar INTCCP para conmutar el PIN CCP2
cada semiperiodo
// utilizando el recurso CCP en modo compare.
//
////////////////////////////////////////////////////////////////////////////////////
#include <18f4550.h>
#fuses
HS,MCLR,CCP2B3,NOWDT,NOPROTECT,NOPUT,NOBROWNOUT,NOPB
ADEN,NOLVP,NOCPD,NODEBUG,NOWRT,NOVREGEN
#use delay(clock=20000000)
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7)
////////////////////////////////////////////////////////////////////////////////////
//
// Defines y Constantes
//

////////////////////////////////////////////////////////////////////////////////////
#define LED PIN_E0 // Defino el Pin del Led
#define OUT PIN_B3
////////////////////////////////////////////////////////////////////////////////////
//
// Variables en RAM
//
////////////////////////////////////////////////////////////////////////////////////
int1 flagConmuta=0;
////////////////////////////////////////////////////////////////////////////////////
//
// Interrupcin CCP por Compare TMR1
//
////////////////////////////////////////////////////////////////////////////////////
#int_ccp2
void handle_ccp2_int(){
if(++flagConmuta==1){
setup_ccp2(CCP_COMPARE_CLR_ON_MATCH); // Configura CCP2
modo COMPARE cambiando a Bajo
} else{
setup_ccp2(CCP_COMPARE_SET_ON_MATCH); // Configura CCP2
modo COMPARE cambiando a Alto
}
CCP_2=0;
// Pongo a cero el valor a comparar ...
set_timer1(0);
// y el TMR1.
CCP_2 = 1250;
// Quiero que se dispare cada 0.25
ms ...
// luego pongo los Ticks de TMR1 para ese
tiempo.
Output_Toggle(LED);
// Monitorizo visualmente con el
PIN de Led;
}
////////////////////////////////////////////////////////////////////////////////////
//
// Main
//
////////////////////////////////////////////////////////////////////////////////////
void main() {
////////////////////////////////////////// INICIALIZACIONES GENERALES
delay_ms(333);
// Espero a que todo se estabilice e ...
disable_interrupts(global);
// Inicializo el Micro y ...
disable_interrupts(int_timer1); // deshabilitando todo lo no
necesario ...
disable_interrupts(int_rda);
disable_interrupts(int_ext);

disable_interrupts(int_ext1);
disable_interrupts(int_ext2);
setup_adc_ports(NO_ANALOGS);
setup_adc(ADC_OFF);
setup_spi(FALSE);
setup_psp(PSP_DISABLED);
setup_counters(RTCC_INTERNAL,RTCC_DIV_2);
setup_timer_0(RTCC_OFF);
setup_timer_1(T1_INTERNAL | T1_DIV_BY_1);
setup_timer_2(T2_DISABLED,0,1);
setup_timer_3(T3_DISABLED);
setup_comparator(NC_NC_NC_NC);
setup_vref(FALSE);
port_b_pullups(FALSE);
delay_ms(333);
/////////////////////////////////////////// INICIALIZACIONES PERTINENTES A LA
APLICACION
setup_ccp2(CCP_COMPARE_SET_ON_MATCH); // Configura CCP2
modo COMPARE cambiando a Alto
enable_interrupts(int_ccp2);
// Habilito la interrpcin INT_CCP2
enable_interrupts(global);
// Habilito las interrupciones
necesarias
printf("\r\nGenerando un pulso : INTCCP Compare\r\n");
printf("By Redpic para Foro TODOPIC\r\n\n");

do {
} while (TRUE);

D.- Ejemplo funcionando:

La fotografa es la misma que en Generando un pulso. 1 Parte. Onda


simtrica con INTRTCC ya que la nica diferencia est en el Firmware del
PIC: el resto visualmente y desde fuera es absolutamente igual.

Esta serie de contenidos


titulada Tcnicas en C pretende
mostrar con ejemplos comentados
la forma
deimplementar ciertos procesos
muy usuales durante el uso de los
Microcontroladores PIC de las
series 16F y 18F.

Librera de Funciones "tiles"

A.- Un poco de Teora:

Utilizando una Librera de Funciones "tiles" en CCS C


Los conceptos fundamentales a tener en cuenta son varios y
todos importantes, voy a intentar haceros una descripcin simple
y a ser posible completa.
1.- Se hace uso una directiva de compilacin que permite
insertar cualquier cdigo fuente all donde la utilicemos. Se trata
de la directiva #include que hace exactamente eso: Incluye en
esa posicin el contenido del fichero descrito en la directiva.
As #include "lib_utils.c" coloca, a partir de la lnea 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 direccin absoluta, podemos entonces escribir
#include "c:\proyectos\librerias\lib_utils.c" y entonces se
buscar el fichero en esa direccin. Si omitimos alguna parte de
su direccin 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 aqul.
Delimitado con los smbolos mayor y menor <> que hace que
el fichero a incluir sea buscado en el/los directorio/s
predefinido/s en el compilador para la inclusin de fuentes. En
CCS C esta opcin se encuentra en el men Options/Include
Dirs...

Por lo dems 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 funcin en nuestro programa si, y
solo si, el compilador conoce la funcin 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 funcin sta debe estar
colocada antes del main() para que pueda ser compilada, en otro
caso el compilador nos lanzar un error indicndonos que dicha
funcin no est declarada.
Este caso compilara perfectamente ya que al utilizar en main()
la funcin 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 hara ya que al compilar main() el


compilador no sabra qu es miFuncion() al estar desarrollada
despus de ser usada:

void main(void){
int8 n;
}

n = miFuncion();

int8 miFuncion(void){
return 100
}

La solucin a ste ltimo caso sera o mover miFuncin() para


colocarla antes del main(), que es lo que tenamos en el ejemplo
anterior, o mejor an: Declararla, colocando su prototipo antes
del mismo y podramos as dejar su desarrollo donde
quisisemos. Esto lo haramos 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 lnea exactamente igual que la


que describe mi funcin pero sin desarrollarla, sin poner qu
hace la funcin por dentro, solo qu parmetros recibe la
funcin 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 an no sepa qu
debe hacer dentro de la funcin, que viene mas abajo y an 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 cdigo 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
extensin .h viene de la palabra inglesa header o sea cabecera
en espaol. La parte que hemos llamado Desarrollo la vamos a
colocar en un fichero que se llame "lib_utils.c" ya que contiene el
cdigo 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 cdigo 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 debera 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 lneas de cdigo en el
mismo orden generando exactamente el mismo cdigo objeto
compilado. La nica diferencia entre ellos es que el compilador
ha tenido que unir las distintas lneas de cdigo contenidas en los
distintos ficheros mediante los #include antes de compilar.
Y 4.- Hacemos uso por fin de una caracterstica muy til del
compilador y que es que ste solo compila efectivamente, y
traslada al cdigo objeto compilado, aquellas funciones que
realmente son llamadas desde el main(), o que son llamadas
desde una funcin que a su vez es llamada desde el main(), o
que son llamadas desde una funcin que a su vez es llamada
desde una funcin que a su vez es llamada desde el main() ....
Esta caracterstica 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 slo 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
libera 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 Librera 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 Funcin 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 Funcin 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 Funcin 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 Funcin 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 Funcin que lee el valor de un bit del byte en la posicin
dada \c pbit.
*
* Lee un Bit en la posicin \c pbit del Byte.
* \param byte_for_bits Byte contenedor de los bits.
* \param pbit Posicin en el Byte del bit a leer.
* \return bit ledo.
*/
int1 get_bit_in_byte(int8 byte_for_bits, int8 pbit){
int1 r;
r=bit_test(byte_for_bits,pbit);
return r;
}
/** \brief Funcin que guarda un bit de valor \c vbit en el Byte en la
posicin dada \c pbit.
*
* Guarda un bit de valor \c vbit en la posicin \c pbit del Byte dado.
* \param byte_for_bits Puntero al Byte contenedor de bits.
* \param vbit Valor del bit a escribir.
* \param pbit Posicin 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 Funcin que rellena con un caracter dado un string a partir
de NULL hasta una longitud dada.
*
* \param c Carcter de relleno.
* \param maxlen Longitud mxima 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 Funcin que convierte un string numrico decimal ASCII
NULL-END a su valor entero (8 bits).
*
* \param pF Puntero al buffer que contiene el string numrico ASCII.
*
* \return int8 Valor numrico.
*/
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 Funcin decodifica una variable TimeStamp extrayendo sus
componentes.
*
* Acepta una variable TimeStamp y devuelve valores enteros de Ao,
Mes, Da, Hora, Minuto, y Segundo.
* \param pF Puntero a string TimeStamp
* \param[out] day Dia
* \param[out] mth Mes
* \param[out] year Ao (4 dgitos, 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 Funcin codifica una variable TimeStamp dando sus
componentes.
*
* Acepta variables de valores enteros de Ao, Mes, Da, 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 Ao (4 dgitos, 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 carcter en string null-terminated.
*
* \param c Carcter a sustituir.
* \param p Carcter 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 Funcin que convierte un carcter Hexadecimal ASCII NULLEND a su valor entero de 8 bits.
*
* \param d Caracter Hexadecimal a convertir.
*
* \return int8 Valor numrico.
*/
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 Funcin que convierte un string numrico Hexadecimal
ASCII NULL-END a su valor entero de 8 bits.
*
* \param pF Puntero al buffer que contiene el string numrico ASCII
Hexadecimal de 2 digitos (00h a FFh).
*
* \return int8 Valor numrico.
*/
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 Funcin que convierte un string numrico Hexadecimal
ASCII NULL-END a su valor entero de 16 bits.
*
* \param pF Puntero al buffer que contiene el string numrico ASCII
Hexadecimal de 4 digitos (0000h a FFFFh).
*
* \return int16 Valor numrico.

*/
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 Funcin que convierte un string numrico Hexadecimal
ASCII NULL-END a su valor entero de 32 bits.
*
* \param pF Puntero al buffer que contiene el string numrico ASCII.
*
* \return int32 Valor numrico.
*/
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 Funcin 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 NULLTerminated son imprimibles ('0'..'9','A'..'Z' ' ') y hay al menos uno.
*
* \param pF Puntero al string a investigar.
*
* \return int1 1 si todos los carcteres son vlidos, 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 Funcin 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 queramos conseguir, una librera de funciones


tiles lista para usar.

A.- Un poco de Prctica: Un ejemplo.

Con todo lo descrito anteriormente he realizado un par de


programas main absolutamente idnticos salvo que cada uno de
ellos llama a una funcin distinta de nuestra librera 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 Recepcin
enable_interrupts(int_rda); // Habilito la interrpcin
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 convirtindolos 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 Recepcin
enable_interrupts(int_rda); // Habilito la interrpcin
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);
}

También podría gustarte