ADC - analog-to-digital converter
IIR - infinite impulse response
LPF - low pass filter
SPI - serial peripheral interface
TFT - thin film transistor
LCD - liquid crystal display
GUI - graphical user interface
My first problem: how to get two Arduino’s to chat with each other. My work is based upon (copied from) a thorough and accurate forum post. The motivation to use SPI came from wanting to learn more about it. In researching the TVout library, I found someone generated an NTSC signal with the hardware based SPI master signal. Given my current fascination with resurrecting my Atari days, I had an idea of trying to recreate a simplified ANTIC-like system on an Arduino in black and white. SPI seems the way to go there. But, back to my project.
The system here uses a Nano to read an analog voltage and apply a low pass filter. The Mega polls the Nano for the LPF output over SPI and displays the value on a bar graph. I offloaded the ADC work to the Nano thinking I would eventually use an interrupt-driven ADC to get a constant sampling rate. Because the TFT polls various analog channels to read the touchscreen, it seemed best to just off-load the other ADC work to a different microcontroller. The TFT fits on the Mega and leaves the header with the SPI interface unobstructed.
The SPI connections on the Nano are located on the ICSP header or pins D11-D13 and Slave Select is D10. The connections on the Mega are on the bottom header pins D50-D53. Connections are one-to-one. That is MISO:MISO, MOSI:MOSI, SCK:SCK, and SS:SS. The Master-In-Slave-Out MISO signal transfers data from the Nano to the Mega because the Nano is set up as SPI Slave and the Mega as Master. Vice-versa for MOSI. Serial Clock (SCK) sends a 2 MHz clock from the Mega to the Nano. And Slave Select (SS) signals the Nano to listen on SCK & MOSI for data and to send data on MISO synchronized to SCK. I use 2 Mbps because single-ended communications over long wires can’t go all that fast.
To low-pass filter the data, I use an exponential moving average filter implemented as an infinite impulse response (IIR) digital filter. This is very efficient because it requires only a weighted average of the current sample with the previous output. For a C++ implementation, I followed the integer implementation here. I had to add some additional 64-bit integer type casting to the coefficients in the filter equation to get it to operate correctly.. I also changed how the filter coefficient is implemented as a 16-bit unsigned integer (0-65535). The original author designed it to represent floats ranging from 1/65535 to 1 and I changed it to the range 0 to 65535/65536. Just personal preference. You can see the effect of the filter in the video - I adjust the potentiometer abruptly and it takes some time for the bargraph to totally respond.