Using Max7221 and DS1396 on SPI

Hello,

I'm trying to build a clock. I have a Maxim 7221 running six 7-segment displays. I'm using the LedControl library and the demo sketches work fine. I'm also running a DS1306 real time clock chip on the same SPI bus. It also works with a demo program and spits out data. The DS1306 and Max7221 share the MOSI and CLK, the DS1306 uses the MISO, and they both have separate CS pins.

My problem seems to be that they don't get along for some reason. Both work with their individual demo programs, but wont work together. It seems that reading my RTC registers causes the Max7221 to stop working.

Here is some simplified code that I'm using. It reads a few registers off the RTC and spits them out on serial, and rotates through some numbers on the display. The display wont work unless I remove the read_rtc_register commands from setup(). Can someone look through the read_rtc_register function and possibly diagnose why it's confusing my Max7221?

Thanks!

//We always have to include the library
#include "LedControl.h"

#define DATAOUT 11 //MOSI
#define DATAIN 12 //MISO
#define SPICLOCK 13 //sck
#define RTC_CHIPSELECT 7 // chip select (ss/ce) for RTC, active high
#define LED_CHIPSELECT 10
/*
 Now we need a LedControl to work with.
 ***** These pin numbers will probably not work with your hardware *****
 pin 12 is connected to the DataIn 
 pin 11 is connected to the CLK 
 pin 10 is connected to LOAD 
 We have only a single MAX72XX.
 */
LedControl lc=LedControl(11,13,10,1);

/* we always wait a bit between updates of the display */
unsigned long delaytime=250;

byte clr;
//Function SPI_Transfer
char spi_transfer(volatile char data)
{
/*
Writing to the SPDR register begins an SPI transaction
*/
SPDR = data;
/*
Loop here until the transaction complete. 
SPIF bit = SPI Interrupt Flag. When interrupts are enabled, & 
SPIE bit = 1 enabling SPI interrupts, this bit sets =1 when
transaction finished.
*/
while (!(SPSR & (1<<SPIF)))
{

};
// received data appears in the SPDR register

return SPDR;
}



void setup() {

  
  
   
char in_byte;

clr = 0;
in_byte = clr;

Serial.begin(9600);
// set direction of pins

//pinMode(LED, OUTPUT);
pinMode(DATAOUT, OUTPUT);

pinMode(DATAIN, INPUT);
pinMode(SPICLOCK,OUTPUT);

pinMode(RTC_CHIPSELECT,OUTPUT);
pinMode(LED_CHIPSELECT,OUTPUT);
digitalWrite(RTC_CHIPSELECT,LOW); //disable RTC

in_byte = read_rtc_register(0x0F);

Serial.print("CTRL REG [");
Serial.print(in_byte, HEX);
write_rtc_register(0x8F,0x01|0x02|0x04);
// little sanity checks
in_byte = read_rtc_register(0x0F);

Serial.print("CTRL REG [");
Serial.print(in_byte, HEX);

Serial.println("]");
delay(10);

in_byte = read_rtc_register(0x10);
Serial.print("STATUS REG [");

Serial.print(in_byte, BIN);
Serial.println("]");

/*
   The MAX72XX is in power-saving mode on startup,
   we have to do a wakeup call
   */
  lc.shutdown(0,false);
  /* Set the brightness to a medium values */
  lc.setIntensity(0,8);
  /* and clear the display */
  lc.clearDisplay(0);

}

void write_rtc_register(char register_name, byte data) 
{
write_register(register_name, data, RTC_CHIPSELECT, HIGH, true, true);
}

char read_rtc_register(char register_name) 
{
return read_register(register_name, RTC_CHIPSELECT, HIGH, false, true);
}

// reads a register
char read_register(char register_name, byte cs_pin, byte cs_active_level, boolean read_high, boolean cpha_trailing)
{
char in_byte;
if(cpha_trailing) 
{
SPCR = (1<<SPE)|(1<<MSTR)|(1<<CPHA)|(0<<SPR1)|(0<<SPR0);
}
else 
{
SPCR = (1<<SPE)|(1<<MSTR)|(0<<CPHA)|(0<<SPR1)|(0<<SPR0);
}
clr = SPCR;
clr = SPDR;
if(read_high) 
{
// need to set bit 7 to indicate a read for the slave device
register_name |= 128;
}
else 
{
// if read low, means A7 bit should be cleared when reading for the slave device
register_name &= 127;
}
// SS is active low
digitalWrite(cs_pin, cs_active_level);
// send the address of the register we want to read first
spi_transfer(register_name);
// send nothing, but here's when the device sends back the register's value as an 8 bit byte
in_byte = spi_transfer(0);
// deselect the device..
if(cs_active_level == HIGH) 
{
digitalWrite(cs_pin, LOW);
}
else 
{
digitalWrite(cs_pin, HIGH);
}
return in_byte;
}

