An arduino library to communicate using the Dallas one-wire protocol, where the Arduino takes the role of a slave. Implementation of a DS2413 on Arduino UNO and ATTINY85
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.
 
 

297 lines
5.4 KiB

#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* OneWireSlave::inst_ = 0;
ISR(TIMER1_COMPA_vect) // timer1 interrupt
{
TCCR1B = 0; // disable clock
void(*event)() = timerEvent;
timerEvent = 0;
event();
}
OneWireSlave::OneWireSlave(byte* rom, byte pinNumber)
: pin_(pinNumber)
, resetStart_((unsigned long)-1)
, lastReset_(0)
, ignoreNextEdge_(false)
{
inst_ = this; // we can have only one instance in the current implementation
memcpy(rom_, rom, 7);
rom_[7] = crc8_(rom_, 7);
}
void OneWireSlave::enable()
{
#ifdef DEBUG_LOG
debug.append("Enabling 1-wire library");
dbgOutput.outputMode();
dbgOutput.writeHigh();
#endif
cli(); // disable interrupts
pin_.inputMode();
// 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
beginWaitReset_();
sei(); // enable interrupts
}
void OneWireSlave::disable()
{
#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::waitResetHandler_, 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::beginPresenceHandler_);
}
}
else
{
resetStart_ = now;
}
onLeaveInterrupt_();
}
void OneWireSlave::beginPresence_()
{
unsigned long now = micros();
pullLow_();
setTimerEvent_(PresenceDuration, &OneWireSlave::endPresenceHandler_);
#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_()
{
ignoreNextEdge_ = true;
pin_.attachInterrupt(&OneWireSlave::receiveHandler_, FALLING);
receivingByte_ = 0;
receivingBitPos_ = 0;
}
void OneWireSlave::receive_()
{
onEnterInterrupt_();
if (!ignoreNextEdge_)
setTimerEvent_(ReadBitSamplingTime, &OneWireSlave::receiveBitHandler_);
ignoreNextEdge_ = false;
onLeaveInterrupt_();
}
void OneWireSlave::receiveBit_()
{
onEnterInterrupt_();
bool bit = pin_.read();
//dbgOutput.writeLow();
//dbgOutput.writeHigh();
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_();
}
else
{
// TODO: send command to client code
beginWaitReset_();
}
}
else
{
// TODO: add byte in receive buffer
beginWaitReset_();
}
}
onLeaveInterrupt_();
}
void OneWireSlave::beginSearchRom_()
{
pin_.detachInterrupt();
}