Manages the 1-wire bus

Dependents:   oldheating heating

1-wire/1-wire.c

Committer:
andrewboyson
Date:
2021-02-18
Revision:
11:3859fee99d5d
Parent:
7:0a226e4fff25

File content as of revision 11:3859fee99d5d:

#include           "log.h"
#include    "1-wire-bus.h"
#include        "1-wire.h"
#include "1-wire-device.h"
#include       "hrtimer.h"

bool OneWireTrace = false;

#define BUS_TIMEOUT_MS 20000

//Exchange zone
//=============
#define WAIT_FOR_SOMETHING_TO_DO     0
#define HANDLE_RESET_END             1
#define SEND_NEXT_XCHG_WRITE         2
#define HANDLE_XCHG_READ             3
#define SEND_NEXT_SEARCH_WRITE       4
#define SEND_NEXT_SEARCH_READ_BIT    5
#define HANDLE_SEARCH_READ_BIT_TRUE  6
#define HANDLE_SEARCH_READ_BIT_COMP  7
static int state = WAIT_FOR_SOMETHING_TO_DO;

#define JOB_NONE   0
#define JOB_XCHG   1
#define JOB_SEARCH 2
static int job = JOB_NONE;

static int  lensend = 0;
static int  lenrecv = 0;
static char*  psend = NULL;
static char*  precv = NULL;
static int pullupms = 0;

static char crc;
static void resetCrc()
{
    crc = 0;
}
static void addBitToCrc(int bit)
{
    int feedback = !(crc & 0x80) != !bit; //Logical Exclusive Or of the msb of the shift register with the input
    crc <<= 1;                            //Move the shift register one place to the left leaving a zero in the lsb and losing the msb
    if (feedback) crc ^= 0x31;            //Exclusive Or the shift register with polynomial X5 + X4 + 1
}

static void resetBitPosition(int* pByteIndex, char* pBitMask)
{
    *pBitMask = 1;
    *pByteIndex = 0;
}
static void incrementBitPosition(int* pByteIndex, char* pBitMask)
{
    *pBitMask <<= 1;
    if (!*pBitMask)
    {
        *pBitMask = 1;
        *pByteIndex += 1;
    }
}

int getBitAtPosition(int byteIndex, char bitMask)
{
    return psend[byteIndex] & bitMask;
}
void setBitAtPosition(int byteIndex, char bitMask, int bit)
{
    if ( bit) precv[byteIndex] |=  bitMask;
    else      precv[byteIndex] &= ~bitMask;
}
int moreBitsToRead(int byteIndex)
{
    return byteIndex < lenrecv;
}
int moreBitsToWrite(int byteIndex)
{
    return byteIndex < lensend;
}

