Pages: [1]   Go Down
Author Topic: Using SPI with 74HC595 and TPIC6B595  (Read 5725 times)
0 Members and 1 Guest are viewing this topic.
Offline Offline
Newbie
*
Karma: 0
Posts: 14
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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?
Logged

Manchester (England England)
Offline Offline
Brattain Member
*****
Karma: 637
Posts: 34602
Solder is electric glue
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
Logged

Global Moderator
Boston area, metrowest
Offline Offline
Brattain Member
*****
Karma: 548
Posts: 27386
Author of "Arduino for Teens". Available for Design & Build services. Now with Unlimited Eagle board sizes!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

1.Connect SCK to SRCK (shift register clock)
2. Connect MOSI to serial data in.
3. Connect SS to RCK (output register clock)
Code:
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.

Logged

Designing & building electrical circuits for over 25 years. Check out the ATMega1284P based Bobuino and other '328P & '1284P creations & offerings at  www.crossroadsfencing.com/BobuinoRev17.
Arduino for Teens available at Amazon.com.

Offline Offline
Newbie
*
Karma: 0
Posts: 14
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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:

Code:
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:

Code:
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.
Logged

Global Moderator
Boston area, metrowest
Offline Offline
Brattain Member
*****
Karma: 548
Posts: 27386
Author of "Arduino for Teens". Available for Design & Build services. Now with Unlimited Eagle board sizes!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

"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.
Logged

Designing & building electrical circuits for over 25 years. Check out the ATMega1284P based Bobuino and other '328P & '1284P creations & offerings at  www.crossroadsfencing.com/BobuinoRev17.
Arduino for Teens available at Amazon.com.

Offline Offline
Newbie
*
Karma: 0
Posts: 3
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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
Logged

Beijing
Offline Offline
Full Member
***
Karma: 3
Posts: 193
Skype name habib.derbyshire
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

You can use one MAX7219.
Logged

What is man's best friend? The breadboard!

texas
Offline Offline
God Member
*****
Karma: 27
Posts: 862
old, but not dead
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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:

Code:
    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.
Logged

Experience, it's what you get when you were expecting something else.

Global Moderator
Boston area, metrowest
Offline Offline
Brattain Member
*****
Karma: 548
Posts: 27386
Author of "Arduino for Teens". Available for Design & Build services. Now with Unlimited Eagle board sizes!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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
Code:
#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);
}

Logged

Designing & building electrical circuits for over 25 years. Check out the ATMega1284P based Bobuino and other '328P & '1284P creations & offerings at  www.crossroadsfencing.com/BobuinoRev17.
Arduino for Teens available at Amazon.com.

Offline Offline
Newbie
*
Karma: 0
Posts: 3
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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
Logged

Global Moderator
Boston area, metrowest
Offline Offline
Brattain Member
*****
Karma: 548
Posts: 27386
Author of "Arduino for Teens". Available for Design & Build services. Now with Unlimited Eagle board sizes!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
Logged

Designing & building electrical circuits for over 25 years. Check out the ATMega1284P based Bobuino and other '328P & '1284P creations & offerings at  www.crossroadsfencing.com/BobuinoRev17.
Arduino for Teens available at Amazon.com.

Offline Offline
Newbie
*
Karma: 0
Posts: 3
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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
Logged

United Kingdom
Offline Offline
Tesla Member
***
Karma: 227
Posts: 6637
Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law.
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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:

Code:
   // 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().
Logged

Formal verification of safety-critical software, software development, and electronic design and prototyping. See http://www.eschertech.com. Please do not ask for unpaid help via PM, use the forum.

Pages: [1]   Go Up
Jump to: