Help regarding 40 hz clock

Hello, I have been programming and using Arduino for over a year now, so I am very familiar with the basics (including Strings). I am having an issue trying to find resources (or maybe I am just not comprehending or looking for the right resources, but I am trying to serial and read a 400 Hz clock signal being sent from a computer into an Arduino. Basically this is for a bowling scoring system where a camera will read the standing pins and send the signal to the Arduino to illuminate LED's to the appropriate standing bowling pin(s). I would appreciate any help to direct me to the appropriate educational materials to help me understand clock signals. Attached is the clock signals that I need the Arduino to read. Thanks in advance.

400 hz.pdf (83.8 KB)

The picture:

1 Like

The clock can go to a interrupt for the rising edge.
Read the signal in the interrupt.

There is not enough information to say for sure what the start and stop is.
400 Hz divided by 16 bits is the timing frame of 40ms.
Is known what the code is for the specific things ? Is there a list for that ?

It could be compatible with some kind of standard bus signal, but I think that reading the data in a interrupt is the easiest and most flexible way.

1 Like

Cool signal--each bowling pin has its own bit.

Yes, read data on the rising edge of clock, and fill up an array or word with the bits.

hm ... I think it will be very good if you use such a 10$ 24 MHz 8 channel logic analyser
https://www.ebay.com/itm/253841718379

There is a freeware called PulseView that can read the data from this logic analyser
http://sigrok.org/wiki/Downloads
With this logic analyser you can record how the signals really look like.
2,5ms is medium fast. It would be possible to record this with an arduino too.

To make things clearer I have added some marks and numbers to the picture

It seems that changes low-HIGH on the dataline start with falling edges of the clock-signal.

before start the dataline is HIGH.
On start the dataline goes low on a falling edge of the clock-signal

if datasignal rises low-HIGH on one of the next 1 to 10 falling edges of the clock-signal this is a pin

If the datasignal stays HIGH for falling-edge 11 to 15 this is a strike

If the datasignal goes HIGH for falling-edge 11
goes low on falling-edge 12
goes HIGH at falling-edge 13
goes low on falling-edge 14
this is a miss

if dataline stays low at falling-edge 11
dataline goes HIGH on falling-edge 12
dataline stays HIGH on falling edge 13
dataline goes low on falling edge 14
this is indicating some pins are fallen

So one idea I have is to use a state-machine
each falling edge of the clock-signal increases the state-variable by 1

1: falling-edge-state

  • make snapshot of time with millis()
  • increase state-variable by 1

2: wait 500 microseconds

  • if 500 microseconds have passed by
    • read dataline and store bit

on falling-edge 11,12,13,14,15
check which conditions are there
to determine strike, miss, or some pins

on falling-edge 16 the dataline should go HIGH again

This concept requires to run the main-loop fast enough to call the state-machine function every 500 microseconds.

So I'm thinking about - additionally - using a timer-interrupt occuring each 250 mircoseconds that does change the state-variable.

But this is pretty fast. I don't know if an arduino can do this so fast.

question: how would you code a 500 microsecond delay inside an interrupt? I'm not sure but inside an ISR microseconds() gets not updated

Or would an approach with storing timestamps when did the next change on the dataline occur be easier?

maybe using two interrupt-pins
first interrupt for the clock-signal falling
second interrupt for the dataline-signal changing
both storing snapshots of microseconds and then analysing the
was it rising or falling and time-differences.

Be the change you want to see in the world
best regards Stefan

1 Like

I guess the clock signal is not available?
The OP must implement something like "software serial" which read 16 bit words (and STOP is zero) on multiple lines with a baud rate 400.

In short wait for falling edge on all lines and start sampling 3.75 ms later first bit and each additional after 2.5 ms from the previous.

Sample the data on each rising edge of the clock. The first LOW bit is the start signal. The next 10 bits are HIGH if the pin is down and LOW if the pin is present. The next 4 bits are reverse, inverse number of pins standing:
1111 = 0x0F = 0 standing (strike)
0111 = 0x07 = 1 standing
1011 = 0x0B = 2 standing
0011 = 0x03 = 3 standing
1101 = 0x0D = 4 standing
0101 = 0x05 = 5 standing
1001 = 0x09 = 6 standing
0001 = 0x01 = 7 standing
1110 = 0x0E = 8 standing
0110 = 0x06 = 9 standing
1010 = 0x0A = 10 standing (miss)

const byte PinsPresent[16] =
  { -1, 7, -1, 3, -1, 5, 9, 1, 
    -1, 6, 10, 2, -1, 4, 8, 0};

The last of the 16 bits is LOW.

Then there is a 16 bit-time (40 ms) HIGH until the next Start Bit.

Here is how I would write it:

const byte ClockPin = 2;  // Must be External Interrupt pin
const byte DataPin = 4;

const int PinsPresent[16] =
{
  -1, 7, -1, 3, -1, 5, 9, 1,
  -1, 6, 10, 2, -1, 4, 8, 0 
};

volatile uint16_t NewData = 0;

// Sample the data on each rising edge of the clock
void ClockRisingISR()
{
  static uint16_t dataWord = 0;
  static byte bitCount = 0;
  
  int thisBit = digitalRead(DataPin);

  if (bitCount == 0 && thisBit == HIGH)
    return; // No start bit yet.

  dataWord <<= 1;
  dataWord |= thisBit;
  bitCount++;

  if (bitCount == 16)
  {
    NewData = dataWord;
    bitCount = 0;
  }
}

// Message Bits:
// START P10 P9 P8 P7 P6 P5 P4 P3 P2 P1 C1 C2 C4 C8 STOP
void processData(uint16_t messageBits)
{
  // Process P10 thorugh P1
  for (int i = 0; i < 10; i++)
  {
    Serial.print("Pin ");
    Serial.print(10 - i);

    if (messageBits & (0x4000 >> i))
      Serial.println(" is up.");
    else
      Serial.println(" is down.");
  }

  // Process the 4-bit count (C1 C2 C4 C8)
  byte pinsUp = PinsPresent[(messageBits>>1) & 0x0F];
  Serial.print("There are ");
  Serial.print(pinsUp);
  Serial.println(" pins up.");
}

void setup()
{
  Serial.begin(115200);
  delay(200);

  pinMode(ClockPin, INPUT);
  pinMode(DataPin, INPUT);
  attachInterrupt(digitalPinToInterrupt(ClockPin), ClockRisingISR, RISING);
}

void loop()
{
  noInterrupts();
  uint16_t data = NewData;
  NewData = 0;
  interrupts();

  // Do we have new data?
  if (data)
    processData(data);
}
1 Like

Hi @johnwasser

that is a brilliant analysis of the signal train. Wow!
And Yes using the rising edge of the clock-signal syncs to the middle of the databit.

Though I have a question

You configured the interrupt on the falling edge

shouldn't this be RISING?

Be the change you want to see in the world
best regards Stefan

Absolutely right!

Obviously, I only put that in to test you. :slight_smile:

Now that you have passed the test I have changed the code so as not to confuse the less observant. :slight_smile:

1 Like

Andy you provided the right information: the timing-diagram and asked for the basics.

This is what makes all the difference.
Now you have a spot-on customised tutorial to understand clock-signals.

Be the change you want to see in the world
best regards Stefan

1 Like

Thank you for all the responses, this helps a ton. Just an update, there is a separate signal for clock and for data. I did connect an O scope to the clock and data terminals, it is showing a 40 Hz signal constantly, not sure what is going on with that. I did take StefanL38's recommendation and ordered a logic analyzer, that will be here tomorrow, so hopefully that will shed some light on the subject.

I didn't realize I could just use regular pins (one interrupt pin and a digital pin) for the Arduino to receive the signal, I thought it had to be through some other format like RS-232, I2C or something similar. Thanks johnwasser for the code example, that is a huge help and a lot more information than I was expecting, much appreciated.

How many clock pulses are there between the frames ? I assume that the data signal is always high there.
The constant 40 Hz signal is no problem. It is also an indication that the device is present. If everything works, you could add a timeout for the clock signal :nerd_face:

The diagram says 16. I wrote my sketch so that number is not important. After a frame it just looks for the next LOW bit.

1 Like

At these speeds, you don't need an interrupt pin.
Just look for falling edges in the Clock signal by using the "StateChangeDetection example. You have 1.25 milliseconds (20,000 instruction cycles) between edges and you can be late by over 10,000 instruction cycles without missing any data.

1 Like

Well, in my infinite wisdom I was obsessed in trying to see the signals the clock and data would produce and then use the code you provided. I just realized that I can use this really cool code and see if it works. I threw the code into an UNO and I was able to get it to work (for the most part.)

I have the clock into pin 2 and the data into pin 4. It doesn't show anything on the serial monitor until I pull the clock pin and place the data pin into pin 2, then it show up immediately. I did this multiple times and it is consistent. I did try changing pin 2 from RISING to FALLING, it didn't work at all, even when switching the wires. I did try reversing the clock and data and that did not produce any information.

It looks like the signals aren't doing what the diagram says to expect. Let's use the "poor Man's Oscilloscope" (Serial Plotter) to see what is actually happening:

// Connect the Clock line to A0
// Connect the Data line to A1

void setup()
{
  Serial.begin(115200);
  delay(200);

  Serial.println("Clock,Data");
}

void loop()
{
  // Wait for a 'start' (LOW) bit
  while (analogRead(A1) > 10) {}

  for (int i = 0; i < 500; i++)
  {
    int clock = analogRead(A0);
    int data = analogRead(A1);

    Serial.print(clock);
    Serial.print(',');
    Serial.println(data);
    // The pulses are about 2.5 milliseconds so full speed may be fast enough
  }

  delay(500);
}

Do the waveforms look anything like what the diagram shows? Are the LOW values very near 0 and the HIGH values very near 1023?

That's pretty cool. Yes, tons of 0's, the highest value I saw for the clock was 354 and the data was 696.


Quick update, I was able to get the analyzer to work, below are the pics.

The oscilloscope picture shows the clock is inverted from the one in the diagram. Change:

void ClockRisingISR()
to
void ClockFallingISR()

and
attachInterrupt(digitalPinToInterrupt(ClockPin), ClockRisingISR, RISING);
to:
attachInterrupt(digitalPinToInterrupt(ClockPin), ClockFallingISR, FALLING);

FYI, the oscilloscope shows:
0: Start Bit
1100000111: Pins 8, 7, 6, 5, and 4 are standing
0101: (inverted & reversed: 0101) Five pins standing
0: Stop bit

Yes, those are the pins that I had standing when the scoring read the pins. I made the changes but I am still having the same issue. It won't read on the serial monitor until I remove the data connection and plug it into the clock pin, then it will read.

A couple of observations I made on the Serial Plotter, it was showing readings on both the clock and data pins, but there shouldn't have been any data on the data line (or in this case should have been HIGH the entire time due to an inverted signal) right? I also noticed that the Serial Plotter would not begin to record the information until I scored the pins. Once it sent the data, the Serial Monitor would start recording the random information and continue to run until I physically disconnected either the clock or the data pin. I did this multiple times with the same results.