Introduction: Make Any Sensor Out of an FPGA
Most makers tried building at least once in their life a thermometer, maybe the one they have at home is not smart enough, or maybe they think that they can build the next NEST. Nevertheless, at some point they had a microcontroller with their state-of-the-art software hooked up to a temperature sensor (and maybe other sensors: pressure, light). Until now everything perfect, the software is running and the sensor is sensing. Let's test it!
Hmmmm... maybe he should heat up the sensor using a hair dryer and cool it down using ice, it works for a time. But it doesn't seem professional, the sensor changes values too quickly if you heat it up, it doesn't heat up more than a couple of degrees. The project is a bust! But the algorithm is new, takes into account a lot of factors, what a shame that he got stuck at this stupidly minor thing.
My solution is this: make an FPGA act as a sensor with values streamed from a PC (or stored in memory, or created ad-hoc inside the FPGA). So for your precious MCU the FPGA looks like a sensor, but not any sensor: whichever sensor you like. Maybe you decide that you need more resolution or faster response time than expected, you have to change the sensor. Order it online, it will arrive in a couple of days, of a couple of months, who knows. Respin your PCB or order a module with the new sensor. Or ... a couple of clicks and the FPGA is configured as your brand new sensor and it can emulate the exact internal configuration.
At the moment of writing this, the FPGA could act as a LM75 with temperature data stored in BRAM (on the FPGA).
Step 1: The MCU
My MCU of choice is a LPC4337 on an LPCXpresso. On top of it I have a shield (LPC General Purpose Shield) with a display and a real LM75 sensor. LPC4337 is a ARM Cortex M4 running at 200MHz and a smaller Cortex M0 (not used here). The real sensor is connected to I2C1 peripheral and our virtual one will be connected to I2C0. The source is available on my GitHub.
How to build it? Download LPCXpresso IDE together with LPCOpen library. Import that library into the IDE and also open the project from GitHub. Everything should be configured and you can click on "Debug " in the bottom left corner.
The entire project is based on one of the NXP's examples (as to show that my project simulates a real sensor and doesn't need special code on the MCU side). In the main file (called iox_sensor.cpp) lies this code:
#define SENSORS_ON_SHIELD #if defined(SENSORS_ON_SHIELD) #define SHIELD_I2C I2C1 #elif defined(SENSORS_ON_FPGA) #define SHIELD_I2C I2C0 #endif
By changing SENSOR_ON_SHIELD and SENSOR_OR_FPGA the user is able to switch at compile time to which sensor to talk, the real one or the virtual one, as they are on different I2C pins.
Step 2: The FPGA
My FPGA board of choice is an Artix 7 made by Digilent, having a Xilinx Arty 7. Two of the PMod connectors are used, one for debuging and one for the real payload, the connection with the MCU board.
Again, the source code for the FPGA is available on my GitHub (fpgaSide folder).
How to build it? Download, buy or open Xilinx Vivado IDE. Import the project files from GitHub. One of the files (content.coe) is the temperature data in raw format that is going to be streamed to the fake sensor. There is also an Excel file with the same name that helps with converting human readable temperature data to raw LM75 data. I'm planning on changing this to an automated process with a piece of software written in Java but until then this solution works. Synthesis and Implemantation should take a while, take this into consideration.
Step 3: How Does It Work?
As I said, for the MCU, the FPGA looks like a sensor, more exactly an I2C sensor. The output of the I2C peripheral is connected to the FPGA's input. Inside the FPGA there are 3 main components: - I2C Controller- I2C Device- DataThe I2C Controller receives I2C data from the FPGA's pins and sends them to the rest of the FPGA and does the same in reverse order. It maintains an internal state machine for the I2C Protocol (by the way, here is the documentation for it). What does this component send to the I2C Device? The currently received byte, position of that byte in the current communication and whether the MCU is writing to or reading from the FPGA. The I2C Device receives the sent bytes and updates the simulated internal structure of the sensor. It might just update the register pointer or request new data from the data source. The Data component streams new data points. Currently it is just a ROM memory whose address is incremented (approximately) twice per second.
What is my end goal? It is shown in the second picture. That is : make it possible for more I2C devices( sensors and other) to be simulateble at the same time inside the FPGA. The data at the backend of the sensor to be cached in the FPGA and streamed from PC via USB or Ethernet. Support more advanced sensors and other I2C Devices (memory, LED drivers etc).
Step 4: Putting It All Together
Now is the time to connect everything togheter. Theoretically, it is simple: the mcu board has a PMod connector (I2C0 & SSP0 (can work like SPI)). The Artix board has 4 PMod connectors that can be used however you want. I choose connector D to talk to the MCU and connector B to connect to my Logic Analyser.
Warning
You can't connect the two boards together just like that. Why? PMod was build to ease the connection of a Master/Host board (that gives power) to a Slave/Sensor board (that receives power). But in this project both boards give power and if you connect the 3.3V output from one board to the 3.3V output of the other board bad things could happen. But they might not and you might just change the parameters of the power rails of the FPGA (they are very carefully designed). So don't take this risk and move the connector one pin to the left (and also flip the FPGA board) as seen in the above pictures. Here is the PMod specification, you cand study it, what I did in short words is to not connect the VCCs of the two boards.