Manages the 1-wire bus

Dependents:   oldheating heating

1-wire/1-wire-device.c

Committer:
andrewboyson
Date:
2021-02-18
Revision:
11:3859fee99d5d
Parent:
1:c272b1fcc834

File content as of revision 11:3859fee99d5d:

#include <stdint.h>
#include <stdbool.h>

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

#define SEND_BUFFER_LENGTH 10
#define RECV_BUFFER_LENGTH 10
static char send[SEND_BUFFER_LENGTH];
static char recv[RECV_BUFFER_LENGTH];
static int sendlen = 0;
static int recvlen = 0;

int   DeviceScanMs = 0;
int   DeviceCount  = 0;
char  DeviceList[DEVICE_MAX * 8];
char* DeviceAddress(int device)
{
    return DeviceList + device * 8;
}
void DeviceAddressToString(char* pAddress, char* pText)
{
    char *pAddressAfter = pAddress + 8;
    for (char* p = pAddress; p < pAddressAfter; p++)
    {
        char highnibble = *p >> 4; //Unsigned so fills with zeros
        char  lownibble = *p & 0xF;
        *pText++ = highnibble < 0xA ? highnibble + '0' : highnibble - 0xA + 'A'; //Replace high nibble with its ascii equivalent
        *pText++ =  lownibble < 0xA ?  lownibble + '0' :  lownibble - 0xA + 'A'; //Replace low  nibble with its ascii equivalent
        *pText++ = p < pAddressAfter - 1 ? ' ' : 0;                              //Put in a space between the bytes or a NUL at the end of the last one
    }
}
void DeviceParseAddress(char* pText, char *pAddress)
{
    int highNibble = 1;
    char *pAddressAfter = pAddress + 8;
    while(*pText && pAddress < pAddressAfter)
    {
        int nibble = -1;
        if (*pText >= '0' && *pText <= '9') nibble = *pText - '0';
        if (*pText >= 'A' && *pText <= 'F') nibble = *pText - 'A' + 0xA;
        if (*pText >= 'a' && *pText <= 'f') nibble = *pText - 'a' + 0xA;
        if (nibble >= 0)                                  //Ignore characters which do not represent a hex number
        {
            if (highNibble)
            {
                *pAddress = nibble << 4;                  //Set the high nibble of the current address byte and zero the low nibble
                highNibble = 0;                           //Move onto the low nibble of this address byte
            }
            else //low nibble
            {
                *pAddress += nibble;                      //Add the low nibble to the current address byte
                highNibble = 1;                           //Move onto the high nibble of the next address byte
                pAddress++;
            }
        }
        pText++;
    }
    while (pAddress < pAddressAfter) *pAddress++ = 0; //Set any remaining bytes to zero
}

char rom[8];
bool allRomsFound = false;
static void searchRom(int first)
{
    sendlen = 1;
    send[0] = 0xF0; //Search Rom
    recvlen = 0;
    for (int i = 0; i < recvlen; i++) recv[i] = 0;
    if (first)  OneWireSearch(send[0], rom, &allRomsFound);
    else        OneWireSearch(send[0], NULL, NULL);
}
static void readScratchpad(int device)
{   
    sendlen = 10;
    send[0] = 0x55; //Match Rom
    for (int i = 0; i < 8; i++) send[i+1] = DeviceList[device * 8 + i];
    send[9] = 0xBE; //Read Scratchpad
    recvlen = 9;
    for (int i = 0; i < recvlen; i++) recv[i] = 0;
    OneWireExchange(sendlen, recvlen, send, recv, 0);
}
static void convertT()
{
    sendlen = 2;
    send[0] = 0xCC; //Skip Rom
    send[1] = 0x44; //Convert T
    recvlen = 0;
    for (int i = 0; i < recvlen; i++) recv[i] = 0;
    OneWireExchange(sendlen, recvlen, send, recv, 750);
}
enum {
    IDLE,
    LIST_FIRST_DEVICE,
    LIST_NEXT_DEVICE,
    LIST_DEVICE_CHECK,
    CONVERT_T,
    CONVERT_T_CHECK,
    ENUMERATE_START,
    ENUMERATE,
    READ_SCRATCHPAD,
    EXTRACT_TEMPERATURE
    };
