Las redes de adaptación son necesarias cuando queremos maximizar la transferencia de potencia entre dos sistemas conectados en cascada. Las redes de adaptación se comportan como filtros en los que en la banda de paso hay adaptación. En la banda de frecuencia a la cual la red no está adaptada, la potencia es reflejada.

Para asegurar la máxima transferencia de potencia, la impedancia de carga debe ser la compleja conjugada del generador.

Máxima transferencia de potencia (MTP)
Máxima transferencia de potencia (MTP)

Ceros de transmisión

Dependiendo de la disposición de inductores y condensadores, es posible crear ceros de transmisión en los cuales no haya transferencia de señal entre la entrada y la salida.

Cero de transmisión en DC ( \(f= 0\) ).

Para conseguir un cero de transmisión en DC podemos poner un condensador en serie entre entrada y salida o una inductancia en derivación (en paralelo a masa).

zero1

La pendiente con la que tiende a 0 el diagrama de Bode es de -20 dB/dec o 6 dB/oct.

Para calcular las octavas que hay entre dos frecuencias:
\[N_{octavas} = \log_2{\frac{f_{sup}}{f_{inf}}}\]
Para calcular el número de décadas entre dos frecuencias:
\[N_{dec} = \log_{10}{\frac{f_{sup}}{f_{inf}}}\]
Para pasar de décadas a octavas:
\[N_{dec} = 0.3 N_{octavas}\]
Cero de transmisión en \(f=\infty\)

Para conseguir un cero de transmisión a frecuencias muy altas, hay que poner un inductor en serie o un condensador en derivación.

La pendiente en el diagrama de Bode es de 20 dB/dec o 6 dB/oct.

zero2

Cero a \(0< f_R < \infty\)

Para crear un cero a una frecuencia que no es ni DC ni a frecuencias infinitas, es necesario utilizar circuitos resonadores (también llamados circuitos tanque).

Existen dos tipos de resonadores: los serie y los paralelo.

Los resonadores serie constan de un condensador y un inductor en serie que a la frecuencia de resonancia equivalen a un cortocircuito. Por tanto, para forzar un cero de transmisión, será necesario poner un resonador serie en derivación.

Por otra parte, tenemos los resonadores paralelo, que a la frecuencia de resonancia son equivalentes a un circuito abierto. Para crear un cero de transmisión basta con ponerlos en serie entre la entrada y la salida del circuito.

La frecuencia de resonancia de un resonador es: \(f_R = \frac{1}{2\pi\sqrt{LC}}\)

zero3Topologías de adaptación con elementos concentrados

Existen 3 topologías de redes en las que se necesita una red de adaptación para conseguir MTP:

  1. \(R_s = R_L\)
  2. \(R_s \neq R_L\)
  3. La impedancia de carga o fuente es compleja.

1) Impedancia compleja con \(R_S = R_L\)

adp1En esta red de adaptación el valor de la parte real de la impedancia coincide con la impedancia del generador. Sin embargo, hay un parte imaginaria que es necesario eliminar. En este caso es tan sencillo como añadir en serie una impedancia imaginaria de signo contrario. Es decir, si la reactancia es positiva (carácter inductivo), habrá que poner un condesador. Si la reactancia es negativa (carácter capacitivo), habrá que poner un inductor. De esta manera, es equivalente a tener un circuito tanque en serie. Sin embargo, como ya hemos visto anteriormente esta solución solo es válida para una frecuencia ya que ambos valores solo se anulan a su frecuencia de resonancia.

Una manera de medir cómo de estrecha será la banda a la cual se da la adaptación es utilizando el factor de calidad Q. En circuitos paso banda, el factor de calidad establece la relación que hay entre la frecuencia de paso y el ancho de banda a -3 dB. Es decir:
\[Q = \frac{f_0}{BW}\]
qComo vemos, si el ancho de banda es grande (poco selectivo), la Q es pequeña. Si el filtro es muy selectivo, la Q es grande.

Este parámetro puede ser medido en función de la reactancia y el valor de la resistencia de carga:
\[Q = \frac{X_L}{2R}\]
Por tanto, si la resistencia es grande la Q es pequeña (el filtro es poco selectivo) y la banda de adaptación es ancha. Si por el contrario la resistencia es pequeña lleva a Q muy grandes (muy selectivo). Ambos extremos son difíciles de solucionar en la práctica, ya que conseguir un filtro muy selectivo o de gran ancho de banda es complicado.

2) Impedancia compleja con \(R_S \neq R_L\)

En este caso, las resistencia de carga y de fuente no coinciden. Es por ello que se necesita subir o bajor el valor de la resistencia de carga para adaptar con la impedancia de fuente.

2Para diseñar este tipo de redes de adaptación se utilizan redes en L:

rsnerl

Sin embargo el principal problema del diseño de este tipo de redes es que su cálculo es muy largo sobretodo cuando el número de elementos aumenta.

Este proceso se puede simplificar si utilizamos equivalencias serie-paralelo de circuitos RC y RL.

rc

RL
\[Q_s = \frac{X_s}{R_s} = Q_p = \frac{R_p}{X_p} = Q\]
\[R_p = \left( 1 + Q^2 \right)R_s\]
\[C_p \approx C_s\]
\[L_p \approx L_s\]
El factor de calidad (Q) indica cómo de ideal es un dispositivo a la hora de almacenar energía. Un dispositivo que pueda almacenar toda la energía sin disiparla tendrá un factor de calidad que tenderá a infinito. Una manera fácil de saber cómo es la expresión del factor de calidad con una resistencia en serie es pensando que si la resistencia en serie es muy grande, esta disipará mucha potencia y el factor de calidad será bajo. Por tanto, en la expresión del factor de calidad para un dispositivo con una resistencia en serie, el valor de la resistencia está en el denominador \(Q_p = \frac{R_p}{X_p}\)

La manera en la que se deben colocar los elementos es la siguiente: con la resistencia de mayor valor, un elemento en paralelo (para bajar su impendancia). Y con la resistencia de menor valor un elemento en serie (para subir su impedancia).

ejRLCPara calcular los valores de los elementos hay que igualar el factor de calidad Q de las redes serie y paralelo:
\[Q_s = Q_p = \sqrt{\frac{R}{r} – 1}\]
donde R es la resistencia con el valor nominal más alto y r la resistencia más baja.

Una vez calculado el factor de calidad, se obtiene el valor de la reactancia y con ello, el valor nominal del condensador o inductor.

Existen 4 disposiciones distintas:

l

Ejemplo:

EnEjQueremos adaptar estos dos puertos para que a la frecuencia de 850 MHz haya MTP y al mismo tiempo que la polarización en DC también pueda pasar del puerto 1 al 2.

Debido a que no podemos bloquear la continua, no podemos utilizar un condensador en serie con la resistencia del puerto 1. Por tanto, tenemos que utilizar la red en L con una bobina en serie y un condensador en derivación:

