#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; }