2
\$\begingroup\$

I'm trying to write a driver to control a MAX7301 GPIO port expander that will be controlled via SPI.

I've used SPI before and it's pretty simple, but I've been working on this for a few days and I can't get a response from the chip when I try to read and write to registers.

I've pored over the datasheet and I can't find anything that strikes me as a solution. I am 100% confident that the signals I am sending are indeed received by the chip, as I've stuck a few oscilloscope probes on the chip's legs.

The one thing I have noticed is that occasionally a 5V blip will appear on the MISO line, often where I would like to see a signal that exists long enough to be clocked. I suspect this may be the root of the problem.

On the MAX7301, Dout is not high-impedance and has to be connected to an external tri-state buffer. I don't think it's in a Z state when I want to read it, but could the buffer be doing something else?

I would be immensely grateful if anybody could provide me with some advice on how to approach the problem.

The MAX7301 datasheet can be found here

My code at present is below:

 /** 
 \mainpage Arduino / 2436W10003 SPI Comms.
 \file project5.ino
 \brief Control SPI comms. and display results on PC.
 \details Another time perhaps.
 \author
 \version 0.1
 \date 12/08/2013
 */
/*! SPI.h */
#include <SPI.h>
/*! Baud rate used in serial comms. between Arduino and PC */
#define SERIALSPEED 9600
/*! Pin number of SSSEL_GPIO */
#define SSEL_GPIO 2
/*! Pin number of SSEL_ADC */
#define SSEL_ADC 3
#define NOP 0x00
/**
 * \brief Brief
 * 
 * \param [in] void
 * 
 * \return void
 * 
 * \details Details
 */
void loop(void)
{
 int result;
 result = readGPIO(0x04);
 Serial.println(result, HEX);
}
/**
 * \brief Brief
 * 
 * \return Return_Description
 * 
 * \details Details
 */
void setup(void)
{
 // Configure serial comms. with PC
 Serial.begin(SERIALSPEED);
 //Serial.print("Serial communiciations set up at %d baud.", 9600); // sort the %d thing out later
 // Configure GPIO pins
 pinMode(SSEL_GPIO, OUTPUT);
 pinMode(SSEL_ADC, OUTPUT);
 // Slave select pins active LOW, so set inactive HIGH
 digitalWrite(SSEL_GPIO, HIGH);
 digitalWrite(SSEL_ADC, HIGH);
 // Configure SPI
 SPI.setDataMode(SPI_MODE0);
 // SPI_CLOCK_DIV2 --> 128
 // 16 MHz / divider = 16 / 4 = 4MHz
 SPI.setClockDivider(SPI_CLOCK_DIV4);
 // MSBFIRST or LSBFIRST
 SPI.setBitOrder(MSBFIRST);
 SPI.begin();
 writeGPIO(0x04, 0x01);
 writeGPIO(0x04, 0x01);
}
/**
 * \brief Brief
 * 
 * \return void
 * 
 * \details Details
 */
void writeGPIO(byte address, byte data)
{ 
 address &= ~0x80; // ensure bit 15 is cleared
 digitalWrite(SSEL_GPIO, LOW);
 SPI.transfer(0x04);
 SPI.transfer(0x01);
 digitalWrite(SSEL_GPIO, HIGH);
}
/**
 * \brief Brief
 * 
 * \return address
 * 
 * \details Details
 */
byte readGPIO(byte address)
{
 byte result;
 // Ensure that the WRITE bit is set
 address |= 0x80;
 digitalWrite(SSEL_GPIO, LOW);
 // D15 to D8 contain R/W bit and register address
 SPI.transfer(address);
 // D7 to D0 discarded
 SPI.transfer(NOP);
 digitalWrite(SSEL_GPIO, HIGH);
 // Allow a few ns for MAX7301 register to fill up
 digitalWrite(SSEL_GPIO, LOW);
 // No useful data in MSByte
 SPI.transfer(NOP);
 // Results from register should be in LSbyte
 result |= SPI.transfer(NOP);
 digitalWrite(SSEL_GPIO, HIGH);
 return(result);
}
Passerby
73.8k7 gold badges97 silver badges214 bronze badges
asked Aug 16, 2013 at 8:31
\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

