Implementation of 1-Wire with added Alarm Search Functionality
Dependents: Max32630_One_Wire_Interface
Masters/DS2480B/DS2480B.cpp
- Committer:
- IanBenzMaxim
- Date:
- 2016-05-13
- Revision:
- 75:8b627804927c
- Parent:
- 74:23be10c32fa3
- Child:
- 76:84e6c4994e29
File content as of revision 75:8b627804927c:
/******************************************************************//** * Copyright (C) 2016 Maxim Integrated Products, Inc., 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 MAXIM INTEGRATED 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 Maxim Integrated * Products, Inc. shall not be used except as stated in the Maxim Integrated * Products, Inc. Branding Policy. * * The mere transfer of this software does not imply any licenses * of trade secrets, proprietary technology, copyrights, patents, * trademarks, maskwork rights, or any other form of intellectual * property whatsoever. Maxim Integrated Products, Inc. retains all * ownership rights. **********************************************************************/ #include "DS2480B.h" #include "Serial.h" #include "Timer.h" #include "wait_api.h" #define WRITE_FUNCTION 0 #define READ_FUNCTION 1 // Mode Commands #define MODE_DATA 0xE1 #define MODE_COMMAND 0xE3 #define MODE_STOP_PULSE 0xF1 // Return byte value #define RB_CHIPID_MASK 0x1C #define RB_RESET_MASK 0x03 #define RB_1WIRESHORT 0x00 #define RB_PRESENCE 0x01 #define RB_ALARMPRESENCE 0x02 #define RB_NOPRESENCE 0x03 #define RB_BIT_MASK 0x03 #define RB_BIT_ONE 0x03 #define RB_BIT_ZERO 0x00 // Masks for all bit ranges #define CMD_MASK 0x80 #define FUNCTSEL_MASK 0x60 #define BITPOL_MASK 0x10 #define SPEEDSEL_MASK 0x0C #define MODSEL_MASK 0x02 #define PARMSEL_MASK 0x70 #define PARMSET_MASK 0x0E // Command or config bit #define CMD_COMM 0x81 #define CMD_CONFIG 0x01 // Function select bits #define FUNCTSEL_BIT 0x00 #define FUNCTSEL_SEARCHON 0x30 #define FUNCTSEL_SEARCHOFF 0x20 #define FUNCTSEL_RESET 0x40 #define FUNCTSEL_CHMOD 0x60 // Bit polarity/Pulse voltage bits #define BITPOL_ONE 0x10 #define BITPOL_ZERO 0x00 #define BITPOL_5V 0x00 #define BITPOL_12V 0x10 // One Wire speed bits #define SPEEDSEL_STD 0x00 #define SPEEDSEL_FLEX 0x04 #define SPEEDSEL_OD 0x08 #define SPEEDSEL_PULSE 0x0C // Data/Command mode select bits #define MODSEL_DATA 0x00 #define MODSEL_COMMAND 0x02 // 5V Follow Pulse select bits #define PRIME5V_TRUE 0x02 #define PRIME5V_FALSE 0x00 // Parameter select bits #define PARMSEL_PARMREAD 0x00 #define PARMSEL_SLEW 0x10 #define PARMSEL_12VPULSE 0x20 #define PARMSEL_5VPULSE 0x30 #define PARMSEL_WRITE1LOW 0x40 #define PARMSEL_SAMPLEOFFSET 0x50 #define PARMSEL_ACTIVEPULLUPTIME 0x60 #define PARMSEL_BAUDRATE 0x70 // Pull down slew rate. #define PARMSET_Slew15Vus 0x00 #define PARMSET_Slew2p2Vus 0x02 #define PARMSET_Slew1p65Vus 0x04 #define PARMSET_Slew1p37Vus 0x06 #define PARMSET_Slew1p1Vus 0x08 #define PARMSET_Slew0p83Vus 0x0A #define PARMSET_Slew0p7Vus 0x0C #define PARMSET_Slew0p55Vus 0x0E // 12V programming pulse time table #define PARMSET_32us 0x00 #define PARMSET_64us 0x02 #define PARMSET_128us 0x04 #define PARMSET_256us 0x06 #define PARMSET_512us 0x08 #define PARMSET_1024us 0x0A #define PARMSET_2048us 0x0C #define PARMSET_infinite 0x0E // 5V strong pull up pulse time table #define PARMSET_16p4ms 0x00 #define PARMSET_65p5ms 0x02 #define PARMSET_131ms 0x04 #define PARMSET_262ms 0x06 #define PARMSET_524ms 0x08 #define PARMSET_1p05s 0x0A #define PARMSET_dynamic 0x0C #define PARMSET_infinite 0x0E // Write 1 low time #define PARMSET_Write8us 0x00 #define PARMSET_Write9us 0x02 #define PARMSET_Write10us 0x04 #define PARMSET_Write11us 0x06 #define PARMSET_Write12us 0x08 #define PARMSET_Write13us 0x0A #define PARMSET_Write14us 0x0C #define PARMSET_Write15us 0x0E // Data sample offset and Write 0 recovery time #define PARMSET_SampOff3us 0x00 #define PARMSET_SampOff4us 0x02 #define PARMSET_SampOff5us 0x04 #define PARMSET_SampOff6us 0x06 #define PARMSET_SampOff7us 0x08 #define PARMSET_SampOff8us 0x0A #define PARMSET_SampOff9us 0x0C #define PARMSET_SampOff10us 0x0E // Active pull up on time #define PARMSET_PullUp0p0us 0x00 #define PARMSET_PullUp0p5us 0x02 #define PARMSET_PullUp1p0us 0x04 #define PARMSET_PullUp1p5us 0x06 #define PARMSET_PullUp2p0us 0x08 #define PARMSET_PullUp2p5us 0x0A #define PARMSET_PullUp3p0us 0x0C #define PARMSET_PullUp3p5us 0x0E // Baud rate bits #define PARMSET_9600 0x00 #define PARMSET_19200 0x02 #define PARMSET_57600 0x04 #define PARMSET_115200 0x06 // DS2480B program voltage available #define DS2480BPROG_MASK 0x20 // mode bit flags #define MODE_NORMAL 0x00 #define MODE_OVERDRIVE 0x01 #define MODE_STRONG5 0x02 #define MODE_PROGRAM 0x04 #define MODE_BREAK 0x08 #define MAX_BAUD PARMSET_115200 using OneWire::Masters::DS2480B; using OneWire::Masters::OneWireMaster; //********************************************************************* DS2480B::DS2480B(mbed::Serial &p_serial) :_p_serial(&p_serial), _serial_owner(false) { } //********************************************************************* DS2480B::DS2480B(PinName tx, PinName rx) :_p_serial(new mbed::Serial(tx, rx)), _serial_owner(true) { } //********************************************************************* DS2480B::~DS2480B() { if (_serial_owner) { delete _p_serial; } } //********************************************************************* void DS2480B::rx_callback(void) { while (_p_serial->readable()) { rx_buffer.buff[rx_buffer.w_idx++] = _p_serial->getc(); rx_buffer.rx_bytes_available++; } if (rx_buffer.w_idx == rx_buffer.r_idx) { rx_buffer.wrap_error = true; } } //********************************************************************* OneWireMaster::CmdResult DS2480B::OWInitMaster() { _p_serial->attach(this, &DS2480B::rx_callback, mbed::Serial::RxIrq); rx_buffer.w_idx = 0; rx_buffer.r_idx = 0; rx_buffer.rx_bytes_available = 0; rx_buffer.wrap_error = false; _ULevel = OneWireMaster::NormalLevel; _UBaud = Bps9600; _UMode = MODSEL_COMMAND; _USpeed = OneWireMaster::StandardSpeed; return DS2480B_Detect(); } //********************************************************************* OneWireMaster::CmdResult DS2480B::OWReset() { OneWireMaster::CmdResult result; uint8_t readbuffer[10], sendpacket[10]; uint8_t sendlen = 0; // make sure normal level result = OWSetLevel(OneWireMaster::NormalLevel); if (result == OneWireMaster::Success) { // check for correct mode if (_UMode != MODSEL_COMMAND) { _UMode = MODSEL_COMMAND; sendpacket[sendlen++] = MODE_COMMAND; } // construct the command sendpacket[sendlen++] = (uint8_t)(CMD_COMM | FUNCTSEL_RESET | _USpeed); FlushCOM(); // send the packet result = WriteCOM(sendlen, sendpacket); if (result == OneWireMaster::Success) { // read back the 1 byte response result = ReadCOM(1, readbuffer); if (result == OneWireMaster::Success) { // make sure this byte looks like a reset byte if (((readbuffer[0] & RB_RESET_MASK) == RB_PRESENCE) || ((readbuffer[0] & RB_RESET_MASK) == RB_ALARMPRESENCE)) { result = OneWireMaster::Success; } else { result = OneWireMaster::OperationFailure; } } } if (result != OneWireMaster::Success) { // an error occured so re-sync with DS2480B DS2480B_Detect(); } } return result; } //********************************************************************* OneWireMaster::CmdResult DS2480B::OWTouchBitSetLevel(uint8_t & sendRecvBit, OWLevel afterLevel) { OneWireMaster::CmdResult result = OneWireMaster::OperationFailure; uint8_t readbuffer[10], sendpacket[10]; uint8_t sendlen = 0; // make sure normal level OWSetLevel(OneWireMaster::NormalLevel); // check for correct mode if (_UMode != MODSEL_COMMAND) { _UMode = MODSEL_COMMAND; sendpacket[sendlen++] = MODE_COMMAND; } // construct the command sendpacket[sendlen] = (sendRecvBit != 0) ? BITPOL_ONE : BITPOL_ZERO; sendpacket[sendlen++] |= CMD_COMM | FUNCTSEL_BIT | _USpeed; // flush the buffers FlushCOM(); // send the packet result = WriteCOM(sendlen, sendpacket); if (result == OneWireMaster::Success) { // read back the response result = ReadCOM(1, readbuffer); if (result == OneWireMaster::Success) { // interpret the response if (((readbuffer[0] & 0xE0) == 0x80) && ((readbuffer[0] & RB_BIT_MASK) == RB_BIT_ONE)) { sendRecvBit = 1; result = OneWireMaster::Success; } else { sendRecvBit = 0; result = OneWireMaster::Success; } } else { result = OneWireMaster::CommunicationReadError; } } else { result = OneWireMaster::CommunicationWriteError; } // an error occured so re-sync with DS2480B if (result != OneWireMaster::Success) { DS2480B_Detect(); } else { result = OWSetLevel(afterLevel); } return result; } //********************************************************************* OneWireMaster::CmdResult DS2480B::OWWriteByteSetLevel(uint8_t sendByte, OWLevel afterLevel) { OneWireMaster::CmdResult result = OneWireMaster::OperationFailure; uint8_t readbuffer[10], sendpacket[10]; uint8_t sendlen = 0; // make sure normal level OWSetLevel(OneWireMaster::NormalLevel); // check for correct mode if (_UMode != MODSEL_DATA) { _UMode = MODSEL_DATA; sendpacket[sendlen++] = MODE_DATA; } // add the byte to send sendpacket[sendlen++] = sendByte; // check for duplication of data that looks like COMMAND mode if (sendByte == MODE_COMMAND) { sendpacket[sendlen++] = sendByte; } // flush the buffers FlushCOM(); // send the packet result = WriteCOM(sendlen, sendpacket); if (result == OneWireMaster::Success) { // read back the 1 byte response result = ReadCOM(1, readbuffer); if ((result == OneWireMaster::Success) && (readbuffer[0] == sendByte)) { result = OneWireMaster::Success; } else { result = OneWireMaster::CommunicationReadError; } } else { result = OneWireMaster::CommunicationWriteError; } // an error occured so re-sync with DS2480B if (result != OneWireMaster::Success) { DS2480B_Detect(); } else { result = OWSetLevel(afterLevel); } return result; } //********************************************************************* OneWireMaster::CmdResult DS2480B::OWReadByteSetLevel(uint8_t & recvByte, OWLevel afterLevel) { OneWireMaster::CmdResult result = OneWireMaster::OperationFailure; uint8_t readbuffer[10], sendpacket[10]; uint8_t sendlen = 0; // make sure normal level OWSetLevel(OneWireMaster::NormalLevel); // check for correct mode if (_UMode != MODSEL_DATA) { _UMode = MODSEL_DATA; sendpacket[sendlen++] = MODE_DATA; } // add the byte to send sendpacket[sendlen++] = 0xFF; // flush the buffers FlushCOM(); // send the packet result = WriteCOM(sendlen, sendpacket); if (result == OneWireMaster::Success) { // read back the 1 byte response result = ReadCOM(1, readbuffer); if (result == OneWireMaster::Success) { recvByte = readbuffer[0]; result = OneWireMaster::Success; } else { result = OneWireMaster::CommunicationReadError; } } else { result = OneWireMaster::CommunicationWriteError; } // an error occured so re-sync with DS2480B if (result != OneWireMaster::Success) { DS2480B_Detect(); } else { result = OWSetLevel(afterLevel); } return result; } //********************************************************************* OneWireMaster::CmdResult DS2480B::OWSetSpeed(OWSpeed newSpeed) { OneWireMaster::CmdResult result = OneWireMaster::OperationFailure; //TODO return result; } //********************************************************************* OneWireMaster::CmdResult DS2480B::OWSetLevel(OWLevel newLevel) { OneWireMaster::CmdResult result = OneWireMaster::Success; uint8_t sendpacket[10], readbuffer[10]; uint8_t sendlen = 0; // check if need to change level if (newLevel != _ULevel) { // check for correct mode if (_UMode != MODSEL_COMMAND) { _UMode = MODSEL_COMMAND; sendpacket[sendlen++] = MODE_COMMAND; } // check if just putting back to normal if (newLevel == OneWireMaster::NormalLevel) { // stop pulse command sendpacket[sendlen++] = MODE_STOP_PULSE; // add the command to begin the pulse WITHOUT prime sendpacket[sendlen++] = CMD_COMM | FUNCTSEL_CHMOD | SPEEDSEL_PULSE | BITPOL_5V | PRIME5V_FALSE; // stop pulse command sendpacket[sendlen++] = MODE_STOP_PULSE; FlushCOM(); // send the packet result = WriteCOM(sendlen, sendpacket); if (result == OneWireMaster::Success) { // read back the 1 byte response result = ReadCOM(2, readbuffer); if (result == OneWireMaster::Success) { // check response byte if (((readbuffer[0] & 0xE0) == 0xE0) && ((readbuffer[1] & 0xE0) == 0xE0)) { _ULevel = OneWireMaster::NormalLevel; } else { result = OneWireMaster::OperationFailure; } } } } // set new level else { // set the SPUD time value sendpacket[sendlen++] = CMD_CONFIG | PARMSEL_5VPULSE | PARMSET_infinite; // add the command to begin the pulse sendpacket[sendlen++] = CMD_COMM | FUNCTSEL_CHMOD | SPEEDSEL_PULSE | BITPOL_5V; FlushCOM(); // send the packet result = WriteCOM(sendlen, sendpacket); if (result == OneWireMaster::Success) { // read back the 1 byte response from setting time limit result = ReadCOM(1, readbuffer); if (result == OneWireMaster::Success) { // check response byte if ((readbuffer[0] & 0x81) == 0) { _ULevel = newLevel; } else { result = OneWireMaster::OperationFailure; } } } } // if lost communication with DS2480B then reset if (result != OneWireMaster::Success) { DS2480B_Detect(); } } return result; } //********************************************************************* OneWireMaster::CmdResult DS2480B::DS2480B_Detect(void) { OneWireMaster::CmdResult result; uint8_t sendpacket[10], readbuffer[10]; uint8_t sendlen = 0; // reset modes _UMode = MODSEL_COMMAND; _UBaud = Bps9600; _USpeed = SPEEDSEL_FLEX; // set the baud rate to 9600 SetBaudCOM(_UBaud); // send a break to reset the DS2480B BreakCOM(); // delay to let line settle wait_ms(2); FlushCOM(); // send the timing byte sendpacket[0] = 0xC1; result = WriteCOM(1, sendpacket); if (result == OneWireMaster::Success) { // delay to let line settle wait_ms(2); // set the FLEX configuration parameters // default PDSRC = 1.37Vus sendpacket[sendlen++] = CMD_CONFIG | PARMSEL_SLEW | PARMSET_Slew1p37Vus; // default W1LT = 10us sendpacket[sendlen++] = CMD_CONFIG | PARMSEL_WRITE1LOW | PARMSET_Write10us; // default DSO/WORT = 8us sendpacket[sendlen++] = CMD_CONFIG | PARMSEL_SAMPLEOFFSET | PARMSET_SampOff8us; // construct the command to read the baud rate (to test command block) sendpacket[sendlen++] = CMD_CONFIG | PARMSEL_PARMREAD | (PARMSEL_BAUDRATE >> 3); // also do 1 bit operation (to test 1-Wire block) sendpacket[sendlen++] = CMD_COMM | FUNCTSEL_BIT | _UBaud | BITPOL_ONE; FlushCOM(); // send the packet result = WriteCOM(sendlen, sendpacket); if (result == OneWireMaster::Success) { // read back the response result = ReadCOM(5, readbuffer); if (result == OneWireMaster::Success) { // look at the baud rate and bit operation // to see if the response makes sense if (((readbuffer[3] & 0xF1) == 0x00) && ((readbuffer[3] & 0x0E) == _UBaud) && ((readbuffer[4] & 0xF0) == 0x90) && ((readbuffer[4] & 0x0C) == _UBaud)) { result = OneWireMaster::Success; } else { result = OneWireMaster::OperationFailure; } } } } return result; } //********************************************************************* OneWireMaster::CmdResult DS2480B::DS2480B_ChangeBaud(Baud newBaud) { OneWireMaster::CmdResult result = OneWireMaster::Success; uint8_t readbuffer[5], sendpacket[5], sendpacket2[5]; uint8_t sendlen = 0, sendlen2 = 0; //see if diffenent then current baud rate if (_UBaud != newBaud) { // build the command packet // check for correct mode if (_UMode != MODSEL_COMMAND) { _UMode = MODSEL_COMMAND; sendpacket[sendlen++] = MODE_COMMAND; } // build the command sendpacket[sendlen++] = CMD_CONFIG | PARMSEL_BAUDRATE | newBaud; // flush the buffers FlushCOM(); // send the packet result = WriteCOM(sendlen, sendpacket); if (result == OneWireMaster::Success) { // make sure buffer is flushed wait_ms(5); // change our baud rate SetBaudCOM(newBaud); _UBaud = newBaud; // wait for things to settle wait_ms(5); // build a command packet to read back baud rate sendpacket2[sendlen2++] = CMD_CONFIG | PARMSEL_PARMREAD | (PARMSEL_BAUDRATE >> 3); // flush the buffers FlushCOM(); // send the packet result = WriteCOM(sendlen2, sendpacket2); if (result == OneWireMaster::Success) { // read back the 1 byte response result = ReadCOM(1, readbuffer); if (result == OneWireMaster::Success) { // verify correct baud if ((readbuffer[0] & 0x0E) == (sendpacket[sendlen - 1] & 0x0E)) { result = OneWireMaster::Success; } else { result = OneWireMaster::OperationFailure; } } else { result = OneWireMaster::CommunicationReadError; } } else { result = OneWireMaster::CommunicationWriteError; } } else { result = OneWireMaster::CommunicationWriteError; } } // if lost communication with DS2480B then reset if (result != OneWireMaster::Success) { DS2480B_Detect(); } return result; } //********************************************************************* OneWireMaster::CmdResult DS2480B::WriteCOM(uint32_t outlen, uint8_t *outbuf) { OneWireMaster::CmdResult result = OneWireMaster::OperationFailure; mbed::Timer t; uint32_t t_val; uint32_t timeout; uint8_t idx = 0; //calculate timeout, the time needed to tx 1 byte //double for 115200 due to timer object inaccuracies switch (_UBaud) { case Bps115200: timeout = ((1000000 / 115200) * 20); break; case Bps57600: timeout = ((1000000 / 57600) * 10); break; case Bps19200: timeout = ((1000000 / 19200) * 10); break; case Bps9600: default: timeout = ((1000000 / 9600) * 10); break; } t.start(); do { t.reset(); do { t_val = t.read_us(); } while (!_p_serial->writeable() && (t_val < timeout)); if (t_val < timeout) { _p_serial->putc(outbuf[idx++]); result = OneWireMaster::Success; } else { result = OneWireMaster::TimeoutError; } } while ((idx < outlen) && (result == OneWireMaster::Success)); return result; } //********************************************************************* OneWireMaster::CmdResult DS2480B::ReadCOM(uint32_t inlen, uint8_t *inbuf) { OneWireMaster::CmdResult result; mbed::Timer t; uint32_t num_bytes_read = 0; uint32_t timeout; uint32_t micro_seconds; //calculate timeout, 10x the time needed to recieve inlen bytes //double for 115200 due to timer object inaccuracies switch (_UBaud) { case Bps115200: timeout = ((1000000 / 115200) * 200) * inlen; break; case Bps57600: timeout = ((1000000 / 57600) * 100) * inlen; break; case Bps19200: timeout = ((1000000 / 19200) * 100) * inlen; break; case Bps9600: default: timeout = ((1000000 / 9600) * 100) * inlen; break; } if (rx_buffer.wrap_error) { //reset rx buffer, error, and return failure rx_buffer.w_idx = 0; rx_buffer.r_idx = 0; rx_buffer.rx_bytes_available = 0; rx_buffer.wrap_error = false; result = OneWireMaster::OperationFailure; } else { t.start(); t.reset(); do { do { micro_seconds = t.read_us(); } while (!rx_buffer.rx_bytes_available && (micro_seconds < timeout)); if (rx_buffer.rx_bytes_available) { inbuf[num_bytes_read++] = rx_buffer.buff[rx_buffer.r_idx++]; rx_buffer.rx_bytes_available--; } } while ((num_bytes_read < inlen) && (micro_seconds < timeout) && !rx_buffer.wrap_error); t.stop(); if (num_bytes_read == inlen) { result = OneWireMaster::Success; } else if (micro_seconds > timeout) { result = OneWireMaster::TimeoutError; } else { //reset rx buffer, error, and return failure rx_buffer.w_idx = 0; rx_buffer.r_idx = 0; rx_buffer.rx_bytes_available = 0; rx_buffer.wrap_error = false; result = OneWireMaster::CommunicationReadError; } } return result; } //********************************************************************* void DS2480B::BreakCOM(void) { while (!_p_serial->writeable()); //for some reason send_break wouldn't work unless first sending char _p_serial->putc(0); _p_serial->send_break(); } void DS2480B::FlushCOM(void) { //reset soft rx_buffer rx_buffer.w_idx = 0; rx_buffer.r_idx = 0; rx_buffer.wrap_error = false; //make sure hardware rx buffer is empty while (_p_serial->readable()) { _p_serial->getc(); } /*Not sure how to flush tx buffer without sending data out on the bus. from the example in AN192, the data shouldn't be sent, just aborted and the buffer cleaned out, http://pdfserv.maximintegrated.com/en/an/AN192.pdf Below is what was used in AN192 example code using windows drivers */ //PurgeComm(ComID, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR ); } //********************************************************************* void DS2480B::SetBaudCOM(uint8_t new_baud) { switch (new_baud) { case Bps115200: _p_serial->baud(115200); break; case Bps57600: _p_serial->baud(57600); break; case Bps19200: _p_serial->baud(19200); break; case Bps9600: default: _p_serial->baud(9600); break; } } //********************************************************************* int32_t DS2480B::bitacc(uint32_t op, uint32_t state, uint32_t loc, uint8_t *buf) { int nbyt, nbit; nbyt = (loc / 8); nbit = loc - (nbyt * 8); if (op == WRITE_FUNCTION) { if (state) { buf[nbyt] |= (0x01 << nbit); } else { buf[nbyt] &= ~(0x01 << nbit); } return 1; } else { return ((buf[nbyt] >> nbit) & 0x01); } }