lPara calcular los valores de L y C:
\[Q_s = Q_p = \sqrt{\frac{R}{r} -1 } = \sqrt{\frac{50}{5} -1} = 3\]
\[Q_s = \frac{X_s}{R_s} \Rightarrow X_s = Q \cdot R_s = 2\pi f L\]
\[L = \frac{Q \cdot R_s}{2\pi f} = \frac{3 \cdot 5}{2\pi \cdot 850\cdot 10^6} = 2.8\text{ nH}\]
\[Q_p = \frac{R_p}{X_p} \Rightarrow X_p = \frac{R_p}{Q_p} = \frac{1}{2\pi f C}\]
\[C = \frac{Q_p}{2\pi f R_p} = 11.2\text{ pF}\]
ejemploQ

Respuestra en frecuencia del parámetro S21
Respuestra en frecuencia del parámetro S21

Sin embargo, cuando la diferencia de resistencias es muy grande, es posible que necesitemos unos valores de Q que no nos convengan en la práctica (recordar que \(Q = \sqrt{\frac{R}{r} – 1}\) ).

 Es decir, este salto de impedancia puede ser muy grande
Es decir, este salto de impedancia puede ser muy grande

Podemos hacer varios saltos de impedancia en lugar de uno solo. Esto da lugar a las redes en T y redes en π:

RT

rpiPor tanto disponemos de 3 tipos de redes: las redes en L, las redes en T y las redes en π. Con estas redes en T y π podemos aumentar el ancho de banda ya que al hacer saltos más pequeños de impedancia, la Q disminuye y el ancho de banda aumenta. Con las redes en L podemos disminuir el ancho de banda, ya que la resistencia virtual está entre un valor de \(R_{int} < r\) o \(R_{int} > R\).

Aumento del ancho de banda

Para aumentar el ancho de banda hay que utilizar una red en T o π. Si queremos una red de adaptación con un ancho de banda grande, la Q deberá se baja. Para que la Q sea baja, los saltos de impedancia deben de ser pequeños. Por tanto, podemos hacer saltos intermedios que tengan una Q menor y de esta manera aumentar el ancho de banda:

saltoimpe

Para que una red en T o en π dé un ancho de banda mayor, el valor de la resistencia \(R_L\). Un valor adecuado es el que da la media geométrica entre los dos valores:
\[R_{int} = \sqrt{R_s R_L}\]
Por tanto, las redes en T y en π no sirven para adaptar resistencias de carga y generador ya iguales ya que se hacen saltos de impedancia.

Si queremos poner \(m\) sería:
\[R_m = R_S^{\frac{n-m}{n}} R_L^\frac{m}{n}\]

Reducción del ancho de banda

Para reducir el ancho de banda debemos hacer saltos de impedancia más grandes. Esto se puede conseguir de la siguiente manera:

saltoimpeEl valor de \(R_{int} > R\). Ambas combinaciones harán que el ancho de banda sea menor.

3) Impedancias complejas

Una tercera forma de adaptar las impedancias de carga y fuente es mediante absorción o resonancia de la parte compleja de impedancia. Estas soluciones son de banda muy estrecha.

absorcionLa absorción consiste en utilizar la parte compleja de la carga o fuente como parte de la red de adaptación. Es decir, si necesitamos un condensador de 10 pF y la carga tiene un condesandor equivalente de 5 pF, solo será necesario poner un condensador de 5 pF ya que los otros 5 pF restantes los pondrá la parte imaginaria de la impedancia de carga.

La otra estrategia es hacer resonar la reactancia de la carga para poder eliminarla a la frecuencia de trabajo. Es decir, si tenemos un condensador en derivación, podemos ponerle un inductor en paralelo para que resuene a la frecuencia de trabajo y haya máxima transferencia de potencia.

resonanciaTambién podemos resonar solo una parte de la reactancia y utilizar la otra para conformar la red de adaptación en L:

resonanciaparcialAdaptación con líneas de transmisión

En las líneas de transmisión tenemos 2 grados de libertad: la impedancia característica y la longitud eléctrica. Jugando con estos valores podemos sintetizar valores de inductancia y capacitancia utilizando transformadores en \(\frac{\lambda}{4}\) y stubs en paralelo. Sin embargo, las líneas de transmisión no sirven para adaptar los casos en los los valores de resistencia de carga y fuente sean diferentes.

lt lt2

esp8266

Hace algunas semanas compré por Ebay 3 módulos ESP8266 con la esperanza de poder utilizarlos en aplicaciones de IoT. Oí hablar de este módulo en Hackaday y solo leer de lo que era capaz no me lo pensé dos veces:

  • Procesador con puertos GPIO (General Purpose Input Output)
  • Wifi integrado
  • ¡Solo vale 4€! (Actualización: actualmente es posible encontrarlo por ~1.5€)

Por tanto, este chip es capaz de conectarse con la red Wifi de tu casa, guardar o escribir información en una base de datos, hacer las veces de servidor, leer información de sensores, activar otros actuadores (como relés)… ¡Las posibilidades son enormes! Es un pequeñísimo Arduino con Wifi integrado.

Los primeros meses desde la salida del ESP8266 fueron bastante oscuros, ya que no se tenía apenas información, no había un SDK oficial y lo que había estaba en chino.

Pero recientemente la comunidad ESP8266 ha empezado a florecer ofreciendo diferentes vertientes por las que poder configurarlo. Yo me voy a centrar únicamente en la que utiliza el firmware NodeMCU.

Este firmware hace correr un compilador de LUA en el procesador, de manera que podremos escribir los programas que queramos correr en este mismo lenguaje.

Para poder instalar el firmware solo hay que conectar un FTDI (USB-UART converter) a nuestro ESP8266 de la siguiente manera:

esp826

Advertir que es obligatorio alimentar el ESP8266 a 3.3V, ya que por lo contrario quemaréis el chip.

Para entrar en modo de escritura de firmware hay que conectar el pin GPIO0 a masa. Yo he utilizado un jumper con que puedo conectar y desconectar fácilmente el pin.

Cuando tengamos este montaje hecho, necesitaremos descargar el grabador del firmware, que lo podemos encontrar también en el GitHub de NodeMCU.

Una vez descargado, abrimos el flasher:

ESP8266-flasher-folder

Con lo que nos aperecerá la siguiente aplicación:

ESP8266-flasher

Automáticamente cuando conectemos nuestro FTDI cambiará el puerto COM. Si no te sucede asegúrate de tener los drivers para tu FTDI instalados.

Flashear el ESP8266 con NodeMCU es tan sencillo como darle al botón Flash(F), y se pondrá a escribir el firmware en la memoria del procesador. Como se puede ver en la pestaña Config hace cuatro escrituras distintas, siendo la tercera la que más tiempo lleva. Cuando termine el FTDI se desconectará del equipo y aparecerá un símbolo verde abajo en la esquina izquierda. En mi caso dicho símbolo no ha aparecido pero el flasheo ha sido correcto igualmente.

Ahora que ya tenemos el firmware subido, tenemos que cargar nuestro programa. Para ello utilizaremos el programa LUAUploader de Hari Wiguna. Con esta aplicación especialmente desarrollada para el ESP8266 podremos subir nuestros programas de una manera muy sencilla.

Cabe destacar que la corriente que entrega un puerto USB del ordenador no es suficiente para iniciar el módulo de Wi-Fi, así que a partir de este momento es necesario alimentar el ESP8266 con una fuente externa. En mi caso he utilizado dos pilas LR03 (las pequeñas de los mandos) para obtener 3V. Recuerda que no sirven los 5V o el chip se quemará. Cuando conectes la fuente externa desconecta el pin de 3.3V del FTDI para evitar problemas, pero deja el pin de GND para unificar las masas del FTDI y de la fuente externa.

Una vez lo tengamos todo listo podemos pasar a lo divertido. Abrimos el LUAUploader y nos aparecerá la siguiente pantalla:

LuaUploaderPara comprobar que el firmware ha sido grabado correctamente iremos a la pestaña “Immediate” y escribiremos un pequeño chunk de prueba (un chunk es cada trozo de código que Lua puede ejecutar)

dummy = 5
print(dummy)

Si el compilador nos devuelve 5, quiere decir que el firmware ha sido escrito correctamente:

LUAtest

 

Para poder conectarnos a nuestra red Wi-Fi es necesario introducir el siguiente chunk:

ip = wifi.sta.getip()
print(ip)
--nos devolverá nil
wifi.setmode(wifi.STATION)
wifi.sta.config("SSID","password")
ip = wifi.sta.getip()
print(ip)
--nos devolverá la IP asignada

Hay veces que después de intentar conectarse a la red Wi-Fi continúa devolviendo nil. Probad a imprimir de nuevo la dirección IP con:

ip = wifi.sta.getip()
print(ip)

Ahora, para hacer una pequeña prueba, podemos montar un servidor en el propio ESP8266 con el chunk:

srv=net.createServer(net.TCP) 
srv:listen(80,function(conn) 
   conn:on("receive",function(conn,payload) 
      print(payload) 
      conn:send("<h1>Hola Ruben!</h1>")
   end) 
   conn:on("sent",function(conn) conn:close() end)
end)

