En proyectos con FPGA o microcontroladores se utiliza para:
- Generar ondas senoidales, cuadradas o triangulares.
- Producir señales de audio.
- Controlar dispositivos analógicos.
- Ajustar voltajes de referencia.
- Crear generadores de funciones.
Placa roja
La dirección I²C se configura mediante un puente de soldadura, por lo que requiere una modificación física de la placa.
Placa azul
Permite modificar la dirección I²C mediante el pin A0, facilitando la selección de la dirección desde el hardware.
Para trabajar con una FPGA y comprender correctamente el funcionamiento del MCP4725 , es fundamental consultar su hoja de datos y utilizar un analizador lógico. Estas herramientas permiten estudiar detalladamente la comunicación I²C, verificar las tramas enviadas y conocer el comportamiento real del dispositivo.
Lamentablemente, existen pocos recursos prácticos que expliquen cómo trabajar con este tipo de dispositivos utilizando una FPGA, especialmente dentro de la comunidad latinoamericana. Por ello, desarrollaremos el proceso paso a paso, desde el análisis del protocolo hasta su implementación en hardware.
En la página 1 de la hoja de datos del MCP4725 se indica que este convertidor DAC utiliza el protocolo I²C y puede trabajar con tres velocidades de comunicación: Standard-mode a 100 kHz, Fast-mode a 400 kHz y High-Speed mode hasta 3,4 MHz.
En el caso de los microcontroladores STM32, normalmente podemos utilizar los modos Standard y Fast. Para trabajar en High-Speed mode es necesario comprobar que el modelo de STM32 y su periférico I²C sean compatibles con esta velocidad, ya que no todos los dispositivos la soportan.
Con una FPGA es posible implementar un controlador I²C personalizado y alcanzar velocidades mayores, incluido el modo High-Speed, siempre que se respeten los tiempos y requisitos eléctricos establecidos en la hoja de datos. Además, nuestras placas FPGA incorporan bloques PLL, que permiten generar frecuencias de reloj adecuadas para implementar y controlar con precisión la comunicación con el MCP4725.
Dirección I²C del MCP4725
Para comenzar, primero debemos identificar la dirección del periférico. Según la hoja de datos del MCP4725, existen dos formas de modificar la dirección I²C del dispositivo, dependiendo del módulo utilizado.
La mayoría de los módulos MCP4725 disponibles comercialmente tienen, por defecto, la dirección 0x60. Sin embargo, es posible cambiarla a 0x61 mediante el pin ADDR o mediante un puente de soldadura, según el diseño de la placa.
| Estado del pin ADDR (A0) | Dirección I²C | Dirección binaria |
|---|---|---|
| GND (0) | 0x60 | 1100000 |
| VDD (1) | 0x61 | 1100001 |
Como se observa en la Figura 7-1 de la hoja de datos, la dirección del MCP4725 está formada por un código fijo del dispositivo y por los bits de dirección A2, A1 y A0.
Los bits A2 y A1 son configurados de fábrica (hard-wired) y no pueden modificarse por el usuario. Microchip fabrica distintas versiones del MCP4725 con diferentes combinaciones de estos bits para disponer de varias direcciones I²C.
En los módulos comerciales más utilizados, los bits A2 = 0 y A1 = 0, por lo que únicamente es posible modificar el bit A0 mediante el pin correspondiente o mediante un puente de soldadura.
Debido a esta configuración, la mayoría de placas MCP4725 disponibles en el mercado utilizan las direcciones 0x60 y 0x61, permitiendo conectar más de un dispositivo en el mismo bus I²C.
- 0x60: A2 = 0, A1 = 0 y A0 = 0.
- 0x61: A2 = 0, A1 = 0 y A0 = 1.
Formato de configuración del MCP4725
Lo segundo que debemos tener en cuenta al trabajar con el MCP4725 es el formato de configuración utilizado para enviar los datos mediante el protocolo I²C. Como se observa en la Figura 6-2, la transmisión está compuesta por cuatro bytes: la dirección del dispositivo, el byte de configuración y dos bytes correspondientes al valor digital del DAC.
El segundo byte es especialmente importante, ya que define el tipo de comando que ejecutará el dispositivo mediante los bits C2, C1 y C0. Además, los bits PD1 y PD0 permiten seleccionar el modo de operación o el modo de bajo consumo del MCP4725.
Para aplicaciones típicas con FPGA o microcontroladores, normalmente se utiliza la configuración PD1 = 0 y PD0 = 0, lo que mantiene al dispositivo en modo normal y permite generar la señal analógica correspondiente.
Finalmente, los bits D11-D0 representan el dato digital de 12 bits que será convertido a un voltaje analógico en la salida VOUT.
Bits de comando C2, C1 y C0 del MCP4725
En la página 23 del datasheet se explica que los bits C2, C1 y C0 se utilizan para definir el tipo de comando de escritura que ejecutará el MCP4725.
Según la combinación de estos tres bits, el dispositivo puede actualizar únicamente el registro del DAC o actualizar el registro y guardar también la configuración en la memoria EEPROM.
| C2 | C1 | C0 | Comando | Función |
|---|---|---|---|---|
| 0 | 0 | X | Fast Mode | Actualiza el registro del DAC, incluyendo los bits de modo de bajo consumo PD1 y PD0 y el dato digital de 12 bits. La memoria EEPROM no es modificada. |
| 0 | 1 | 0 | Write DAC Register | Carga los bits de configuración y el dato digital de 12 bits en el registro del DAC. En el MCP4725, este comando realiza la misma función que el comando Fast Mode, pero utiliza un formato de transmisión de cuatro bytes. |
| 0 | 1 | 1 | Write DAC Register and EEPROM | Carga los bits de configuración y el dato digital de 12 bits en el registro del DAC y, además, los almacena en la memoria EEPROM. Esto permite recuperar el valor después de apagar y volver a encender el dispositivo. |
| 1 | 0 | 0 | Reservado | Comando reservado para uso futuro. El MCP4725 ignora este comando. |
| 1 | 0 | 1 | Reservado | Comando reservado para uso futuro. El MCP4725 ignora este comando. |
| 1 | 1 | 0 | Reservado | Comando reservado para uso futuro. El MCP4725 ignora este comando. |
| 1 | 1 | 1 | Reservado | Comando reservado para uso futuro. El MCP4725 ignora este comando. |
La letra X significa “Don’t Care”, es decir, que el valor de ese bit puede ser 0 o 1 sin afectar la ejecución del comando. En el modo rápido, el bit C0 no se utiliza.
Para generar una señal senoidal o actualizar continuamente la salida del DAC, se recomienda utilizar el comando Fast Mode con C2 = 0, C1 = 0 y C0 = X, porque actualiza el registro del DAC sin escribir repetidamente en la EEPROM.
La escritura en EEPROM debe utilizarse solamente cuando se desea conservar el valor después de apagar el dispositivo, debido a que esta operación es más lenta y la memoria EEPROM tiene un número limitado de ciclos de escritura.
Bits PD1 y PD0 del MCP4725
Los bits PD1 y PD0 se utilizan para configurar el modo de operación del MCP4725. Dependiendo de su valor, el dispositivo puede funcionar normalmente o entrar en modo de ahorro de energía.
| PD1 | PD0 | Modo | Función |
|---|---|---|---|
| 0 | 0 | Modo normal | El DAC funciona normalmente y genera el voltaje de salida. |
| 0 | 1 | Power-Down 1 | La salida VOUT se conecta internamente a una resistencia de 1 kΩ hacia tierra. |
| 1 | 0 | Power-Down 2 | La salida VOUT se conecta internamente a una resistencia de 100 kΩ hacia tierra. |
| 1 | 1 | Power-Down 3 | La salida VOUT se conecta internamente a una resistencia de 500 kΩ hacia tierra. |
Durante el modo Power-Down, la mayoría de los circuitos internos se deshabilitan para reducir el consumo de energía, aunque la interfaz I²C continúa activa.
Primera configuración: modo normal con Write DAC Register
El primer modo que vamos a utilizar será el modo normal con escritura directa, conocido como Write DAC Register. Para ello, debemos tener en cuenta la trama mostrada en la Figura 6-2 de la hoja de datos.
En este ejemplo, se utilizará la dirección A0 = 0, por lo que la dirección I²C del dispositivo será 0x60. De esta manera, la trama de comunicación tendrá el siguiente formato:
En el primer byte se ha colocado el valor 0x60, el cual corresponde a la dirección I²C del MCP4725 cuando el pin A0 = 0. Además, el bit R/W = 0 indica que se realizará una operación de escritura (Write).
El segundo byte tiene el valor 0x40, correspondiente al comando Write DAC Register. En este caso, se está trabajando con la configuración C2 = 0, C1 = 1 y C0 = 0.
Asimismo, los bits PD1 y PD0 se han configurado en cero, permitiendo que el dispositivo opere en modo normal. Finalmente, los bits marcados como Don't Care han sido rellenados con ceros.
Los bytes tercero y cuarto corresponden al dato digital del DAC. En el MCP4725, el valor de entrada está formado por 12 bits, por lo que es posible representar valores comprendidos entre 0 y 4095, los cuales serán convertidos en un voltaje analógico a la salida VOUT.
Además, puede observarse que los cuatro últimos bits del cuarto byte siempre toman el valor cero en esta configuración. Estos bits no son utilizados por el dispositivo y corresponden a bits Don't Care, tal como se muestra en la Figura 6-2 de la hoja de datos.
Descarga del maestro I²C y plantilla en SystemVerilog
Teniendo esto en cuenta, Cápsula Electrónica entregará el maestro I²C y la plantilla de la máquina de estados para controlar el DAC MCP4725 en SystemVerilog. Puedes descargar los archivos haciendo clic aquí.
Este primer ejemplo se realizará con una placa orientada a principiantes, la FPGA INIT, basada en el dispositivo 10M08SCE144C8G.
Sin embargo, el proyecto también puede migrarse a otras FPGAs, como StratoSky. Para ello, se debe cambiar el dispositivo seleccionado desde la sección Assignments > Device de Quartus Prime y realizar nuevamente la asignación de pines correspondiente a la placa utilizada.
En la plantilla i2c_master.sv es posible configurar la velocidad de comunicación modificando el parámetro BUS_CLK. De esta manera, el mismo maestro I²C puede trabajar en los distintos modos soportados por el MCP4725:
- Standard Mode: 100 kHz.
- Fast Mode: 400 kHz.
- High-Speed Mode: 3.4 MHz.
Para ello, basta con modificar el valor del parámetro BUS_CLK, tal como se muestra a continuación:
parameter int BUS_CLK = 100_000; // Standard Mode
parameter int BUS_CLK = 400_000; // Fast Mode
parameter int BUS_CLK = 3_400_000; // High-Speed Mode
En la plantilla MCP4725.sv debemos asegurarnos de que la señal addr coincida con la dirección configurada mediante el pin A0 del dispositivo. Por ello, es importante verificar el estado de dicho pin en el módulo MCP4725 utilizado.
En nuestro caso, utilizaremos el módulo MCP4725 de color rojo, el cual posee el pin A0 desoldado. Por lo tanto, el pin se encuentra conectado a tierra, es decir, A0 = '0' o GND.
Debido a esta configuración, la dirección I²C del dispositivo será 0x60, por lo que en la plantilla se debe configurar:
localparam logic [6:0] addr = 7'h60; Máquina de estados para controlar el MCP4725
Lo siguiente que debemos tener en cuenta es la máquina de estados. Su funcionamiento es relativamente sencillo, ya que el módulo i2c_master se encarga de enviar automáticamente la dirección I²C configurada mediante la señal addr.
Primero ingresamos al estado IDLE, en el cual se implementa un pequeño tiempo de espera después de la energización del sistema. Este tiempo permite que el MCP4725 y los demás componentes se estabilicen antes de iniciar la comunicación.
En nuestro ejemplo se utiliza un tiempo de espera de 100 ms. Debido a que el reloj de la FPGA trabaja a 50 MHz, se necesitan 5 000 000 ciclos de reloj:
localparam int COUNTER_5M = 5_000_000; // 100 ms
localparam int COUNTER_500K = 500_000;
El estado IDLE se ejecuta solamente una vez después del reinicio. Cuando finaliza el tiempo de espera, la máquina de estados pasa al estado MCP4725_W, encargado de transmitir los bytes de configuración y los datos del DAC.
En el estado MCP4725_W se envían el segundo, tercer y cuarto byte de la trama mostrada en la Figura 6-2 de la hoja de datos. El primer byte, correspondiente a la dirección I²C 0x60, es enviado automáticamente por el módulo i2c_master.
La variable busy_count permite identificar qué byte debe enviarse en cada etapa. El contador aumenta cada vez que la señal busy cambia de cero a uno, indicando que el maestro I²C ha comenzado una nueva transmisión.
always_ff @(posedge clk or negedge reset) begin
if (!reset) begin
state <= IDLE;
en <= 1'b0;
rw <= 1'b0;
data_wr <= 8'h00;
counter <= 0;
busy_count <= 0;
busy_prev <= 1'b0;
// Inicializar los registros en cero
end else begin
busy_prev <= busy;
unique case (state)
IDLE: begin
en <= 1'b0;
if (counter < COUNTER_5M) begin
counter <= counter + 1'b1;
end else begin
counter <= 0;
busy_count <= 0;
state <= MCP4725_W;
end
end
MCP4725_W: begin
if (!busy_prev && busy)
busy_count <= busy_count + 1'b1;
unique case (busy_count)
0: begin
en <= 1'b1;
rw <= 1'b0;
data_wr <= 8'h40;
// Segundo byte:
// Write DAC Register
// C2 = 0, C1 = 1, C0 = 0
// PD1 = 0, PD0 = 0
end
1: begin
en <= 1'b1;
rw <= 1'b0;
data_wr <= onda[11:4];
// Tercer byte:
// bits D11 hasta D4
end
2: begin
en <= 1'b1;
rw <= 1'b0;
data_wr <= {onda[3:0], 4'b0000};
// Cuarto byte:
// bits D3 hasta D0
// seguidos por cuatro bits Don't Care
end
3: begin
en <= 1'b0;
if (!busy) begin
busy_count <= 0;
state <= MCP4725_W;
end
end
default: begin
en <= 1'b0;
end
endcase
end
default: begin
state <= IDLE;
end
endcase
end
end
En la primera transmisión se envía el valor 0x40, correspondiente al comando Write DAC Register en modo normal. Posteriormente se envían los 12 bits de la señal onda.
El tercer byte contiene los bits D11-D4, mientras que el cuarto byte contiene los bits D3-D0 en su parte superior. Los cuatro bits restantes se rellenan con ceros, ya que son bits Don't Care.
Como se representa en la figura inferior, el proyecto ingresa una sola vez al estado IDLE, donde espera aproximadamente 100 ms para permitir la correcta estabilización del sistema después de la energización.
Una vez finalizado este tiempo de espera, la máquina de estados pasa al estado MCP4725_W. En este estado se transmiten el segundo, tercer y cuarto byte de la trama I²C, correspondientes a la configuración del dispositivo y al dato digital de 12 bits del DAC.
Después de completar la transmisión, la máquina de estados permanece en MCP4725_W y repite continuamente este estado en forma de bucle, permitiendo actualizar de manera constante el voltaje de salida del MCP4725.
Interfaz externa del módulo MCP4725
Teniendo bien comprendido el funcionamiento del MCP4725, ahora debemos analizar la interfaz externa del módulo, encargada de conectar la FPGA con el periférico DAC mediante el protocolo I²C.
Como puede observarse en el siguiente fragmento de código, el módulo dispone de las señales de reloj y reinicio, además de las líneas SDA y SCL, las cuales permiten la comunicación con el MCP4725.
module MCP4725(
input logic clk,
input logic reset,
inout wire sda,
inout wire scl,
input logic [15:0] onda,
output logic ack
);
La señal más importante del módulo es onda, ya que a través de ella se entregará la resolución digital que será convertida posteriormente en un voltaje analógico por el DAC.
Dependiendo del voltaje de referencia utilizado y de la aplicación desarrollada, esta señal contendrá los datos correspondientes a la amplitud de la señal de salida que se desea generar.
Finalmente, la señal ack permite verificar que la comunicación I²C se ha realizado correctamente entre la FPGA y el periférico MCP4725.
Cálculo de la resolución del MCP4725
El cálculo del valor digital que debemos enviar al MCP4725 se encuentra en la página 19 de la hoja de datos, mediante la siguiente ecuación:
Despejando el código digital Dn, obtenemos:
En el MCP4725, el voltaje de referencia es igual al voltaje de alimentación, es decir, VREF = VDD. El resultado debe encontrarse entre 0 y 4095.
Calculadora de resolución
Ingresa el voltaje de alimentación del MCP4725 y el voltaje de salida que deseas obtener.
Primera prueba de funcionamiento del MCP4725
Para realizar una primera prueba, el archivo MCP4725.sv se convertirá en un bloque de hardware dentro del proyecto. Posteriormente, este bloque será conectado a un módulo superior encargado de entregarle el valor digital correspondiente al voltaje de salida deseado.
La resolución será enviada mediante una señal de 16 bits. Sin embargo, no es necesario preocuparse por los cuatro bits adicionales, ya que el módulo MCP4725.sv utilizará únicamente los 12 bits menos significativos para realizar la escritura del dato en el DAC.
Por lo tanto, el valor recibido por la señal onda[15:0] será reducido internamente a onda[11:0], debido a que el MCP4725 posee una resolución de 12 bits y admite valores digitales comprendidos entre 0 y 4095.
input logic [15:0] onda;
// El MCP4725 utiliza únicamente los 12 bits menos significativos
logic [11:0] dato_dac;
assign dato_dac = onda[11:0];
Módulo de salida constante para el MCP4725
Para realizar la primera prueba, se creará el archivo salida_vout.sv. Este módulo entregará un valor digital constante de 16 bits al módulo MCP4725.sv.
En este ejemplo se utilizará el valor decimal 1241, equivalente a 0x04D9 en hexadecimal. Este valor corresponde aproximadamente a una salida de 1 V cuando el MCP4725 trabaja con un voltaje de referencia de 3.3 V.
module salida_vout (
output logic [15:0] onda
);
// Valor constante entregado al MCP4725
assign onda = 16'd1228;
endmodule
La señal onda se conecta posteriormente a la entrada del módulo MCP4725.sv. Aunque la señal tiene 16 bits, el módulo del DAC utilizará únicamente los 12 bits menos significativos, debido a que el MCP4725 posee una resolución de 12 bits.
Conexión de los bloques salida_vout y MCP4725
Para este último módulo también crearemos su respectivo bloque de hardware. Posteriormente, el bloque salida_vout será conectado con el bloque MCP4725, tal como se muestra en la figura.
La salida onda[15:0] del bloque salida_vout se conecta directamente a la entrada onda[15:0] del bloque MCP4725. De esta manera, el valor constante generado será enviado al módulo encargado de realizar la comunicación I²C con el DAC.
También podemos adelantarnos y generar de una vez los pines de entrada y salida del diseño. Para ello, se crean los pines clk y reset como entradas, mientras que las señales sda y scl se configuran como pines bidireccionales.
En esta primera prueba, la señal ack se dejará sin conexión, debido a que por el momento no será utilizada para verificar el estado de la comunicación I²C.
Compilación y asignación de pines
Como hemos ido advirtiendo a lo largo del desarrollo, la conexión del MCP4725 es bastante sencilla. Una vez creados los bloques de hardware y realizadas todas las conexiones necesarias, el siguiente paso consiste en compilar el proyecto y asignar los pines correspondientes en la FPGA.
En nuestro caso, se han configurado los pines de entrada clk y reset, mientras que las señales scl y sda han sido configuradas como pines bidireccionales para la comunicación mediante el protocolo I²C.
Una vez finalizada la compilación y la asignación de pines, únicamente queda verificar el funcionamiento del sistema midiendo el voltaje de salida en el pin VOUT del módulo MCP4725 mediante un multímetro.
Finalmente, es importante tener en cuenta que la señal reset debe permanecer en estado alto para permitir el correcto funcionamiento del diseño. No olvides este detalle antes de realizar las mediciones.
Prueba experimental: generación de 1 V
Finalmente, realizamos una prueba experimental utilizando la placa FPGA INIT, el módulo MCP4725 y un multímetro digital para verificar el correcto funcionamiento del sistema.
Para esta prueba, configuramos el valor digital del DAC con la resolución 1241, equivalente a 0x04D9 en hexadecimal. Según la ecuación presentada anteriormente, el voltaje esperado en la salida del MCP4725 debía ser aproximadamente 1.0 V.
Como se puede observar en la imagen, el multímetro registra un valor de 1.004 V, lo que representa un error muy pequeño con respecto al valor teórico calculado.
Resulta impresionante comprobar cómo, a partir de unos pocos bytes enviados mediante el protocolo I²C, somos capaces de generar un voltaje analógico con una precisión tan elevada utilizando una FPGA y el DAC MCP4725.
Hazlo tú mismo
Ahora que todo ha funcionado correctamente, es momento de que realices tus propias pruebas. Modifica el segundo byte de configuración y observa cómo cambia el comportamiento del MCP4725.
Puedes comenzar activando el modo de escritura en EEPROM mediante la configuración C2 = 0, C1 = 1 y C0 = 1. De esta manera, el valor del DAC quedará almacenado y podrá recuperarse después de apagar y volver a encender el dispositivo.
También puedes intentar trabajar con el modo High-Speed de 3.4 MHz. Sin embargo, este modo tiene un detalle importante indicado en la hoja de datos: no basta con modificar únicamente el parámetro BUS_CLK. El maestro I²C debe enviar previamente la secuencia especial de entrada a HS-mode.
Experimenta con las diferentes configuraciones, analiza las tramas con un analizador lógico y comprueba cómo responde el periférico.
Desarrollado por Alexis Castillo
Senior FPGA Developer · Cápsula Electrónica Aerospace
