Arduino as a shift register?

Hello everyone,

I am making a DCC decoder and a feedback decoder on one PCB. The feedback pins needs to be send wireless with an RFM12B to the receiver.
The bus is the S88-bus. How can I set the Arduino as a simulator to simulate a S88-decoder? I mean as a shift register because the S88 bus works like that.

Dylan

Simple enough - connect the clock to an interrupt pin (ideally) and on each leading (or trailing, depending on your protocol) edge read the value on the data pin. Shift a variable to the left (or right, again depending on your protocol) and add the newly read bit to the variable.

If you have some kind of "chip select" pin (it's not clear from that diagram what everything is) then you could do it using slave mode SPI, which is just a glorified shift register.

I read some website about the S88-protocol.
It works like this:

CLOCK:

HIGH? ===> Do some action

LOAD/LATCH:

LOW? ===> Pass the information
HIGH? ===> Read out

RESET:

HIGH? ===> Clear 'memory' for the following cycles.

DATA IN:

Incoming data from the previous module

DATA OUT:

Outgoing data to the central.

I thought to do this:

boolean CLOCK = false;
int LOAD = 3;
int S88pins[] = {
  4,5,6,7,8,9,10,11};
int nbS88pins = 8;
int DataOut[] = {
  0,0,0,0,0,0,0,0};
byte outByte

void setup()
{
  attachInterrupt(0, CLOCKSignalCheck, RISING);
  pinMode(LOAD, INPUT);

  for(int i = 0; i <= nbS88pins; i++)
  {
    pinMode(S88pins[i],INPUT);
  }

}

void loop()
{
  if(CLOCK == true) //if CLOCK is HIGH do some action
  {
    
    if(digitalRead(LOAD) == HIGH) //LOAD HIGH? Read out!
    {
      for(int i = 0; i <= nbS88pins; i++)
      {
        digitalRead(S88pins[i]) == DataOut[i]; // Reading out 8 ports and store it in DataOut[]
        DataOut[i] = outByte; //Store DataOut[] values in the outByte, ready to send
      }
    }
    
    else // LOAD LOW? pass the data
    {
      send outByte; //I don't get here how can I do that and how can I send the bytes from the previous modules? 
    }
    
    CLOCK = false; //Turn ClOCK back OFF
  }

}


void CLOCKSignalCheck()
{
  CLOCK = true;
}

But handeling the DATA is something I don't get. And I don't know if that part of my code really works which is handling CLOCK and LOAD

Dylan

minitreintje:
I read some website about the S88-protocol.
It works like this:

CLOCK:

HIGH? ===> Do some action

LOAD/LATCH:

LOW? ===> Pass the information
HIGH? ===> Read out

RESET:

HIGH? ===> Clear 'memory' for the following cycles.

DATA IN:

Incoming data from the previous module

DATA OUT:

Outgoing data to the central.

I thought to do this:

boolean CLOCK = false;

int LOAD = 3;
int S88pins[] = {
  4,5,6,7,8,9,10,11};
int nbS88pins = 8;
int DataOut[] = {
  0,0,0,0,0,0,0,0};
byte outByte

void setup()
{
  attachInterrupt(0, CLOCKSignalCheck, RISING);
  pinMode(LOAD, INPUT);

for(int i = 0; i <= nbS88pins; i++)
  {
    pinMode(S88pins[i],INPUT);
  }

}

void loop()
{
  if(CLOCK == true) //if CLOCK is HIGH do some action
  {
   
    if(digitalRead(LOAD) == HIGH) //LOAD HIGH? Read out!
    {
      for(int i = 0; i <= nbS88pins; i++)
      {
        digitalRead(S88pins[i]) == DataOut[i]; // Reading out 8 ports and store it in DataOut[]
        DataOut[i] = outByte; //Store DataOut[] values in the outByte, ready to send
      }
    }
   
    else // LOAD LOW? pass the data
    {
      send outByte; //I don't get here how can I do that and how can I send the bytes from the previous modules?
    }
   
    CLOCK = false; //Turn ClOCK back OFF
  }

}

void CLOCKSignalCheck()
{
  CLOCK = true;
}




But handeling the DATA is something I don't get. And I don't know if that part of my code really works which is handling CLOCK and LOAD


Dylan

on each clock you only deal with one bit. Either read in or write out a single bit, with a digitalread or digitalwrite. Use the shift operators << and >> along with the boolean operators & and | to get or combine bits into a "running" byte.

I would like to understand but it's the first time I use shift registers and bytes so it's a little bit confusing for me.
I don't get it quiet well with the bytes how you save it and pass it.

Dylan

minitreintje:
I would like to understand but it's the first time I use shift registers and bytes so it's a little bit confusing for me.
I don't get it quiet well with the bytes how you save it and pass it.

Dylan

When the clock traverses from LOW to HIGH the interrupt triggers. At this point there is a valid data bit on the input. You need to read that data bit:

byte dataValue = digitalRead(DATA_LINE);

and shift it into a storage variable:

incomingByte = incomingByte << 1; // Make room for it
incomingByte = incomingByte | dataValue;

incomingByte is a global and "volatile" variable:

volatile byte incomingByte = 0;

So say incomingByte starts with 0. In binary that would be, as it's a byte variable, 00000000. Then let's toggle the clock once, with a HIGH on the data line. The shift instruction (<< 1) would make incomingByte move all its bits left one. The leftmost one drops off the end. As it's all 0 at the moment it'll stay all 0. Then we "OR" the incoming bit with it. 00000000 | 1 = 00000001.

Then we toggle the clock again, for the second bit. Let's say it's a LOW on the data line at this point. Shift incomingByte once left, so 00000001 becomes 00000010, then OR in the data line (0). So now the byte contains 00000010. Then again, we toggle the clock - let's have a HIGH again. incomingByte shifted becomes 00000100, then ORed with the data line, is 00000101. Etc.

So looking at a full transfer of the byte 10100111, we would have:

Clock 1: Before shift: 00000000 After shift: 00000000 Data: 1: After OR: 00000001
Clock 2: Before shift: 00000001 After shift: 00000010 Data: 0: After OR: 00000010
Clock 3: Before shift: 00000010 After shift: 00000100 Data: 1: After OR: 00000101
Clock 4: Before shift: 00000101 After shift: 00001010 Data: 0: After OR: 00001010
Clock 5: Before shift: 00001010 After shift: 00010100 Data: 0: After OR: 00010100
Clock 6: Before shift: 00010100 After shift: 00101000 Data: 1: After OR: 00101001
Clock 7: Before shift: 00101001 After shift: 01010010 Data: 1: After OR: 01010011
Clock 8: Before shift: 01010011 After shift: 10100110 Data: 1: After OR: 10100111

If you want outgoing data for daisy chaining of registers you want to know what the bit was that "fell off the end of the byte" and send that out on the low-going edge of the clock. So, just before you do your left shift, you should examine that top bit and save it in a variable:

topBit = incomingData >> 7 & 0x01

Then when the clock goes low you can write it out:

digitalWrite(DATA_OUT, topBit);

So, you might have something like this in your program, amongst other things, like setting up the IO ports and configuring the interrupts:

volatile byte incomingData = 0;
volatile byte topBit = 0;

void risingEdge() {
  topBit = incomingData >> 7 & 0x01;
  incomingData <<= 1;
  incomingData |= digitalRead(DATA_IN);
}

void fallingEdge() {
  digitalWrite(DATA_OUT, topBit);
}

Then you can look at your latch input to know when the contents of incomingData contain valid data, and do something with them.

The Arduino has its own 8bit shift register built in, the SPI hardware.

The Load pin is essentially the !SS pin
The Clock pin is essentially SCLK
The Data In pin is MOSI pin
The Data Out pin is the MISO pin

Set the SPI module to slave mode, and to SPI mode 0

If you need a 16bit shift register it is possible to do so with a bit of extra programming. I can make an example if you are interested.

Thanks for your replies :slight_smile:
The example of the SPI would be very great to see.

In the mean time, I will do some tests with the pieces code from majenko...

regards,
Dylan

I tried your code, but I can't get it how I need to do it. Sorry for this.

volatile byte incomingData = 00000;
volatile byte topBit = 00000;
int CLOCK = 2;
int LOAD = 3;
int DATA_IN = 4;
int DATA_OUT = 5;

void setup()
{
  attachInterrupt(0, risingEdge, RISING);
  attachInterrupt(0, fallingEdge, FALLING);
}

void loop()
{
  
}

void risingEdge() {
  topBit = incomingData >> 7 & 0x01;
  incomingData <<= 1;
  incomingData |= digitalRead(DATA_IN);
}

void fallingEdge() {
  digitalWrite(DATA_OUT, topBit);
}

Dylan