Using SPI with 74HC595 and TPIC6B595

Hello,

I'm controlling a 8x8 matrix with a 74HC595 (anode, line) and a TPIC6B595 (cathode, column). Thanks to the help of the forum this is working now!

But it's too slow to display multiples LEDs at once by multiplexing them. I found that I should probably use the SPI protocol.

  1. Is there a good tutorial on how to use SPI with a 74HC595 / TPIC6B595 ? For instance I have no idea where to connect the slave select on the chip or where to connect SRCLK, RCLK and SER.

  2. I'm trying to use only the C subset of Arduino. But the SPI library uses C++. Is there a C library somewhere?

There is no need to go to SPI, post your code and we can see how to speed it up.
You can mix C and C++ so that is not an issue.

1.Connect SCK to SRCK (shift register clock)
2. Connect MOSI to serial data in.
3. Connect SS to RCK (output register clock)

digitalWrite (SSpin, LOW);
SPI.transfer(byte_of_data);
// if you are daisychaining, add 2nd, 3rd SPI.transfer(next_byte_of_data )
digitalWrite (SSpin,  HIGH);

After that, it comes down to where the data is coming from, such as an array:
SPI.transfer(myArray[ counter ] ); // where counter is incremented in a for:next loop or something.

Thanks for the answer. It makes sense (afterwards!).

If the multiplexing can be fast enough without SPI, I would prefer not to use SPI. So I'm posting here my code and you can tell me if something makes it slow.

I represent a "picture" on my 8x8 matrix by a uint64_t. When a bit is high in int, so should be the corresponding LED.

First we loop on each bit:

void pic_display(pic_t pic)
{
    int line;
    int column;
    
    for(int i = 0; i < 64; i++) {
        line = i / 8;
        column = i % 8;

        if(pic & (0x8000000000000000 >> i)) {
            matrix_display_dot(line, column);
        }
    }
}

And then we call matrix_display_dot:

void matrix_display_dot(char line, char column)
{
    // set RCLK low; wait till we transmit the byte, and they moving it high will output the data
    digitalWrite(RCLKPin, LOW);
    digitalWrite(RCKPin_tpic6b595, LOW);
    
    // shift out the bits (MSBFIRST = most significant bit first)
    shiftOut(SERPin, SRCLKPin, MSBFIRST, 1 << line);
    shiftOut(SERPin_tpic6b595, SRCKPin_tpic6b595, MSBFIRST, 1 << column);
    
    // send shift register data to the storage register
    digitalWrite(RCLKPin, HIGH);
    digitalWrite(RCKPin_tpic6b595, HIGH);
}

Can this be made fast enough to have persistence of vision? For now it's just flickering.

P.S: I know I can mix C++ but I'm using a makefile without the Arduino IDE and would prefer to have it in plain C since I don't thing C++ is relevant for such small projects, I don't like C++ that much and I'm considering to learn the AVR behind Arduino in the long term.

"Can this be made fast enough to have persistence of vision?"

Sure - replace this

shiftOut(SERPin, SRCLKPin, MSBFIRST, 1 << line);
shiftOut(SERPin_tpic6b595, SRCKPin_tpic6b595, MSBFIRST, 1 << column);

with this
SPI.transfer(1<<line);
SPI.transfer(1<<column);

Now that data will go out in far fewer clock cycles.

Hello, I'm using arduino nano to initialize five r32 bit shift registers. Uning Arduino nano everything seems to work ok!
Using same sketch in arduino mini nothing works...I do not found what is wrong. Sketch has been correclty uploaded (i can debug it with serial terminal), but SPI does not work...I need a different setup in Arduino mini than the nano?
here my sketch (in Arduino Nano I test it, it works):

#include <SPI.h>

///////////////////////
//

//int pll_d=11; //piedino data
//int pll_c=13; //piedino clock
int pll_l=10; //piedino latch
#define DATAOUT 11//MOSI
#define DATAIN 12//MISO
#define SPICLOCK 13//SCK

