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.
435 lines
9.4 KiB
435 lines
9.4 KiB
10 years ago
|
#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 ReadBitSamplingTime 13 // the theorical time is about 30us, but given various overhead, this is the empirical delay I've found works best (on Arduino Uno)
|
||
|
|
||
|
#define SendBitDuration 35
|
||
|
|
||
|
const byte InvalidBit = (byte)-1;
|
||
|
const byte IncompleteBit = (byte)-2;
|
||
|
|
||
|
SerialChannel debug("debug");
|
||
|
|
||
|
Pin owPin(OWPin);
|
||
|
Pin owOutTestPin(3);
|
||
|
Pin led(LEDPin);
|
||
|
|
||
|
enum OwStatus
|
||
|
{
|
||
|
OS_WaitReset,
|
||
|
OS_Presence,
|
||
|
OS_WaitCommand,
|
||
|
OS_SearchRom,
|
||
|
};
|
||
|
OwStatus status;
|
||
|
|
||
|
byte owROM[8] = { 0xE2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00 };
|
||
|
byte searchROMCurrentByte = 0;
|
||
|
byte searchROMCurrentBit = 0;
|
||
|
bool searchROMSendingInverse = false;
|
||
|
bool searchROMReadingMasterResponseBit = false;
|
||
|
|
||
|
void owPullLow()
|
||
|
{
|
||
|
owPin.outputMode();
|
||
|
owPin.writeLow();
|
||
|
owOutTestPin.writeLow();
|
||
|
}
|
||
|
|
||
|
void owRelease()
|
||
|
{
|
||
|
owPin.inputMode();
|
||
|
owOutTestPin.writeHigh();
|
||
|
}
|
||
|
|
||
|
void onEnterInterrupt()
|
||
|
{
|
||
|
//owOutTestPin.writeLow();
|
||
|
}
|
||
|
|
||
|
void onLeaveInterrupt()
|
||
|
{
|
||
|
//owOutTestPin.writeHigh();
|
||
|
}
|
||
|
|
||
|
volatile unsigned long resetStart = (unsigned long)-1;
|
||
|
unsigned long lastReset = (unsigned long)-1;
|
||
|
unsigned long bitStart = (unsigned long)-1;
|
||
|
byte receivingByte = 0;
|
||
|
byte receivingBitPos = 0;
|
||
|
bool searchRomNextBit = false;
|
||
|
bool searchRomNextBitToSend = false;
|
||
|
|
||
|
void setup()
|
||
|
{
|
||
|
owROM[7] = crc8((char*)owROM, 7);
|
||
|
|
||
|
led.outputMode();
|
||
|
owPin.inputMode();
|
||
|
owOutTestPin.outputMode();
|
||
|
owOutTestPin.writeHigh();
|
||
|
|
||
|
led.writeLow();
|
||
|
|
||
|
cli(); // disable interrupts
|
||
|
attachInterrupt(InterruptNumber, owWaitResetInterrupt, 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();
|
||
|
status = OS_WaitReset;
|
||
|
}
|
||
|
|
||
|
void owClearError()
|
||
|
{
|
||
|
led.writeLow();
|
||
|
}
|
||
|
|
||
|
void owWaitResetInterrupt()
|
||
|
{
|
||
|
onEnterInterrupt();
|
||
|
bool state = owPin.read();
|
||
|
unsigned long now = micros();
|
||
|
if (state)
|
||
|
{
|
||
|
if (resetStart == (unsigned int)-1)
|
||
|
{
|
||
|
onLeaveInterrupt();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
unsigned long resetDuration = now - resetStart;
|
||
|
resetStart = (unsigned int)-1;
|
||
|
if (resetDuration >= ResetMinDuration)
|
||
|
{
|
||
|
if (resetDuration > ResetMaxDuration)
|
||
|
{
|
||
|
owError("Reset too long");
|
||
|
onLeaveInterrupt();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
owClearError();
|
||
|
lastReset = now;
|
||
|
status = OS_Presence;
|
||
|
setTimerEvent(PresenceWaitDuration - (micros() - now), &beginPresence);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
resetStart = now;
|
||
|
}
|
||
|
onLeaveInterrupt();
|
||
|
}
|
||
|
|
||
|
//bool debugState = false;
|
||
|
volatile unsigned long lastInterrupt = 0;
|
||
|
void owReceiveCommandInterrupt(void)
|
||
|
{
|
||
|
onEnterInterrupt();
|
||
|
unsigned long now = micros();
|
||
|
if (now < lastInterrupt + 20)
|
||
|
{
|
||
|
onLeaveInterrupt();
|
||
|
return; // don't react to our own actions
|
||
|
}
|
||
|
lastInterrupt = now;
|
||
|
|
||
|
//debugState = !debugState;
|
||
|
//owOutTestPin.write(debugState);
|
||
|
|
||
|
//led.write(state);
|
||
|
|
||
|
bool bit = readBit();
|
||
|
/*if (bit)
|
||
|
debug.SC_APPEND_STR("received bit 1");
|
||
|
else
|
||
|
debug.SC_APPEND_STR("received bit 0");*/
|
||
|
|
||
|
receivingByte |= ((bit ? 1 : 0) << receivingBitPos);
|
||
|
++receivingBitPos;
|
||
|
|
||
|
if (receivingBitPos == 8)
|
||
|
{
|
||
|
byte receivedByte = receivingByte;
|
||
|
receivingBitPos = 0;
|
||
|
receivingByte = 0;
|
||
|
//debug.SC_APPEND_STR_INT("received byte", (long)receivedByte);
|
||
|
|
||
|
if (status == OS_WaitCommand && receivedByte == 0xF0)
|
||
|
{
|
||
|
status = OS_SearchRom;
|
||
|
searchROMReadingMasterResponseBit = false;
|
||
|
searchROMSendingInverse = false;
|
||
|
searchROMCurrentByte = 0;
|
||
|
searchROMCurrentBit = 0;
|
||
|
byte currentByte = owROM[searchROMCurrentByte];
|
||
|
searchRomNextBit = bitRead(currentByte, searchROMCurrentBit);
|
||
|
searchRomNextBitToSend = searchROMSendingInverse ? !searchRomNextBit : searchRomNextBit;
|
||
|
//attachInterrupt(InterruptNumber, onewireInterruptSearchROM, FALLING);
|
||
|
setTimerEvent(10, owSearchSendBit);
|
||
|
detachInterrupt(InterruptNumber);
|
||
|
onLeaveInterrupt();
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
onLeaveInterrupt();
|
||
|
}
|
||
|
|
||
|
bool ignoreNextFallingEdge = false;
|
||
|
|
||
|
void owSearchSendBit()
|
||
|
{
|
||
|
onEnterInterrupt();
|
||
|
|
||
|
// wait for a falling edge (active wait is more reliable than interrupts to send the bit fast enough)
|
||
|
while (!owPin.read());
|
||
|
while (owPin.read());
|
||
|
|
||
|
//sendBit(searchRomNextBitToSend);
|
||
|
if (searchRomNextBitToSend)
|
||
|
{
|
||
|
//delayMicroseconds(SendBitDuration);
|
||
|
ignoreNextFallingEdge = false;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
owPullLow();
|
||
|
//delayMicroseconds(SendBitDuration);
|
||
|
//owRelease();
|
||
|
ignoreNextFallingEdge = true;
|
||
|
}
|
||
|
|
||
|
unsigned long sendBitStart = micros();
|
||
|
|
||
|
if (searchROMSendingInverse)
|
||
|
{
|
||
|
searchROMSendingInverse = false;
|
||
|
searchROMReadingMasterResponseBit = true;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
searchROMSendingInverse = true;
|
||
|
}
|
||
|
|
||
|
byte currentByte = owROM[searchROMCurrentByte];
|
||
|
searchRomNextBit = bitRead(currentByte, searchROMCurrentBit);
|
||
|
searchRomNextBitToSend = searchROMSendingInverse ? !searchRomNextBit : searchRomNextBit;
|
||
|
|
||
|
if (searchROMReadingMasterResponseBit)
|
||
|
{
|
||
|
ignoreNextFallingEdge = true;
|
||
|
attachInterrupt(InterruptNumber, onewireInterruptSearchROM, FALLING);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
setTimerEvent(10, owSearchSendBit);
|
||
|
}
|
||
|
|
||
|
delayMicroseconds(SendBitDuration - (micros() - sendBitStart));
|
||
|
owRelease();
|
||
|
|
||
|
onLeaveInterrupt();
|
||
|
}
|
||
|
|
||
|
void onewireInterruptSearchROM()
|
||
|
{
|
||
|
onEnterInterrupt();
|
||
|
|
||
|
if (ignoreNextFallingEdge)
|
||
|
{
|
||
|
ignoreNextFallingEdge = false;
|
||
|
onLeaveInterrupt();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (searchROMReadingMasterResponseBit)
|
||
|
{
|
||
|
bool bit = readBit();
|
||
|
|
||
|
if (bit != searchRomNextBit)
|
||
|
{
|
||
|
debug.SC_APPEND_STR("Master didn't send our bit, leaving ROM search");
|
||
|
status = OS_WaitReset;
|
||
|
attachInterrupt(InterruptNumber, owWaitResetInterrupt, CHANGE);
|
||
|
onLeaveInterrupt();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
searchROMReadingMasterResponseBit = false;
|
||
|
++searchROMCurrentBit;
|
||
|
if (searchROMCurrentBit == 8)
|
||
|
{
|
||
|
++searchROMCurrentByte;
|
||
|
searchROMCurrentBit = 0;
|
||
|
//debug.SC_APPEND_STR("sent another ROM byte");
|
||
|
}
|
||
|
|
||
|
if (searchROMCurrentByte == 8)
|
||
|
{
|
||
|
searchROMCurrentByte = 0;
|
||
|
status = OS_WaitReset;
|
||
|
debug.SC_APPEND_STR("ROM sent entirely");
|
||
|
attachInterrupt(InterruptNumber, owWaitResetInterrupt, CHANGE);
|
||
|
onLeaveInterrupt();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
byte currentByte = owROM[searchROMCurrentByte];
|
||
|
searchRomNextBit = bitRead(currentByte, searchROMCurrentBit);
|
||
|
searchRomNextBitToSend = searchROMSendingInverse ? !searchRomNextBit : searchRomNextBit;
|
||
|
|
||
|
setTimerEvent(10, owSearchSendBit);
|
||
|
detachInterrupt(InterruptNumber);
|
||
|
onLeaveInterrupt();
|
||
|
return;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
sendBit(searchRomNextBitToSend);
|
||
|
/*if (bitToSend)
|
||
|
debug.SC_APPEND_STR("sent ROM search bit : 1");
|
||
|
else
|
||
|
debug.SC_APPEND_STR("sent ROM search bit : 0");*/
|
||
|
|
||
|
if (searchROMSendingInverse)
|
||
|
{
|
||
|
searchROMSendingInverse = false;
|
||
|
searchROMReadingMasterResponseBit = true;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
searchROMSendingInverse = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
byte currentByte = owROM[searchROMCurrentByte];
|
||
|
searchRomNextBit = bitRead(currentByte, searchROMCurrentBit);
|
||
|
searchRomNextBitToSend = searchROMSendingInverse ? !searchRomNextBit : searchRomNextBit;
|
||
|
|
||
|
onLeaveInterrupt();
|
||
|
}
|
||
|
|
||
|
bool readBit()
|
||
|
{
|
||
|
delayMicroseconds(ReadBitSamplingTime);
|
||
|
bool bit = owPin.read();
|
||
|
//owOutTestPin.write(!owOutTestPin.read());
|
||
|
//owOutTestPin.write(!owOutTestPin.read());
|
||
|
return bit;
|
||
|
}
|
||
|
|
||
|
void sendBit(bool bit)
|
||
|
{
|
||
|
if (bit)
|
||
|
{
|
||
|
delayMicroseconds(SendBitDuration);
|
||
|
ignoreNextFallingEdge = false;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
owPullLow();
|
||
|
delayMicroseconds(SendBitDuration);
|
||
|
owRelease();
|
||
|
ignoreNextFallingEdge = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void beginPresence()
|
||
|
{
|
||
|
unsigned long now = micros();
|
||
|
owPullLow();
|
||
|
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_WaitCommand;
|
||
|
attachInterrupt(InterruptNumber, owReceiveCommandInterrupt, FALLING);
|
||
|
receivingByte = 0;
|
||
|
receivingBitPos = 0;
|
||
|
bitStart = (unsigned long)-1;
|
||
|
}
|
||
|
|
||
|
ISR(TIMER1_COMPA_vect) // timer1 interrupt
|
||
|
{
|
||
|
TCCR1B = 0; // disable clock
|
||
|
void(*event)() = timerEvent;
|
||
|
timerEvent = 0;
|
||
|
if (event != 0)
|
||
|
event();
|
||
|
}
|
||
|
|
||
|
uint8_t crc8(char addr[], uint8_t len) {
|
||
|
uint8_t crc = 0;
|
||
|
|
||
|
while (len--) {
|
||
|
uint8_t inbyte = *addr++;
|
||
|
for (uint8_t i = 8; i; i--) {
|
||
|
uint8_t mix = (crc ^ inbyte) & 0x01;
|
||
|
crc >>= 1;
|
||
|
if (mix) crc ^= 0x8C;
|
||
|
inbyte >>= 1;
|
||
|
}
|
||
|
}
|
||
|
return crc;
|
||
|
}
|