Una vez subido, si accedemos a la dirección del ESP8266 nos devolverá la página con un heading y el texto “Hola Ruben!” (para poder escribir acentos hay que utilizar los códigos ASCII en HTML y utilizar la columna HTML Número: é = &#233;)

web

Podéis encontrar muchos más ejemplos en el GitHub de NodeMCU.

Por fin podemos hablar de el primer proyecto más o menos serio que he hecho con ESP8266 y esta vez se trata de un medidor de nivel agua con los datos guardados en la nube, de manera que sean accesibles desde cualquier sitio.

Principios de funcionamiento

Para empezar, imaginemos que tenemos una cisterna donde tenemos agua. Primero debemos asumir que las medidas de la cisterna se mantienen constantes en todo momento, esto es, ancho, largo y alto. Para conocer el volumen de agua que reside en el interior tan solo necesitaremos saber cuál es la altura del agua, ya que es la única variable del sistema.

Con esto claro, necesitamos un mecanismo que sea capaz de medir a qué altura de encuentra el agua. Mi idea ha sido utilizar un sensor ultrasónico, cuyo funcionamiento es el siguiente:

  1. El altavoz emite un sonido de frecuencia ultrasónica (imperceptible al oido humano)
  2. Se pone un cronómetro en marcha.
  3. El micrófono recibe la señal que había sido emitida por el altavoz.
  4. Se para el cronómetro que se había puesto en marcha al emitir el sonido
  5. Se calcula sabiendo la velocidad del sonido, la distancia a la que está el objeto sobre el cual ha rebotado la pulsación ultrasónica.

hc

Materiales

Para empezar los materiales que he utilizado son:

  • ESP-01 (ESP8266)
  • Fuente de alimentación de 3.3V
  • Fuente de alimentación de 5V
  • Programador FTDI
  • Sensor ultrasónico HC-SR04

Montaje hardware

El módulo ultrasónico HC-SR04 tiene 4 pines:

  1. Vdd: debemos conectar 5V. No podemos conectar directamente los 3.3V con los que alimentamos el ESP-01. Una lástima.
  2. Trig: es el encargado de dar la orden al altavoz de que emita el pulso.
  3. Echo: es el micrófono que espera el rebote del pulso emitido.
  4. GND: es importante unificar la masa del ESP-01 con la propia de la fuente de 5V para evitar problemas de grounding.

En mi caso he utilizado los 5V que tiene disponible el Arduino, aunque es algo bastante engorroso. Para la versión final, utilizaré un convertidor lineal (para conseguir los 3.3V) y una sola alimentación.

La conexión entre el ESP-01 y el HC-SR04 es extremadamente sencilla:

  1. GPIO0 – Echo
  2. GPIO2 – Trig

Software

Para hacer funcionar el HC-SR04 con NodeMCU, he utilizado el código que ha subido sza2 en GitHub modificándolo un poco.

Se trata de dos archivos: init.lua y hcsr04.lua. Cabe destacar que el ESP8266 ejecuta init.lua en cuanto se enchufa. La base de su funcionamiento reside en el uso de la función tmr.alarm(), que ejecuta una función cada intervalo de tiempo que nosotros definamos. En este caso, la función de medir distancia.

tmr.alarm(0, 3600000, 1, function() send(device.measure()) end)

El código de sza2 envía vía serie el resultado cada 0,5s, pero podemos configurarlo como nosotros queramos. Solo recordar que hay que introducir el tiempo en milisegundos.

En el código original se imprime por pantalla el resultado, pero como hemos dicho anteriormente nuestro objetivo es que la lectura sea subida a un servidor para poder ver el nivel de agua desde cualquier sitio.

Para ello he utilizado una plataforma llamada ThingSpeak, que sirve precisamente para esto. Su funcionamiento es muy sencillo. La información se guarda en los que ThingSpeak llama canales. En cada canal podemos añadir hasta 8 variables que queramos monitorizar, y la manera en que podemos guardar nuevos datos es haciendo una petición HTTP con el método GET. Las variables que tenemos que incluir en la petición HTTP es el código del canal (para saber dónde hay que guardar la información), el número del campo a guardar seguido del valor.

conn:send("GET /update?key=CodigoAPI&field1="..value1.." &amp;field2="..value2.."HTTP/1.1\r\n")

De esta manera quedará registrado automáticamente en ThingSpeak, como podemos ver en este applet:

También decir que el HC-SR04 devuelve valores contiguos que difieren unos de otros por motivos obvios, por lo que he modificado el código para que devuelva el valor medio de 500 medidas.

El código completo del archivo init.lua es:

dofile("hcsr04.lua")
wifi.setmode(wifi.STATION)
wifi.sta.config("SSID","pass")
print(wifi.sta.getip())
device = hcsr04.init()
print("Loading...")
 
function send(value)
  if value ~= -1 then
    print(value)
    conn=net.createConnection(net.TCP, 0)
    conn:on("receive", function(conn, payload) print(payload) end)
    conn:connect(80,'184.106.153.149')
    conn:send("GET /update?key=CodigoAPI&field1="..value.." HTTP/1.1\r\n")
    conn:send("Host: api.thingspeak.com\r\n")
    conn:send("Accept: */*\r\n")
    conn:send("User-Agent: Mozilla/4.0 (compatible;ESP8266; Windows NT 5.1)\r\n")
    conn:send("\r\n")
  end
end
 
tmr.alarm(0, 60000, 1, function() send(device.measure()) end)
hcsr04 = {};
 
function hcsr04.init(pin_trig, pin_echo)
	local self = {}
	self.time_start = 0
	self.time_end = 0
	self.trig = pin_trig or 4
	self.echo = pin_echo or 3
	gpio.mode(self.trig, gpio.OUTPUT)
	gpio.mode(self.echo, gpio.INT)
 
	function self.echo_cb(level)
		if level == 1 then
			self.time_start = tmr.now()
			gpio.trig(self.echo, "down")
		else
			self.time_end = tmr.now()
		end
	end
 
	function self.measure()
          medidas = 500
          mean = 0
		for i=1,medidas,1 do
			gpio.trig(self.echo, "up", self.echo_cb)
			gpio.write(self.trig, gpio.HIGH)
			tmr.delay(100)
			gpio.write(self.trig, gpio.LOW)
			tmr.delay(100000)
			if (self.time_end - self.time_start) < 0 then
				return -1
			end
			mean = mean + (self.time_end - self.time_start) / 58
		end
		print(mean/medidas)
		return mean/medidas
	end
	return self
end

Screen Shot 2014-11-04 at 12.17.23

Frecuencia de resonancia \(f_r=\frac{1}{2\pi\sqrt{LC}}\)

Para el calculo de la resistencia parásita serie:

\[R_s = \frac{L\omega}{Q}\]

donde \(Q = \frac{f}{BW}\)

Si \(Q \geq 5\):

\[R_p=\frac{L}{R_s\cdot C}\]

A la frecuencia \(f_r\) el circuito es puramente resistivo. El valor de su impedancia entonces es \(R_p\)

Gracias a los bots de Telegram, es sencillo interaccionar entre Telegram y una Raspberry. La idea es la siguiente:
Telegram-Raspberry
Desde la aplicación de Telegram, enviaremos un comando al servidor de Telegram. Se puede configurar mediante webhooks que redireccione este mensaje a un servidor propio con SSL, tal y como expliqué en una entrada anterior.. Este servidor con SSL reenviará la información a otro servidor montado sobre la Raspberry. En la Raspberry, recibiremos las notificaciones enviadas por el usuario. Para conseguir que el que el servidor con cifrado SSL consiga enviar a nuestra Raspberry la información, será necesario configurar la Raspberry con un IP estática y utilizar un servidor de DNS dinámicas, configurar nuestro router de casa para redireccionar todas las peticiones provinientes del exterior (de Internet) en el puerto 80 al puerto 80 de nuestra Raspberry. Este paso quizá sea el más complicado debido a la cantidad de pasos que hay que hacer, sin embargo es posible.

Sería posible utilizar un solo servidor para entregar el comando de Telegram directamente sobre la Raspberry. Sin embargo, es necesario que el servidor de la Raspberry cuente con un certificado SSL. La API de Telegram permite utilizar certificados autofirmados, sin embargo yo no lo he probado ya que disponía en el servidor de un certificado SSL de Let’s Encrypt.

En el servidor con SSL el código del archivo PHP es:

<?php
 
$bottoken = "myToken";
$website = "https://api.telegram.org/bot".$bottoken;
 
$update = file_get_contents('php://input');
$update = json_decode($update, TRUE);
 
$chatId = $update["message"]["chat"]["id"];
$message = $update["message"]["text"];
 
switch($message) {
        case "/radio":
		$result = file_get_contents('http://raspberryLocation/test.php');
		if(!strcmp($result, "Encendido"))
			sendMessage($chatId, "Enchufando radio");
		else
			sendMessage($chatId, "Apagando radio");
                break;
        case "/temperatura1":
                sendMessage($chatId, "La temperatura es de 18 ºC");
                break;
        default:
                sendMessage($chatId, "default");
}
 
function sendMessage ($chatId, $message) {
 
        $url = $GLOBALS['website']."/sendMessage?chat_id=".$chatId."&text=".urlencode($message);
        file_get_contents($url);
 
}
 
?>

Para interactuar con los pines GPIO de la Raspberry, es necesario utilizar la librería WiringPi. El principal problema que existe con esta librería es que se necesitan permisos de super usuario para poder utilizarla. Para hacer una prueba rápida, le he dado permisos de superusuario al usuario www-data, que es el que ejecuta los archivos PHP. Sin embargo, esto es potencialmente peligroso. En cuanto encuentre una solución la subiré.

En mi prueba, he hecho que al enviar un comando desde Telegram, ponga a nivel alto o bajo un pin GPIO. El script que corre en el servidor de la Raspberry cuenta de dos partes. Una escrita en C que se encarga de interaccionar con la libreria WiringPi y otra en PHP que ejecuta el programa en C propio para cada comando.

La parte de PHP es la siguiente (archivo test.php que es llamado por el servidor con SSL):

<?php
	exec("sudo ./switch", $return);
	echo($return[0]);
?>

El programa en C es el que se ejecuta con la instrucción exec() es el siguiente:

#include <stdio.h>    // Used for printf() statements
#include <wiringPi.h> // Include WiringPi library!
#include <fcntl.h>  //File management
#include <unistd.h>  //File checker
 
// Pin number declarations. We're using the Broadcom chip pin numbers.
const int radioPin = 4; // Radio pin
 
char radio[] = "radio";
 
int main(void)
{
 
    wiringPiSetupGpio(); // Initializa wiringPi -- utilizando los números de pines de Broadcom
    pinMode(radioPin, OUTPUT);
    FILE *fp;
    if(access(radio, F_OK) != -1){
    // file exists
        printf("Apagado");
        remove(radio);
        digitalWrite(radioPin, LOW);
    } else {
    // file doesn't exist
        printf("Encendido");
        fp = fopen(radio, "w+");
        fclose(fp);
        digitalWrite(radioPin, HIGH);
    }
        return 0;
}

Este programa es muy sencillo y simplemente comprueba que exista un archivo llamado radio en la carpeta donde se encuentra. Si el archivo existe, entonces quiere decir que la radio estaba encendida y por tanto elimina el archivo y apaga la radio. Si el archivo no existe, la radio estaba apagada y enciende la radio poniendo el pin a nivel alto y crea el archivo radio.

Para compilar este programa es necesario utilizar la siguiente instrucción:

gcc -Wall -o switch switch.c -lwiringPi

Es posible que sea necesario ejecutar el compilador gcc como superusuario añadiendo sudo si estáis en la carpeta del servidor (es decir /var/www).

De esta manera, es posible encender y apagar un relé, por ejemplo, si conectamos el pin en cuestión a la Raspberry. Por tanto, estaríamos controlando una lampara, una luz o una radio.

El resultado es el siguiente:

ia-with-three-oa
Amplificador de instrumentación con 3 AO

Esta disposición de AI se puede separar en dos etapas:

Etapa 1stage-1Por superposición y haciendo el KCL:
\(\)
\[ V_{in_1} = 0 \]
\[R_3 + \alpha R_4 = R_G\]
\[ \frac{V_{in_2}-V_B}{R_5} = \frac{0-V_{in_2}}{R_G}\]
\[V_{in_2}R_G -V_B R_G =-V_{in_2}R_5\]
\[V_B = \left( 1 + \frac{R_5}{R_G} \right) V_{in_2}\]
\[\frac{0 – V_{in_2}}{R_G} + \frac{0 – V_A}{R_2} = 0\]
\[V_A = – \frac{R_2}{R_G}V_{in_2}\]

Ahora con \(V_{in_2} = 0\),

\[\frac{V_{in_1}-0}{R_G} = \frac{0-V_B}{R_5} \]
\[V_{in_1}R_5 =-V_B R_G\]
\[V_B =-\frac{R_5}{R_G}V_{in_1}\]
\[\frac{0 – V_{in_2}}{R_G} + \frac{0 – V_A}{R_2} = 0 \]
\[V_A = \left( 1 + \frac{R_2}{R_G} \right) V_{in_1} \]
Por lo que finalmente, tenemos que:
\[V_A = \left( 1 + \frac{R_2}{R_G} \right) V_{in_1} – \frac{R_2}{R_G}V_{in_2} \]
\[V_B = \left( 1 + \frac{R_5}{R_G} \right) V_{in_2} -\frac{R_5}{R_G}V_{in_1} \]
En cuanto a la ganancia en modo común de esta etapa, la podemos calcular haciendo \(V_{in_1} = V_{in_2}\). De esta manera,
\[V_A = \left( 1 + \frac{R_2}{R_G} \right) V_{in_1} – \frac{R_2}{R_G} V_{in_1} = V_{in_1}\]
\[V_B = \left( 1 + \frac{R_5}{R_G} \right) V_{in_1} -\frac{R_5}{R_G}V_{in_1} = V_{in_1}\]
Por tanto, sea cual sea el valor de \(R_2\), \(R_5\) y \(R_G\), la tensión en modo común pasa a los nodos \(V_A\) y \(V_B\).

La otra etapa restante es:
De nuevo, aplicando superposición podemos llegar a la expresión final de la salida.
\[V_{out} = – \frac{R_9}{R_1} V_A + \left(1 + \frac{R_9}{R_1} \right) \frac{\beta R_8 + R_7 }{ \beta R_8 + R_7 + R_6} V_B \]
\[V_{out} = – \frac{R_9}{R_1} \left[ \left( 1 + \frac{R_2}{R_G } \right) V_{in_1} – \frac{R_2}{R_G }V_{in_2} \right]+ \left(1 + \frac{R_9}{R_1} \right) \frac{\beta R_8 + R_7 }{ \beta R_8 + R_7 + R_6} \left[ \left( 1 + \frac{R_5}{R_G} \right) V_{in_2} -\frac{R_5}{R_G}V_{in_1} \right] \]

Para calcular la ganancia en modo común de esta etapa vamos a aplicar una tensión igual en las dos entradas diferenciales. Por tanto \(V_A = V_B = V_{CM}\). Así conseguimos la siguiente expresión para la tensión de salida.
\[V_{out} = – \frac{R_9}{R_1} V_{CM} + \left(1 + \frac{R_9}{R_1} \right) \frac{\beta R_8 + R_7 }{ \beta R_8 + R_7 + R_6} V_{CM} \]
También identificaremos \(R_{ref} = \beta R_8 + R_7\) para facilitar las operaciones.
\[V_{out} = – \frac{R_9}{R_1} V_{CM} + \left(1 + \frac{R_9}{R_1} \right) \frac{R_{ref}}{R_{ref}+ R_6} V_{CM} \]
Idealmente, querríamos que esta tensión fuese igual a 0, de manera que el amplificador de instrumentación pudiese rechazar completamente para poder, por ejemplo, eliminar el ruido acoplado en ambas entradas tal y como puede verse en la figura.

CMRR

\[V_{out} = – \frac{R_9}{R_1} V_{CM} + \left(1 + \frac{R_9}{R_1} \right) \frac{R_{ref}}{R_{ref}+ R_6} V_{CM} \]
\[G_{cm} = \frac{V_{out}}{V_{CM} } = – \frac{R_9}{R_1} + \left(1 + \frac{R_9}{R_1} \right) \frac{R_{ref}}{R_{ref}+ R_6} \]
\[G_{cm} = \frac{R_{ref}}{R_{ref}+R_6} – \frac{R_9 R_6}{R_1 R_{ref} + R_1 R_6} \]
Por tanto, resolviendo la ecuación de ganancia en modo común igual a 0, \(G_{cm} = 0\), tenemos que
\[- \frac{R_9}{R_1} + \left(1 + \frac{R_9}{R_1} \right) \frac{R_{ref}}{R_{ref}+ R_6} = 0\]
Ecuación que podemos identificar de la siguiente manera:
\[A = \frac{R_9}{R_1}\]
\[ B =\frac{R_{ref}}{R_{ref}+ R_6}\]
\[ – A + \left(1 + A \right) B = 0 \]
\[-A + B + AB = 0 \]
\[\left( B -1 \right) A = -B \]
\[ A = \frac{-B}{B-1} = \frac{B}{1-B} \]
\[A = \frac{\frac{R_{ref}}{R_{ref}+ R_6}}{1-\frac{R_{ref}}{R_{ref}+ R_6}} = \frac{R_{ref}}{R_6} \]
\[\frac{R_9}{R_1} = \frac{R_{ref}}{R_6} \]
Por tanto, para tener una ganacia en modo común de 0 (o lo que es lo mismo un CMRR\( = \infty\)), \(R_9 = R_{ref}\) y \(R_{1} = R_6\)

Por otra parte, si queremos que \(V_A = V_B\) cuando \(V_{in_1} = V_{in_2}\), necesitamos que se cumpla la siguiente relación entre las resistencias \(R_2\) y \(R_5\).
\[ V_A = V_B \left( 1 + \frac{R_2}{R_G} \right) V_{in_1} – \frac{R_2}{R_G}V_{in_2} = \left( 1 + \frac{R_5}{R_G} \right) V_{in_2} -\frac{R_5}{R_G}V_{in_1} \left( 1 + \frac{R_2}{R_G} \right) – \frac{R_2}{R_G} = \left( 1 + \frac{R_5}{R_G} \right) -\frac{R_5}{R_G} \]
Por simple inspección se llega a la conclusión de que \(R_2 = R_5\).

Por último, podemos calcular la ganancia diferencial y la ganancia de modo común de otro modo. Si definimos la tensión diferencial como \(V_d = V_{in_1}- V_{in_2}\) y la tensión en modo común como \(V_c = \frac{V_{in_1} + V_{in_2}}{2}\), sustituimos en la expresión de \(V_o\) e identificamos la expresión resultante como \(V_o = G_d V_d + G_c V_c\), en la que \(G_d\) es la ganancia diferencial y \(G_c\) es la ganancia en modo común (la misma que hemos calculado arriba, obtenemos que \(G_d\) es igual a:
\[ G_d = \frac{-2 R_9 R_G R_{ref} – R_9 R_G R_6 – 2 R_9 R_2 R_{ref} -2 R_9 R_2 R_6 – R_{ref}R_1 R_G – 2 R_1 R_5 R_{ref} – 2 R_9 R_{ref} R_5}{2R_1 R_G \left( R_{ref} + R_6 \right) } \]
En caso de tener las resistencias balanceadas, la expresión de la ganancia diferencial se simplifica a:
\[G_d = -\frac{R_G^2}{R_1 \left( R_9 + R_1 \right)} – \frac{R_9}{2 \left( R_9 + R_1\right)} – \frac{R_9 R_2}{R_G \left( R_9 + R_1 \right)} – \frac{R_9}{2 \left( R_9+ R_1 \right)} – \frac{R_2 R_9}{R_G \left( R_9+ R_1 \right)} – \\ \frac{R_9^2 R_2}{R_1 R_G \left( R_9 + R_1 \right)} \]

Conclusiones

Este amplificador de instrumentación es muy utilizado para amplificar salidas de sensores y demás aplicaciones de instrumentación. Sus ventajas residen en que si está balanceado se puede conseguir tener una ganancia de modo común baja (o CMRR alto) a la vez que se mantiene una ganancia ajustable a través de \(R_G = R_3 + \alpha R_4\). Por último, también podemos ver como la impedancia de entrada de este amplificador es muy alta, ya que ambas entradas son las entradas de un AO.
\[V_{out} = – \frac{R_9}{R_1} \left[ \left( 1 + \frac{R_2}{R_G } \right) V_{in_1} – \frac{R_2}{R_G }V_{in_2} \right]+\left(1 + \frac{R_9}{R_1} \right) \frac{R_{ref} }{R_{ref} + R_6} \left[ \left( 1 + \frac{R_5}{R_G} \right) V_{in_2} -\frac{R_5}{R_G}V_{in_1} \right] \]
\[G_d = \frac{-2 R_9 R_G R_{ref} – R_9 R_G R_6 – 2 R_9 R_2 R_{ref} -2 R_9 R_2 R_6 – R_{ref}R_1 R_G – 2 R_1 R_5 R_{ref} – 2 R_9 R_{ref} R_5}{2R_1 R_G \left( R_{ref} + R_6 \right)} \]
\[G_{cm} = \frac{R_{ref}}{R_{ref}+R_6} – \frac{R_9 R_6}{R_1 R_{ref} + R_1 R_6} \]

Enlaces a CircuitLab para simulación



En anteriores entradas he hablado del ESP8266, de cómo configurarlo y cómo introducirnos un poco en el amplio espectro de posibilidades que este pequeño chip nos brinda. No obstante, en la documentación que he publicado falta una parte importante, y esta es la de hardware.

Dado que el pinout del ESP-01 no es apto para conectarlo a una protoboard (a no ser que quieras cortocircuitarlo) decidí hacer un pequeño adaptador para conectar el programador serie (FTDI) y la alimentación, así como añadir un jumper para iniciar el chip en modo programación.

Para ello he utilizado:

  • Tira doble de cuatro pines hembra (o dos tiras simples de cuatro pines hembra), para la conexión del ESP.
  • Tira de 6 pines macho, para la conexión del ESP y salidas de GPIO.
  • Tira de 2 pines hembra, para alimentación.
  • Dos pilas AAA (1.5V cada una)

Estos son los distintos pines disponibles en el ESP-01:

pinout
ESP-01 pinout

Las conexiones que he hecho en mi adaptador son las siguientes:

 

esp-conn
Conexiones adaptador

 

El número de pines corresponde al de los pines del ESP.

El resultado final es este:

programador
Adaptador ESP-01

También decir que la alimentación que utilizo son dos pilas AAA conectadas en serie soldadas (podéis utilizar un portapilas, pero si no tenéis uno, esta opción también puede funcionar):

power
Alimentación a 3V

La comunicación I2C es muy útil a la hora de comunicarse con dispositivos de un mismo circuito. Con esta entrada quiero dar un poco de luz a este protocolo que pese a su sencillez es un auténtico misterio a la hora de implementarlo.

Cabe destacar que, atendiendo a la definición del protocolo, la dirección del slave es de 7 bits (del bit 7 al 1). De esta manera el bit 0 se utiliza para decirle al slave si el master quiere leer (bit 0 = 1) o quiere escribir (bit 0 = 1). Es por eso que en el código se utilizan dos direcciones diferentes en función de si se quiere leer o escribir.

El master escribe continuamente “Saludos desde el Master” y después lee 23 bytes del Slave, que devuelve “Por el Slave todo bien”.
Para poder cambiar el número de bytes para leer o escribir desde el master hay que cambiar las variables n_read y n_write. (Si sabéis de algún método mejor, soy todo oídos). La implementación utiliza el propio hardware del PIC (en este caso el PIC16F877) aunque se podría haber hecho mediante software y haber utilizado cualquier pin del procesador. En este caso habría que añadir la opción FORCE_SW en la inicialización del I2C.
NOTA: Solo se puede implementar I2C por software para el master. Para el Slave debe de ser forzosamente hardware.
El código para el master es:


#include "16F877.h"
#device ADC=16
 
#FUSES NOWDT                    //No Watch Dog Timer
#FUSES NOBROWNOUT               //No brownout reset
#FUSES NOLVP                    //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
 
#use delay(crystal=14745600)
#use rs232(baud=115200,parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=8,stream=PORT1)
#use i2c(Master,Fast,sda=PIN_C4,scl=PIN_C3)
 
#define SLAVE_WRITE_ADDR 0xA0
#define SLAVE_READ_ADDR 0xA1
 
void write_slave(int address, int *buffer, int n_read){
   int n_byte = 0;
   i2c_start();
   i2c_write(address);
   for(n_byte = 0;n_byte<n_read;n_byte++)
      i2c_write(buffer[n_byte]);
   i2c_stop();
}
 
void read_slave(int address, int *buffer, int n_read){
   int n_byte = 0;
   i2c_start();
   i2c_write(address);
   for(n_byte = 0;n_byte<n_read-1;n_byte++)
      buffer[n_byte] = i2c_read();
   buffer[n_byte] = i2c_read(0);
   i2c_stop();
}
 
void print_buffer(int *buffer, int length){
   int n_byte;
   for(n_byte = 0;n_byte<length;n_byte++)
         printf("%c",buffer[n_byte]);
    printf("\r\n");
}
 
void main(){
   int n_read=23,n_write = 23;
   int writeBuffer[] = {0x53,0x61,0x6c,0x75,0x64,0x6f,0x73,0x20,0x64,0x65,0x73,0x64,0x65,0x20,0x65,0x6c,0x20,0x4d,0x61,0x73,0x74,0x65,0x72}; //"Por el Slave todo bien"
   int readBuffer[23];
   printf("MASTER\r\n");
   while(TRUE){
      write_slave(SLAVE_WRITE_ADDR, writeBuffer, n_write);
      delay_ms(100);
      read_slave(SLAVE_READ_ADDR, readBuffer, n_read);
      print_buffer(readBuffer,n_read);
      delay_ms(100);
   }
}

El Slave lo único que hace es gestionar la llegada de paquetes I2C, tanto de escritura como de lectura. Para entender un poco más el código recomiendo leer la documentación del PIC C Compiler (CCS) (pulsando F1), donde explica con detalle las diferentes funciones que se utilizan.

Faltan añadir algunas mejores en el Slave para recibir un número flexible de bytes. Ahora solo imprime los datos por consola (RS232) cuando se envían el número exacto de bytes descritos en max_written.

El código del Slave es:

#include "16F877.h"
#fuses HS,NOWDT,NOPROTECT,PUT,BROWNOUT,NOLVP
#device ADC=10
#use delay(clock=14745600)
#use rs232(baud=115200, xmit=PIN_C6, rcv=PIN_C7,ERRORS)
#use i2c(SLAVE, SDA=PIN_C4, SCL=PIN_C3,FAST=100000, address=0xA0,FORCE_HW) 
#use spi(DI=PIN_A0, DO=PIN_A1, CLK=PIN_A2, ENABLE=PIN_A3, BITS=16)
  
 int max_read = 23,max_written = 23;
 int writeBuffer[] = {0x50,0x6F,0x72,0x20,0x65,0x6C,0x20,0x53,0x6C,0x61,0x76,0x65,0x20,0x74,0x6F,0x64,0x6F,0x20,0x62,0x69,0x65,0x6E};
 
#int_SSP 
void i2c_interrupt() {
   int state;
   //Get state 
   int readBuffer[23]; 
   state = i2c_isr_state(); 
   if(state==0) //Address match received with R/W bit clear, perform i2c_read( ) to read the I2C address. 
      i2c_read(); 
 
   else if (state==0x80) //Address match received with R/W bit set; perform i2c_read( ) to read the I2C address, and use i2c_write( ) to pre-load the transmit buffer for the next transaction (next I2C read performed by master will read this byte). 
      i2c_read(2); 
 
   if(state>=0x80){ //Master is waiting for data    
      i2c_write(writeBuffer[state - 0x81]); //Write appropriate byte, based on how many have already been written 
      if ((state-0x80)==max_written){
         //printf("\nFull data sent\r\n");
      }
   } 
   else if(state>0){ //Master has sent data; read. 
      readBuffer[state - 1] = i2c_read(); //LSB first and MSB secound 
      if (state==max_read){ 
         for(int i = 0;i<max_read;i++)
            printf("%c",readBuffer[i]); 
         printf("\r\n");
      } 
   }
} 
 
void main() {
   enable_interrupts(INT_SSP);
   enable_interrupts(GLOBAL);
   printf("SLAVE\r\n");
   while(1){
 
   } 
}

El código ha sido depurado y probado utilizando Proteus 8, así que os aseguro que el código funciona, aunque si vais a copiar y pegarlo, os recomiendo que os hagáis vuestra propia cabecera y solo copiéis las funciones.

Captura de pantalla (8)

Podemos construir un filtro paso banda de muchas maneras, pero la mayoría se basan en el mismo concepto: un filtro paso alto + un filtro paso bajo. Si lo queremos hacer de primer orden, a parte de que no podremos tener amplificación, necesitaremos inductores, que suelen ser grandes, caros o con un poco de complejidad para construir. Sin embargo, si optamos por un filtro paso banda activo de segundo orden, solo necesitaremos un amplificador operacional, condensadores y resistencias. Existen varias tipologías de filtros paso banda activos, pero hay una en particular, con realimentación multiple que tiene la ventaja de conseguir factores de calidad Q relativamente altos (~25). Podéis revisar algunos conceptos básicos en la entrada de Filtros paso bajo.

Esquemático

La configuración del filtro paso banda con realimentación multiple es la siguiente:

multiple-feedback-band-pass-active-filter
Filtro paso banda activo con realimentación multiple

En el esquemático podemos identificar cuatro tensiones nodales \(v_{in}\), \(v_o\).

Análisis

Haciendo el KCL en \(v_x\) obtenemos la ecuación:
\[\left( v_{in} – v_{x} \right) G_1 – \left( v_x – v_n \right) sC_2 – \left(v_x – v_o \right)sC_1 = 0\]
Dado que tenemos realimentación negativa podemos realizar la hipotesis de que \(V_n = V_p = 0\), por lo que la ecuación resultante es:
\[v_{in} G_1 – v_{x} G_1 -v_x sC_2 -v_x sC_1 +v_o sC_1 = 0\]
Del mismo modo, haciendo el KCL en \(v_n\):

\[\left( v_{x} – v_{n} \right) s C_2 – \left( v_{n} – v_{0} \right) G_2 = 0\] \[v_{x} s C_2 + v_{0} G_2 = 0\] \[v_x = \frac{-v_o G_2}{s C_2}\]

Con estas dos ecuaciones tenemos completamente definido el circuito. Podemos escribirlo en forma matricial como:
\[\begin{bmatrix} G_1 + sC_2 + sC_1 & -sC_1\\ sC_2 & G_2 \end{bmatrix}\begin{bmatrix} v_x \\ v_o \end{bmatrix}=\begin{bmatrix} v_{in}G_1\\ 0 \end{bmatrix}\]
Por lo que finalmente podemos calcular la función de transferencia, que tiene la siguiente expresión:
\[H(s) = \frac{v_o}{v_{in}} = \frac{- s \frac{1}{R_1 C_1}}{s^2 + s \left( \frac{1}{C_1 R_2} + \frac{1}{C_2 R_2} \right) + \frac{1}{C_1 C_2 R_1 R_2}}\]
de la que identificando con la expresión genérica podemos extraer los valores de \(f_0\), BW y Q:

\[T(s) = \frac{K s \omega_0}{s^2 +\underbrace{ 2 \zeta \omega_0}_{BW(rad/s)} + \omega_0^2}\] \[f_0(Hz) = \frac{1}{2 \pi \sqrt{R_1 R_2 C_1 C_2}}\] \[BW(Hz) = \frac{1}{2\pi} \left( \frac{C_1 +C_2}{C_1 C_2 R_2} \right)\] \[Q = \frac{f_0}{BW}=\sqrt{\frac{C_1 C_2 R_2}{C_2}} \left( C_1 +C_2 \right)\]

Diseño

Vamos a diseñar un filtro paso banda con una frecuencia central de 800 Hz y un ancho de banda 200 Hz. Para ello, fijamos los valores de los condensadores a un valor razonable \(R_2\). Para ello podemos hacerlo a mano o utilizar MATLAB:

%  Calculo de R2
syms R2;
C1 = 100E-9;
C2 = 100E-9;
eqn = 1/(2*pi) * ((C1+C2)/(C1*C2*R2)) == 200
solR2 = solve(eqn,R2)
	solR2 = 1.5915e+04 
%  Calculo de R1
f0 = 800;
syms R1;
eqn2 = 1/(2*pi * sqrt(R1 * R2 * C1 * C2)) == 800;
solR1 = solve(eqn2, R1)
	solR1 = 248.6796

El valor calculado para \(R_1 = 248.67~\Omega\) y \(R_2 = 15~k\Omega\).

Evaluación

Vamos a comprobar que los valores escogidos son correctos. Para ello, podemos simular con MATLAB la función de transferencia y dibujar su diagrama de Bode o de una manera más rápida y sencilla, utilizar CircuitLab.

Si lo queremos hacer con MATLAB el procedimiento es el siguiente:

%  Calculamos la función de transferencia
T = tf([-1/(R1*C1) 0],[1 (1/(C1*R2)+1/(C2*R2)) 1/(C1*C2*R1*R2)]);
%  Generamos el eje de coordenadas
w = logspace(0,5, 10000);
%  Mostramos el diagrama de Bode de la función de transferencia
bodemag(T,w);
grid;

El resultado es el siguiente:

Diagrama de Bode del filtro paso banda con realimentación multiple
Diagrama de Bode del filtro paso banda con realimentación multiple

(Al hacer la gráfica, tened en cuenta que por defecto el eje de coordenadas está en radianes/s)

Como vemos el pico de resonancia está alrededor de los 800 Hz y tiene un ancho de banda aproximado de 200 Hz. Los valores no se ajustan perfectamente porque he elegido las resistencias comerciales y no son los valores exactos que deberían ser, pero la aproximación es bastante buena.

Si lo queremos simular con CircuitLab, tiene la ventaja de que también tiene en cuenta efectos no previstos por la función de transferencia al contemplar las no idealidades del amplificador de instrumentación que elijamos. El efecto más notorio que se puede observar es que la ganancia no es tan alta como la predicha por la función de transferencia en MATLAB. Podéis acceder al circuito desde CircuitLab

Diagrama de Bode de magnitud y fase con CircuitLab
Magnitud del diagrama de Bode de magnitud con CircuitLab
Diagrama de Bode de fase con CircuitLab
Fase del diagrama de Bode con CircuitLab

LPCXpresso es un IDE de la compañia NXP (antiguamente Philips Semiconductors) basado en Eclipse y que nos permite programar placas de desarrollo de una manera muy sencilla. En la web no hay demasiada información pero sí son muy útiles los ejemplos que hay en las librerías. En mi caso utilizo la placa de desarrolo LPC11U68 con un ARM M0+ (el ARM con menor consumo de los que existen).

M-07710

ARM tiene por defecto un contador llamado SysTick, muy fácil de configurar pero que ofrece poca flexibilidad a la hora de programar. Además el M0+ (desconozco si el resto también) solo cuenta con un contador SysTick. Por este motivo si necesitamos de más de 1 timer en nuestra aplicación deberemos recurrir a los contadores internos del microprocesador. En nuestra caso contamos con 2 contadores de 16 bits y otros dos contandores de 32 bits. El código está comentado y es muy fácil de entender. En caso contrario siempre puedes acudir al datasheet del procesador para entender cómo funciona internamente.

/**
 * @brief	Initialize 32 bits timer for 5 Hz
 * @param	void
 * @return	nothing
 */
void initialize_timers(void){
	uint32_t timerFreq;
        //Inicia el CLK en IOCON para poder modificar las funciones de los pines y en CT32B0 (timer de 32 bits)
        LPC_SYSCTL->SYSAHBCLKCTRL |= (1<<9);
	timerFreq = Chip_Clock_GetSystemClockRate();
	Chip_TIMER_Init(LPC_TIMER32_0);	               //Inicializa el timer CT320 para los 5 Hz
	Chip_TIMER_Init(LPC_TIMER16_0);		       //Inicializa el timer CT160 para los 10 ms
	Chip_TIMER_Reset(LPC_TIMER32_0); 	       //Hace un reset para eliminar configuraciones anteriores
	Chip_TIMER_Reset(LPC_TIMER16_0);	       //Hace un reset para eliminar configuraciones anteriores
	Chip_TIMER_MatchEnableInt(LPC_TIMER32_0, 1);   //Configura el valor al que salta la interrupción
	Chip_TIMER_MatchEnableInt(LPC_TIMER16_0, 1);   //Configura el valor al que salta la interrupción
	//Chip_TIMER_PrescaleSet(LPC_TIMER16_0, PRESCALE_HZ2);  //Preescalado para el timer de 16 bits
        Chip_TIMER_SetMatch(LPC_TIMER32_0, 1, (timerFreq / (TICKRATE_HZ5*2)));	//Configura la duración del timer (0.2 s)
	Chip_TIMER_SetMatch(LPC_TIMER16_0, 1, (timerFreq / (TICKRATE_HZ100*2)));//Configura la duración del timer (10 ms)
	Chip_TIMER_ResetOnMatchEnable(LPC_TIMER32_0, 1);//Configura para que el timer sea cíclico
	Chip_TIMER_StopOnMatchEnable(LPC_TIMER16_0, 1);	//Configura para que el timer no sea cíclico
        Chip_TIMER_Enable(LPC_TIMER32_0);		//Activa el timer
	NVIC_ClearPendingIRQ(TIMER_32_0_IRQn);		//Borra interrupciones pendientes del timer
	NVIC_ClearPendingIRQ(TIMER_16_0_IRQn);		//Borra interrupciones pendientes del timer
	NVIC_EnableIRQ(TIMER_32_0_IRQn);		//Activa la interrupción del timer
	NVIC_EnableIRQ(TIMER_16_0_IRQn);		//Activa la interrupción del timer
}

Un timer, el de 32 bits, saltaría cada 0.2 s (5 Hz) y el otro acabaría a los 100 ms una vez se inicie.

Por razones de mi aplicación el timer de 16 bits no lo activo inmediatamente ni lo configuro para que sea cíclico, si no que es el propio timer de 32 bits el que inicia el de 16.

Para desactivar y activar un timer lo podemos hacer mediante la funcion

Chip_TIMER_Enable(LPC_TIMER16_0);

Por último, para cazar una interrupción deberemos utilizar el siguiente código:

void TIMER32_0_IRQHandler(void){
	if (Chip_TIMER_MatchPending(LPC_TIMER32_0, 1)) {
		Chip_TIMER_ClearMatch(LPC_TIMER32_0, 1);
		//Resto de código...
	}
}
 
void TIMER16_0_IRQHandler(void)
{
	if (Chip_TIMER_MatchPending(LPC_TIMER16_0, 1)) {
		Chip_TIMER_ClearMatch(LPC_TIMER16_0, 1);
                //Resto de código...
	}
}

La precisión para timers de la frecuencia de kHz es muy buena. Como veis, configurar un timer es mucho más complejo que el SysTick, pero con esta receta solo es cuestión de copiar, pegar y editar la frecuencia entre interrupciones.