forked from youen/OneWireArduinoSlave
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.
608 lines
12 KiB
608 lines
12 KiB
#include "OneWireSlave.h" |
|
|
|
//#define DEBUG_LOG |
|
#define ERROR_MESSAGES |
|
|
|
#ifdef DEBUG_LOG |
|
#include "SerialChannel.h" |
|
extern SerialChannel debug; |
|
Pin dbgOutput(3); |
|
#else |
|
#undef ERROR_MESSAGES |
|
#endif |
|
|
|
#ifdef ERROR_MESSAGES |
|
#define ERROR(msg) error_(msg) |
|
#else |
|
#define ERROR(msg) error_(0) |
|
#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 = 25; |
|
|
|
const unsigned long SendBitDuration = 35; |
|
|
|
const byte ReceiveCommand = (byte)-1; |
|
|
|
void(*timerEvent)() = 0; |
|
} |
|
|
|
OneWireSlave OneWire; |
|
|
|
byte OneWireSlave::rom_[8]; |
|
byte OneWireSlave::scratchpad_[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); |
|
void(*OneWireSlave::clientReceiveCallback_)(ReceiveEvent evt, byte data); |
|
|
|
byte OneWireSlave::receivingByte_; |
|
|
|
byte OneWireSlave::searchRomBytePos_; |
|
byte OneWireSlave::searchRomBitPos_; |
|
bool OneWireSlave::searchRomInverse_; |
|
|
|
byte* OneWireSlave::buffer_; |
|
short OneWireSlave::bufferLength_; |
|
byte OneWireSlave::bufferBitPos_; |
|
short OneWireSlave::bufferPos_; |
|
void(*OneWireSlave::receiveBytesCallback_)(bool error); |
|
void(*OneWireSlave::sendBytesCallback_)(bool error); |
|
|
|
|
|
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(); |
|
} |
|
|
|
void OneWireSlave::write(byte* bytes, short numBytes, void(*complete)(bool error)) |
|
{ |
|
cli(); |
|
beginWriteBytes_(bytes, numBytes, complete == 0 ? noOpCallback_ : complete); |
|
sei(); |
|
} |
|
|
|
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_() |
|
{ |
|
#ifdef DEBUG_LOG |
|
dbgOutput.writeLow(); |
|
#endif |
|
} |
|
|
|
void OneWireSlave::onLeaveInterrupt_() |
|
{ |
|
#ifdef DEBUG_LOG |
|
dbgOutput.writeHigh(); |
|
#endif |
|
} |
|
|
|
void OneWireSlave::error_(const char* message) |
|
{ |
|
#ifdef DEBUG_LOG |
|
if (message == 0) |
|
debug.append("unspecified error"); |
|
else |
|
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::beginResetDetection_() |
|
{ |
|
setTimerEvent_(ResetMinDuration - 50, &OneWireSlave::resetCheck_); |
|
resetStart_ = micros() - 50; |
|
} |
|
|
|
void OneWireSlave::cancelResetDetection_() |
|
{ |
|
disableTimer_(); |
|
resetStart_ = (unsigned long)-1; |
|
} |
|
|
|
void OneWireSlave::resetCheck_() |
|
{ |
|
onEnterInterrupt_(); |
|
if (!pin_.read()) |
|
{ |
|
pin_.attachInterrupt(&OneWireSlave::waitReset_, CHANGE); |
|
#ifdef DEBUG_LOG |
|
debug.SC_APPEND_STR("Reset detected during another operation"); |
|
#endif |
|
} |
|
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_(); |
|
bitSentCallback_(false); |
|
|
|
onLeaveInterrupt_(); |
|
} |
|
|
|
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_); |
|
if (clientReceiveCallback_ != 0) |
|
clientReceiveCallback_(RE_Reset, 0); |
|
} |
|
} |
|
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_() |
|
{ |
|
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 (bufferBitPos_ == 8) |
|
{ |
|
#ifdef DEBUG_LOG |
|
debug.SC_APPEND_STR_INT("received byte", (long)receivingByte_); |
|
#endif |
|
if (bufferPos_ == ReceiveCommand) |
|
{ |
|
bufferPos_ = 0; |
|
switch (receivingByte_) |
|
{ |
|
case 0xF0: // SEARCH ROM |
|
beginSearchRom_(); |
|
return; |
|
case 0x33: // READ ROM |
|
beginWriteBytes_(rom_, 8, &OneWireSlave::noOpCallback_); |
|
return; |
|
case 0x55: // MATCH ROM |
|
beginReceiveBytes_(scratchpad_, 8, &OneWireSlave::matchRomBytesReceived_); |
|
return; |
|
case 0xCC: // SKIP ROM |
|
// emulate a match rom operation |
|
memcpy(scratchpad_, rom_, 8); |
|
matchRomBytesReceived_(false); |
|
return; |
|
default: |
|
ERROR("Unknown command"); |
|
return; |
|
} |
|
} |
|
else |
|
{ |
|
buffer_[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) |
|
{ |
|
#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_(); |
|
} |
|
} |
|
|
|
void OneWireSlave::beginWriteBytes_(byte* data, short numBytes, void(*complete)(bool error)) |
|
{ |
|
buffer_ = data; |
|
bufferLength_ = numBytes; |
|
bufferPos_ = 0; |
|
bufferBitPos_ = 0; |
|
sendBytesCallback_ = complete; |
|
|
|
bool bit = bitRead(buffer_[0], 0); |
|
beginSendBit_(bit, &OneWireSlave::bitSent_); |
|
} |
|
|
|
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_) |
|
{ |
|
beginWaitReset_(); |
|
sendBytesCallback_(false); |
|
return; |
|
} |
|
|
|
bool bit = bitRead(buffer_[bufferPos_], bufferBitPos_); |
|
beginSendBit_(bit, &OneWireSlave::bitSent_); |
|
} |
|
|
|
void OneWireSlave::beginReceiveBytes_(byte* buffer, short numBytes, void(*complete)(bool error)) |
|
{ |
|
buffer_ = 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) |
|
{ |
|
ERROR("error receiving match rom bytes"); |
|
return; |
|
} |
|
|
|
if (memcmp(rom_, scratchpad_, 8) == 0) |
|
{ |
|
#ifdef DEBUG_LOG |
|
debug.SC_APPEND_STR("ROM matched"); |
|
#endif |
|
beginReceiveBytes_(scratchpad_, 1, &OneWireSlave::notifyClientByteReceived_); |
|
} |
|
else |
|
{ |
|
#ifdef DEBUG_LOG |
|
debug.SC_APPEND_STR("ROM not matched"); |
|
#endif |
|
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]); |
|
}
|
|
|