From d698f10a08a08e92961053c0206516edd7ff32c8 Mon Sep 17 00:00:00 2001 From: IanF Date: Mon, 10 Dec 2018 09:52:46 +0100 Subject: [PATCH] Upload files to '' Added support for attiny85 at 16mhz. Attiny85 fuses settings and OSCCAL calibration are required to meet the OneWire timing specifications --- LowLevel.h | 51 +- OneWireSlave.cpp | 1479 ++++++++++++++++++++++++---------------------- OneWireSlave.h | 210 +++---- 3 files changed, 902 insertions(+), 838 deletions(-) diff --git a/LowLevel.h b/LowLevel.h index db7e332..cb612e7 100644 --- a/LowLevel.h +++ b/LowLevel.h @@ -29,20 +29,38 @@ #define DIRECT_WRITE_HIGH(base, mask) ((*((base)+2)) |= (mask)) #if defined (__AVR_ATtiny85__) -#define CLEARINTERRUPT GIFR |= (1 << INTF0) -#include "UserTimer.h" //ATtiny-support based on TinyCore1 Arduino-core for ATtiny at http://github.com/Coding-Badly/TinyCore1.git + +/* Note : The attiny85 clock speed = 16mhz (fuses L 0xF1, H 0xDF. E oxFF + OSCCAL VALUE must also be calibrated to 16mhz + +*/ +#define CLEARINTERRUPT GIFR |= (1 << INTF0) | (1<>= 1; - if (mix) crc ^= 0x8C; - inbyte >>= 1; - } - } - return crc; -} - -void OneWireSlave::setTimerEvent_(short delayMicroSeconds, void(*handler)()) -{ - delayMicroSeconds -= 10; // remove overhead (tuned on Arduino Uno) - - short skipTicks = (delayMicroSeconds - 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; - timerEvent = handler; - UserTimer_Run(skipTicks); -} - -void OneWireSlave::disableTimer_() -{ - UserTimer_Stop(); -} - -void OneWireSlave::onEnterInterrupt_() -{ -} - -void OneWireSlave::onLeaveInterrupt_() -{ -} - -void OneWireSlave::error_(const char* message) -{ - if (logCallback_ != 0) - logCallback_(message); - endWrite_(true); - if (clientReceiveCallback_ != 0) - clientReceiveCallback_(RE_Error, 0); -} - -void OneWireSlave::pullLow_() -{ - pin_.outputMode(); - pin_.writeLow(); -} - -void OneWireSlave::releaseBus_() -{ - pin_.inputMode(); -} - -void OneWireSlave::beginResetDetection_() -{ - setTimerEvent_(ResetMinDuration - 50, &OneWireSlave::resetCheck_); - resetStart_ = micros() - 50; -} - -void OneWireSlave::beginResetDetectionSendZero_() -{ - setTimerEvent_(ResetMinDuration - SendBitDuration - 50, &OneWireSlave::resetCheck_); - resetStart_ = micros() - SendBitDuration - 50; -} - -void OneWireSlave::cancelResetDetection_() -{ - disableTimer_(); - resetStart_ = (unsigned long)-1; -} - -void OneWireSlave::resetCheck_() -{ - onEnterInterrupt_(); - if (!pin_.read()) - { - pin_.attachInterrupt(&OneWireSlave::waitReset_, CHANGE); - // log("Reset detected during another operation"); - } - onLeaveInterrupt_(); -} - -void OneWireSlave::beginReceiveBit_(void(*completeCallback)(bool bit, bool error)) -{ - receiveBitCallback_ = completeCallback; - pin_.attachInterrupt(&OneWireSlave::receive_, FALLING); -} - -void OneWireSlave::receive_() -{ - onEnterInterrupt_(); - - pin_.detachInterrupt(); - setTimerEvent_(ReadBitSamplingTime, &OneWireSlave::readBit_); - - onLeaveInterrupt_(); -} - -void OneWireSlave::readBit_() -{ - onEnterInterrupt_(); - - bool bit = pin_.read(); - if (bit) - cancelResetDetection_(); - else - beginResetDetection_(); - receiveBitCallback_(bit, false); - //dbgOutput.writeLow(); - //dbgOutput.writeHigh(); - - onLeaveInterrupt_(); -} - -void OneWireSlave::beginSendBit_(bool bit, void(*completeCallback)(bool error)) -{ - bitSentCallback_ = completeCallback; - if (bit) - { - pin_.attachInterrupt(&OneWireSlave::sendBitOne_, FALLING); - } - else - { - pin_.attachInterrupt(&OneWireSlave::sendBitZero_, FALLING); - } -} - -void OneWireSlave::sendBitOne_() -{ - onEnterInterrupt_(); - - beginResetDetection_(); - bitSentCallback_(false); - - onLeaveInterrupt_(); -} - -void OneWireSlave::sendBitZero_() -{ - pullLow_(); // this must be executed first because the timing is very tight with some master devices - - onEnterInterrupt_(); - - pin_.detachInterrupt(); - setTimerEvent_(SendBitDuration, &OneWireSlave::endSendBitZero_); - - onLeaveInterrupt_(); -} - -void OneWireSlave::endSendBitZero_() -{ - onEnterInterrupt_(); - - releaseBus_(); - beginResetDetectionSendZero_(); - bitSentCallback_(false); - - onLeaveInterrupt_(); -} - -void OneWireSlave::beginWaitReset_() -{ - disableTimer_(); - pin_.inputMode(); - pin_.attachInterrupt(&OneWireSlave::waitReset_, CHANGE); - resetStart_ = (unsigned int)-1; -} - -void OneWireSlave::waitReset_() -{ - onEnterInterrupt_(); - bool state = pin_.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) - { - ERROR("Reset too long"); - onLeaveInterrupt_(); - return; - } - - lastReset_ = now; - pin_.detachInterrupt(); - unsigned long alreadyElapsedTime = micros() - now; - setTimerEvent_(alreadyElapsedTime < PresenceWaitDuration ? PresenceWaitDuration - alreadyElapsedTime : 0, &OneWireSlave::beginPresence_); - endWrite_(true, false); - if (clientReceiveCallback_ != 0) - clientReceiveCallback_(RE_Reset, 0); - } - } - else - { - resetStart_ = now; - } - onLeaveInterrupt_(); -} - -void OneWireSlave::beginPresence_() -{ - pullLow_(); - setTimerEvent_(PresenceDuration, &OneWireSlave::endPresence_); -} - -void OneWireSlave::endPresence_() -{ - releaseBus_(); - - beginWaitCommand_(); -} - -void OneWireSlave::beginWaitCommand_() -{ - bufferPos_ = ReceiveCommand; - beginReceive_(); -} - -void OneWireSlave::beginReceive_() -{ - receivingByte_ = 0; - bufferBitPos_ = 0; - beginReceiveBit_(&OneWireSlave::onBitReceived_); -} - -void OneWireSlave::onBitReceived_(bool bit, bool error) -{ - if (error) - { - ERROR("Invalid bit"); - if (bufferPos_ >= 0) - receiveBytesCallback_(true); - return; - } - - receivingByte_ |= ((bit ? 1 : 0) << bufferBitPos_); - ++bufferBitPos_; - - if (clientReceiveBitCallback_ != 0 && bufferPos_ != ReceiveCommand) - clientReceiveBitCallback_(bit); - - if (bufferBitPos_ == 8) - { - // log("received byte", (long)receivingByte_); - - if (bufferPos_ == ReceiveCommand) - { - bufferPos_ = 0; - switch (receivingByte_) - { - case 0xF0: // SEARCH ROM - resumeCommandFlag_ = false; - beginSearchRom_(); - return; - case 0xEC: // CONDITIONAL SEARCH ROM - resumeCommandFlag_ = false; - if (alarmedFlag_) - { - beginSearchRom_(); - } - else - { - beginWaitReset_(); - } - return; - case 0x33: // READ ROM - resumeCommandFlag_ = false; - beginWriteBytes_(rom_, 8, &OneWireSlave::noOpCallback_); - return; - case 0x55: // MATCH ROM - resumeCommandFlag_ = false; - beginReceiveBytes_(scratchpad_, 8, &OneWireSlave::matchRomBytesReceived_); - return; - case 0xCC: // SKIP ROM - resumeCommandFlag_ = false; - beginReceiveBytes_(scratchpad_, 1, &OneWireSlave::notifyClientByteReceived_); - return; - case 0xA5: // RESUME - if (resumeCommandFlag_) - { - beginReceiveBytes_(scratchpad_, 1, &OneWireSlave::notifyClientByteReceived_); - } - else - { - beginWaitReset_(); - } - return; - default: - ERROR("Unknown command"); - return; - } - } - else - { - recvBuffer_[bufferPos_++] = receivingByte_; - receivingByte_ = 0; - bufferBitPos_ = 0; - if (bufferPos_ == bufferLength_) - { - beginWaitReset_(); - receiveBytesCallback_(false); - return; - } - } - } - - beginReceiveBit_(&OneWireSlave::onBitReceived_); -} - -void OneWireSlave::beginSearchRom_() -{ - searchRomBytePos_ = 0; - searchRomBitPos_ = 0; - searchRomInverse_ = false; - - beginSearchRomSendBit_(); -} - -void OneWireSlave::beginSearchRomSendBit_() -{ - byte currentByte = rom_[searchRomBytePos_]; - bool currentBit = bitRead(currentByte, searchRomBitPos_); - bool bitToSend = searchRomInverse_ ? !currentBit : currentBit; - - beginSendBit_(bitToSend, &OneWireSlave::continueSearchRom_); -} - -void OneWireSlave::continueSearchRom_(bool error) -{ - if (error) - { - ERROR("Failed to send bit"); - return; - } - - searchRomInverse_ = !searchRomInverse_; - if (searchRomInverse_) - { - beginSearchRomSendBit_(); - } - else - { - beginReceiveBit_(&OneWireSlave::searchRomOnBitReceived_); - } -} - -void OneWireSlave::searchRomOnBitReceived_(bool bit, bool error) -{ - if (error) - { - ERROR("Bit read error during ROM search"); - return; - } - - byte currentByte = rom_[searchRomBytePos_]; - bool currentBit = bitRead(currentByte, searchRomBitPos_); - - if (bit == currentBit) - { - ++searchRomBitPos_; - if (searchRomBitPos_ == 8) - { - searchRomBitPos_ = 0; - ++searchRomBytePos_; - } - - if (searchRomBytePos_ == 8) - { - // log("ROM sent entirely"); - - beginWaitReset_(); - } - else - { - beginSearchRomSendBit_(); - } - } - else - { - // log("Leaving ROM search"); - beginWaitReset_(); - } -} - -void OneWireSlave::beginWriteBytes_(const byte* data, short numBytes, void(*complete)(bool error)) -{ - sendBuffer_ = data; - bufferLength_ = numBytes; - bufferPos_ = 0; - bufferBitPos_ = 0; - sendBytesCallback_ = complete; - - if (sendBuffer_ != 0 && bufferLength_ > 0) - { - bool bit = bitRead(sendBuffer_[0], 0); - beginSendBit_(bit, &OneWireSlave::bitSent_); - } - else - { - endWrite_(true); - beginReceiveBytes_(scratchpad_, 1, &OneWireSlave::notifyClientByteReceived_); - } -} - -void OneWireSlave::bitSent_(bool error) -{ - if (error) - { - ERROR("error sending a bit"); - sendBytesCallback_(true); - return; - } - - ++bufferBitPos_; - if (bufferBitPos_ == 8) - { - bufferBitPos_ = 0; - ++bufferPos_; - } - - if (bufferPos_ == bufferLength_) - { - endWrite_(false); - sendBytesCallback_(false); - return; - } - - bool bit = bitRead(sendBuffer_[bufferPos_], bufferBitPos_); - beginSendBit_(bit, &OneWireSlave::bitSent_); -} - -void OneWireSlave::beginReceiveBytes_(byte* buffer, short numBytes, void(*complete)(bool error)) -{ - recvBuffer_ = buffer; - bufferLength_ = numBytes; - bufferPos_ = 0; - receiveBytesCallback_ = complete; - beginReceive_(); -} - -void OneWireSlave::noOpCallback_(bool error) -{ - if (error) - ERROR("error during an internal 1-wire operation"); -} - -void OneWireSlave::matchRomBytesReceived_(bool error) -{ - if (error) - { - resumeCommandFlag_ = false; - ERROR("error receiving match rom bytes"); - return; - } - - if (memcmp(rom_, scratchpad_, 8) == 0) - { - // log("ROM matched"); - resumeCommandFlag_ = true; - beginReceiveBytes_(scratchpad_, 1, &OneWireSlave::notifyClientByteReceived_); - } - else - { - // log("ROM not matched"); - resumeCommandFlag_ = false; - beginWaitReset_(); - } -} - -void OneWireSlave::notifyClientByteReceived_(bool error) -{ - if (error) - { - if (clientReceiveCallback_ != 0) - clientReceiveCallback_(RE_Error, 0); - ERROR("error receiving custom bytes"); - return; - } - - beginReceiveBytes_(scratchpad_, 1, &OneWireSlave::notifyClientByteReceived_); - if (clientReceiveCallback_ != 0) - clientReceiveCallback_(RE_Byte, scratchpad_[0]); -} +#include "OneWireSlave.h" + +// uncomment this line to enable sending messages along with errors (but takes more program memory) +//#define ERROR_MESSAGES + +#ifdef ERROR_MESSAGES +#define ERROR(msg) error_(msg) +#else +#define ERROR(msg) error_(0) +#endif + +namespace +{ +#if defined (__AVR_ATtiny85__) + +const unsigned long ResetMinDuration = 480; // min 480us / 484us measured +const unsigned long ResetMaxDuration = 640; // + +const unsigned long PresenceWaitDuration = 35; // spec 15us to 60us / 40us measured + +const unsigned long PresenceDuration = 150; // spec 60us to 240us / 148us measured + +const unsigned long ReadBitSamplingTime = 15; // spec > 15us to 60us / 31us measured + +// SendBitDuration varies the bus release time to indicate a "1" +// measured total send bit duration is 63us versus 60us in the spec +const unsigned long SendBitDuration = 20; // bus release time spec 15us / measured 19us + + +#elif defined (__AVR_ATmega328P__) + +const unsigned long ResetMinDuration = 480; +const unsigned long ResetMaxDuration = 900; + +const unsigned long PresenceWaitDuration = 15; + +const unsigned long PresenceDuration = 200; + +const unsigned long ReadBitSamplingTime = 25; + +const unsigned long SendBitDuration = 35; + +#endif + +const byte ReceiveCommand = (byte) - 1; + +void(*timerEvent)() = 0; +} + +OneWireSlave OWSlave; + +byte OneWireSlave::rom_[8]; +byte OneWireSlave::scratchpad_[8]; +Pin OneWireSlave::pin_; + +unsigned long OneWireSlave::resetStart_; +unsigned long OneWireSlave::lastReset_; + +void(*OneWireSlave::receiveBitCallback_)(bool bit, bool error); +void(*OneWireSlave::bitSentCallback_)(bool error); +void(*OneWireSlave::clientReceiveCallback_)(ReceiveEvent evt, byte data); +void(*OneWireSlave::clientReceiveBitCallback_)(bool bit); + +byte OneWireSlave::receivingByte_; + +byte OneWireSlave::searchRomBytePos_; +byte OneWireSlave::searchRomBitPos_; +bool OneWireSlave::searchRomInverse_; +bool OneWireSlave::resumeCommandFlag_; +bool OneWireSlave::alarmedFlag_; + +const byte* OneWireSlave::sendBuffer_; +byte* OneWireSlave::recvBuffer_; +short OneWireSlave::bufferLength_; +byte OneWireSlave::bufferBitPos_; +short OneWireSlave::bufferPos_; +void(*OneWireSlave::receiveBytesCallback_)(bool error); +void(*OneWireSlave::sendBytesCallback_)(bool error); + +volatile bool OneWireSlave::waitingSynchronousWriteToComplete_; +volatile bool OneWireSlave::synchronousWriteError_; + +bool OneWireSlave::sendingClientBytes_; + +bool OneWireSlave::singleBit_; +bool OneWireSlave::singleBitRepeat_; +void(*OneWireSlave::singleBitSentCallback_)(bool error); + +void(*OneWireSlave::logCallback_)(const char* message); + + +ISR(USERTIMER_COMPA_vect) // timer1 interrupt +{ + UserTimer_Stop(); // disable clock + void(*event)() = timerEvent; + timerEvent = 0; + event(); +} + +void OneWireSlave::begin(const byte* rom, byte pinNumber) +{ + pin_ = Pin(pinNumber); + resetStart_ = (unsigned long) - 1; + lastReset_ = 0; + + memcpy(rom_, rom, 7); + rom_[7] = crc8(rom_, 7); + + resumeCommandFlag_ = false; + alarmedFlag_ = false; + + clientReceiveBitCallback_ = 0; + sendingClientBytes_ = false; + + // log("Enabling 1-wire library") + + cli(); // disable interrupts + pin_.inputMode(); + pin_.writeLow(); // make sure the internal pull-up resistor is disabled + + // prepare hardware timer + UserTimer_Init(); + + // start 1-wire activity + beginWaitReset_(); + sei(); // enable interrupts +} + +void OneWireSlave::end() +{ + // log("Disabling 1-wire library"); + + cli(); + disableTimer_(); + pin_.detachInterrupt(); + releaseBus_(); + sei(); +} + +bool OneWireSlave::write(const byte* bytes, short numBytes) +{ + // TODO: put the arduino to sleep between interrupts to save power? + waitingSynchronousWriteToComplete_ = true; + beginWrite(bytes, numBytes, &OneWireSlave::onSynchronousWriteComplete_); + while (waitingSynchronousWriteToComplete_) + delay(1); + return !synchronousWriteError_; +} + +void OneWireSlave::onSynchronousWriteComplete_(bool error) +{ + synchronousWriteError_ = error; + waitingSynchronousWriteToComplete_ = false; +} + +void OneWireSlave::beginWrite(const byte* bytes, short numBytes, void(*complete)(bool error)) +{ + cli(); + endWrite_(true); + sendingClientBytes_ = true; + beginWriteBytes_(bytes, numBytes, complete == 0 ? noOpCallback_ : complete); + sei(); +} + +void OneWireSlave::endWrite_(bool error, bool resetInterrupts) +{ + if (resetInterrupts) + beginWaitReset_(); + + if (sendingClientBytes_) + { + sendingClientBytes_ = false; + if (sendBytesCallback_ != 0) + { + void(*callback)(bool error) = sendBytesCallback_; + sendBytesCallback_ = noOpCallback_; + callback(error); + } + } + else if (singleBitSentCallback_ != 0) + { + void(*callback)(bool) = singleBitSentCallback_; + singleBitSentCallback_ = 0; + callback(error); + } +} + +bool OneWireSlave::writeBit(bool value) +{ + // TODO: put the arduino to sleep between interrupts to save power? + waitingSynchronousWriteToComplete_ = true; + beginWriteBit(value, false, &OneWireSlave::onSynchronousWriteComplete_); + while (waitingSynchronousWriteToComplete_) + delay(1); + return !synchronousWriteError_; +} + +void OneWireSlave::beginWriteBit(bool value, bool repeat, void(*bitSent)(bool)) +{ + cli(); + endWrite_(true); + + singleBit_ = value; + singleBitRepeat_ = repeat; + singleBitSentCallback_ = bitSent; + beginSendBit_(value, &OneWireSlave::onSingleBitSent_); + sei(); +} + +void OneWireSlave::onSingleBitSent_(bool error) +{ + if (!error && singleBitRepeat_) + { + beginSendBit_(singleBit_, &OneWireSlave::onSingleBitSent_); + } + else + { + beginReceiveBytes_(scratchpad_, 1, &OneWireSlave::notifyClientByteReceived_); + } + + if (singleBitSentCallback_ != 0) + { + void(*callback)(bool) = singleBitSentCallback_; + singleBitSentCallback_ = 0; + callback(error); + } +} + +void OneWireSlave::stopWrite() +{ + beginWrite(0, 0, 0); +} + +void OneWireSlave::alarmed(bool value) +{ + alarmedFlag_ = value; +} + +byte OneWireSlave::crc8(const byte* data, short numBytes) +{ + byte crc = 0; + + while (numBytes--) { + byte inbyte = *data++; + for (byte i = 8; i; i--) { + byte mix = (crc ^ inbyte) & 0x01; + crc >>= 1; + if (mix) crc ^= 0x8C; + inbyte >>= 1; + } + } + return crc; +} + +#if defined (__AVR_ATtiny85__) + +void OneWireSlave::setTimerEvent_(short delayMicroSeconds, void(*handler)()) +{ + delayMicroSeconds -= 8; // remove overhead (tuned on attiny85, values 0 - 10 work ok) + + short skipTicks = delayMicroSeconds / 2; // 16mhz clock, prescaler = 32 + if (skipTicks < 1) skipTicks = 1; + timerEvent = handler; + UserTimer_Run(skipTicks); +} + +#elif defined (__AVR_ATmega328P__) + +void OneWireSlave::setTimerEvent_(short delayMicroSeconds, void(*handler)()) +{ + delayMicroSeconds -= 10; // remove overhead (tuned on Arduino Uno) + + // prescaler 64 + short skipTicks = (delayMicroSeconds - 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; + timerEvent = handler; + UserTimer_Run(skipTicks); +} + +#endif + +void OneWireSlave::disableTimer_() +{ + UserTimer_Stop(); +} + +void OneWireSlave::onEnterInterrupt_() +{ +} + +void OneWireSlave::onLeaveInterrupt_() +{ +} + +void OneWireSlave::error_(const char* message) +{ + if (logCallback_ != 0) + logCallback_(message); + endWrite_(true); + if (clientReceiveCallback_ != 0) + clientReceiveCallback_(RE_Error, 0); +} + +void OneWireSlave::pullLow_() +{ + pin_.outputMode(); + pin_.writeLow(); +} + +void OneWireSlave::releaseBus_() +{ + pin_.inputMode(); +} + +void OneWireSlave::beginResetDetection_() +{ + setTimerEvent_(ResetMinDuration - 50, &OneWireSlave::resetCheck_); + resetStart_ = micros() - 50; +} + +void OneWireSlave::beginResetDetectionSendZero_() +{ + setTimerEvent_(ResetMinDuration - SendBitDuration - 50, &OneWireSlave::resetCheck_); + resetStart_ = micros() - SendBitDuration - 50; +} + +void OneWireSlave::cancelResetDetection_() +{ + disableTimer_(); + resetStart_ = (unsigned long) - 1; +} + +void OneWireSlave::resetCheck_() +{ + onEnterInterrupt_(); + if (!pin_.read()) + { + pin_.attachInterrupt(&OneWireSlave::waitReset_, CHANGE); + // log("Reset detected during another operation"); + } + onLeaveInterrupt_(); +} + +void OneWireSlave::beginReceiveBit_(void(*completeCallback)(bool bit, bool error)) +{ + receiveBitCallback_ = completeCallback; + pin_.attachInterrupt(&OneWireSlave::receive_, FALLING); +} + +void OneWireSlave::receive_() +{ + onEnterInterrupt_(); + + pin_.detachInterrupt(); + setTimerEvent_(ReadBitSamplingTime, &OneWireSlave::readBit_); + + onLeaveInterrupt_(); +} + +void OneWireSlave::readBit_() +{ + onEnterInterrupt_(); + + bool bit = pin_.read(); + if (bit) + cancelResetDetection_(); + else + beginResetDetection_(); + receiveBitCallback_(bit, false); + //dbgOutput.writeLow(); + //dbgOutput.writeHigh(); + + onLeaveInterrupt_(); +} + +void OneWireSlave::beginSendBit_(bool bit, void(*completeCallback)(bool error)) +{ + bitSentCallback_ = completeCallback; + if (bit) + { + pin_.attachInterrupt(&OneWireSlave::sendBitOne_, FALLING); + } + else + { + pin_.attachInterrupt(&OneWireSlave::sendBitZero_, FALLING); + } +} + +void OneWireSlave::sendBitOne_() +{ + onEnterInterrupt_(); + + beginResetDetection_(); + bitSentCallback_(false); + + onLeaveInterrupt_(); +} + +void OneWireSlave::sendBitZero_() +{ + pullLow_(); // this must be executed first because the timing is very tight with some master devices + + onEnterInterrupt_(); + + pin_.detachInterrupt(); + setTimerEvent_(SendBitDuration, &OneWireSlave::endSendBitZero_); + + onLeaveInterrupt_(); +} + +void OneWireSlave::endSendBitZero_() +{ + onEnterInterrupt_(); + + releaseBus_(); + beginResetDetectionSendZero_(); + bitSentCallback_(false); + + onLeaveInterrupt_(); +} + +void OneWireSlave::beginWaitReset_() +{ + disableTimer_(); + pin_.inputMode(); + pin_.attachInterrupt(&OneWireSlave::waitReset_, CHANGE); + //resetStart_ = (unsigned int) - 1; + resetStart_ = (unsigned long) - 1; +} + + +void OneWireSlave::waitReset_() +{ + onEnterInterrupt_(); + bool state = pin_.read(); + unsigned long now = micros(); + if (state) + { + if (resetStart_ == (unsigned long) - 1) + { + onLeaveInterrupt_(); + return; + } + static unsigned long resetDuration = now - resetStart_; + //resetStart_ = (unsigned int) - 1; + resetStart_ = (unsigned long) - 1; + if (resetDuration >= ResetMinDuration) + { + + // The following test has been removed because of a bug which causes the value of + // resetduration to exceed ResetMaxDuration intermittently. This happens + // only on the attiny platform - possibly due to the implementation of + // micros() in the attiny core + + // if (resetDuration > ResetMaxDuration) + // { + // ERROR("Reset too long"); + // onLeaveInterrupt_(); + // return; + // } + + lastReset_ = now; + pin_.detachInterrupt(); + unsigned long alreadyElapsedTime = micros() - now; + setTimerEvent_(alreadyElapsedTime < PresenceWaitDuration ? PresenceWaitDuration - alreadyElapsedTime : 0, &OneWireSlave::beginPresence_); + endWrite_(true, false); + if (clientReceiveCallback_ != 0) + clientReceiveCallback_(RE_Reset, 0); + } else + { + //ERROR("Reset too short"); + } + } + else + { + resetStart_ = now; + } + onLeaveInterrupt_(); +} + +void OneWireSlave::beginPresence_() +{ + pullLow_(); + setTimerEvent_(PresenceDuration, &OneWireSlave::endPresence_); +} + +void OneWireSlave::endPresence_() +{ + releaseBus_(); + + beginWaitCommand_(); +} + +void OneWireSlave::beginWaitCommand_() +{ + bufferPos_ = ReceiveCommand; + beginReceive_(); +} + +void OneWireSlave::beginReceive_() +{ + receivingByte_ = 0; + bufferBitPos_ = 0; + beginReceiveBit_(&OneWireSlave::onBitReceived_); +} + +void OneWireSlave::onBitReceived_(bool bit, bool error) +{ + if (error) + { + ERROR("Invalid bit"); + if (bufferPos_ >= 0) + receiveBytesCallback_(true); + return; + } + + receivingByte_ |= ((bit ? 1 : 0) << bufferBitPos_); + ++bufferBitPos_; + + if (clientReceiveBitCallback_ != 0 && bufferPos_ != ReceiveCommand) + clientReceiveBitCallback_(bit); + + if (bufferBitPos_ == 8) + { + // log("received byte", (long)receivingByte_); + + if (bufferPos_ == ReceiveCommand) + { + bufferPos_ = 0; + switch (receivingByte_) + { + case 0xF0: // SEARCH ROM + resumeCommandFlag_ = false; + beginSearchRom_(); + return; + case 0xEC: // CONDITIONAL SEARCH ROM + resumeCommandFlag_ = false; + if (alarmedFlag_) + { + beginSearchRom_(); + } + else + { + beginWaitReset_(); + } + return; + case 0x33: // READ ROM + resumeCommandFlag_ = false; + beginWriteBytes_(rom_, 8, &OneWireSlave::noOpCallback_); + return; + case 0x55: // MATCH ROM + resumeCommandFlag_ = false; + beginReceiveBytes_(scratchpad_, 8, &OneWireSlave::matchRomBytesReceived_); + return; + case 0xCC: // SKIP ROM + resumeCommandFlag_ = false; + beginReceiveBytes_(scratchpad_, 1, &OneWireSlave::notifyClientByteReceived_); + return; + case 0xA5: // RESUME + if (resumeCommandFlag_) + { + beginReceiveBytes_(scratchpad_, 1, &OneWireSlave::notifyClientByteReceived_); + } + else + { + beginWaitReset_(); + } + return; + default: + ERROR("Unknown command"); + return; + } + } + else + { + recvBuffer_[bufferPos_++] = receivingByte_; + receivingByte_ = 0; + bufferBitPos_ = 0; + if (bufferPos_ == bufferLength_) + { + beginWaitReset_(); + receiveBytesCallback_(false); + return; + } + } + } + + beginReceiveBit_(&OneWireSlave::onBitReceived_); +} + +void OneWireSlave::beginSearchRom_() +{ + searchRomBytePos_ = 0; + searchRomBitPos_ = 0; + searchRomInverse_ = false; + + beginSearchRomSendBit_(); +} + +void OneWireSlave::beginSearchRomSendBit_() +{ + byte currentByte = rom_[searchRomBytePos_]; + bool currentBit = bitRead(currentByte, searchRomBitPos_); + bool bitToSend = searchRomInverse_ ? !currentBit : currentBit; + + beginSendBit_(bitToSend, &OneWireSlave::continueSearchRom_); +} + +void OneWireSlave::continueSearchRom_(bool error) +{ + if (error) + { + ERROR("Failed to send bit"); + return; + } + + searchRomInverse_ = !searchRomInverse_; + if (searchRomInverse_) + { + beginSearchRomSendBit_(); + } + else + { + beginReceiveBit_(&OneWireSlave::searchRomOnBitReceived_); + } +} + +void OneWireSlave::searchRomOnBitReceived_(bool bit, bool error) +{ + if (error) + { + ERROR("Bit read error during ROM search"); + return; + } + + byte currentByte = rom_[searchRomBytePos_]; + bool currentBit = bitRead(currentByte, searchRomBitPos_); + + if (bit == currentBit) + { + ++searchRomBitPos_; + if (searchRomBitPos_ == 8) + { + searchRomBitPos_ = 0; + ++searchRomBytePos_; + } + + if (searchRomBytePos_ == 8) + { + // log("ROM sent entirely"); + + beginWaitReset_(); + } + else + { + beginSearchRomSendBit_(); + } + } + else + { + // log("Leaving ROM search"); + beginWaitReset_(); + } +} + +void OneWireSlave::beginWriteBytes_(const byte * data, short numBytes, void(*complete)(bool error)) +{ + sendBuffer_ = data; + bufferLength_ = numBytes; + bufferPos_ = 0; + bufferBitPos_ = 0; + sendBytesCallback_ = complete; + + if (sendBuffer_ != 0 && bufferLength_ > 0) + { + bool bit = bitRead(sendBuffer_[0], 0); + beginSendBit_(bit, &OneWireSlave::bitSent_); + } + else + { + endWrite_(true); + beginReceiveBytes_(scratchpad_, 1, &OneWireSlave::notifyClientByteReceived_); + } +} + +void OneWireSlave::bitSent_(bool error) +{ + if (error) + { + ERROR("error sending a bit"); + sendBytesCallback_(true); + return; + } + + ++bufferBitPos_; + if (bufferBitPos_ == 8) + { + bufferBitPos_ = 0; + ++bufferPos_; + } + + if (bufferPos_ == bufferLength_) + { + endWrite_(false); + sendBytesCallback_(false); + return; + } + + bool bit = bitRead(sendBuffer_[bufferPos_], bufferBitPos_); + beginSendBit_(bit, &OneWireSlave::bitSent_); +} + +void OneWireSlave::beginReceiveBytes_(byte * buffer, short numBytes, void(*complete)(bool error)) +{ + recvBuffer_ = buffer; + bufferLength_ = numBytes; + bufferPos_ = 0; + receiveBytesCallback_ = complete; + beginReceive_(); +} + +void OneWireSlave::noOpCallback_(bool error) +{ + if (error) + ERROR("error during an internal 1-wire operation"); +} + +void OneWireSlave::matchRomBytesReceived_(bool error) +{ + if (error) + { + resumeCommandFlag_ = false; + ERROR("error receiving match rom bytes"); + return; + } + + if (memcmp(rom_, scratchpad_, 8) == 0) + { + // log("ROM matched"); + resumeCommandFlag_ = true; + beginReceiveBytes_(scratchpad_, 1, &OneWireSlave::notifyClientByteReceived_); + } + else + { + // log("ROM not matched"); + resumeCommandFlag_ = false; + beginWaitReset_(); + } +} + +void OneWireSlave::notifyClientByteReceived_(bool error) +{ + if (error) + { + if (clientReceiveCallback_ != 0) + clientReceiveCallback_(RE_Error, 0); + ERROR("error receiving custom bytes"); + return; + } + + beginReceiveBytes_(scratchpad_, 1, &OneWireSlave::notifyClientByteReceived_); + if (clientReceiveCallback_ != 0) + clientReceiveCallback_(RE_Byte, scratchpad_[0]); +} diff --git a/OneWireSlave.h b/OneWireSlave.h index dd834dc..1981fe1 100644 --- a/OneWireSlave.h +++ b/OneWireSlave.h @@ -6,141 +6,147 @@ class OneWireSlave { -public: - enum ReceiveEvent - { - RE_Reset, //!< The master has sent a general reset - RE_Byte, //!< The master just sent a byte of data - RE_Error //!< A communication error happened (such as a timeout) ; the library will stop all 1-wire activities until the next reset - }; + public: + enum ReceiveEvent + { + RE_Reset, //!< The master has sent a general reset + RE_Byte, //!< The master just sent a byte of data + RE_Error //!< A communication error happened (such as a timeout) ; the library will stop all 1-wire activities until the next reset + }; - //! Starts listening for the 1-wire master, on the specified pin, as a virtual slave device identified by the specified ROM (7 bytes, starting from the family code, CRC will be computed internally). Reset, Presence, SearchRom and MatchRom are handled automatically. The library will use the external interrupt on the specified pin (note that this is usually not possible with all pins, depending on the board), as well as one hardware timer. Blocking interrupts (either by disabling them explicitely with sei/cli, or by spending time in another interrupt) can lead to malfunction of the library, due to tight timing for some 1-wire operations. - void begin(const byte* rom, byte pinNumber); + //! Starts listening for the 1-wire master, on the specified pin, as a virtual slave device identified by the specified ROM (7 bytes, starting from the family code, CRC will be computed internally). Reset, Presence, SearchRom and MatchRom are handled automatically. The library will use the external interrupt on the specified pin (note that this is usually not possible with all pins, depending on the board), as well as one hardware timer. Blocking interrupts (either by disabling them explicitely with sei/cli, or by spending time in another interrupt) can lead to malfunction of the library, due to tight timing for some 1-wire operations. + void begin(const byte* rom, byte pinNumber); - //! Stops all 1-wire activities, which frees hardware resources for other purposes. - void end(); + //! Stops all 1-wire activities, which frees hardware resources for other purposes. + void end(); - //! Sets (or replaces) a function to be called when something is received. The callback is executed from interrupts and should be as short as possible. Failure to return quickly can prevent the library from correctly reading the next byte. - void setReceiveCallback(void(*callback)(ReceiveEvent evt, byte data)) { clientReceiveCallback_ = callback; } + //! Sets (or replaces) a function to be called when something is received. The callback is executed from interrupts and should be as short as possible. Failure to return quickly can prevent the library from correctly reading the next byte. + void setReceiveCallback(void(*callback)(ReceiveEvent evt, byte data)) { + clientReceiveCallback_ = callback; + } - //! Sets (or replaces) a function to be called when a bit is received. The byte reception callback is called after that if the received bit was the last of a byte. The callback is executed from interrupts and should be as short as possible. Failure to return quickly can prevent the library from correctly reading the next bit. - void setReceiveBitCallback(void(*callback)(bool bit)) { clientReceiveBitCallback_ = callback; } + //! Sets (or replaces) a function to be called when a bit is received. The byte reception callback is called after that if the received bit was the last of a byte. The callback is executed from interrupts and should be as short as possible. Failure to return quickly can prevent the library from correctly reading the next bit. + void setReceiveBitCallback(void(*callback)(bool bit)) { + clientReceiveBitCallback_ = callback; + } + + //! Sets (or replaces) a function to be called when the library has a message to log, if the functionality is enabled in OneWireSlave.cpp. This is for debugging purposes. + void setLogCallback(void(*callback)(const char* message)) { + logCallback_ = callback; + } + + //! Writes the specified bytes synchronously. This function blocks until the write operation has finished. Do not call from an interrupt handler! Returns true in case of success, false if an error occurs. + bool write(const byte* bytes, short numBytes); - //! Sets (or replaces) a function to be called when the library has a message to log, if the functionality is enabled in OneWireSlave.cpp. This is for debugging purposes. - void setLogCallback(void(*callback)(const char* message)) { logCallback_ = callback; } + //! Starts sending the specified bytes. They will be sent in the background, and the buffer must remain valid and unchanged until the write operation has finished or is cancelled. The optional callback is used to notify when the bytes are sent, or if an error occured. Callbacks are executed from interrupts and should be as short as possible. If bytes is null or numBytes is 0, nothing is sent, which is equivalent to calling stopWrite. In any case, calling the write function will cancel the previous write operation if it didn't complete yet. + void beginWrite(const byte* bytes, short numBytes, void(*complete)(bool error)); - //! Writes the specified bytes synchronously. This function blocks until the write operation has finished. Do not call from an interrupt handler! Returns true in case of success, false if an error occurs. - bool write(const byte* bytes, short numBytes); + //! Writes a single bit synchronously. This function blocks until the bit is sent. Do not call from an interrupt handler! Returns true in case of success, false if an error occurs. + bool writeBit(bool value); - //! Starts sending the specified bytes. They will be sent in the background, and the buffer must remain valid and unchanged until the write operation has finished or is cancelled. The optional callback is used to notify when the bytes are sent, or if an error occured. Callbacks are executed from interrupts and should be as short as possible. If bytes is null or numBytes is 0, nothing is sent, which is equivalent to calling stopWrite. In any case, calling the write function will cancel the previous write operation if it didn't complete yet. - void beginWrite(const byte* bytes, short numBytes, void(*complete)(bool error)); + //! Sets a bit that will be sent next time the master asks for one. Optionnaly, the repeat parameter can be set to true to continue sending the same bit each time. In both cases, the send operation can be canceled by calling stopWrite. + void beginWriteBit(bool value, bool repeat = false, void(*bitSent)(bool error) = 0); - //! Writes a single bit synchronously. This function blocks until the bit is sent. Do not call from an interrupt handler! Returns true in case of success, false if an error occurs. - bool writeBit(bool value); + //! Cancels any pending write operation, started by writeBit or write. If this function is called before the master asked for a bit, then nothing is sent to the master. + void stopWrite(); - //! Sets a bit that will be sent next time the master asks for one. Optionnaly, the repeat parameter can be set to true to continue sending the same bit each time. In both cases, the send operation can be canceled by calling stopWrite. - void beginWriteBit(bool value, bool repeat = false, void(*bitSent)(bool error) = 0); + //! Sets the alarmed state, that is used when the master makes a conditional search of alarmed devices. + void alarmed(bool value); - //! Cancels any pending write operation, started by writeBit or write. If this function is called before the master asked for a bit, then nothing is sent to the master. - void stopWrite(); + static byte crc8(const byte* data, short numBytes); - //! Sets the alarmed state, that is used when the master makes a conditional search of alarmed devices. - void alarmed(bool value); + private: + static void setTimerEvent_(short delayMicroSeconds, void(*handler)()); + static void disableTimer_(); - static byte crc8(const byte* data, short numBytes); + static void onEnterInterrupt_(); + static void onLeaveInterrupt_(); -private: - static void setTimerEvent_(short delayMicroSeconds, void(*handler)()); - static void disableTimer_(); + static void error_(const char* message); - static void onEnterInterrupt_(); - static void onLeaveInterrupt_(); + static void pullLow_(); + static void releaseBus_(); - static void error_(const char* message); + static void beginReceiveBit_(void(*completeCallback)(bool bit, bool error)); + static void beginSendBit_(bool bit, void(*completeCallback)(bool error)); - static void pullLow_(); - static void releaseBus_(); + static void beginResetDetection_(); + static void beginResetDetectionSendZero_(); + static void cancelResetDetection_(); - static void beginReceiveBit_(void(*completeCallback)(bool bit, bool error)); - static void beginSendBit_(bool bit, void(*completeCallback)(bool error)); + static void beginWaitReset_(); + static void beginWaitCommand_(); + static void beginReceive_(); + static void onBitReceived_(bool bit, bool error); - static void beginResetDetection_(); - static void beginResetDetectionSendZero_(); - static void cancelResetDetection_(); + static void beginSearchRom_(); + static void beginSearchRomSendBit_(); + static void continueSearchRom_(bool error); + static void searchRomOnBitReceived_(bool bit, bool error); - static void beginWaitReset_(); - static void beginWaitCommand_(); - static void beginReceive_(); - static void onBitReceived_(bool bit, bool error); + static void beginWriteBytes_(const byte* data, short numBytes, void(*complete)(bool error)); + static void beginReceiveBytes_(byte* buffer, short numBytes, void(*complete)(bool error)); - static void beginSearchRom_(); - static void beginSearchRomSendBit_(); - static void continueSearchRom_(bool error); - static void searchRomOnBitReceived_(bool bit, bool error); + static void endWrite_(bool error, bool resetInterrupts = true); - static void beginWriteBytes_(const byte* data, short numBytes, void(*complete)(bool error)); - static void beginReceiveBytes_(byte* buffer, short numBytes, void(*complete)(bool error)); + static void onSynchronousWriteComplete_(bool error); + static void noOpCallback_(bool error); + static void matchRomBytesReceived_(bool error); + static void notifyClientByteReceived_(bool error); + static void bitSent_(bool error); - static void endWrite_(bool error, bool resetInterrupts = true); + // interrupt handlers + static void waitReset_(); + static void beginPresence_(); + static void endPresence_(); + static void receive_(); + static void readBit_(); + static void sendBitOne_(); + static void sendBitZero_(); + static void endSendBitZero_(); + static void resetCheck_(); - static void onSynchronousWriteComplete_(bool error); - static void noOpCallback_(bool error); - static void matchRomBytesReceived_(bool error); - static void notifyClientByteReceived_(bool error); - static void bitSent_(bool error); + private: + static byte rom_[8]; + static byte scratchpad_[8]; + static Pin pin_; - // interrupt handlers - static void waitReset_(); - static void beginPresence_(); - static void endPresence_(); - static void receive_(); - static void readBit_(); - static void sendBitOne_(); - static void sendBitZero_(); - static void endSendBitZero_(); - static void resetCheck_(); + static unsigned long resetStart_; + static unsigned long lastReset_; -private: - static byte rom_[8]; - static byte scratchpad_[8]; - static Pin pin_; + static void(*receiveBitCallback_)(bool bit, bool error); + static void(*bitSentCallback_)(bool error); - static unsigned long resetStart_; - static unsigned long lastReset_; + static byte receivingByte_; - static void(*receiveBitCallback_)(bool bit, bool error); - static void(*bitSentCallback_)(bool error); + static byte searchRomBytePos_; + static byte searchRomBitPos_; + static bool searchRomInverse_; + static bool resumeCommandFlag_; + static bool alarmedFlag_; - static byte receivingByte_; + static const byte* sendBuffer_; + static byte* recvBuffer_; + static short bufferLength_; + static short bufferPos_; + static byte bufferBitPos_; + static void(*receiveBytesCallback_)(bool error); + static void(*sendBytesCallback_)(bool error); - static byte searchRomBytePos_; - static byte searchRomBitPos_; - static bool searchRomInverse_; - static bool resumeCommandFlag_; - static bool alarmedFlag_; + static volatile bool waitingSynchronousWriteToComplete_; + static volatile bool synchronousWriteError_; - static const byte* sendBuffer_; - static byte* recvBuffer_; - static short bufferLength_; - static short bufferPos_; - static byte bufferBitPos_; - static void(*receiveBytesCallback_)(bool error); - static void(*sendBytesCallback_)(bool error); + static bool sendingClientBytes_; - static volatile bool waitingSynchronousWriteToComplete_; - static volatile bool synchronousWriteError_; + static bool singleBit_; + static bool singleBitRepeat_; + static void (*singleBitSentCallback_)(bool error); + static void onSingleBitSent_(bool error); - static bool sendingClientBytes_; + static void(*clientReceiveCallback_)(ReceiveEvent evt, byte data); + static void(*clientReceiveBitCallback_)(bool bit); - static bool singleBit_; - static bool singleBitRepeat_; - static void (*singleBitSentCallback_)(bool error); - static void onSingleBitSent_(bool error); - - static void(*clientReceiveCallback_)(ReceiveEvent evt, byte data); - static void(*clientReceiveBitCallback_)(bool bit); - - static void(*logCallback_)(const char* message); + static void(*logCallback_)(const char* message); }; extern OneWireSlave OWSlave;