How to get better Clock signal performance

I posted a while back about generating a Synchronous serial interface. I am going to build it in SW (vs using some of the hardware components. I wrote up a quick program based on the “Blink without Delay” but used micros() to get me to a granularity level i needed. I am attempting to generate a simple 9600bps clock (data will come later). I also am setting the IO directly, bypassing the DigitalWrite(). What I am seeing on the oscope is a ~7us fluctuation. I am gathering that this is due to the granularity of the micros() vs. my interval of ~107us for 9600. Is this about the expected accuracy for the arduino? IS there a way to improve it? You can see a video of the scope here:

// constants won't change. Used here to
// set pin numbers:
const int ledPin =  13;      // the number of the LED pin
const int buttonPin = 12;

// Variables will change:
int ledState = LOW;             // ledState used to set the LED
long previousmicros = 0;        // will store last time LED was updated

// the follow variables is a long because the time, measured in miliseconds,
// will quickly become a bigger number than can be stored in an int.
long microtosec = 1000000;
long baudrate = 9600;
long interval = 49; //(1 * microtosec) /baudrate;           // interval at which to blink (milliseconds)
unsigned long currentMicros = 0;
#define PC7 7

void setup() {
  // set the digital pin as output:
  pinMode(ledPin, OUTPUT);   
  Serial.println(interval);  
}

void loop()
{
  // here is where you'd put code that needs to be running all the time.

  // check to see if it's time to blink the LED; that is, if the
  // difference between the current time and last time you blinked
  // the LED is bigger than the interval at which you want to
  // blink the LED.
  currentMicros = micros();
 
  if(currentMicros - previousmicros > interval) {
    // save the last time you blinked the LED
    previousmicros = currentMicros;  

    // if the LED is off turn it on and vice-versa:
    if (ledState == LOW)
    {
      ledState = HIGH;
      PORTC |= 1<<PC7;       // sets output bit 2 high
    }
    else
    {
      ledState = LOW;
      PORTC &= ~(1<<PC7);
    }
    // set the LED with the ledState of the variable:
    //digitalWrite(ledPin, ledState);
  }
}

Should be if(currentMicros - previousmicros >= interval)

good one. That might help a little. With that in I am at 7.04us. There a a few slightly shorter dropoff’s that happen at 800ns. Those are the two big numbers I am seeing.

Second video, closer up:

Another question is “Is this an acceptable level or not?” This will be the clock for serial data, so I would think that as long as the data transited with the clock change, the receiving device would handle i, but I am not sure.

Why not use the Synchronous port that's already in the chip?

  1. USART0 20.1 Features • Full Duplex Operation (Independent Serial Receive and Transmit Registers) • Asynchronous or Synchronous Operation • Master or Slave Clocked Synchronous Operation

The Clock Generation logic consists of synchronization logic for external clock input used by synchronous slave operation, and the baud rate generator. The XCKn (Transfer Clock) pin is only used by synchronous transfer mode.

Changing this

  previousmicros = currentMicros;

to this

  previousmicros += interval;

should improve consistency by eliminating accumulating small errors.

The width of a pulse for 9600 baud is 104 usecs. An error of 7usecs won't matter as long as it doesn't accumulate over all 10 bits.

You might be interested in the code I wrote to do yet another version of Software Serial

...R

I will take a look at the hardware USART. For some reason, no one was recommending it in my prior post, but I will take a look. Based on the 32U4 datasheet and referenced to the Arduino Micro board:

pin 20 -> RXD -> RX Pin (Micro) Pin 21 -> TXD -> TX Pin Pin 22 -> XCK1 - > Runs to LED light, will need to remove LED and Attach wire.

I am still looking through the data sheet but section 18.3 talks all about framing (5-8 bits, and 9 bit framing). Unfortunatly the data I would be dealing with is nt framed. It is a pure data stream with a clock. No start or stop bits. 18.5 and 18.6 reference framed data also.

What's wrong with SPI?

I will take a look at the SPI interface in the chi datasheet again, but for some reason I thought that I read that the clock speeds were much higher. That was for a PIC microcontroller though. Can SPI do 9600bps? Does SPI frame its data packets too or is it pure stream?

It is a pure data stream with a clock.

If it's got a clock, what's the big deal with absolute timing accuracy?

I guess that is the crux of the issue. If there is a clock, then goes the pattern of the clock really matter?

thomaskuhn: I guess that is the crux of the issue. If there is a clock, then goes the pattern of the clock really matter?

I'm sorry, I don't understand that.

The clock signal is there to tell you when to sample the data; as long as you sample within a reasonably short delay of finding the appropriate clock edge, you shouldn't be worrying at all about blink without delay. Direct port manipulation may be used, or the "fast" version of "digitalRead" to minimise delays.

Thanks AWOL. I understand. I am going to move forward as-is and get the data side working. You are correct. The clock should be telling the end device when to sample.

Can SPI do 9600bps? Does SPI frame its data packets too or is it pure stream?

SPI will do 8Mbps - nearly 1 byte/microsecond. Bytes of data with a clock edge per bit. Up to you to define the protocol - my current project, I am sending out 2 bytes, then 12 bytes then 2 bytes, doing other other stuff for 3mS, then repeating again. The reciever is just shift registers. I've done similar on other projects with 45 bytes at a time blasted out.

But it looks liek the SPI data rate can only be set as a division of the clock (16Mhz) with the following: SPI_CLOCK_DIV2 SPI_CLOCK_DIV4 SPI_CLOCK_DIV8 SPI_CLOCK_DIV16 SPI_CLOCK_DIV32 SPI_CLOCK_DIV64 SPI_CLOCK_DIV128

How can I get 9600? I might be missing something here.

Oh, thought you wanted to run faster. I don’t do anything but the simplest of tests at 9600.
Use the Synchronous feature of the UART, it adds a clock line to tell the receiving device when to sample the serial bits (vs the “slave” generating its own based on its UART settings).