From 22e5d690a034c63f11747dd8f3063d116c48576e Mon Sep 17 00:00:00 2001 From: Youen Toupin Date: Sat, 25 Apr 2015 21:40:38 +0200 Subject: [PATCH] refactoring (wip) --- LowLevel.h | 31 ++++-- OneWireIO.ino | 8 +- OneWireIO.sln | 3 + OneWireIO.vcxproj | 37 ++++++- OneWireIO.vcxproj.filters | 2 +- OneWireSlave.cpp | 196 +++++++++++++++++++++++++++++++++++++- OneWireSlave.h | 35 +++++++ 7 files changed, 292 insertions(+), 20 deletions(-) diff --git a/LowLevel.h b/LowLevel.h index 41ad123..4f86264 100644 --- a/LowLevel.h +++ b/LowLevel.h @@ -71,23 +71,34 @@ class Pin { private: - IO_REG_TYPE mask; - volatile IO_REG_TYPE *reg; + IO_REG_TYPE mask_; + volatile IO_REG_TYPE *reg_; + byte interruptNumber_; public: - inline Pin(uint8_t pin) + Pin(uint8_t pin) { - mask = PIN_TO_BITMASK(pin); - reg = PIN_TO_BASEREG(pin); + mask_ = PIN_TO_BITMASK(pin); + reg_ = PIN_TO_BASEREG(pin); + + switch (pin) + { + case 2: interruptNumber_ = 0; break; + case 3: interruptNumber_ = 1; break; + default: interruptNumber_ = (byte)-1; + } } - inline void inputMode() { DIRECT_MODE_INPUT(reg, mask); } - inline void outputMode() { DIRECT_MODE_OUTPUT(reg, mask); } + inline void inputMode() { DIRECT_MODE_INPUT(reg_, mask_); } + inline void outputMode() { DIRECT_MODE_OUTPUT(reg_, mask_); } - inline bool read() { return DIRECT_READ(reg, mask); } - inline void writeLow() { DIRECT_WRITE_LOW(reg, mask); } - inline void writeHigh() { DIRECT_WRITE_HIGH(reg, mask); } + inline bool read() { return DIRECT_READ(reg_, mask_) == 1; } + inline void writeLow() { DIRECT_WRITE_LOW(reg_, mask_); } + inline void writeHigh() { DIRECT_WRITE_HIGH(reg_, mask_); } inline void write(bool value) { if (value) writeHigh(); else writeLow(); } + + inline void attachInterrupt(void (*handler)(), int mode) { ::attachInterrupt(interruptNumber_, handler, mode); } + inline void detachInterrupt() { ::detachInterrupt(interruptNumber_); } }; #endif diff --git a/OneWireIO.ino b/OneWireIO.ino index b6e1ced..af35fbb 100644 --- a/OneWireIO.ino +++ b/OneWireIO.ino @@ -16,7 +16,6 @@ OneWireSlave oneWire(owROM, OWPin); void setup() { led.outputMode(); - led.writeLow(); oneWire.enable(); @@ -24,14 +23,15 @@ void setup() Serial.begin(9600); } -//int count = 0; +int count = 0; void loop() { - /*if (count++ == 10000) + delay(1); + if (count++ == 1000) { led.write(!led.read()); count = 0; - }*/ + } cli();//disable interrupts SerialChannel::swap(); diff --git a/OneWireIO.sln b/OneWireIO.sln index 873e73d..1efede9 100644 --- a/OneWireIO.sln +++ b/OneWireIO.sln @@ -6,6 +6,9 @@ MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "OneWireIO", "OneWireIO.vcxproj", "{3B500971-1570-460F-81C3-22AC3B7764B9}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SerialMonitor", "SerialMonitor\SerialMonitor\SerialMonitor.csproj", "{97704F53-6CA1-4155-9E8F-AEBFEEC20A8B}" + ProjectSection(ProjectDependencies) = postProject + {3B500971-1570-460F-81C3-22AC3B7764B9} = {3B500971-1570-460F-81C3-22AC3B7764B9} + EndProjectSection EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/OneWireIO.vcxproj b/OneWireIO.vcxproj index 3194aa6..308080d 100644 --- a/OneWireIO.vcxproj +++ b/OneWireIO.vcxproj @@ -50,6 +50,23 @@ true + + if exist $(ProjectDir)$(IntDir)$(ProjectName).cpp.elf ( +echo Elf generated +) else ( +echo no output +exit /B 1 +) + + + + if exist $(ProjectDir)$(IntDir)$(ProjectName).cpp.elf ( +echo Elf generated +) else ( +echo no output +exit /B 1 +) + @@ -66,13 +83,25 @@ - + Document - + false + if exist $(ProjectDir)$(IntDir)$(ProjectName).cpp.elf del $(ProjectDir)$(IntDir)$(ProjectName).cpp.elf +D:\Outils\Arduino\arduino.exe --pref build.path=$(ProjectDir)$(IntDir) --upload %(FullPath) + + always_build_$(ProjectConfiguration).dummy + + + Building and uploading sketch... + - - + + true + + + true + diff --git a/OneWireIO.vcxproj.filters b/OneWireIO.vcxproj.filters index 5960a2f..22a782b 100644 --- a/OneWireIO.vcxproj.filters +++ b/OneWireIO.vcxproj.filters @@ -10,6 +10,6 @@ - + \ No newline at end of file diff --git a/OneWireSlave.cpp b/OneWireSlave.cpp index 4a98e55..c8eb5a8 100644 --- a/OneWireSlave.cpp +++ b/OneWireSlave.cpp @@ -1,18 +1,77 @@ #include "OneWireSlave.h" -OneWireSlave::OneWireSlave(byte* rom, byte pinNumber) +#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; + + 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) +{ + 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"); + #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) @@ -29,3 +88,138 @@ 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.outputMode(); + 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(); +} + +void OneWireSlave::releaseBus_() +{ + pin_.inputMode(); +} + +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; + 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_() +{ + /*pin_.attachInterrupt(&OneWireSlave::waitCommand_, FALLING); + receivingByte = 0; + receivingBitPos = 0; + bitStart = (unsigned long)-1;*/ +} diff --git a/OneWireSlave.h b/OneWireSlave.h index 3483cea..ccb46e2 100644 --- a/OneWireSlave.h +++ b/OneWireSlave.h @@ -2,6 +2,7 @@ #define _OneWireSlave_h_ #include "Arduino.h" +#include "LowLevel.h" class OneWireSlave { @@ -23,6 +24,40 @@ public: ///! Enqueues the specified bytes in the send buffer. They will be sent in the background. 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. void write(byte* bytes, short numBytes, void(*complete)(bool error)); + +private: + byte crc8_(byte* data, short numBytes); + + void setTimerEvent_(short delayMicroSeconds, void(*handler)()); + void disableTimer_(); + + void onEnterInterrupt_(); + void onLeaveInterrupt_(); + + void error_(const char* message); + + void pullLow_(); + void releaseBus_(); + + void beginWaitReset_(); + void beginWaitCommand_(); + + // interrupt handlers + inline static void waitResetHandler_() { inst_->waitReset_(); } + void waitReset_(); + inline static void beginPresenceHandler_() { inst_->beginPresence_(); } + void beginPresence_(); + inline static void endPresenceHandler_() { inst_->endPresence_(); } + void endPresence_(); + +private: + static OneWireSlave* inst_; + byte rom_[8]; + Pin pin_; + byte tccr1bEnable_; + + unsigned long resetStart_; + unsigned long lastReset_; }; #endif