Páginas

miércoles, 27 de mayo de 2015

"Diseño de un ECG" Parte I

"Diseño Electrónico de un ECG"

¡Quién no ha pensado alguna vez como poder capturar a sí mismo la señal eléctrica del corazón! Pues bien, en esta entrada os voy a mostrar como podéis construiros un circuito electrónico para haceros un sistema capaz de medir vuestra señal de ECG, es decir obtener vuestro electrocardiograma.
Bien ... manos a la obra.

Para la parte electrónica he usado un amplificador de instrumentación, concretamente el INA128. ¿Por que este y no otro? Este tiene unas características que lo hacen idóneo para sistemas de electromedicina y captura de señales biomédicas. Existen muchos más modelos de amplificadores de instrumentación, pero este concretamente tiene un margen de alimentación que va desde los 2,25V a los 18V simetricos, lo que lo hace perfecto para aplicaciones donde la alimentación sea solo de 5V. Este también presenta la posibilidad de regular la ganancia de amplificación por medio de una resistencia que permite obtener un margen de ganancia desde 1 hasta 10.000, lo cual es necesario cuando se trabaja con señales biomédicas. Tiene un encapsulado de 8DIP, lo que también le hace ser mucho más manejable. Respecto al resto de características, no se diferencia mucho de otros modelos de amplificadores de instrumentación de la familia INA. Te aconsejo que te metas en la WEB de Burr-Brown, bueno ya es propiedad de Texas Instrument, 


 y eches un vistazo a todos los modelos por si en tu caso te interesa usar otro diferente. Por si te interesa también he probado el modelo INA110K y funciona perfectamente como este otro.

Para más datos al respecto sobre este amplificador de instrumentación, os dejo el datasheet del mismo para que podáis consultar cualquier de las características que tiene.



A continuación paso a mostraros el circuito electrónico inicial que he diseñado para construir el sistema ECG. Mas abajo encontrareis el esquema realizado con un software de diseño de esquemas electrónicos



Como podéis observar lo he dividido en varios bloques:

1. Amplificador de Instrumentación
2. Guarda Activa.
3. Filtrado (Etapa de Filtro Paso Alto y Filtro Paso Bajo)
4. Ajuste de Offset e indicación sonora.

  • Guarda Activa
Para la Guarda Activa, he seguido exactamente los consejos de diseño del fabricante en el datasheet, como a continuación os indico:



Además aquí os dejo un documento teórico explicativo de la Realimentación Activa:  



Para el diseño del filtrado he montado una etapa con un filtro paso alto y luego seguidamente otro filtro paso bajo, para quedarme con las frecuencias por debajo de los 40Hz y así despreciar la frecuencia de red.

  • Filtro Paso alto:

La frecuencia de paso es de 0,1Hz en adelante. Para ello he calculado los componentes necesarios por medio de las siguientes fórmulas. La ecuación característica de un filtro normalizado paso bajo de Butterworth de 2º orden es la siguiente.


Particularizando para f=0,1Hz, se tiene finalmente la función de transferencia buscada:



Para implementar físicamente esta función de transferencia se va a usar la siguiente red activa.

 

 La cual tiene la función de transferencia:


Comparando esta función de transferencia con la función de transferencia deseada, se tiene:

 

Para calcular el valor teórico de los componentes R y C se fija el valor de C y se obtiene el valor de la resistencia R, es decir:


2 * 0,1 * pi * R * C  = 1  fijando C = 1uF se despeja R

R = 1 / ( 2 * 0,1 * pi * 1uF ) = 1,6MΩ

A continuación se muestra el esquema definitivo con los correspondientes valores ajustados y finalmente montados.




  • Filtro Paso bajo:

 Siguiendo la misma metodología que en el punto anterior y fijando una frecuencia de corte superior de 40 Hz para eliminar la componente de 50 Hz de la red, el filtro usado es uno normalizado de 2º orden de Butterworth el cual queda definido por la siguiente función de transferencia:

 

 Que tras hacer el cambio a la variable de la frecuencia se obtiene la función de transferencia siguiente:

 

Y particularizando para una determinada frecuencia, (en el caso de la formula se fija la frecuencia a 100Hz)
  
 


 Luego finalmente la función de transferencia del circuito total es:


Que ajustando los valores de uno de los componentes, por ejemplo el valor de C =100nF se determina el circuito de la figura, con la frecuencia de corte deseada de 100Hz según las formulas mostradas. 


R = 1 / (2 * 100Hz * pi * 100nF) = 15,9KΩ    cuyo valor comercial existente seria 15KΩ

Para el caso de una frecuencia de 40 Hz el valor de R hubiera sido:


R = 1 / (2 * 40Hz * pi * 100nF) = 39,8KΩ    cuyo valor comercial existente seria 39KΩ

A continuación se muestra el esquema montado con el ajuste de los valores de los componentes para el ejemplo de frecuencia de corte de 100Hz.



  • Ajuste de Offset e indicación sonora:
El circuito de ajuste de offset lo que permite es quitar la componente negativa de señal continua que exista a la salida de la etapa de filtrado. Es importante hacer esto porque el siguiente paso sería digitalizar la señal por medio de un conversor A/D y estos no admiten señales con componente negativa. 

Además es una forma de amplificar la señal ademas de eliminar su componente negativa.


Para que quede mucho más real nuestro electrocardiograma, se puede hacer uso del famoso NE555 para generar un circuito oscilador o aestable y obtener una señal sonara idéntica a la del ritmo cardíaco. Para controlar que la emisión del pitido del altavoz vaya acorde al ritmo cardíaco se conecta la salida del circuito de ajuste de offset al pin 4  (RST) del NE555, para que cuando se tenga la señal del ECG en el punto más elevado el oscilador se active y por tanto genere el correspondiente tono en el altavoz.

Para ello el circuito a montar sería el siguiente:


El NE555, por ejemplo, lo fijamos para que oscile a una frecuencia de 3000Hz. Los cálculos para obtener el valor de las resistencias y el condensador son los siguientes:

F = 1 / (Tc + Td)     donde:

Tc = 0,69 * (R1 + R2) * C 
Td = 0,69 * R2 * C 

Fijamos el valor del condensador del pin 2 a un valor de 10nF. El motivo es porque se tienen comercialmente menos margen de valores en condensadores, si lo comparamos con los valores comerciales de una resistencia (pues se tienen series como la E192 para encontrar casi cualquier valor o incluso llegado el caso usar una resistencia variable).

Tc + Td = 1/ 3000 = 3,3e-4

Dado que con una configuración estándar no es posible obtener que Tc sea igual a Td, lo que hacemos es que por ejemplo: el Tc = 75% del tiempo total y el Td = 25% del tiempo total.


Luego los cálculos quedan de la siguiente forma:

3.3e-4 * 0,25  = 0,69 * R2 * 10nF  despejando queda 

R2 = (3.3e-4 * 0,25) / 0,69 * 10nF = 12077  cuyo valor comercial existente seria 12KΩ

R1 = [ (0,75 * 3.3e-4 ) / (0,69 * 10nF) ] -12KΩ = 35869 - 12077 = 23792Ω  cuyo valor comercial existente sería 22KΩ

En el circuito finalmente montado las resistencias usadas fueron R1 = 33K y R2 = 10K, porque eran de los valores que disponía en ese momento. La diferencia se aprecia en la parte audible y por tanto en la frecuencia del pitido, por lo que estaría bien que como prueba, que varieis esos valores y observar el efecto que produce en la salida de la señal generada hacia el altavoz.


