Monday 25 August 2014

I SPI with my little eye

A number of the key pieces of technology I'm planning to use rely on the SPI protocol. Therefore before I committed too much effort to any particular one of them I wanted to ensure that the ones I'm hoping to use would be compatible. The SPI protocol uses a single bus which all devices are connected to for clock and data transfer and each device has a "slave select" pin that is set low when the device should expect to receive data. One complication is that SPI is only a de facto standard and there are variants in protocol that mean some devices may not cooperate on the same bus.

The Arduino Mega has both hardware support for SPI, allowing a very fast data rate, and a fully featured SPI library. The devices I want to use are a TFT LCD screen, this has an SD card reader built-in which is also controlled via SPI. I also wanted to use a number of digital potentiometers in place of conventional pots. The proof of concept stage was to convince myself I knew how to control each of these devices and that they would work well together.

The TFT screen was the first thing I tried out. Like all Adafruit products it's well supported and in this case is driven by their GFX library which is used for a number of different displays. This covers a number of basic graphics operations and once one gets used to the coordinate system (0,0 at top left) then it's pretty straight-forward to use. One thing I found almost right away was that the speed of drawing is relatively slow and it's not possible to use full screen redraw to animate without flicker. This was a problem since I want to be able to show dials of some sort to represent internal settings. There's also apparently no "draw filled arc" type function so it's not possible to draw a pie-type dial. However a linear bar "slider" is easier to do anyway and fairly intuitive. I discovered by some trial and error that it's possible to draw a fairly responsive slider bar by careful redrawing of only the parts that change. The code looks like:

  double frac = (double)val/maxval;
  int width = (int)( frac * (RECT_WIDTH-2));

  if( val >= last_val ) {
    // Bar grows - draw it in black:
    tft.fillRect( RECT_X + 1, RECT_Y+1, width, RECT_HEIGHT-2,0x0 );
  } 
  else {
    // Bar shrinks - draw the exposed background area in white:
    tft.fillRect( RECT_X+width, RECT_Y+1, RECT_WIDTH-width-2, RECT_HEIGHT-2,
    0xFFFF );
  }

  last_val = val;

In this way there's minimal redrawing required and the performance, while not perfectly smooth, is acceptable. I also experimented with text, showing the currently playing MIDI note using code like the following in the noteOn() handler for the MIDI library:

  static char* note_string[] = { 
    "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"   };
  int octave = (pitch / 12) - 1;
  int note_index = (pitch % 12);

  tft.fillRect(40, 200, 80, 40, 0xFFFF);
  tft.setTextSize( 4 );
  tft.setTextColor( 0x0);
  tft.setCursor( 40,200);
  tft.print(note_string[note_index]);
  tft.print(octave);


This gives a display of the currently playing note as shown in the video below.

Once the screen was working it was on to test if the SD card worked. The simplest way to test seemed to load an image from the card and display it on the TFT. There's an example supplied by Adafruit that does just this. I created my own "splash screen" image and loaded it from the card, it worked just fine. The image is relatively slow to load but it's fine for this one-off usage. So it seems the SD card reading is working OK, for now I'll assume writing will also be fine.

The final step was to try out some digital pots. My first effort was with the MCP4161 Digital Pot.  There was some suggestion these are Arduino compatible but when I looked more closely at the code it seemed the SPI requirements may not play well with the other two devices as it claims to only work with a non-standard SPI library. Indeed I couldn't seem to send them any data. To be honest I didn't try that hard because I already had an alternative to hand - the Analog Devices AD8402 digital pot. Using this tutorial it was pretty easy to figure out how to use them, and this forum post was a reminder that the RS (and SHDN) pins need to be tied to VDD. In the end it was all pretty easy, I first got it working first with a simple sketch from the tutorial that just changed the resistance, and then integrated into my MegaMiniSynth code and hooked up to the rotary encoder and a "slider" display on the TFT. I'd been using a manual pot to control the PWM for the square wave shaper, a good test was if the digital pot could take its place. Again it worked first time, set up as a voltage divider it worked fine. I had been expecting to have to use the logic encoder feature of my DSO to debug the SPI protocol but fortunately that was not necessary. I'm sure there'll be some problems downstream it will come in handy for.

Here's a rather shaky video that illustrates all these working together. My breadboard is getting pretty full - it's time to start thinking about moving some of this stuff to a PCB!






No comments:

Post a Comment