static volatile int state = IDLE;
bool DeviceBusy() { return state; }
static void establishDeviceScanMs()
{
    
    //Get the scan time in counts and stop if it's the first one
    static uint32_t scanTimer = 0;
    bool firstScan = !scanTimer;
    uint32_t elapsed = HrTimerSinceRepetitive(&scanTimer);
    if (firstScan) return;
    
    //Calculate the scan time in ms and record the scan time for the next pass
    int scanMs = elapsed / 96000;
    
    //Calculate the difference beween this scan time and the average and update the average
    int diffMs = scanMs - DeviceScanMs;
    if (diffMs > 0xF || diffMs < 0xF)
    {
        DeviceScanMs += (scanMs - DeviceScanMs) >> 4;
    }
    else
    {
        if (scanMs > DeviceScanMs) DeviceScanMs++;
        if (scanMs < DeviceScanMs) DeviceScanMs--;
    }
}
static int handlestate()
{
    if (OneWireBusy()) return 0;
    static int device;
    switch (state)
    {
        case IDLE:
            establishDeviceScanMs();
            state = LIST_FIRST_DEVICE;
            break;
            
        case LIST_FIRST_DEVICE:
            device = 0;
            searchRom(1);
            state = LIST_DEVICE_CHECK;
            break;
        case LIST_DEVICE_CHECK:
            if (OneWireResult()) state = IDLE;
            else                 state = LIST_NEXT_DEVICE;
            break;
        case LIST_NEXT_DEVICE:
            for (int i = 0; i < 8; i++) DeviceList[8 * device + i] = rom[i];
            device++;
            if (allRomsFound || device >= DEVICE_MAX)
            {
                DeviceCount = device;
                state = CONVERT_T;
            }
            else
            {
                searchRom(0);
                state = LIST_DEVICE_CHECK;
            }
            break;
            
        case CONVERT_T:
            convertT();
            state = CONVERT_T_CHECK;
            break;
        case CONVERT_T_CHECK:
            if (OneWireResult()) state = IDLE;
            else                 state = ENUMERATE_START;
            break;
            
        case ENUMERATE_START:
            device = -1;
            state = ENUMERATE;
            break;
        case ENUMERATE:
            device++;
            if (device >= DeviceCount)                              state = IDLE;
            else if (DeviceList[device * 8] == DS18B20_FAMILY_CODE) state = READ_SCRATCHPAD;
            else                                                    state = ENUMERATE;
            break;
            
        case READ_SCRATCHPAD:
            readScratchpad(device);
            state = EXTRACT_TEMPERATURE;
            break;
        case EXTRACT_TEMPERATURE:
            DS18B20ReadValue(OneWireResult(), device, recv[0], recv[1]);
            state = ENUMERATE;
            break;
            
        default:
            LogF("Unknown DS18B20 state %d\r\n", state);
            return -1;
    }
    return 0;
}
static void logcomms()
{
    static int wasbusy = 0;
    if (!OneWireBusy() && wasbusy)
    {
        LogF("1-wire | send:");
        for (int i = 0; i < sendlen; i++) LogF(" %02x", send[i]);
        LogF(" | recv:");
        for (int i = 0; i < recvlen; i++) LogF(" %02x", recv[i]);
        LogF("\r\n");
    }
    wasbusy = OneWireBusy();
}
void DeviceInit()
{
    DS18B20Init();
}
int DeviceMain()
{
    
    if (OneWireTrace) logcomms();
    
    int r = handlestate(); if (r) return -1;
    
    return 0;
}