"Esquema Electrónico del ECG"

Finalmente el esquema electrónico de como quedaría todo el sistema os lo muestro a continuación, el cual es idéntico al realizado a mano y que os he mostrado al principio de la entrada:




"Diseño del PCB del ECG"

Esto otro es como quedaría el diseño de su correspondiente PCB. 



"POVRay PCB"

A continuación os muestro como ha quedado el modelado en 3D con POV Ray del PCB del ECG:

"Electrodos ECG"

En esta imagen os muestro como son los electrodos que son necesarios usar para poder adherirlos a nuestro cuerpo. 



He usado tres. Uno se coloca en el pecho izquierdo, otro en el derecho y el último a la altura del ombligo. Concretamente el que se coloca en el ombligo es la guarda activa. Con esa disposición y con unos cables como los que os muestro en la figura no debéis tener mayor problema para poder captar la señal. Eso sí, es importante que los electródos hagan contacto sobre la piel, por lo que es aconsejable depilarse un poco la zona si se tuviese mucho pelo, ...je,je... 





Como se observa en la foto, el extremo que se coloca en los electrodos no son ni mas ni menos que unos cocodrilos, los cuales son fáciles de quitar y poner y perfectos para este primer prototipo.


Montaje sobre placa board del ECG

A continuación os muestro la imagen de como ha quedado el circuito en las pruebas realizadas sobre la placa board. 



Es importante que los cables no sean excesivamente largos y crucen de una lado a otro de la placa board, para así evitar ruidos e interferencias. El rango de alimentación del amplificador de instrumentación y del resto de circuitos intregrados permite que se pueda usar una pila de 9V para hacer portable el sistema ECG.


A continuación os muestro una prueba del funcionamiento del circuito ECG, visualizando la señal en un osciloscopio.


Vídeo de funcionamiento del Circuito ECG





Señales reales capturadas del Circuito ECG













Os muestro la comparación de las señales obtenidas de la salida directa del amplificador de instrumentación, sin pasarla por la etapa de filtrado y la señal a la salida del circuito de filtrado, abajo y arriba respectivamente. 








Como se puede apreciar a la salida del circuito de filtrado la señal queda mucho más limpia de ruido y un poco más amplificada (Canal 1). La siguiente etapa, la de ajuste de offset es la encargada ya, de amplificar esta señal limpia de ruido y bien filtrada para poder ser digitalizada. Esta otra fase la dejo para una futura y nueva entrada en la que estoy trabajando. 

En la parte II, os mostraré la forma de como digitalizar la señal que obtenemos a la salida del circuito de ajuste de offset por medio de un microcontrolador (el cual tenga un subsistema conversor A/D) y llevarla a un software de alto nivel para representarla gráficamente en un PC.

Espero que os haya gustado ..... y ya sabéis, ahora no hay excusa para no poder analizar en que estado se encuentra vuestro pulso cardíaco!! 

Un saludo.

----------------------------------------------------------------------------------------------------------------------------------------------------------------
Por favor, si te ha gustado el blog, te ha ayudado o has visto algún proyecto interesante del que te has podido descargar alguna documentación por favor deja algún comentario. Gracias.
----------------------------------------------------------------------------------------------------------------------------------------------------------------

APORTO PARTE DEL DISEÑO ANTIGUO DE LA PARTE DE ALTO NIVEL SOLICITADO POR UNA DE LAS PERSONAS QUE HA DEJADO UN COMENTARIO EN EL BLOG EN EL QUE COMENTA INTERÉS POR EL MISMO.



Ø  CAPTURA POR EL PUERTO SERIE LOS DATOS RECIBIDOS CON UN PROGRAMA DE ALTO NIVEL

Esta parte del sistema, es el programa de alto nivel, el cual se encarga de capturar los datos que le envía el microcontrolador por el puerto serie y a partir de ahí los representa, obtiene información de los datos capturados, etc. Para hacer este programa se ha utilizado el entorno de programación LabCVI. Entre las funcionalidades del programa las que se destacan desde un punto de vista práctico son principalmente dos:

1.Captura de la señal del ECG y dibujarla en pantalla en tiempo real.

2.Medida del tiempo HRV y obtención de una evolución en el tiempo.

A continuación se va a describir un poco la funcionalidad del programa y de todas sus opciones para ver se puede hacer con él. El aspecto del programa al ejecutarlo es el que se muestra en la siguiente figura.





Tras ser arrancado el programa lo primero que debe hacer, es elegir el puerto que se va usar para comunicarse con el microcontrolador. Eso se elige con la opción que existe denominada “Puerto de Entrada”. Una vez seleccionado el puerto, el programa ya esta listo para poder capturar los datos que le envíe el microcontrolador. Para ello se debe seleccionar la opción “Iniciar Captura”. En ese momento el programa comienza a capturar datos y los representa en pantalla. A continuación se muestra un ejemplo de la señal de ECG captura en tiempo real.

   

Mientras el programa esta capturando la señal se puede variar el número de puntos de la pantalla con la opción “Puntos del Visualizador” para ver más o menos puntos en la pantalla según nos convenga, variar el umbral de detección que no es más que una alarma que no indica cuando la señal que capturamos sobrepasa el valor que fijamos. La opción es “Umbral de Detección”. También se puede variar el tiempo de adquisición con la opción “Velocidad de Adquisición”, bien puede ser con el ratón o bien número en la caja de texto y se puede borrar la pantalla de adquisición con la opción “Inicializar”.

Respecto al resto de opciones como son “Salvar Datos a Disco”, lo que hace es grabar en un fichero nuevo o bien añadir en otro que ya exista, los valores de la señal que se esté visualizando en ese momento en la pantalla del visualizador. De este modo tenemos un fichero el cual tiene registrados todos los valores de la señal en formato ASCII, la fecha y hora en la que han sido capturados, el directorio donde han sido almacenados y el puerto por el que se han capturado. Al seleccionar esta opción nos aparece el formulario siguiente:


Si se quisiera ver el contenido de alguno de estos fichero guardados, se debe elegir la opción de “Visualizar Fichero”. Al ejecutar la opción nos pide que seleccionemos que fichero queremos ver y muestra el contenido del mismo. Se muestra un ejemplo:



  
Las opciones de “Salva señal a Fichero” es parecida a anterior. En este caso solo se guardan los valores de la señal en otro fichero con extensión distinta.



 Para poder visualizar un fichero que hayamos guardado basta con dar a la opción de “Cargar Señal”.


En ese instante aparece un menú como el que muestra la figura anterior y tras seleccionar la señal deseada, esta es dibujada en la pantalla del visualizador. A continuación se muestra un ejemplo de carga de señal a pantalla.

 

Finalmente la opción de “Medida de Tiempos” muestra otro formulario en el que aparece la medida de tiempos del HRV. En la caja de información de “Tiempo medido” indica el tiempo medido entre pico y pico de la señal de ECG, es decir el HRV, el resto de indicadores son a nivel informativos para saber como se va comparando la señal con el umbral seleccionado. En el grafico que aparece se van representado lo valores de tiempos que vamos midiendo en función también del tiempo para obtener un evolución del HRV. A continuación se muestra el formulario que aparece al seleccionar dicha opción:

 



·        Código de todo el programa

#include <analysis.h>
#include "FB2.h"
#include <formatio.h>
#include <rs232.h>
#include <utility.h>
//#include "amp_bio.h"
#include <easyio.h>
#include <ansi_c.h>
//#include <dataacq.h> 
#include <userint.h>  
#include "fb2.h"


