03-20-2015, 12:03 PM
My contribution is a start to the code ...
The .h file
and the .cpp file
Submitted as a place to start the discussion, not as 'the last word'.
Arvid
The .h file
Code:
// Only modify this file to include
// - function definitions (prototypes)
// - include files
// - extern variable definitions
// In the appropriate section
#ifndef _scaleReader_H_
#define _scaleReader_H_
#include "Arduino.h"
//add your includes for the project scaleReader here
#include <util/atomic.h>
//end of add your includes here
#ifdef __cplusplus
extern "C" {
#endif
void loop();
void setup();
#ifdef __cplusplus
} // extern "C"
#endif
//add your function definitions for the project scaleReader here
//Do not add code below this line
#endif /* _scaleReader_H_ */
and the .cpp file
Code:
// Do not remove the include below
#include "scaleReader.h"
#define CLOCK_PIN 2 // The pin associated with the clock pulse
#define CLOCK_TRANSITION FALLING // What will happen on the clock pulse that will want us to read the data pin
#define CLOCK_TERMINATION INPUT_PULLUP // What kind of termination do we want on the clock pin
#define DATA_PIN 3 // The pin associated with the data
#define DATA_TERMINATION INPUT_PULLUP // What kind of termination do we want on the data pin
#define CLOCK_NOISE_FILTER 1000 // How many microseconds long must the clock be stable for it to be consider 'good'
#define MINIMUM_BURST_START_PULSE_WIDTH 100000 // At a minimum, how many microseconds long is the pulse at the beginning of a burst
#define MAXIMUM_TIME_FOR_A_SINGLE_BURST 100000 // The maximum amount of time that a burst should take before considered invalid
#define DATA_BITS_IN_A_VALID_BURST 24 // The number of data bits that are expected in a complete burst of data
volatile boolean initialized = false; // Lets the interrupt get set up right after application start up
volatile boolean processingBurst = false; // Does the interrupt think it is processing a burst
volatile byte dataBitsReceived = 0; // Data bit counter used by the interrupt routine
volatile unsigned long lastGoodClockTime = 0; // The last time the interrupt saw what it thought was a valid clock pulse
volatile unsigned long burstStartTime = 0; // The time when the interrupt thought the burst started
volatile unsigned long inProcessBurstData = 0; // The burst of data as it is being captured by the interrupt routine
volatile unsigned long capturedBurstData = 0; // A complete and validated burst of data
volatile byte capturedBurstCounter = 0; // A rolling counter of the number of complete bursts we have seen. It must be a byte so increments will be atomic.
// This looks like a lot of code for an interrupt but the if statements should ensure that
// only a small portion of it actually runs during any given interrupt
void clockTransitioned() {
unsigned long timeAtInterupt = micros();
unsigned long intervalSinceLastInterrupt = timeAtInterupt - lastGoodClockTime;
// If we do not have our ducks in a row so we will get organized
if (!initialized) {
lastGoodClockTime = timeAtInterupt;
processingBurst = false;
initialized = true;
}
// Is the interrupt because of noise?
else if (intervalSinceLastInterrupt < CLOCK_NOISE_FILTER) {
/*
Note that we are going to do absolutely nothing in this case, not even update the last valid clock time.
We are really waiting for a transition that is at least CLOCK_NOISE_FILTER later than the
last transition that we thought was good.
*/
}
// Is the interrupt because of a 'beginning of burst' pulse?
else if (intervalSinceLastInterrupt > MINIMUM_BURST_START_PULSE_WIDTH) {
if (!processingBurst) {
lastGoodClockTime = timeAtInterupt;
burstStartTime = timeAtInterupt;
inProcessBurstData = digitalRead(DATA_PIN);
dataBitsReceived = 1;
processingBurst = true;
}
// OK, we were in the process of receiving a burst but just got another 'beginning of burst' pulse.
// We'll need to do something here but I will have to figure out exactly what after another cup of coffee.
else {
}
}
// OK, we are here because we are initialized and we do not think it is noise or a 'beginning of burst' pulse.
// Let see if we believe it is a valid data bit.
else {
// If we are not in the process of reading a burst then we have a problem and need more coffee
if (!processingBurst) {
}
// If it has taken longer than we thought is should to show up then we have a problem ... see the coffe issue mentined earlier
else if ((timeAtInterupt - burstStartTime) > MAXIMUM_TIME_FOR_A_SINGLE_BURST) {
}
// We seem to be at the right place at the right time so grab what ever is on the data line.
else {
lastGoodClockTime = timeAtInterupt;
inProcessBurstData << 1; // Make room for the next data bit
inProcessBurstData | digitalRead(DATA_PIN); // and then capture it
dataBitsReceived += 1;
if (dataBitsReceived == DATA_BITS_IN_A_VALID_BURST) { // Are we done with this burst?
capturedBurstData = inProcessBurstData;
capturedBurstCounter ++;
processingBurst = false;
}
}
}
}
// Variables used by the application loop
byte lastCapturedBurstCounter = 0;
unsigned long burstData = 0;
//The setup function is called once at startup of the sketch
void setup() {
// Pin Set Up
pinMode(DATA_PIN, INPUT_PULLUP);
pinMode(CLOCK_PIN, INPUT_PULLUP);
attachInterrupt(CLOCK_PIN, clockTransitioned, CLOCK_TRANSITION);
Serial.begin(115200);
Serial.println("Ready: ");}
// The loop function is called in an endless loop
void loop() {
if ((lastCapturedBurstCounter - capturedBurstCounter) > 0) { // If there is something new available then get it and print it
lastCapturedBurstCounter = capturedBurstCounter; // This is a byte so it should be an atomic assignment
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { // Needs to be marked as atomic so the interrupt does not mess with it while we get a local copy of it
burstData = capturedBurstData = 0;
}
Serial.print(lastCapturedBurstCounter);
Serial.print(", ");
Serial.print(burstData, HEX);
Serial.print(", ");
Serial.println(burstData, BIN);
}
}
Submitted as a place to start the discussion, not as 'the last word'.
Arvid