/* OneWireSlave v1.1 by Joshua Fuller - Modified based on versions noted below for Digispark OneWireSlave v1.0 by Alexander Gordeyev It is based on Jim's Studt OneWire library v2.0 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Much of the code was inspired by Derek Yerger's code, though I don't think much of that remains. In any event that was.. (copyleft) 2006 by Derek Yerger - Free to distribute freely. The CRC code was excerpted and inspired by the Dallas Semiconductor sample code bearing this copyright. //--------------------------------------------------------------------------- // Copyright (C) 2000 Dallas Semiconductor Corporation, All Rights Reserved. // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the "Software"), // to deal in the Software without restriction, including without limitation // the rights to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to whom the // Software is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included // in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. // IN NO EVENT SHALL DALLAS SEMICONDUCTOR BE LIABLE FOR ANY CLAIM, DAMAGES // OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR // OTHER DEALINGS IN THE SOFTWARE. // // Except as contained in this notice, the name of Dallas Semiconductor // shall not be used except as stated in the Dallas Semiconductor // Branding Policy. //-------------------------------------------------------------------------- */ #include "OneWireSlave.h" #include "pins_arduino.h" extern "C" { // #include "WConstants.h" #include #include #include } #define DIRECT_READ(base, mask) (((*(base)) & (mask)) ? 1 : 0) #define DIRECT_MODE_INPUT(base, mask) ((*(base+1)) &= ~(mask)) #define DIRECT_MODE_OUTPUT(base, mask) ((*(base+1)) |= (mask)) #define DIRECT_WRITE_LOW(base, mask) ((*(base+2)) &= ~(mask)) #define DIRECT_WRITE_HIGH(base, mask) ((*(base+2)) |= (mask)) //#define microsecondsToClockCycles(a) ( (a) * clockCyclesPerMicrosecond() ) //ORIG: #define TIMESLOT_WAIT_RETRY_COUNT microsecondsToClockCycles(120) / 10L //FULL: #define TIMESLOT_WAIT_RETRY_COUNT ( ((120) * (8000000L / 1000L)) / 1000L ) //WORKING: #define TIMESLOT_WAIT_RETRY_COUNT microsecondsToClockCycles(240000) / 10L //These are the major change from original, we now wait quite a bit longer for some things #define TIMESLOT_WAIT_RETRY_COUNT microsecondsToClockCycles(120) / 10L //This TIMESLOT_WAIT_READ_RETRY_COUNT is new, and only used when waiting for the line to go low on a read //It was derived from knowing that the Arduino based master may go up to 130 micros more than our wait after reset #define TIMESLOT_WAIT_READ_RETRY_COUNT microsecondsToClockCycles(135) OneWireSlave::OneWireSlave(uint8_t pin) { pin_bitmask = digitalPinToBitMask(pin); baseReg = portInputRegister(digitalPinToPort(pin)); } void OneWireSlave::init(unsigned char rom[8]) { for (int i=0; i<7; i++) this->rom[i] = rom[i]; this->rom[7] = crc8(this->rom, 7); } void OneWireSlave::setScratchpad(unsigned char scratchpad[9]) { for (int i=0; i<8; i++) this->scratchpad[i] = scratchpad[i]; this->scratchpad[8] = crc8(this->scratchpad, 8); } bool OneWireSlave::waitForRequest(bool ignore_errors) { errno = ONEWIRE_NO_ERROR; for (;;) { //delayMicroseconds(40); //Once reset is done, it waits another 30 micros //Master wait is 65, so we have 35 more to send our presence now that reset is done if (!waitReset(0) ) { continue; } //Reset is complete, tell the master we are prsent // This will pull the line low for 125 micros (155 micros since the reset) and // then wait another 275 plus whatever wait for the line to go high to a max of 480 // This has been modified from original to wait for the line to go high to a max of 480. if (!presence() ) { continue; } //Now that the master should know we are here, we will get a command from the line //Because of our changes to the presence code, the line should be guranteed to be high if (recvAndProcessCmd() ) { return TRUE; } else if ((errno == ONEWIRE_NO_ERROR) || ignore_errors) { continue; } else { return FALSE; } } } bool OneWireSlave::waitForRequestInterrupt(bool ignore_errors) { errno = ONEWIRE_NO_ERROR; // for (;;) { //delayMicroseconds(40); //Once reset is done, it waits another 30 micros //Master wait is 65, so we have 35 more to send our presence now that reset is done // if (!waitReset(0) ) { // continue; // } delayMicroseconds(30); //Reset is complete, tell the master we are prsent // This will pull the line low for 125 micros (155 micros since the reset) and // then wait another 275 plus whatever wait for the line to go high to a max of 480 // This has been modified from original to wait for the line to go high to a max of 480. // if (!presence() ) { // continue; // } while (!presence() ) {}; //Now that the master should know we are here, we will get a command from the line //Because of our changes to the presence code, the line should be guranteed to be high // if (recvAndProcessCmd() ) { // return TRUE; // } while (recvAndProcessCmd() ) {}; // else if ((errno == ONEWIRE_NO_ERROR) || ignore_errors) { // continue; // } // else { // return FALSE; // } // } } bool OneWireSlave::recvAndProcessCmd() { char addr[8]; uint8_t oldSREG = 0; for (;;) { uint8_t cmd = recv(); switch (cmd) { case 0xF0: // SEARCH ROM search(); delayMicroseconds(6900); return FALSE; case 0x33: // READ ROM sendData(rom, 8); if (errno != ONEWIRE_NO_ERROR) return FALSE; break; case 0x55: // MATCH ROM - Choose/Select ROM recvData(addr, 8); if (errno != ONEWIRE_NO_ERROR) return FALSE; for (int i=0; i<8; i++) if (rom[i] != addr[i]) return FALSE; duty(); case 0xCC: // SKIP ROM return TRUE; default: // Unknow command if (errno == ONEWIRE_NO_ERROR) break; // skip if no error else return FALSE; } } } bool OneWireSlave::duty() { uint8_t done = recv(); switch (done) { case 0xBE: // READ SCREATCHPAD sendData(scratchpad, 9); if (errno != ONEWIRE_NO_ERROR) return FALSE; break; case 0xB4: sendBit(0); break; case 0x44: break; default: break; if (errno == ONEWIRE_NO_ERROR) break; // skip if no error else return FALSE; return TRUE; } } inline void OneWireSlave::tunedDelay(volatile uint16_t delay) { volatile uint8_t tmp=0; asm volatile("sbiw %0, 0x01 \n\t" "ldi %1, 0xFF \n\t" "cpi %A0, 0xFF \n\t" "cpc %B0, %1 \n\t" "brne .-10 \n\t" : "+w" (delay), "+a" (tmp) : "0" (delay) ); } void OneWireSlave::customDelay(uint16_t _tx_delay) { //uint8_t oldSREG = SREG; //cli(); // turn off interrupts tunedDelay(_tx_delay); //SREG = oldSREG; // turn interrupts back on } bool OneWireSlave::search() { uint8_t bitmask; uint8_t bit_send, bit_recv; for (int i=0; i<8; i++) { for (bitmask = 0x01; bitmask; bitmask <<= 1) { bit_send = (bitmask & rom[i])?1:0; sendBit(bit_send); sendBit(!bit_send); bit_recv = recvBit(); if (errno != ONEWIRE_NO_ERROR) return FALSE; if (bit_recv != bit_send) return FALSE; } } return TRUE; } bool OneWireSlave::waitReset(uint16_t timeout_ms) { uint8_t mask = pin_bitmask; volatile uint8_t *reg asm("r30") = baseReg; unsigned long time_stamp; errno = ONEWIRE_NO_ERROR; cli(); DIRECT_MODE_INPUT(reg, mask); sei(); //Wait for the line to fall if (timeout_ms != 0) { time_stamp = micros() + timeout_ms*1000; while (DIRECT_READ(reg, mask)) { if (micros() > time_stamp) { errno = ONEWIRE_WAIT_RESET_TIMEOUT; return FALSE; } } } else { //Will wait forever for the line to fall while (DIRECT_READ(reg, mask)) {}; } //Set to wait for rise up to 540 micros //Master code sets the line low for 500 micros //TODO The actual documented max is 640, not 540 time_stamp = micros() + 540; //Wait for the rise on the line up to 540 micros while (DIRECT_READ(reg, mask) == 0) { if (micros() > time_stamp) { errno = ONEWIRE_VERY_LONG_RESET; return FALSE; } } //If the master pulled low for exactly 500, then this will be 40 wait time // Recommended for master is 480, which would be 60 here then // Max is 640, which makes this negative, but it returns above as a "ONEWIRE_VERY_LONG_RESET" // this gives an extra 10 to 30 micros befor calling the reset invalid if ((time_stamp - micros()) > 70) { errno = ONEWIRE_VERY_SHORT_RESET; return FALSE; } //Master will now delay for 65 to 70 recommended or max of 75 before it's "presence" check // and then read the pin value (checking for a presence on the line) // then wait another 490 (so, 500 + 64 + 490 = 1054 total without consideration of actual op time) on Arduino, // but recommended is 410 with total reset length of 480 + 70 + 410 (or 480x2=960) delayMicroseconds(30); //Master wait is 65, so we have 35 more to send our presence now that reset is done return TRUE; } bool OneWireSlave::waitReset() { return waitReset(1000); } bool OneWireSlave::presence(uint8_t delta) { uint8_t mask = pin_bitmask; volatile uint8_t *reg asm("r30") = baseReg; //Reset code already waited 30 prior to calling this // Master will not read until 70 recommended, but could read as early as 60 // so we should be well enough ahead of that. Arduino waits 65 errno = ONEWIRE_NO_ERROR; cli(); DIRECT_WRITE_LOW(reg, mask); DIRECT_MODE_OUTPUT(reg, mask); // drive output low sei(); //Delaying for another 125 (orignal was 120) with the line set low is a total of at least 155 micros // total since reset high depends on commands done prior, is technically a little longer delayMicroseconds(125); cli(); DIRECT_MODE_INPUT(reg, mask); // allow it to float sei(); //Default "delta" is 25, so this is 275 in that condition, totaling to 155+275=430 since the reset rise // docs call for a total of 480 possible from start of rise before reset timing is completed //This gives us 50 micros to play with, but being early is probably best for timing on read later //delayMicroseconds(300 - delta); delayMicroseconds(250 - delta); //Modified to wait a while (roughly 50 micros) for the line to go high // since the above wait is about 430 micros, this makes this 480 closer // to the 480 standard spec and the 490 used on the Arduino master code // anything longer then is most likely something going wrong. uint8_t retries = 25; while (!DIRECT_READ(reg, mask)); do { if ( retries-- == 0) return FALSE; delayMicroseconds(2); } while(!DIRECT_READ(reg, mask)); /* if ( !DIRECT_READ(reg, mask)) { errno = ONEWIRE_PRESENCE_LOW_ON_LINE; return FALSE; } else return TRUE; */ } bool OneWireSlave::presence() { return presence(25); } uint8_t OneWireSlave::sendData(char buf[], uint8_t len) { uint8_t bytes_sended = 0; for (int i=0; i>= 1; if (mix) crc ^= 0x8C; inbyte >>= 1; } } return crc; } #endif #endif