MCP3204 ADC SPI and Wiz5500 Ethernet SPI not getting along

Status
Not open for further replies.

edsuom

New member
I have built a board with a Teensy 3.2, an Adafruit Ethernet Featherwing, and a connector for external connection of SPI lines. Those lines are hooked up with short twisted-pair wires from a Cat5 cable to an MCP3204 evaluation board. Here is the relevant part of my schematic: View attachment 10091

The A/D converter access is working fine as long as I don't try using the Ethernet module. And the Ethernet module works fine when I run it without trying to access the A/D converter. But if I have started listening for UDP packets (rpt.startUDP(mac, ip, localPort);), processing does not proceed past my first attempt to access the SPI bus for the A/D converter transaction.

Below is the pertinent code from my header and CPP files.

Should I expect to be able to make this work? I might be able to live with only enabling Ethernet after doing my A/D converter sampling to report the results and then disabling it to resume sampling, but that would be a pain. I've been looking into Bill Greiman's DigitalIO bit-banging SPI code to just put the A/D converter SPI on its own pins, and might proceed to try to deal with its current crop of compiler errors (templates! inlines! #defines galore!) if that winds up being the best approach. The MCP3204 only supports an SPI clock of 2MHz, so bit-banging with a 72 MHz CPU actually doesn't seem too impractical. But if there is an easy fix for my code as-is, I'd obviously rather stick with it and the hardware SPI.

Thanks for any assistance! The Teensy is a great little product.

Best regards,
Ed Suominen

Code:
#include <IPAddress.h>
#include <EthernetUdp.h>
#include <Ethernet.h>
#include <SPI.h>

class ADC {
 public:
  ADC(uint8_t csPin, SPISettings &);
  void setVoltageChannel(uint8_t ch);
  uint16_t readChannel(uint8_t ch);
  void getVI(VoltageCurrent &); 
 private:
  uint8_t csPin;
  uint8_t ch_V = 0;
  const uint8_t ch_I = 1; // In real system, it will be 2
  SPISettings settings;
};

class Reporter {
 public:
  Reporter(uint8_t csPin, uint8_t resetPin);
  void startUDP(byte mac[], IPAddress &, uint16_t localPort);
  int checkForPacket();
  void stopUDP();
 private:
  EthernetUDP udp;
};

// Provides SPI access to a Microchip MCP3204 A/D converter
//------------------------------------------------------------------------
ADC::ADC(uint8_t csPin, SPISettings & settings)
{
  // Constructs with the CS pin and SPI settings
  this->csPin = csPin;
  pinMode(csPin, OUTPUT);
  digitalWrite(csPin, HIGH);
  this->settings = settings;
}

void ADC::setVoltageChannel(uint8_t ch)
{
  // Sets the voltage channel to either 0 or 1.
  //
  // Channel 0 is used if an illegal value is supplied.
  if (ch == 1)
    ch_V = 1;
  else
    ch_V = 0;
}

uint16_t ADC::readChannel(uint8_t ch)
{
  // Adapted from Mikko Karvonen's MCP3204 code,
  // https://github.com/onttoni/mcp3204.git

  // There's no multi-threading, so static variables are fine
  static uint8_t spi_tx;
  static uint8_t rx1;
  static uint8_t rx2;

  // Reads the specified channel of the A/D converter
  SPI.beginTransaction(settings);
  digitalWrite(csPin, LOW);

  // Refer to FIGURE 6-1 in MCP3204 datasheet
  // DEBUG: Gets stuck here when Reporter.startUDP (see below) is not commented out.
  SPI.transfer(0x06);
  // /DEBUG

  spi_tx = ch << 6;
  rx1 = SPI.transfer(spi_tx);
  rx2 = SPI.transfer(0xFF);
  
  digitalWrite(csPin, HIGH);
  SPI.endTransaction();
  
  return ((rx1 & 0x0F) << 8) | rx2;
}

// Provides access to a module running the WIZ5500 Ethernet chip
//------------------------------------------------------------------------
Reporter::Reporter(uint8_t csPin, uint8_t resetPin)
{
  // Adapated from demo code at https://learn.adafruit.com/
  //  adafruit-wiz5500-wiznet-ethernet-featherwing/usage
  pinMode(resetPin, OUTPUT);
  digitalWrite(resetPin, HIGH);
  delay(100);
  digitalWrite(resetPin, LOW);
  delay(100);
  digitalWrite(resetPin, HIGH);
  Ethernet.init(csPin);
  // Give the Ethernet module time to boot up
  delay(1000);
}

void Reporter::startUDP(byte mac[], IPAddress & ip, uint16_t localPort)
{
  // Start the Ethernet connection
  // DEBUG: Running this next line causes ADC reads to hang even with the udp lines below commented out.
  Ethernet.begin(mac, ip);
  // Start UDP listening
  udp = EthernetUDP();
  udp.begin(localPort);
}
 
Maybe there's more code at play here than you're showing us?

I tried compiling this in Arduino, but I get:

Code:
sketch_mar22a:11: error: 'VoltageCurrent' has not been declared
   void getVI(VoltageCurrent &);

Or maybe you could trim this down to a small but complete program which demonstrates the problem, when actually compiled in Arduino and run on a real board?
 
And on your original question "Should I expect to be able to make this work?", the answer is yes. It should be possible.

But there are lots of little details to get right. Code in constructors can be particularly tricky (or "risky") if the objects are global or static, since the constructors can run before other stuff is initialized. Could be lots of other possible things, but at least you're using SPI transactions. That's good.
 
Thanks, Paul. Good idea re the trimming down. Here's a single-file sketch that compiles fine on my system and opens port 4567 for UDP (I'm seeing it with nmap) but still does not make it past "Preparing to read an ADC sample." CS (pin 10) is LOW, MISO is LOW, and MOSI is HIGH, as would be expected for an SPI setup waiting for something from the master.