void setup () {
pinMode(DATAOUT, OUTPUT);
pinMode(DATAIN, INPUT);
pinMode(SPICLOCK,OUTPUT);
pinMode(pll_l, OUTPUT);

SPI.begin();
Serial.begin(9600);
Serial.print("spi begin");
SPI.setDataMode(SPI_MODE0);
SPI.setBitOrder(MSBFIRST);
SPI.setClockDivider(SPI_CLOCK_DIV128);

digitalWrite(pll_l,LOW);
delayMicroseconds(1000);
Serial.print("latch enable down");
//}

//void loop(){
// digitalWrite(pll_l,HIGH);
WriteADF(0x00, 0x58, 0x00, 0x05); //Reg5
delayMicroseconds(2500);
WriteADF(0x00, 0x8c, 0x80, 0x3c); //Reg4
delayMicroseconds(2500);
WriteADF(0x00, 0x00 ,0x04, 0xb3); //Reg3
delayMicroseconds(2500);
WriteADF(0x00, 0x00, 0x4e, 0x42); //Reg2
delayMicroseconds(2500);
WriteADF(0x08, 0x00, 0x80, 0x09); //Reg1
delayMicroseconds(2500);
WriteADF(0x00, 0x3c, 0x00, 0x00); //Reg0
SPI.end();
}

int WriteADF(byte a1, byte a2,byte a3,byte a4) {
SPI.transfer(a1);
SPI.transfer(a2);
SPI.transfer(a3);
SPI.transfer(a4);
Toggle();
return 0;
}

void Toggle() {
digitalWrite(pll_l,HIGH);
delayMicroseconds(1);
digitalWrite(pll_l,LOW);
}

void loop(){
digitalWrite(8,HIGH);
delay(2000);
digitalWrite(8,LOW);
delay(2000);
}

////////////////

thank you for support

You can use one MAX7219.

This might save a few instructions:

You don't need to divide or to call the modulus function to get your line and column. Just do this:

    line = i >> 3;           // same as dividing by 8
    column = i & 0x7;    //  extract modulus 8 the easy way

Depending upon how smart the compiler is, this will save allot of time or maybe nothing. You could also create line and column as global variables instead of auto-creating them in the function. This would save a few cycles perhaps.

Arduino Nano and Arduino Mini both have the same processor - '328P.
Suggests a miswiring between the boards.
You also have a lot of extra stuff in your sketch that the SPI library takes of for you.
I've taken a shot at cleaning it up & left you some notes

#include <SPI.h>

// commented out a bunch of redundant code that SPI.h takes care of for you
// see comments also

//int pll_d=11; //piedino data
//int pll_c=13; //piedino clock
int pll_l=10; //piedino latch
//#define DATAOUT 11//MOSI
//#define DATAIN  12//MISO 
//#define SPICLOCK  13//SCK

void setup () {

  //missing for - add:
  pinMode(8, OUTPUT);

  //  pinMode(DATAOUT, OUTPUT);
  //  pinMode(DATAIN, INPUT);
  //  pinMode(SPICLOCK,OUTPUT);
  pinMode(pll_l, OUTPUT); 
  digitalWrite (pll_l, HIGH);  // may not be needed - SPI.begin may do this for you
  Serial.print("latch enable up");

  SPI.begin();  // move to end of setup?
  Serial.begin(9600);
  Serial.print("spi begin");
  //SPI.setDataMode(SPI_MODE0); // default - don't need
  //SPI.setBitOrder(MSBFIRST); // default - don't neeed
  SPI.setClockDivider(SPI_CLOCK_DIV128); // why so slow?  might as well use shiftout()
  // HC595 and TPIC6B595 work just fine at default speed (4 MHz)

  // digitalWrite(pll_l,LOW);
  // delayMicroseconds(1000);
  //Serial.print("latch enable down");
  //}


  //void loop(){
  //  digitalWrite(pll_l,HIGH);
  WriteADF(0x00, 0x58, 0x00, 0x05); //Reg5 
  delayMicroseconds(2500); // not needed - make sure shift registers have 0.1uF cap from Vcc pin to Gnd.
  WriteADF(0x00, 0x8c, 0x80, 0x3c); //Reg4
  delayMicroseconds(2500); // not needed
  WriteADF(0x00, 0x00 ,0x04, 0xb3); //Reg3
  delayMicroseconds(2500); // not needed
  WriteADF(0x00, 0x00, 0x4e, 0x42); //Reg2
  delayMicroseconds(2500); // not needed
  WriteADF(0x08, 0x00, 0x80, 0x09); //Reg1
  delayMicroseconds(2500); // not needed
  WriteADF(0x00, 0x3c, 0x00, 0x00); //Reg0
  //  SPI.end();  // don't need this unless you want to free D11-12-13 for other uses
} // end of setup