// write to a register
// write_high if true indicates set A7 bit to 1 during a write
void write_register(char register_name, byte data, byte cs_pin, byte cs_active_level, boolean write_high, boolean cpha_trailing)
{
if(cpha_trailing) 
{
SPCR = (1<<SPE)|(1<<MSTR)|(1<<CPHA)|(0<<SPR1)|(0<<SPR0);
}
else 
{
SPCR = (1<<SPE)|(1<<MSTR)|(0<<CPHA)|(0<<SPR1)|(0<<SPR0);
}
clr=SPCR;
clr=SPDR;
// char in_byte;
if(write_high) 
{
// set A7 bit to 1 during a write for this device
register_name |= 128;
}
else 
{
// clear bit 7 to indicate we're doing a write for this device
register_name &= 127;
}
// SS is active low
digitalWrite(cs_pin, cs_active_level);
// send the address of the register we want to write
spi_transfer(register_name);
// send the data we're writing
spi_transfer(data);
if(cs_active_level == HIGH) 
{
digitalWrite(cs_pin, LOW);
}
else 
{
digitalWrite(cs_pin, HIGH);
}
//return in_byte;
}
/*
 This method will display the characters for the
 word "Arduino" one after the other on digit 0. 
 */
void writeArduinoOn7Segment() { 
  
  lc.setChar(0,0,'a',false);
  delay(delaytime);
  lc.setRow(0,0,0x05);
  delay(delaytime);
  lc.setChar(0,0,'d',false);
  delay(delaytime);
  lc.setRow(0,0,0x1c);
  delay(delaytime);
  lc.setRow(0,0,B00010000);
  delay(delaytime);
  lc.setRow(0,0,0x15);
  delay(delaytime);
  lc.setRow(0,0,0x1D);
  delay(delaytime);
  lc.clearDisplay(0);
  delay(delaytime);
} 

/*
  This method will scroll all the hexa-decimal
 numbers and letters on the display. You will need at least
 four 7-Segment digits. otherwise it won't really look that good.
 */
void scrollDigits() {
  for(int i=0;i<13;i++) {
    lc.setDigit(0,3,i,false);
    lc.setDigit(0,2,i+1,false);
    lc.setDigit(0,1,i+2,false);
    lc.setDigit(0,0,i+3,false);
    delay(delaytime);
  }
  lc.clearDisplay(0);
  delay(delaytime);
}

void loop() { 
  writeArduinoOn7Segment();
  scrollDigits();
}

The specific RTC function I believe might be interfering with my Max7221:

char read_register(char register_name, byte cs_pin, byte cs_active_level, boolean read_high, boolean cpha_trailing)
{
char in_byte;
if(cpha_trailing)
{
SPCR = (1<<SPE)|(1<<MSTR)|(1<<CPHA)|(0<<SPR1)|(0<<SPR0);
}
else
{
SPCR = (1<<SPE)|(1<<MSTR)|(0<<CPHA)|(0<<SPR1)|(0<<SPR0);
}
clr = SPCR;
clr = SPDR;
if(read_high)
{
// need to set bit 7 to indicate a read for the slave device
register_name |= 128;
}
else
{
// if read low, means A7 bit should be cleared when reading for the slave device
register_name &= 127;
}
// SS is active low
digitalWrite(cs_pin, cs_active_level);
// send the address of the register we want to read first
spi_transfer(register_name);
// send nothing, but here's when the device sends back the register's value as an 8 bit byte
in_byte = spi_transfer(0);
// deselect the device..
if(cs_active_level == HIGH)
{
digitalWrite(cs_pin, LOW);
}
else
{
digitalWrite(cs_pin, HIGH);
}
return in_byte;
}

I had a similar situation with LedControl and an SPI SD card. It's possible that the MAX72xx is not playing fair re SPI - I don't know.

However, one thing I'd suggest is that you setup LedControl to use totally separate pins for it's SPI (i.e. 4,5,6). If the code and the wiring is correct, it will then work. At least then you will know more.

I didn't read the whole MAX7221 datasheet, but I do not see any indication of a hi-Z state on DOUT, most conspicuously on the timing diagram on page 6. This leads me to suspect that this is one of those Maxim devices that are not true SPI and won't play well with others.

The suggestion to move the 7221 to another set of pins is a good one.

-j

Hi,
The LedControl-lib is using a sofware-emulation of the SPI-protocol but not the SPI hardware-feature available on the atmega. That is the reason why you can redefine the LedControl-pins to be used on the Arduino.
As long as you use different pins for your devices there should be no interference between the code or the hardware.
Eberhard

Ah, didn't realize I could redefine the pins for LED control. I did get it working using this code:

http://www.ecs.umass.edu/ece/m5/tutorials/7221_led_driver_tutorial.html

Then I switched back to LedControl and used different pins, which works great too. Thanks!

I'm interested to know what you thought of the UMass tutorial from the M5 website