So I have an external master device which bangs out a byte stream (DOUT), it indicates the upcoming stream on an DRDY. The timings are given from the master on an DCLK line.

These three lines (DRDY, DCLK, DOUT) serve the data from that particular master device. Now I want to avoid introducing new hardware, ie an FPGA, and wonder how the Raspberry can handle the data stream.

The clockline is somewhat low baud and the interrupt fires at 100-500 Hz.

Options on the table are software bit-banging and raping the I2C ports through pigpio to receive the data.

What are your thoughts, is this within reach?


Raspberry as an I2C SLAVE


Update: Ended up writing a I2S Kernel driver to ingest the stream. Worked well.

  • 1
    The device (whatever it is) is unlikely to be I²C more likely to be SPI. Without details of the device this is unanswerable. – Milliways Jun 8 '19 at 1:51
  • 1
    the term is bitstream, not bytestream – jsotola Jun 8 '19 at 1:53
  • pigpio will work. You need to clarify. Is clock driven by the Pi or the device. What does ready signal. – joan Jun 8 '19 at 5:45

Based upon this discussion regarding interrupts, it would appear to be very possible to handle something that is 0.1-0.5 kbps.

As with many things on this platform, there are many potential ways to make this happen. From the simplest to the most complex, one could do this with pigpio or one could code a kernel module to function as an interrupt service routine.

From what you have described, the timing seems to be:

     GPIO   Logic Probe Screen
DRDY  2   __/¯~¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯~¯\_______
DCLK  3   ____~_/¯\_/¯\_/¯\_/¯\_/¯\_/¯\_/¯\_/¯\_~_________
DOUT  4   ____~_/¯\_____/¯\_________/¯\_/¯\_____~_________
handler     A   B B B B B B B B B B B B B B B B   A
     Note: Sub A sets/ clears the receive state, B receives the data

Pseudocode for bit-banging: (since your development environment was not stated)

#include <pigpio.h>

const int pinDRDY = 2;
const int pinCLK = 3;
const int pinData = 4;

bool myDRDY;
int myData;

sub init() {

    # Set pins to input
    gpioSetMode(pinDRDY, 0);
    gpioSetMode(pinCLK, 0);
    gpioSetMode(pinData, 0);

    # Set "interrupt" handler for data ready pin
    if (gpioSetAlertFunc(pinDRDY, interruptDataRready()) != 0) {
        # Bad GPIO, gracefully crash
    # GPIO "interrupt" pin is set, wait for callback for data ready

sub interruptDataReady(GPIO, level, tick) {
    myDRDY = level;

    if (myDRDY == true) {
        # Transition low to high, prepare for receiving new data
        # Set "interrupt" handler for clock
        if (gpioSetAlertFunc(pinCLK, interruptClockPulse()) != 0) {
            # A different bad GPIO, turn off data handler, gracefully crash
            gpioSetAlertFunc(pinCLK, null);
        else {
            # Set "interrupt" handler for clock, initialize for new data byte
            myData = 0
    else {
        # Transition high to low, first disable the clock handler
        gpioSetAlertFunc(pinCLK, null);

        # Next, send the data out somewhere

sub interruptClockPulse(GPIO, level, tick) {
    # For example purposes, reads data upon the rising edge
    int intNewBit;

    if (level == 1) {
        # Rising clock edge, reads one bit of data

        # Shift data left one bit
        myData << 1;

        # Read data
        intNewBit = gpioRead(pinData);
        if (intNewBit == 1) {
            myData = myData | 1;
        else {
            # Either bad GPIO pin or zero bit, nothing to do
    else {
        # Falling clock edge, nothing to do

sub doSomething(){
    # Do something
| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.