const int CANAL_ANALOG = 0;
const int NUM_PUNTOS = 1;
double n_puntos = 200.0;

static int panelHandle,panelHandle2;
int i,iniciar=0,inc_mues=0,modo=0, tiempo =0,histogra[1000];
unsigned int numero_puntos,canal,color;
double dato, puntos[100],array[1000],muestras[7000],histo[1000],ejex[10],histo_x[1000];
double valor,velocidad,canal_d;
unsigned long longi;
char conver,pathname[300],pathname2[300];
char puerto[30];
double byte;
unsigned char maximo,aux1,valores[1000],port=2,port_d,umbral,umbral_g=100;

//Definición variables del código que hace la medida de tiempos
double total=0.0,parcial=0.0,auxi1=0.0,auxi2=0.0,auxi3=0.0,duracion=0.0,tempo=0.0,final_t=0.0,vel,utli=0;
int p=0,dos=0,uno=0,media=0,ini=0, c_medias, h=0;

//Variables para leer del fichero
static int file_handle;
static int handle;
static int status;

int main (int argc, char *argv[])
{
                if (InitCVIRTE (0, argv, 0) == 0)            /* Needed if linking in external compiler; harmless otherwise */
                               return -1; /* out of memory */
                if ((panelHandle = LoadPanel (0, "fb2.uir", PANEL)) < 0)
                               return -1;
                               if ((panelHandle2 = LoadPanel (0, "fb2.uir", PANEL2)) < 0)
                               return -1;

                DisplayPanel (panelHandle);
                RunUserInterface ();
                return 0;
}                                                                                          

int CVICALLBACK _salir (int panel, int control, int event,
                               void *callbackData, int eventData1, int eventData2)
{
                switch (event) {
                               case EVENT_COMMIT:
 
                                                 QuitUserInterface (0);
                                               break;
                }
                return 0;
}

int CVICALLBACK _iniciar (int panel, int control, int event,
                               void *callbackData, int eventData1, int eventData2)
{
                switch (event) {
                               case EVENT_COMMIT:

                                                               iniciar = 1;
                                                              
                                                               ResumeTimerCallbacks ();
                                                              
                                                               OpenComConfig (port, "COM2", 9600, 0, 8, 1, 10, 10);  
                                                              
                                                               SetCtrlAttribute (panelHandle, PANEL_LED, ATTR_LABEL_TEXT, "ON");
                                                               SetCtrlAttribute (panelHandle, PANEL_LED, ATTR_OFF_COLOR, VAL_GREEN);
                                                               SetCtrlAttribute (panelHandle, PANEL_INICIAR, ATTR_DIMMED, 1);
                                                               SetCtrlAttribute (panelHandle, PANEL_PARAR, ATTR_DIMMED, 0);
                                                                                                                                
                                               break;
                }
                return 0;
}

int CVICALLBACK _parar (int panel, int control, int event,
                               void *callbackData, int eventData1, int eventData2)
{
                switch (event) {
                               case EVENT_COMMIT:

                                               iniciar = 0;
                                               SuspendTimerCallbacks ();
                                               SetCtrlAttribute (panelHandle, PANEL_LED, ATTR_LABEL_TEXT, "OFF");     
                                               SetCtrlAttribute (panelHandle, PANEL_LED, ATTR_OFF_COLOR, VAL_RED);
                                               SetCtrlAttribute (panelHandle, PANEL_INICIAR, ATTR_DIMMED, 0);
                                               SetCtrlAttribute (panelHandle, PANEL_PARAR, ATTR_DIMMED, 1);
                                               SetCtrlVal(panelHandle, PANEL_MAXIMO, 0);
                                               // AIClearAcquisition (0);    
                                               break;
                }
                return 0;
}

int CVICALLBACK ontimer (int panel, int control, int event,
                               void *callbackData, int eventData1, int eventData2)
{
                switch (event) {
                               case EVENT_TIMER_TICK:
                                              
                                if ( iniciar == 1)     
                               {
                                              
                                               i=0;
                                               //byte = ComRdByte (2);
                                               ComRd (port, valores, 1);
                                               PlotStripChartPoint (panelHandle, PANEL_VISUALIZADOR, valores[i]);                          
                                               SetCtrlVal (panelHandle, PANEL_DATOS, valores[i]);
                                                                                                                             
                                               if(aux1 < valores[i])
                                               {
                                               aux1 = valores[i];
                                               }


               
                // Inicio de la función que cálcula el tiempo  ************
                                                                             
                                 if (ini==1)
                                               {
                                                  if (valores[i]>umbral_g)
                                                   {
                                                      if (dos ==0)
                                                      {
                                                       auxi1=auxi1 + 1.0;
                                                       uno=1;
                                                       SetCtrlVal (panelHandle2, PANEL2_CAJATIEMPO, auxi1);
                                                      }
                                                   }
                                                if (uno==1)
                                                 {
                                                   if (valores[i]<umbral_g)
                                                    {
                                                     auxi2=auxi2 + 1.0;
                                                     dos=1;
                                                      SetCtrlVal (panelHandle2, PANEL2_CAJATIEMPO2, auxi2);
                                                     }
                                                 }
                                                                                                                                                               
                                                 if (dos==1)
                                                   {
                                                                                   
                                                               if (valores[i]>umbral_g)
                                                                {
                                                                               edia = media+1;
                                              parcial = (auxi1 + auxi2);
                                                                               setCtrlVal (panelHandle2, PANEL2_CAJATIEMPO3,parcial);                                                                                             GetCtrlVal (panelHandle, PANEL_VELOCIDAD,&vel);
                                                                              total = total+parcial;
                                                                              tempo = total * vel;
                                                                              duracion = tempo + duracion;
                                                                               dos=0.0;
                                                                              auxi1=0.0;
                                                                              auxi2=0.0;
                                                                              uno=0.0;
                                                                }
                                                   }
                                                                                 
                                                GetCtrlVal (panelHandle2, PANEL2_NUMMEDIAS,&c_medias);                                                                       if (media ==c_medias)
                                                               {
                                                                  SetCtrlVal (panelHandle2, PANEL2_CAJATIEMPO4,duracion/c_medias);                                                         PlotStripChartPoint (panelHandle2, PANEL2_HISTOGRAM, duracion/c_medias);
                                                               parcial=0.0;
                                                               duracion=0.0;
                                                               total=0.0;
                                                               media = 0;
                                                               c_medias=0;
                                                               }
                                                             
                                                 }           
                                                              
                                                else
                                                 {
                                                }
                                                                             
                                                                             
                                                /// ******* fin de la función que cálcula el tiempo *******
 maximo = aux1;
                                               SetCtrlVal (panelHandle, PANEL_MAXIMO, maximo);                                                                                                                                   
                               if (umbral_g < valores[0])
                                {
                               SetCtrlAttribute (panelHandle, PANEL_LED_2, ATTR_LABEL_TEXT, "Alarma de Umbral: ON");                                   SetCtrlAttribute (panelHandle, PANEL_LED_2, ATTR_OFF_COLOR, VAL_RED);                                                          SetCtrlAttribute (panelHandle, PANEL_LED_2, ATTR_LABEL_COLOR, VAL_RED);
                                 }





                                else
                                 {
                                SetCtrlAttribute (panelHandle, PANEL_LED_2, ATTR_LABEL_TEXT, "Alarma de Umbral: OFF");                                 SetCtrlAttribute (panelHandle, PANEL_LED_2, ATTR_OFF_COLOR, VAL_YELLOW);                                  SetCtrlAttribute (panelHandle, PANEL_LED_2, ATTR_LABEL_COLOR, VAL_BLACK);
                                  }
                                              
                                //SALVAR PANTALLA A FICHERO
                                                                             
                                                                              inc_mues = inc_mues + 1;
                                                                              GetCtrlVal (PANEL, PANEL_PUNTOS, &numero_puntos);
                                                                               muestras[inc_mues]= valores[0];
                                                                              if (inc_mues == numero_puntos)
                                                                               {
                                                                                 inc_mues=0;
                                                                               }
                                                }
                                 
                                else{}
                                break;
                }
                return 0;
}