int result = ONE_WIRE_RESULT_OK;
int OneWireResult()
{
    return result;
}
void OneWireInit()
{
    OneWireBusInit();
    DeviceInit();
    state = WAIT_FOR_SOMETHING_TO_DO;
    result = ONE_WIRE_RESULT_OK;
    job   =  JOB_NONE;
}
bool OneWireBusy()
{
    return state;
}
void OneWireExchange(int lenBytesToSend, int lenBytesToRecv, char *pBytesToSend, char *pBytesToRecv, int msToPullUp)
{
    lensend = lenBytesToSend;
    lenrecv = lenBytesToRecv;
    psend = pBytesToSend;
    precv = pBytesToRecv;
    pullupms = msToPullUp;
    state = HANDLE_RESET_END;
    job = JOB_XCHG;
    OneWireBusReset();
}
static bool* pallfound;
static char* prom;
static void setRomBit(int position, int bit)
{
    int bitindex = position & 0x07;
    int byteindex = position >> 3;
    int bitmask = 1 << bitindex;
    if (bit) *(prom + byteindex) |=  bitmask;
    else     *(prom + byteindex) &= ~bitmask;
}
static int getRomBit(int position)
{    
    int bitindex = position & 0x07;
    int byteindex = position >> 3;
    int bitmask = 1 << bitindex;
    return *(prom + byteindex) & bitmask;
}
static int thisFurthestForkLeftPosn;
static int lastFurthestForkLeftPosn;
static char searchCommand;
static int searchBitPosn;
static int searchBitTrue;
static int searchBitComp;
static int chooseDirectionToTake()
{
    if ( searchBitTrue &&  searchBitComp) return -1; //No devices are participating in the search
    if ( searchBitTrue && !searchBitComp) return  1; //Only devices with a one  at this point are participating
    if (!searchBitTrue &&  searchBitComp) return  0; //Only devices with a zero at this point are participating
    //Both bits are zero so devices with both 0s and 1s at this point are still participating
    
    //If we have not yet reached the furthest away point we forked left (0) last time then just do whatever we did last time
    if (searchBitPosn <  lastFurthestForkLeftPosn)
    {
        int romBit = getRomBit(searchBitPosn);
        if (OneWireTrace) LogF("Bit %d:%d Last rom forked further on so follow it.", searchBitPosn, romBit);
        if (romBit == 0)
        {
            thisFurthestForkLeftPosn = searchBitPosn;
            if (OneWireTrace) LogF(" Record furthest left fork.");
        }
        if (OneWireTrace) LogF("\r\n");
        return romBit;
    }
    
    //If we are at the furthest away point that we forked left (0) last time then this time fork right (1)
    if (searchBitPosn == lastFurthestForkLeftPosn)
    {
        if (OneWireTrace) LogF("Bit %d:1 Last rom forked left here so this time fork right.\r\n");
        return 1;
    }
    
    //We are at a new fork point further than we have been before so fork left (0) and record that we did so.
    thisFurthestForkLeftPosn = searchBitPosn;
    if (OneWireTrace) LogF("Bit %d:0 New fork so fork left. Record furthest left fork.\r\n", searchBitPosn);
    return 0; 
}
void OneWireSearch(char command, char* pDeviceRom, bool* pAllDevicesFound) //Specify the buffer to receive the rom for the first search and NULL thereafter.
{
    if (pDeviceRom)
    {
        if (OneWireTrace) Log("\r\n");
        if (OneWireTrace) Log("Search for first device\r\n");
        pallfound = pAllDevicesFound;
        *pallfound = false;
        lastFurthestForkLeftPosn = -1;
        prom = pDeviceRom;
        for (int i = 0; i < 8; i++) *(prom + i) = 0;
    }
    else
    {
        if (OneWireTrace) Log("Search for next device\r\n");
    }
    thisFurthestForkLeftPosn = -1;
    lensend = 1;
    lenrecv = 0;
    searchCommand = command;
    psend = &searchCommand;
    precv = NULL;
    pullupms = 0;
    job = JOB_SEARCH;
    state = HANDLE_RESET_END;
    OneWireBusReset();
}
char OneWireCrc()
{
    return crc;
}
int OneWireMain()
{
    static int byteindex;
    static char bitmask;
    static uint32_t busTimer;
        
    if (state)
    {
        int elapsedMs = HrTimerSinceMs(busTimer);
        if (elapsedMs > BUS_TIMEOUT_MS)
        {
            LogTime("1-wire bus timed out so protocol has been reset to idle.\r\n");
            OneWireInit();
            result = ONE_WIRE_RESULT_TIMED_OUT;
            return 0;
        }
    }
    else
    {
        busTimer = HrTimerCount();
    }

    if (OneWireBusBusy()) return 0;
    
    switch(state)
    {
        case WAIT_FOR_SOMETHING_TO_DO:
            break;
        case HANDLE_RESET_END:
            if (OneWireBusValue)
            {
                if (OneWireTrace) LogTime("No 1-wire device presence detected on the bus\r\n");
                result = ONE_WIRE_RESULT_NO_DEVICE_PRESENT;
                state = WAIT_FOR_SOMETHING_TO_DO;
            }
            else
            {
                resetBitPosition(&byteindex, &bitmask);
                switch (job)
                {
                    case JOB_XCHG:   state =   SEND_NEXT_XCHG_WRITE; break;
                    case JOB_SEARCH: state = SEND_NEXT_SEARCH_WRITE; break;
                    default:
                        LogTimeF("Unknown job in RESET_RELEASE %d\r\n", job);
                        return -1;
                }
            }
            break;
            
        case SEND_NEXT_XCHG_WRITE:
            if (moreBitsToWrite(byteindex))
            {
                int bit = getBitAtPosition(byteindex, bitmask);
                incrementBitPosition(&byteindex, &bitmask);
                if (moreBitsToWrite(byteindex)) OneWireBusWriteBit(bit);
                else                            OneWireBusWriteBitWithPullUp(bit, pullupms);
            }
            else
            {
                resetBitPosition(&byteindex, &bitmask);
                if (moreBitsToRead(byteindex))
                {
                    resetCrc();
                    OneWireBusReadBit();
                    state = HANDLE_XCHG_READ;
                }
                else
                {
                    result = ONE_WIRE_RESULT_OK;
                    state = WAIT_FOR_SOMETHING_TO_DO;
                }
            }
            break;
        case HANDLE_XCHG_READ:
            addBitToCrc(OneWireBusValue);
            setBitAtPosition(byteindex, bitmask, OneWireBusValue);
            incrementBitPosition(&byteindex, &bitmask);
            if (moreBitsToRead(byteindex))
            {
                OneWireBusReadBit();
            }
            else
            {
                state = WAIT_FOR_SOMETHING_TO_DO;
                result = crc ? ONE_WIRE_RESULT_CRC_ERROR : ONE_WIRE_RESULT_OK;
            }
            break;
            
        case SEND_NEXT_SEARCH_WRITE:
            if (moreBitsToWrite(byteindex))
            {
                int bit = getBitAtPosition(byteindex, bitmask);
                incrementBitPosition(&byteindex, &bitmask);
                OneWireBusWriteBit(bit);
            }
            else
            {
                searchBitPosn = 0;
                state = SEND_NEXT_SEARCH_READ_BIT;
            }
            break;
        case SEND_NEXT_SEARCH_READ_BIT: //Have to have this extra step to separate from action in HANDLE_SEARCH_READ_BIT_COMP
            OneWireBusReadBit();
            state = HANDLE_SEARCH_READ_BIT_TRUE;
            break;
        case HANDLE_SEARCH_READ_BIT_TRUE:
            searchBitTrue = OneWireBusValue;
            OneWireBusReadBit();
            state = HANDLE_SEARCH_READ_BIT_COMP;
            break;
        case HANDLE_SEARCH_READ_BIT_COMP:
            searchBitComp = OneWireBusValue;
            int direction;
            direction = chooseDirectionToTake();
            if (direction == -1)
            {
                if (OneWireTrace) LogTime("No devices have responded\r\n");
                result = ONE_WIRE_RESULT_NO_DEVICE_PARTICIPATING;
                state = WAIT_FOR_SOMETHING_TO_DO;
            }
            else
            {
                setRomBit(searchBitPosn, direction);
                OneWireBusWriteBit(direction);
                searchBitPosn++;
                if (searchBitPosn < 64)
                {
                    state = SEND_NEXT_SEARCH_READ_BIT;
                }
                else
                {
                    if (thisFurthestForkLeftPosn == -1)
                    {
                         if (OneWireTrace) Log("All devices found\r\n");
                         *pallfound = 1;
                    }
                    lastFurthestForkLeftPosn = thisFurthestForkLeftPosn;
                    result = ONE_WIRE_RESULT_OK;
                    state = WAIT_FOR_SOMETHING_TO_DO;
                }
            }
            break;
        default:
            LogTimeF("Unknown state %d\r\n", state);
            return -1;
    }
    return 0;
}