#include "OneWireSlave.h" #define DEBUG_LOG #ifdef DEBUG_LOG #include "SerialChannel.h" extern SerialChannel debug; Pin dbgOutput(3); #endif namespace { const unsigned long ResetMinDuration = 480; const unsigned long ResetMaxDuration = 900; const unsigned long PresenceWaitDuration = 30; const unsigned long PresenceDuration = 300; const unsigned long ReadBitSamplingTime = 30; const unsigned long SendBitDuration = 35; const byte ReceiveCommand = (byte)-1; void(*timerEvent)() = 0; } OneWireSlave OneWire; byte OneWireSlave::rom_[8]; Pin OneWireSlave::pin_; byte OneWireSlave::tccr1bEnable_; unsigned long OneWireSlave::resetStart_; unsigned long OneWireSlave::lastReset_; void(*OneWireSlave::receiveBitCallback_)(bool bit, bool error); void(*OneWireSlave::bitSentCallback_)(bool error); byte OneWireSlave::receivingByte_; byte OneWireSlave::receivingBitPos_; byte OneWireSlave::receiveTarget_; byte OneWireSlave::searchRomBytePos_; byte OneWireSlave::searchRomBitPos_; bool OneWireSlave::searchRomInverse_; ISR(TIMER1_COMPA_vect) // timer1 interrupt { TCCR1B = 0; // disable clock void(*event)() = timerEvent; timerEvent = 0; event(); } void OneWireSlave::begin(byte* rom, byte pinNumber) { pin_ = Pin(pinNumber); resetStart_ = (unsigned long)-1; lastReset_ = 0; memcpy(rom_, rom, 7); rom_[7] = crc8_(rom_, 7); #ifdef DEBUG_LOG debug.append("Enabling 1-wire library"); dbgOutput.outputMode(); dbgOutput.writeHigh(); #endif cli(); // disable interrupts pin_.inputMode(); pin_.writeLow(); // make sure the internal pull-up resistor is disabled // prepare hardware timer TCCR1A = 0; TCCR1B = 0; TIMSK1 |= (1 << OCIE1A); // enable timer compare interrupt tccr1bEnable_ = (1 << WGM12) | (1 << CS11) | (1 << CS10); // turn on CTC mode with 64 prescaler // start 1-wire activity beginWaitReset_(); sei(); // enable interrupts } void OneWireSlave::end() { #ifdef DEBUG_LOG debug.append("Disabling 1-wire library"); #endif cli(); disableTimer_(); pin_.detachInterrupt(); releaseBus_(); sei(); } bool OneWireSlave::read(byte& b) { return false; } void OneWireSlave::setReceiveCallback(void(*callback)()) { } void OneWireSlave::write(byte* bytes, short numBytes, void(*complete)(bool error)) { } byte OneWireSlave::crc8_(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; } 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; TCNT1 = 0; OCR1A = skipTicks; timerEvent = handler; TCCR1B = tccr1bEnable_; } void OneWireSlave::disableTimer_() { TCCR1B = 0; } void OneWireSlave::onEnterInterrupt_() { dbgOutput.writeLow(); } void OneWireSlave::onLeaveInterrupt_() { dbgOutput.writeHigh(); } void OneWireSlave::error_(const char* message) { #ifdef DEBUG_LOG debug.append(message); #endif beginWaitReset_(); } void OneWireSlave::pullLow_() { pin_.outputMode(); pin_.writeLow(); #ifdef DEBUG_LOG //dbgOutput.writeLow(); #endif } void OneWireSlave::releaseBus_() { pin_.inputMode(); #ifdef DEBUG_LOG //dbgOutput.writeHigh(); #endif } void OneWireSlave::beginWaitReset_() { disableTimer_(); 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(); setTimerEvent_(PresenceWaitDuration - (micros() - now), &OneWireSlave::beginPresence_); } } else { resetStart_ = now; } onLeaveInterrupt_(); } void OneWireSlave::beginPresence_() { unsigned long now = micros(); pullLow_(); setTimerEvent_(PresenceDuration, &OneWireSlave::endPresence_); #ifdef DEBUG_LOG debug.SC_APPEND_STR_TIME("reset", lastReset_); debug.SC_APPEND_STR_TIME("beginPresence", now); #endif } void OneWireSlave::endPresence_() { unsigned long now = micros(); releaseBus_(); #ifdef DEBUG_LOG debug.SC_APPEND_STR_TIME("endPresence", now); #endif beginWaitCommand_(); } void OneWireSlave::beginWaitCommand_() { receiveTarget_ = ReceiveCommand; beginReceive_(); } void OneWireSlave::beginReceive_() { receivingByte_ = 0; receivingBitPos_ = 0; beginReceiveBit_(&OneWireSlave::onBitReceived_); } 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::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_(); 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_(); bitSentCallback_(false); onLeaveInterrupt_(); } void OneWireSlave::readBit_() { onEnterInterrupt_(); bool bit = pin_.read(); receiveBitCallback_(bit, false); //dbgOutput.writeLow(); //dbgOutput.writeHigh(); onLeaveInterrupt_(); } void OneWireSlave::onBitReceived_(bool bit, bool error) { if (error) { error_("Invalid bit"); beginWaitReset_(); return; } receivingByte_ |= ((bit ? 1 : 0) << receivingBitPos_); ++receivingBitPos_; if (receivingBitPos_ == 8) { #ifdef DEBUG_LOG debug.SC_APPEND_STR_INT("received byte", (long)receivingByte_); #endif if (receiveTarget_ == ReceiveCommand) { if (receivingByte_ == 0xF0) { beginSearchRom_(); return; } else { // TODO: send command to client code beginWaitReset_(); return; } } else { // TODO: add byte in receive buffer beginWaitReset_(); 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"); beginWaitReset_(); 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"); beginWaitReset_(); return; } byte currentByte = rom_[searchRomBytePos_]; bool currentBit = bitRead(currentByte, searchRomBitPos_); if (bit == currentBit) { ++searchRomBitPos_; if (searchRomBitPos_ == 8) { searchRomBitPos_ = 0; ++searchRomBytePos_; } if (searchRomBytePos_ == 8) { #ifdef DEBUG_LOG debug.SC_APPEND_STR("ROM sent entirely"); #endif beginWaitReset_(); } else { beginSearchRomSendBit_(); } } else { #ifdef DEBUG_LOG debug.SC_APPEND_STR("Leaving ROM search"); #endif beginWaitReset_(); } }