int CVICALLBACK _borrado (int panel, int control, int event,
                               void *callbackData, int eventData1, int eventData2)
{
                switch (event) {
                               case EVENT_COMMIT:

                                                               aux1=0;
                                               maximo = 0;
                                                               SetCtrlAttribute (panelHandle, PANEL_MAXIMO, ATTR_CTRL_VAL, 0);
                                                               SetCtrlAttribute (panelHandle, PANEL_DATOS, ATTR_CTRL_VAL, 0);
                                                               ClearStripChart (panelHandle, PANEL_VISUALIZADOR);
                                                               inc_mues=0;
                                                               for(i=0;i<numero_puntos;i++)
                                                               {
                                                                 muestras[i]=0;
                                                               }
                                               break;
                }
                return 0;
}


int CVICALLBACK _velocidad (int panel, int control, int event,
                               void *callbackData, int eventData1, int eventData2)
{
                switch (event) {
                               case EVENT_COMMIT:
                                                               GetCtrlVal (PANEL, PANEL_VELOCIDAD, &velocidad);
                                                               SetCtrlAttribute (panelHandle, PANEL_TIMER, ATTR_INTERVAL, velocidad);
                                               break;
                }
                return 0;
}


int CVICALLBACK _puntos (int panel, int control, int event,
                                void *callbackData, int eventData1, int eventData2)
{
                switch (event) {
                               case EVENT_COMMIT:

                               GetCtrlVal (PANEL, PANEL_PUNTOS, &numero_puntos);                         
                               if (numero_puntos > 7002)
                                {
                                   numero_puntos = 7000;
                                  SetCtrlAttribute (panelHandle, PANEL_PUNTOS, ATTR_MAX_VALUE,numero_puntos);
                               }
                               SetCtrlAttribute (panelHandle, PANEL_VISUALIZADOR, ATTR_POINTS_PER_SCREEN,
                                 numero_puntos+2);                                     



                                               break;
                }
                return 0;
}

int CVICALLBACK _canal (int panel, int control, int event,
                               void *callbackData, int eventData1, int eventData2)
{
                switch (event) {
                               case EVENT_COMMIT:

                                                               GetCtrlVal (panelHandle, PANEL_CANAL, &port);
                                                               port_d = port;
                                               break;
                }
                return 0;
}


int CVICALLBACK _color (int panel, int control, int event,
                               void *callbackData, int eventData1, int eventData2)
{
                switch (event) {
                               case EVENT_COMMIT:

                                GetCtrlVal (panelHandle, PANEL_COLOR, &color);
SetCtrlAttribute (panelHandle, PANEL_VISUALIZADOR, ATTR_PLOT_BGCOLOR,          color);
                               break;
                }
                return 0;
}


int  SaveToDisk(int panel, int control, int event, void *callbackData, int eventData1, int eventData2)       
{                                                                                                            
    char directory[300];                                                                                     
    FILE *filehandle;                                                                                        
    int i;                                                                                                    
                                                                                                             
    if (event == EVENT_COMMIT) {                                                                              

        SuspendTimerCallbacks ();
        SetCtrlAttribute (panelHandle, PANEL_INICIAR, ATTR_DIMMED, 0);
       SetCtrlAttribute (panelHandle, PANEL_PARAR, ATTR_DIMMED, 1);
       GetProjectDir (directory);                                                                            
        if (FileSelectPopup (directory, "*.rub", "RUB Files",                                                
                             "Salvar Señal a Disco", VAL_SAVE_BUTTON, 0,                                     
                             0, 1, 0, pathname) > 0) {                                                       
       
        GetCtrlVal (PANEL, PANEL_BINARYSWITCH, &modo);
       
        if (modo==1)
        {
          filehandle = fopen (pathname, "a+");                                                                 
        }
      if (modo ==0)
         {
          filehandle = fopen (pathname, "w+");                                                                  
         }
        fprintf (filehandle, "SEÑAL ADQUIRIDA DEL ELECTROCARDIOGRAMA \n\n");                                            
        fprintf(filehandle, "Puerto de Adquisición: PUERTO COM%d\n", port);                                             

        fprintf(filehandle, "Fecha %s   Hora: %s \n", DateStr(), TimeStr());                                  
        fprintf (filehandle, "----------------------------------\n");                                        
        for (i=0;i<numero_puntos;i++)                                                                                  
           {
            fprintf (filehandle, "%f\n", muestras[i]);                                                       
           }
        fprintf(filehandle,"\nNúmero de puntos de la pantalla: %d\n",i);
        fprintf(filehandle,"\nLocalización: %s\n\n\n",directory);
        fclose (filehandle);                                                                                 
        SetCtrlAttribute (panelHandle, PANEL_VIEWDATA, ATTR_DIMMED, 0);                                           
        }                                                                                                    
    }                                                                                                         
   

    //ResumeTimerCallbacks ();
    return (0);                                                                                              
}                                                                                                             
                                                                                                             

int  ViewDataPoints(int panel, int control, int event, void *callbackData, int eventData1, int eventData2)   
{                                                                                                            
    char filestring[300];                                                                                    
                                                                                                              
    if (event == EVENT_COMMIT) {                                                                             

    char directory[300];                                                                                      
        GetProjectDir (directory);                                                                           
        if(FileSelectPopup (directory, "*.rub", "RUB Files", "Cargar Fichero de Disco", VAL_LOAD_BUTTON, 0,                                    
                             0, 1, 0, pathname2) > 0) {                                                       
        sprintf (filestring, "notepad.exe %s", pathname2);                                                     
        LaunchExecutable (filestring);                                                                       
        }
           
       if (FileSelectPopup (directory, "*.sen", "SEN Files", "Cargar señal de disco",
              VAL_LOAD_BUTTON, 0, 1, 1, 0, pathname2) > 0)
        {
            file_handle = OpenFile (pathname2, 1, 2, 0);
            for (i=0; i<numero_puntos; i++)
               {
                status = ScanFile (file_handle, "%s>%f[x]", &muestras[i]);
               }
            ClearStripChart (panelHandle, PANEL_VISUALIZADOR);
                                               for (i=0;i<numero_puntos;i++)
                                                {
                                                 PlotStripChartPoint (panelHandle, PANEL_VISUALIZADOR, muestras[i]);           
             }
           
        }

        CloseFile (file_handle);
     
      } 
   return (0);                                                                                              
}                                                                                                              


int CVICALLBACK _umbral (int panel, int control, int event,
                               void *callbackData, int eventData1, int eventData2)
{
                switch (event) {
                               case EVENT_COMMIT:
                GetCtrlVal (panelHandle, PANEL_UMBRAL, &umbral);                         
                umbral_g = umbral;
               
                                               break;
                }
                return 0;
}

