An arduino library to communicate using the Dallas one-wire protocol, where the Arduino takes the role of a slave.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

243 lines
5.1 KiB

#include "Arduino.h"
#include "LowLevel.h"
#include "SerialChannel.h"
#define LEDPin 13
#define OWPin 2
#define InterruptNumber 0 // Must correspond to the OWPin to correctly detect state changes. On Arduino Uno, interrupt 0 is for digital pin 2
#define ResetMinDuration 480
#define ResetMaxDuration 900
#define PresenceWaitDuration 30
#define PresenceDuration 300
#define BitZeroMinDuration 20
#define BitZeroMaxDuration 75
SerialChannel debug("debug");
Pin owPin(OWPin);
Pin owOutTestPin(3);
Pin led(LEDPin);
enum OwStatus
{
OS_WaitReset,
OS_Presence,
OS_AfterPresence,
OS_WaitCommand,
};
OwStatus status;
void owPullLow()
{
owPin.outputMode();
owPin.writeLow();
owOutTestPin.writeLow();
}
void owRelease()
{
owPin.inputMode();
owOutTestPin.writeHigh();
}
void owWrite(bool value)
{
if (value)
owRelease();
else
owPullLow();
}
unsigned long resetStart = (unsigned long)-1;
unsigned long lastReset = (unsigned long)-1;
unsigned long bitStart = (unsigned long)-1;
byte receivingByte = 0;
byte receivingBitPos = 0;
void setup()
{
led.outputMode();
owPin.inputMode();
owOutTestPin.outputMode();
owOutTestPin.writeHigh();
led.writeLow();
cli(); // disable interrupts
attachInterrupt(InterruptNumber,onewireInterrupt,CHANGE);
// set timer0 interrupt at 250KHz (actually depends on compare match register OCR0A)
// 4us between each tick
TCCR1A = 0;
TCCR1B = 0;
TCNT1 = 0; // initialize counter value to 0
//TCCR1B |= (1 << WGM12); // turn on CTC mode
//TCCR1B |= (1 << CS11) | (1 << CS10); // Set 64 prescaler
TIMSK1 |= (1 << OCIE1A); // enable timer compare interrupt
sei(); // enable interrupts
Serial.begin(9600);
}
//int count = 0;
void loop()
{
//if ((count++) % 1000 == 0)
// led.write(!led.read());
cli();//disable interrupts
SerialChannel::swap();
sei();//enable interrupts
SerialChannel::flush();
}
void(*timerEvent)() = 0;
void setTimerEvent(short microSecondsDelay, void(*event)())
{
microSecondsDelay -= 10; // this seems to be the typical time taken to initialize the timer on Arduino Uno
short skipTicks = (microSecondsDelay - 3) / 4; // round the micro seconds delay to a number of ticks to skip (4us per tick, so 4us must skip 0 tick, 8us must skip 1 tick, etc.)
if (skipTicks < 1) skipTicks = 1;
//debug.SC_APPEND_STR_INT("setTimerEvent", (long)skipTicks);
TCNT1 = 0;
OCR1A = skipTicks;
timerEvent = event;
TCCR1B = (1 << WGM12) | (1 << CS11) | (1 << CS10); // turn on CTC mode with 64 prescaler
}
void owError(const char* message)
{
debug.append(message);
led.writeHigh();
}
void owResetError()
{
led.writeLow();
}
void onewireInterrupt(void)
{
bool state = owPin.read();
unsigned long now = micros();
//led.write(state);
if (!state)
resetStart = now;
// handle reset
if (state)
{
unsigned long resetDuration = resetStart == (unsigned long)-1 ? (unsigned long)-1 : now - resetStart;
resetStart = (unsigned long)-1;
lastReset = now;
if (resetDuration >= ResetMinDuration && resetDuration <= ResetMaxDuration)
{
//debug.SC_APPEND_STR_TIME("reset", now);
owResetError();
status = OS_Presence;
setTimerEvent(PresenceWaitDuration - (micros() - now), &beginPresence);
return;
}
}
if (status == OS_AfterPresence)
{
status = OS_WaitCommand;
receivingByte = 0;
receivingBitPos = 0;
if (state)
{
// this is the rising edge of end-of-presence ; don't try to interpret it as a bit
return;
}
}
// read bytes
if (status == OS_WaitCommand)
{
if (!state)
{
// master just pulled low, this is a bit start
//debug.SC_APPEND_STR_TIME("bit start", now);
bitStart = now;
}
else
{
if (bitStart == (unsigned long)-1)
{
//owError("Invalid read sequence");
//return;
// we missed the falling edge, we emulate one here (this can happen if handling of rising edge interrupt takes too long)
bitStart = now;
}
// master released the line, we interpret it as a bit 1 or 0 depending on timing
unsigned long bitLength = now - bitStart;
bitStart = (unsigned long)-1;
if (bitLength < BitZeroMinDuration)
{
// received bit = 1
//debug.SC_APPEND_STR_TIME("received bit 1", now);
receivingByte |= (1 << receivingBitPos);
++receivingBitPos;
}
else if (bitLength < BitZeroMaxDuration)
{
// received bit = 0
//debug.SC_APPEND_STR_TIME("received bit 0", now);
++receivingBitPos;
}
else
{
// this is not a valid bit
owError("Invalid read timing");
return;
}
if (receivingBitPos == 8)
{
debug.SC_APPEND_STR_INT("received byte", (long)receivingByte);
receivingBitPos = 0;
receivingByte = 0;
}
}
}
}
void beginPresence()
{
unsigned long now = micros();
owPullLow();
owOutTestPin.writeLow();
setTimerEvent(PresenceDuration, &endPresence);
debug.SC_APPEND_STR_TIME("reset", lastReset);
debug.SC_APPEND_STR_TIME("beginPresence", now);
}
void endPresence()
{
unsigned long now = micros();
owRelease();
debug.SC_APPEND_STR_TIME("endPresence", now);
status = OS_AfterPresence;
}
ISR(TIMER1_COMPA_vect) // timer1 interrupt
{
TCCR1B = 0; // disable clock
void(*event)() = timerEvent;
timerEvent = 0;
if (event != 0)
event();
}