It may just be that your writeGPIO function throws away its arguments, and will only ever write a value of 0x01 to address 0x04. Using the arguments to the function should fix this:

void writeGPIO(byte address, byte data)
{
 address &= ~0x80; // ensure bit 15 is cleared
 digitalWrite(SSEL_GPIO, LOW);
 SPI.transfer(address); // use the address argument
 SPI.transfer(data); // use the data argument
 digitalWrite(SSEL_GPIO, HIGH);
}

However, it also appears that the MAX7301's 4-wire protocol may not necessarily be as compatible with SPI as expected. Usually, data is clocked in and out of the shift registers at the same time. The timing diagram on page 8 shows that data is clocked in to the device on DIN on the rising edge of SCLK (SPI mode 0 or 2), but that data is clocked out from the device on DOUT on the falling edge of SCLK (SPI mode 1 or 3). You might have better luck with a software SPI given this difference. I have provided a simple delay-based implementation of this below, although I don't have access to an Arduino, so I haven't tested it.

//this function takes the place of SPI.transfer
byte transferMax7301Byte(byte data_out)
{
 byte current_bit, result;
 result = 0;
 //must clock each bit
 for(current_bit = 0; current_bit < 8; current_bit++)
 {
 //SCLK falling edge, MAX7301 changes output
 digitalWrite(SCLK_GPIO, LOW);
 //shift data from DOUT pin read into result
 result = (result << 1) | digitalRead(DOUT_GPIO);
 //9600 baud is about 104 microseconds per pulse
 //so wait half of that time between edges
 delayMicroseconds(52);
 //SCLK rising edge, Arduino changes output
 digitalWrite(SCLK_GPIO, HIGH);
 //shift data out of data_out onto DIN pin
 digitalWrite(DIN_GPIO, data_out & 0x80);
 data_out <<= 1;
 //wait the other half of the 104 microseconds
 delayMicroseconds(52);
 }
 //last falling edge of SCLK
 digitalWrite(SCLK_GPIO, LOW);
 //read last bit from DOUT pin
 result = (result << 1) | digitalRead(DOUT_GPIO);
 return result;
}
void writeGPIO(byte address, byte data)
{ 
 address &= ~0x80; // ensure bit 15 is cleared
 digitalWrite(SSEL_GPIO, LOW);
 transferMax7301Byte(address);
 transferMax7301Byte(data);
 digitalWrite(SSEL_GPIO, HIGH);
}
byte readGPIO(byte address)
{
 byte result;
 // Ensure that the D15 bit is set
 address |= 0x80;
 digitalWrite(SSEL_GPIO, LOW);
 // D15 to D8 contain R/W bit and register address
 transferMax7301Byte(address);
 // D7 to D0 discarded
 transferMax7301Byte(NOP);
 digitalWrite(SSEL_GPIO, HIGH);
 // Allow a few ns for MAX7301 register to fill up
 digitalWrite(SSEL_GPIO, LOW);
 // No useful data in MSByte
 transferMax7301Byte(NOP);
 // Results from register should be in LSbyte
 result |= transferMax7301Byte(NOP);
 digitalWrite(SSEL_GPIO, HIGH);
 return(result);
}
answered Aug 16, 2013 at 15:15
\$\endgroup\$
2
  • \$\begingroup\$ The SPI mode thing is interesting; it appeared to be that way to me too. \$\endgroup\$ Commented Aug 16, 2013 at 17:26
  • \$\begingroup\$ Also, I'm afraid I shan't be able to test it until the Monday after next. It's a project I'm doing while on placement and I've taken next week off. :S I'll test it the morning of e Monday after next. Sorry about that. x__X0 \$\endgroup\$ Commented Aug 16, 2013 at 19:42

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.