int CVICALLBACK medidatiempos (int panel, int control, int event,
                               void *callbackData, int eventData1, int eventData2)
{
                switch (event) {
                               case EVENT_COMMIT:
                                                                
                                                                 //cosas medida de tiempos
                                                                 SetCtrlVal (panelHandle2, PANEL2_CAJATIEMPO, 0.0);
                                                                 SetCtrlVal (panelHandle2, PANEL2_CAJATIEMPO2, 0.0);
                                                                 SetCtrlVal (panelHandle2, PANEL2_CAJATIEMPO3, 0.0);
                                                                 SetCtrlVal (panelHandle2, PANEL2_CAJATIEMPO4, 0.0000);
                                                                 auxi1=0.0;
                                                                 auxi2=0.0;
                                                                 DisplayPanel (panelHandle2);
                                                                 SetCtrlAttribute (panelHandle2, PANEL2_TIMER2, ATTR_ENABLED,0);
                                               break;
                }
                return 0;
}


int CVICALLBACK regreso (int panel, int control, int event,
                               void *callbackData, int eventData1, int eventData2)
{
                switch (event) {
                               case EVENT_COMMIT:
                                                                
                                                                HidePanel (panelHandle2);
                                                                tiempo =0;
                                                                ini=0;
                                                                SetCtrlAttribute (panelHandle2, PANEL2_TIMER2, ATTR_ENABLED,0);
                                               break;
                }
                return 0;
}

int CVICALLBACK timer (int panel, int control, int event,
                               void *callbackData, int eventData1, int eventData2)
{
                switch (event) {
                               case EVENT_TIMER_TICK:
                                              
                                                               //tiempo = tiempo+1;
                                                               //SetCtrlVal (panelHandle2, PANEL2_CAJATIEMPO, tiempo);
                                              
                                               break;
                }
                return 0;
}


int CVICALLBACK botoninicio (int panel, int control, int event,
                               void *callbackData, int eventData1, int eventData2)
{
                switch (event) {
                               case EVENT_COMMIT:
                                                              
                                                               ini=1;
                                                               auxi1=0.0;
                                                               auxi2=0.0;
                                              
                                               break;
                }
                return 0;
}


int CVICALLBACK botonparar (int panel, int control, int event,
                                void *callbackData, int eventData1, int eventData2)
{
                switch (event) {
                               case EVENT_COMMIT:
                                               ini =0;
                                               break;
                }
                return 0;
}


int CVICALLBACK borrado (int panel, int control, int event,
                               void *callbackData, int eventData1, int eventData2)
{
                switch (event) {
                               case EVENT_COMMIT:
                                                
                                                //cosas medida de tiempos
                                                                 SetCtrlVal (panelHandle2, PANEL2_CAJATIEMPO, 0.0000);
                                                                 SetCtrlVal (panelHandle2, PANEL2_CAJATIEMPO2, 0.0000);
                                                                 SetCtrlVal (panelHandle2, PANEL2_CAJATIEMPO3, 0.0000);
                                                                 SetCtrlVal (panelHandle2, PANEL2_CAJATIEMPO4, 0.0000);
                                               break;
                }
                return 0;
}



int CVICALLBACK salvarsenal (int panel, int control, int event,
                               void *callbackData, int eventData1, int eventData2)
{
               
                char directory[300];                                                                                      
    FILE *filehandle;                                                                                        
    int i;                                                                                                    
   
               
                switch (event) {
                               case EVENT_COMMIT:

                               GetProjectDir (directory);
                               SuspendTimerCallbacks ();
                               SetCtrlAttribute (panelHandle, PANEL_INICIAR, ATTR_DIMMED, 0);
                               SetCtrlAttribute (panelHandle, PANEL_PARAR, ATTR_DIMMED, 1);
                               
                               if (FileSelectPopup (directory, "*.sen", "SEN Files", "Salvar señal a disco",
              VAL_SAVE_BUTTON, 0, 1, 1, 0, pathname) > 0)
        {
            file_handle = OpenFile (pathname, 2, 0, 1);
            for (i = 0; i<numero_puntos; i++)
               {
                FmtFile (file_handle, "%s<%f\n", muestras[i]);
               }
            CloseFile (file_handle);
        }
                                              
break;
}
                return 0;
}


int CVICALLBACK cargarsenal (int panel, int control, int event,
                               void *callbackData, int eventData1, int eventData2)
{

                char filestring[300];                                                                                    
   
                if (event == EVENT_COMMIT) {    
               
                                char directory[300];                                                                                      
                                
                                GetProjectDir (directory);
                                GetCtrlVal (panelHandle, PANEL_PUNTOS, &numero_puntos);
                               SuspendTimerCallbacks ();
if (FileSelectPopup (directory, "*.sen", "SEN Files", "Cargar señal de disco",VAL_LOAD_BUTTON, 0, 1, 1, 0, pathname2) > 0)
                                                                                                                                                                                                            {
            file_handle = OpenFile (pathname2, 1, 2, 0);




            for (i=0; i<numero_puntos; i++)
               {
                status = ScanFile (file_handle, "%s>%f[x]", &muestras[i]);
               }
           ClearStripChart (panelHandle, PANEL_VISUALIZADOR);
                                                                             
                                               for (i=0;i<numero_puntos;i++)
                                                {
                                                 PlotStripChartPoint (panelHandle, PANEL_VISUALIZADOR, muestras[i]);           
             }
           
        }

        CloseFile (file_handle);
                 
                }
                return 0;
}


int CVICALLBACK borra_evolu (int panel, int control, int event,
                               void *callbackData, int eventData1, int eventData2)
{
                switch (event)
                               {
                               case EVENT_COMMIT:

                                               ClearStripChart (panelHandle2, PANEL2_HISTOGRAM);

                                               break;
                               }
                return 0;
}
  