I know I failed to end the transaction in this sketch, but it doesn't matter for this demonstration because the code never gets past the first SPI.transfer() anyhow.

Code:
#include <SPI.h>
#include <Ethernet.h>
#include <EthernetUdp.h>
#include <IPAddress.h>

#define M(x) Serial.println(x);

byte mac[] = { 0x98, 0x76, 0xB6, 0x17, 0x02, 0xD9 };
IPAddress ip(192, 168, 1, 132);

SPISettings adcSettings(2000000, MSBFIRST, SPI_MODE0);
EthernetUDP udp;


void setup() {
  Serial.begin(9600);
  M("Beginning setup:");
  SPI.begin();

  M("1. ADC");
  pinMode(10, OUTPUT);
  digitalWrite(10, HIGH);


  M("2. UDP Ethernet");
  pinMode(8, OUTPUT);
  digitalWrite(8, HIGH);
  delay(100);
  digitalWrite(8, LOW);
  delay(100);
  digitalWrite(8, HIGH);
  Ethernet.init(7);
  delay(1000);
  Ethernet.begin(mac, ip);
  udp.begin(4567);
  M("Setup done");
}


void loop() {
  M("Preparing to read an ADC sample")
  SPI.beginTransaction(adcSettings);
  digitalWrite(10, LOW);
  SPI.transfer(0x06);
  M("ADC set up for a single-ended input");
  uint8_t rx1 = SPI.transfer(0x00);
  Serial.print("Specified ch 0, got 4 MSBs: ");
  M(rx1);
  rx1 = SPI.transfer(0xFF);
  Serial.print("Sent don't-care bits, got 8 LSBs: ");
  M(rx1);

  Serial.print("Now checking for UDP packet: ");
  int packetSize = udp.parsePacket();
  Serial.print(packetSize);
  M("bytes received");

  delay(500);
}
 
Code:
  M("Preparing to read an ADC sample")
  SPI.beginTransaction(adcSettings);
  digitalWrite(10, LOW);

This doesn't look right. The Ethernet library uses pin 10 for chip select.

SPI devices share MOSI, MISO, SCK, but each needs a unique CS pin.
 
Status
Not open for further replies.
Back
Top