domingo, 16 de febrero de 2014

Arduino + DAC LTC1661 (comunicación SPI)

Algunos proyectos electrónicos requieren salidas de señales analógicas "ideales" como una señal sinusoidal o una señal de control para determinado proceso en el cual el PWM no es adecuado. Si se quiere utilizar Arduino, ya sea como tarjeta de adquisición de datos o como el cerebro de todo el proceso, es necesario utilizar un DAC o fastidiarse con un arreglo de amplificadores operacionales. Para este ejemplo estoy usando un convertidor integrado de 10-bits y 2 salidas LTC1661 de Linear Technologies. Tal vez no sea el modelo de DAC más común en muchos países pero utiliza el protocolo de comunicación SPI, por lo que el código mostrado puede ser adaptado sin mayor problema a otros modelos.

Lo primero que se debe hacer a la hora de escribir un código de comunicación SPI es revisar la hoja de especificaciones del dispositivo con el que vamos hacer la comunicación y buscar el diagrama de tiempos y la tabla de direcciones. Para el LTC1661 tenemos este diagrama:


Recordemos que aún con la librería SPI.h de Arduino sólo podemos enviar paquetes de 8 bits a la vez. Cuando visualizamos esta secuencia nos damos una buena idea de lo que tenemos que hacer en el código. En este caso habrá que dividir la "palabra" de 16 bits que deseamos enviar en dos paquetes de 8 bits cada uno. Así, la secuencia binaria de un 512 (en binario 1000000000) que se traducirá como la mitad del voltaje de referencia para la salida A es:

 1001100000000000
(dirección, dato y bits "don't care")

y debe ser divido como:

10011000  y 00000000

 Para esto tomaremos una variable que llamaremos "valor" que representará a un dato de 10 bits como valor de voltaje. Es importante recordar que esta variable se declara como entero sin signo que en Arduino es un dato de 16 bits. Para obtener el primer byte será necesario utilizar una variable de tipo char (que es de 8 bits) que llamaremos "temp" y que igualaremos a la variable "valor" después de hacer un corrimiento de 6 bits a la derecha de manera que el entero sin signo 0000001000000000 se convierta en 0000000000001000 y finalmente en el char 00001000. Para agregar la dirección 1001 habrá que sumar simplemente 1001000 a nuestra variable "temp". Toda esta operación en lenguaje Arduino (y también en C) queda:

temp = (valor >> 6) & 0x0F;  // Corrimiento a la derecha
temp = 0x90 + temp; // dirección y parte del dato

Teniendo este numero cargado en "char" queda listo para ser enviado con la fusión SPI.transfer(). Para el segundo byte lo único que falta hacer es aplicar un corrimiento de dos bits a la izquierda e igualar a "temp" y volver a enviar con SPI.transfer():

temp = valor << 2;

Como había escrito al principio, es posible que los dispositivos que estén utilizando trabajen con datos de 8 o 12 bits y no 10 como en este ejemplo. Pero en esos casos lo único que habrá que hacer es ajustar el valor de los corrimientos. Si es la primera vez que conectas un dispositivo SPI a un Arduino es importante que sepas cuales son los pines para este tipo de comunicación.

Código completo:

#include <SPI.h>;
#include < math.h>;
int ss = 10;
float t;
unsigned valor = 0;

void setup(){
  
pinMode(ss, OUTPUT);
SPI.begin(); 

}

void enviar( unsigned int valor_dac){
char temp;

digitalWrite(ss, LOW); // CS o SS en bajo

temp = (valor_dac << 6) & 0x0F;  // Corrimiento a la derecha
temp = 0x90 + temp; // dirección y parte del dato
SPI.transfer(temp); // envio del primer byte
temp = valor_dac << 2; // Corriemiento a la izquierda 
SPI.transfer(temp); // envío del segundo byte

digitalWrite(ss,HIGH); // CS o SS en alto
}

void loop(){
  
for (int i; i < 80; i++){ // función de prueba
  t = t + 0.078;
  valor = (unsigned int) 511*sin(t)+511;
  enviar(valor);
  delay(1);
  if (t > 2*3.14) t = 0;
}

}

Aquí pueden encontrar el código para comunicar un PIC18F2550 con un DAC LTC1660.

2 comentarios:

Francisco Jimenez dijo...

Muy interesante. Un proceso similar se aplicaría a una conexión ADC+Arduino Due por SPI?

Luyten dijo...

Nunca he hecho recepción de datos por SPI pero debe ser más sencillo porque la función debe combinar los dos bytes por si misma.