57 comentarios:

  1. disculpa cual es el material utilizado para el "Diseño de un ECG" me lo podrías proporcionar

    ResponderEliminar
    Respuestas
    1. Buenas Josué, los componentes y material usado viene identificado con sus valores correspondientes que se indican en los esquemas.

      Un saludo.

      Eliminar
    2. disculpa pero ya monte el circuito en el protoboar y al momento de realizar la conección con los electrodos no me produce un cambio en el sonido y melo mantiene constante me podrias ayudar?

      Eliminar
  2. ------------------------------
    Comentario recibido por Email
    ------------------------------
    Bueno dias amigo;
    el presente es para consultarte, ya que he tenido la oportunidad de montar el circuito para un proyecto de clase , pero el caso es la señal no me sale segun lo esperado, segui todos los pasos del proyecto pero no se en que estare fallando, y pues ya que tu lo montaste y te funciono perfectamente queria saber si me puedes dar un consejo por donde empezar a buscar la falla.

    le agradeceria su pronta colaboracion;

    gracias.

    ------------------------
    RESPUESTA
    ------------------------
    Buenas Maikol,

    Lo primero de todo comprueba que los valores de tensión que alimentan al circuito es simetrica y está en los margenes de +5V y - 5V referenciados sobre la masa. Pues según veo en las imágenes que me has enviado la parte negativa de la señal no te sale correctamente y pudiera deberse a ello. Por lo demás te aconsejo que vayas mirando con el osciloscopio (como has estado haciendo) la señal que te va saliendo por cada una de las etapas, las cuales yo delimito en el esquema con linea discontinua. Así podrás acotar mas o menos donde pudiera estar tu problema. Además asegúrate de tener bien puesto los electrodos y que los hilos esten trenzados para evitar el menor ruido posible, pues en función de como los hayas colocado verás que la señal se recibe mejor o incluyo no captura señal. De momento prueba estas cosas y nos vas contando.

    Un saludo.

    ResponderEliminar
  3. hola, quisiera saber de donde eres? es que aquí en Guayaquil, Ecuador no encuentro estos elementos y me encantaría saber donde los conseguiste? quiero hacer esto paro mi proyecto

    ResponderEliminar
    Respuestas
    1. Hola Maritza, soy de España. Aquí es fácil, al menos, encontrar esos integrados y el resto de componentes. De todas formas lo que puedes hacer es buscarlos por internet, puesto que a día de hoy existen infinitas páginas de compra para los mismos. Espero que puedas conseguirlos sin mayor problema. Suerte. Un saludo.

      Eliminar
    2. Hola Ruben, hay alguna posibilidad que consigas esos elementos para nosotros y te hacemos el envio de dinero por algun medio? y en que tiempo podria estar eso aca en Ecuador
      saludos

      Eliminar
    3. Si es que aca en Ecuador hay ese problema muchos componentes no se encuentran. O en tal caso no sabes de alguna página de España que si haga envío acá? ?

      Eliminar
    4. www.mouser.com por ejemplo, pero hay otras muchas. Esta incluso tiene sede en tu país. Échala un vistazo. Un saludo.

      Eliminar
  4. Se termino la segunda parte de digitalizar la señla???

    ResponderEliminar
    Respuestas
    1. Si. Lo que pasa es que la realice en ensamblador con un microcontrolador que a día de hoy se puede decir que es obsoleto (el 68HC11A2 de Motorola) y la aplicación de alto nivel la desarrollé con LabWindows CVI. Mi idea era desarrollarlo nuevamente con una nueva plataforma de microcontrolador: Arduinio y usar Visual Basic .NET para la parte de aplicación de alto nivel. Sinceramente, tengo muy poco avanzado y no esta para ser publicado, puesto que estoy en paralelo con otros proyectos. Si estás interesada en la parte antigua, podría intentar documentarla y subirla al blog. Un saludo.

      Eliminar
    2. Siii si me interesaría porque debo mostrar tambien los bpm y necesito digitalizar la señal. Por favor si puedes subirla te lo agradecería muchisismo :)

      Eliminar
  5. El circuito esta basado bajo la alimentacion de +5 y -5 v o solo los 10v yno necesita etapa negativa

    ResponderEliminar
  6. Buenas Yeisson,
    El amplificador de instrumentación y el resto de amplificadores operaciones deben funcionar con alimentación simétrica, es decir +5V -5V, porque si usas una alimentación de 10V la señal a la salida del amplificador de instrumentación te saldría recortada. Por ello, para hacer una tensión simétrica a partir de un voltaje asimétrico como sería el caso lo que he hecho ha sido de una manera sencilla hacerlo por medio de un divisor resistivo. Que es justo el circuito con dos resistencias que ves en el esquema y de donde se sacan los voltajes de alimentación para todo el resto del circuito. De esta manera, con un voltaje de 10V asimétrico se puede obtener una voltaje simétrico de +5V -5V respecto a masa. Hay otras muchas más maneras de obtener un voltaje simétrico pero la más sencilla y barata es esta. No se si te he resuelto tu duda, pero si no es así, por favor no dudes en decírmelo para aclararte lo que necesites. Un saludo.

    ResponderEliminar
    Respuestas
    1. Hola Ruben.
      Muchas gracias por tu aportacion ante todo.
      la duda me llega a la hora de realizar la tension simétrica, ya que tu has puesto 2 masas, pero con eso, la segunda resistencia no serviría para nada, y tendría tension de 10 V y 0 V.
      Creo que el fallo esta en que las masas son diferentes, pero no las entiendo.
      Podrías explicarme otra vez, y si puedes de forma mas detallada, como conseguir la alimentacion de +-5 V a partir de los 10V asimétricos?
      Muchas gracias.

      Eliminar
  7. Buenas, intento responder tu duda.
    Con un divisor resistivo formado por dos resistencias que son iguales consigues dividir la tensión que aplicas justo a la mitad. Si justo a la mitad del divisor tomas ese punto como referencia para medir el voltaje respecto al punto de arriba y al punto de abajo de ambas resistencias (sin mover ese punto de referencia de medida) comprobarás que hacia arriba obtienes 5V positivos y el voltaje obtenido en la resistencia de abajo son 5V negativos. Por eso en el esquema verás dos flechas de color rojas donde indico los voltajes medidos tomando como punto de referencia justo la mitad del divisor. Esto lo puedes comprobar fácilmente con un voltímetro y midiendo ambos voltajes y comprobaras justo lo que te comento.

    Ahora bien, tu duda quizás venga porque ¡yo he usado para indicar el voltaje de -5V un símbolo de tierra!, pero vamos es simplemente simbólico, podría haber usado otro tipo de símbolo o sencillamente una etiqueta denominada -5V. El motivo de porque lo hice así fue para que se viera que efectivamente con una tensión asimetríca es fácil obtener una tensión simétrica con un divisor resistivo. El voltaje aplicado asimétrico parten de 0V a 10V y claro para mantener esta notación dejé el mismo símbolo eléctrico de una masa en toda regla, cosa que cuando pasé a usar una alimentación simétrica lo mantuve e indiqué el nuevo punto de referencia respecto a ambas resistencias con el otro símbolo diferente de masa que tu comentas. Es sencillamente simbólico.

    Si montas el circuito tal cual como se muestra en el esquema y mides entre ambas masas con un voltímetro verás que lo que obtienes son -5V (compruébalo). Es decir que tendrás respecto al punto central del divisor una tensión simétrica de +5V en la rama de arriba y -5V en la rama de abajo, siendo el punto central la masa de referencia para esas tensiones simétricas.


    Espero haberte aclarado tu duda. De todos modos si necesitas alguna aclaración más dímelo. Un saludo.

    ResponderEliminar
  8. aclarada perfectamente, mi duda estaba en los simbolos de las masas, que pensaban que eran las mismas.
    En la parte de guarda activa, hay una cosa que no me queda clara.
    En el datasheet del ina128, hay un corto entre Vg y el cable donde se encuentran LA y RA, pero en tu circuito no lo veo. Que significaría ese corto, y porque tu no lo tienes implementado?
    Muchisimas gracias.

    ResponderEliminar
    Respuestas
    1. Junto con lo anterior, al hacer la simulación, el amplificador solo me funciona si V- es tension negativa, si V- lo pongo a masa ya no funciona.

      Eliminar
  9. MIS RESPETOS PARA TI, EXCELENTE TRABAJO

    ResponderEliminar
  10. Hola Buenas, Tengo problemas con el audio, en el pin 2 tienes un capactiro electrolitico? porque de ese no encuentro? y queria saber que efecto ocasionaria si utilizo los de lenteja es decir, lo ceramicos?
    El pin 8 a que puntos va conectado? Agradeceria de antemano tu respuesta.
    Mi nombre es Valeria.
    Crees que se podria implementar solo la etapa del NE555 en otro circuito que sea un electrocardiografo y que de salida maximo llegue a 1V

    ResponderEliminar
  11. Buenas Valeria,

    Respondo a tus preguntas.

    1.No es electrolítico. En el esquema lo dibuje como tal, pero en el montaje no lo era. Pues lo que hice fue fijar el valor del condensador a 10nF para poder obtener el valor de las resistencias para el cálculo de la frecuencia. Lo hice así porque es más fácil obtener resistencias prácticamente de cualquier valor, mientras que con los condensadores es difícil encontrarlos de cualquier valor. Ese es el motivo de porqué fijo a 10nF ese condensador.

    Tu perfectamente puedes usar otro condensador (otro que tengas, como el de lenteja que comentas) y rehacer los cálculos de resistencias. Ten en cuenta que si el nuevo valor elegido esta en el margen de los pico faradios (pF), los valores que te saldrán en el cálculo de las resistencias serán del orden de Megas, y lo mismo tienes problemas para encontrar esas resistencias. Otra opción es usar condensadores de plástico, los cuales son más fáciles de encontrar.

    2.El pin 8 va conectado a la parte positiva del divisor resistivo de +10V. En mi caso usé una pila de 9V, por lo que yo lo conectaba a +9V, es decir al positivo de la pila.

    3."Respecto a implementar solo la etapa del 555 en otro circuito que de salida solo de 1V". A ver si lo he entendido ..... Quieres controlar el 555 (desde el pin 4, como hago en mi circuito) pero con una salida de 1V. Para ello tendrías varías opciones que debería probar. La primera sería usar el circuito de offset que yo he incluido y subir este valor cerca de los 4V, que sumados a los de tu salida de 1V, sumarían los 5V para activar el 555. La segunda opción podría ser adaptar tu salida al margen de 0 a 5V, es decir, si el máximo valor que tu obtienes es 1V: pues 1V será a 5V y 0V a 0V. Para eso deberías hacer uso de un amplificador operacional con ganancia 5. Pruébalo y ya me vas contando.

    Espero haber resulto tus dudas.

    Un saludo.

    ResponderEliminar
  12. Hola, tengo una duda, en el montaje en el protoboard usaste unos condensadores de polipropileno para los 100nF y 10nF cierto? podria usar los condensadores de poliester para esos valores (100nF y 10nF)? porque se me hace dificil encontrar los de polipropileno. Agradeceeria tu pronta respuesta

    ResponderEliminar
    Respuestas
    1. y tambien quisiera saber, los condensadores de 1uf y 33uf a cuanto voltaje estan??

      Eliminar
    2. Hola Joaquin,

      Si correcto, son de polipropileno.
      Por supuesto que podrias usar otro tipo de condensadores siempre que respetes los valores que he indicado.

      Un saludo.

      Eliminar
    3. Muchas gracias por tu respuesta. Con respecto al OPA4131P que usaste, podria usar un TL084 en su reemplazo?lo que pasa es que no encuentro el OPA4131P; y con respecto al INA128P, tampoco lo logro encontrar, ni tampoco el INA110K , no se que otro podria usar en su reemplazo, u otros , porque me comentaron que podia usar dos integrados para reemplazar el INA128P, pero no recuerdo el codigo

      Eliminar
    4. La respuesta es que si que se podría reemplazar. Para ello tendrías que estudiar si los operacionales que uses para construirte el "amplificador de instrumentación" presenta parámetros similares a los que tiene el INA128P. Según sea la calidad de estos operacionales que uses así como el resto de parámetros, más se parecerá a la respuesta de este otro. Si bien es cierto, aunque es reemplazable, es lógico que nunca obtendrás las mismas prestaciones que usando un amplificador de instrumentación todo encapsulado en el mismo IC. Echa un vistazo a esta página, para buscar un reemplazo del INA128P pues aquí podrás encontrar alguno equivalente.
      http://www.ti.com/amplifier-circuit/instrumentation/overview.html
      Y para el OPA4131P puedes usar este comparador también de Texas Instrument donde te indica los parámetros que tienen cada IC para ver si cumple con los que necesitas en tu diseño.
      http://www.ti.com/product/OPA4131/compare
      Espero haberte aclarado tus dudas.
      Un saludo.

      Eliminar
  13. Respecto a los voltajes de los condensadores de 1uf y 33uf es suficiente con que tengan un voltaje maximo de DC de 16V (pues van a tener un voltaje maximo de 9v). Este valor maximo suele estar serigrafiado normalmente sobre el propio condensador. Entiendo que esto es lo que quieres saber con tu pregunta.

    Un saludo.

    ResponderEliminar
    Respuestas
    1. hola, muchas gracias por las aclaraciones. en cuanto a los condesadores de 1uf y 33uf, estos son condensadores electroliticos, y yo estaba usando el de 1uf a 50 V , y el de 33uf a 35v, no se si esos valores de voltaje para el condensador electroitico serian adecuados para el proyecto.

      muchas gracias de antemano

      Eliminar
    2. Los voltaje de los condensadores elegidos deben ser tal que nunca se queden por debajo del voltaje máximo de alimentación DC que tenga el circuito. Es decir, si el voltaje máximo del circuito es de 10V DC, significa que el voltaje que debe tener cualquier condensador deberá estar por encima de este valor para que no se deterioren o incluso exploten. El valor adecuado y normalizado para el caso del circuito de mi ECG serían de 16V. Normalmente en el encapsulado del condensador viene el valor de su capacidad y seguido a este, el valor del voltaje máximo DC que soportan. A veces el condensador no indica directamente el voltaje máximo que aguantan, pero si un código que a su vez indica este voltaje. Te digo los más típicos: código=voltaje
      1A=10V, 1C=16V, 1E=25V, 1V=35V, 1H=50V, 1J=63V ... y así. Espero haberte aclarado tu duda. Un saludo.

      Eliminar
  14. alguien puede explicarme por favor, que papel cumple esta parte? es que la veo tan simple que no se... https://i.imgur.com/WroaUFx.png

    ResponderEliminar
  15. En sencillamente la parte del circuito que cumple la función de obtener una tensión simétrica de alimentación de +5V y -5V de una manera simple y económica. ¿Por qué es necesaria? Porque el INA128P requiera de ella. Si observas tiene un sus pines un V+, V- y Ref. Si no usas una alimentación simétrica, la señal que captura quedará recortada. Espero haberte aclarado tu duda. Un saludo.

    ResponderEliminar
  16. buenas tardes, se me hizo muy interesante el ecg y lo estabamos haciendo, al momento de ponerlo con el osciloscopio para ver la gráfica no nos salio por cierto tuvimos que usar el ina217. Que podría ser el error?

    ResponderEliminar
  17. Buenas Isabel, para poder ayudarte necesitaría que me dieras algún dato más. Así sin más, no puedo decirte cual es el error. Envíame el esquema que has montado, las imágenes de las señales que has medido con el osciloscopio, como has hecho el montaje, etc ...

    ResponderEliminar
  18. Hola, me gustó mucho tu ECG, quiero hacerlo, pero antes de comprar el material me gustaria saber algunas cosas.
    1. Si sigo todos los pasos de tu circuito, debe de funcionar correctamente? o tuviste que hacer algo extra que no esta explicado aquí?
    2. Los valores de tu circuito son los mismos que usaste en protoboard o tuviste que cambiar algunos?
    3. En que momento ajustas el Offset con el potenciometro de 5k?

    ResponderEliminar
  19. Buenas, paso a responder en el mismo orden, a tus preguntas:

    1.Si, te debería funcionar sin ningún problema. La habilidad y el conocimiento que tengas de electrónica te ayudará a que te sea más fácil montarlo y ponerlo en marcha, pero todo lo necesario y lo más relevante esta indicado en el blog.
    2.Si. Los valores y las referencias usadas en el esquema electrónico son los mismos que los que use para montar el prototipo en la placa board.
    3.Te explico:
    Con el offset lo que pretendo es que la señal que sale a la salida del LM324 "del subcircuito de Ajuste de Offset" no se recorten los valores negativos de la señal y ajustarla entre valores positivos, ya que el NE555 se alimenta con una tensión asimetrica de +10V y 0V. Lo que buscaba con esta idea era para en el futuro poder digitalizar la señal del ECG con un microcontrolador, entre +5V y 0V.

    En lo que respecta al ajuste del potenciometro y a que suene un pitido por cada pulsación del ritmo cardíaco, es necesario que se cumpla lo siguiente: la señal de entrada en el pin 4 (RESET) del NE555 deberá estar por debajo de +1V y por encima de +2,5V (por ejemplo, pues con este valor el circuito estaría activo) para conseguir una activación y desactivación acorde con el ritmo cardíaco. ¿Por qué de estos valores? Si miras el datasheet del NE555 el valor máximo de activación del pin 4 (RESET) es de +1V, por lo que por debajo de este valor el NE555 no se activa y por encima se activaría. De este modo escucharías de manera intermitente al ritmo del pulso cardíaco. Debes ajustar el potenciometro para obtener que tu señal baje por debajo de +1V pero también que los supere y además no caiga por debajo de 0V. Lo más sencillo es hacer el ajuste una vez lo tengas todo montado y mires con un osciloscopio la señal. De esta manera podrás ver como al aumentar el offset la señal se desplaza por encima de la linea de tierra (0V) del osciloscopio y empezaras a escuchar el pitido según el latido de tu corazón.

    Espero haber aclarado tus dudas.

    Un saludo.

    ResponderEliminar
    Respuestas
    1. Buenas
      Muchas gracias por aclarar mis dudas, ya conseguí la mayoría de material, lo único que no pude conseguir fue el amplificador de la parte guarda activa (OPA 431), que otro amplificador podría utilizar?
      Gracias,
      Un saludo

      Eliminar
    2. Te respondo a tu pregunta. Si que podrías sustituir este IC por otros. Te indico algunos ejemplos en el siguiente enlace:

      https://drive.google.com/file/d/12LkI-QNZJSBCEsTzMS67J2pGQeHqRpRx/view?usp=sharing

      Un saludo.

      Eliminar
  20. Hola crees que me puedas compatir el circuito de tu ECG me intereso mucho y creo me es muy funcional para una práctica que tengo. Muchas Gracias te paso mi correo es jorge_nestor_spider@prodigy.net.mx

    ResponderEliminar
  21. buenas amigo porf favor digame donde va contectado la punta del osciloscopio donde ya sale la señal filtrada

    ResponderEliminar
    Respuestas
    1. Ya se que llega tarde la respuesta, pero hay un refrán que dice más vale tarde que nunca. Respondiendo a tu pregunta, la obtienes pinchando la sonda del osciloscopio en el pin 7 del IC LM324

      Eliminar
  22. Disculpa, porque fijas a una frecuencia de 100Hz y no de 150Hz?
    Espero tu respuesta.

    ResponderEliminar
    Respuestas
    1. Lo de elegir la frecuencia de corte del filtro es una cuestión delicada. Lo primero porque se hace con electrónica analógica (es decir con un A.O. y componentes discretos), esto hace que la frecuencia de cálculo teórica no sea igual a la que luego se prueba en el circuito debido a los valores y tolerancias de los componentes elegidos. Teniendo esto en cuenta, el valor de 100Hz es porque es una frecuencia de corte, la cual hace que el filtrado realizado no afecte a la señal original que se captura y si al ruido que se acopla a la misma en baja frecuencia. Realmente, se puede hacer la prueba de ir comprobando como sale la señal capturada a medida que se sube y baja la frecuencia de corte del filtro, para ver cual es la óptima. También si tienes posibilidad puedes hacer una análisis espectral de la señal y ver a partir de que frecuencia, te interesa fijar el corte del filtro, haciendo así un filtrado más selectivo.

      Eliminar
  23. Respondo a tus preguntas:
    1-No es código para Arduino, es código LabCVI.
    2-Si te refieres a las gráficas que salen de color verde y fondo negro son los controles gráficos hechos con LabCVI, pero si te refieres a las otras gráficas son las capturadas con el osciloscopio que utilizo.

    ResponderEliminar
  24. Este comentario ha sido eliminado por el autor.

    ResponderEliminar
  25. Hola buenas noches compa en donde puedo conseguir el opa4131p.
    Ha y también en la parte de el divisor de voltaje ese va conectado en que parte gracias y buen trabajo un saludo (y).

    ResponderEliminar
    Respuestas
    1. amigo se me olvido en que parte va conectado el pito gracias.

      Eliminar
  26. Hola, es que tengo una duda sobre la masas que son diferentes entonces era para saber si utilizaron alguna fuente mas?.

    ResponderEliminar
  27. Buenas, las masas efectivamente son diferentes porque el circuito de adquisición debe funcionar con una alimentación simétrica, es decir, +5V y -5V, sin embargo otras partes del circuito necesitan alimentación asimétrica, por eso aparecen dos masas. Una de ellas es la que se obtiene si se mide en el divisor resistivo entre +10V y 0V y la otra masa es la que se obtiene de la alimentación simétrica del divisor resistivo medida entre +5V y OV y la otra entre -5V y 0V. Es una forma de no tener que usar una doble fuente de alimentación haciendo uso de ese divisor resistivo. Espero haberte aclarado tu duda.
    Un saludo.

    ResponderEliminar
  28. mmm ok don ruben muchas gracias por aclararme la duda ahora si mas que claro ya hice las pruebas y si. Gracias saludos.

    ResponderEliminar
  29. Hola Rubén 2 preguntas.
    La 1 que altavoz o pito utilizo para el E.C.G.
    La 2 se puede utilizar un buzzer de 5v.
    Gracias espero proto tu respuesta saludos.

    ResponderEliminar
  30. Hola Rubén buenas tardes tengo 2 preguntas.
    La primera que altavoz o pito utilizo para el E.C.G.
    La segunda se puede un buzzer de 5v.
    Gracias espero pronto tu respuesta saludos.

    ResponderEliminar
    Respuestas
    1. Buenas, puedes usar un altavoz piezo cerámico por ejemplo. Son pequeños y baratos. Tambien puedes usar un buzzer de 5V si alimentas el pin 8 del NE555 a 5V

      Eliminar
  31. Buena noche, como obtengo una ganancia de 1000 en el circuito, en que etapa del circuito obtengo esa ganancia. Muchas Gracias.

    ResponderEliminar
  32. Buenas, la ganancia se obtiene en el "amplificador de instrumentación", que es el INA128 y la cual puede ajustarse mediante el valor de la resistencia colocada en los pines "Rg"

    ResponderEliminar
  33. En este amplificador INA128 cual es la ganancia y como la elevo a 1000. Muchas Gracias por tu ayuda.

    ResponderEliminar
  34. En el plano el potenciometro R1 de 5K, en donde se conecta el extremo que tiene la letra E, que no se ve a donde va conectado.

    ResponderEliminar
  35. Hola, si aun sigues por ahi, en el diagrama del guarda activa que te da el fabricante hay como un cilindro en donde se conecta una de las alidas del OPA2131, de casualidad sabes lo que es?

    ResponderEliminar

Deja tu comentario