int WriteADF(byte a1, byte a2,byte a3,byte a4) { 
  SPI.transfer(a1);
  SPI.transfer(a2);
  SPI.transfer(a3);
  SPI.transfer(a4);
  Toggle();
  return 0; // not needed
}

void Toggle() {
  digitalWrite(pll_l,HIGH);
  delayMicroseconds(1);
  digitalWrite(pll_l,LOW);  // this leaves SS pin low - normally it is left high after SPI.transfer
  // try swapping these around
}

void loop(){
  digitalWrite(8,HIGH);
  delay(2000);
  digitalWrite(8,LOW);
  delay(2000);
}

thank you for your reply. I test the code with Arduino Nano and it runs very well, but I have same problemwith Mini: The Nano programs very well my end-device using SPI. If I "clone" nano hex firmware using ICSP in a different Arduino Nano it runs. If I do it with an Arduino MINI, it seems to run but it does not program my end device using SPI.
Between arduino nano and arduino Mini, the only difference is in atmega socket?

thank you

Yes, the same '328P processor 'die' is used in both, the only difference is the package - one has legs/leads, the other is leadless.
Both should operate the same. You are sure both are wired up the same way, using D11-12-13 and the same pin for SS?

Post the code you are running also.

Yes, same code and same SS pin (10)...in fact I clone the software with ICSP from nano to mini...but probably I find out the problem...Using DIGITAL OSCILLOSCOPE - Tektronix MSO4104 and digital probes, I saw that the SPI messages are identical for both nano and mini (I can see them directly in hex words),so my conclusion is that the issuue probably is in wiring. To check out I'm right I will do a more "clear" wiring setup and I will test signal with oscilloscope directly "after" wires before shift register chip

I will let you know

thank you

8rmfyj5U3Qk:
If the multiplexing can be fast enough without SPI, I would prefer not to use SPI. So I'm posting here my code and you can tell me if something makes it slow.

I think the main problem is that shiftOut in the Arduino library is implemented in a way that makes it very slow. Here is a fragment of code that I use in place of shiftOut where I want more speed:

   // Fast shiftOut function
  volatile uint8_t *sclkPort = portOutputRegister(digitalPinToPort(clockPin));
  volatile uint8_t *mosiPort = portOutputRegister(digitalPinToPort(dataPin));
  uint8_t sclkMask = digitalPinToBitMask(clockPin);
  uint8_t mosiMask = digitalPinToBitMask(dataPin);
  
  uint8_t oldSREG = SREG;
  cli();
  for (uint8_t i = 0; i < 8; ++i)
  {
    if (data & 0x80)
    {
      *mosiPort |= mosiMask;
    }
    else
    {
      *mosiPort &= ~mosiMask;
    }
    *sclkPort |= sclkMask;
    data <<= 1;
    *sclkPort &= ~sclkMask;
  }
  SREG = oldSREG;

The reason this is faster than shiftOut is that it calculates the output port registers and bit masks once per byte, instead of once per bit. To make it even faster, you could calculate them just once, in setup().

I also have the same problem as you. Did you find out what was wrong? Please read my problem description here:

https://forum.arduino.cc/index.php?topic=456579.0

Can anyone give me an idea what can possible be wrong here? Is it maybe wrong that my shift register is powered by arduino 5V ? What can I try?

You're asking about a thread from 4+ years ago?