1-Wire® kommunikation

PIC, AVR, Arduino, Raspberry Pi, Basic Stamp, PLC mm.
Användarvisningsbild
Icecap
Inlägg: 26650
Blev medlem: 10 januari 2005, 14:52:15
Ort: Starup (Haderslev), Danmark

1-Wire® kommunikation

Inlägg av Icecap »

Jag använder 1-Wire® som temperatursensor till den 3-fas dimmer som jag vill testa att sänka ljudnivån på verkstaden med.

Samtidig anser jag att alla delay() är av ondo - men ibland kan man inte undvika dom.

Nåväl, jag har gjort som följer:

Kod: Markera allt

typedef struct
  {
  BYTE Temp_LSB;
  BYTE Temp_MSB;
  BYTE Th_Reg;
  BYTE Tl_Reg;
  BYTE Config;
  BYTE Res_2;
  BYTE Count_Remain;
  BYTE Count_Per_Degree;
  BYTE CRC;
  } T_OW_SCRATCHPAD;

typedef struct
  {
  BYTE  Presence;
  BYTE  Timer;
  BYTE  Shift;
  BYTE  Index;
  BYTE  Tx_Bit_Cnt;
  BYTE  Rx_Bit_Cnt;
  BYTE  Tx_Buffer[sizeof(T_OW_SCRATCHPAD)];
  union
    {
    BYTE            Buffer[sizeof(T_OW_SCRATCHPAD)];
    T_OW_SCRATCHPAD Pad;
    } Rx;
  BYTE  Sequence;
  short Temp;
  BYTE  Done;
  } T_ONE_WIRE;

T_ONE_WIRE OW;

#define OW_SKIP_ROM        0xCC
#define OW_CONVERT         0x44
#define OW_READ_SCRATCHPAD 0xBE

void Initiate_OW(void)
  {
  OW.Timer      = false;
  OW.Index      = 0;
  OW.Rx_Bit_Cnt = 0;
  OW.Tx_Bit_Cnt = 0;
  OW_nTx        = false;
  OW.Sequence   = 0;
  OW.Done       = false;
  }

const BYTE DsCRC[] = {
    0,  94, 188, 226,  97,  63, 221, 131, 194, 156, 126,  32, 163, 253,  31,  65,
  157, 195,  33, 127, 252, 162,  64,  30,  95,   1, 227, 189,  62,  96, 130, 220,
   35, 125, 159, 193,  66,  28, 254, 160, 225, 191,  93,   3, 128, 222,  60,  98,
  190, 224,   2,  92, 223, 129,  99,  61, 124,  34, 192, 158,  29,  67, 161, 255,
   70,  24, 250, 164,  39, 121, 155, 197, 132, 218,  56, 102, 229, 187,  89,   7,
  219, 133, 103,  57, 186, 228,   6,  88,  25,  71, 165, 251, 120,  38, 196, 154,
  101,  59, 217, 135,   4,  90, 184, 230, 167, 249,  27,  69, 198, 152, 122,  36,
  248, 166,  68,  26, 153, 199,  37, 123,  58, 100, 134, 216,  91,   5, 231, 185,
  140, 210,  48, 110, 237, 179,  81,  15,  78,  16, 242, 172,  47, 113, 147, 205,
   17,  79, 173, 243, 112,  46, 204, 146, 211, 141, 111,  49, 178, 236,  14,  80,
  175, 241,  19,  77, 206, 144, 114,  44, 109,  51, 209, 143,  12,  82, 176, 238,
   50, 108, 142, 208,  83,  13, 239, 177, 240, 174,  76,  18, 145, 207,  45, 115,
  202, 148, 118,  40, 171, 245,  23,  73,   8,  86, 180, 234, 105,  55, 213, 139,
   87,   9, 235, 181,  54, 104, 138, 212, 149, 203,  41, 119, 244, 170,  72,  22,
  233, 183,  85,  11, 136, 214,  52, 106,  43, 117, 151, 201,  74,  20, 246, 168,
  116,  42, 200, 150,  21,  75, 169, 247, 182, 232,  10,  84, 215, 137, 107,  53};

void OW_Sequence(void)
  {
  BYTE  X, Y;
  short Z, W;
  if(OW.Tx_Bit_Cnt)
    { // If anything needs to be send
    if(!(OW.Tx_Bit_Cnt & 0x07))
      {
      OW.Shift = OW.Tx_Buffer[OW.Index++];
      }
    if(OW.Shift & 0x01) OW_Write_Bit(true);
    else                OW_Write_Bit(false);
    OW.Shift >>= 1;
    if(!--OW.Tx_Bit_Cnt)
      {
      OW.Index = 0;
      }
    }
  else if(OW.Rx_Bit_Cnt)
    { // If anything needs to be recieved
    OW.Rx_Bit_Cnt--;
    OW.Shift >>= 1;
    if(OW_Read_Bit()) OW.Shift |= 0x80;
    if(!(OW.Rx_Bit_Cnt & 0x07))
      {
      OW.Rx.Buffer[OW.Index++] = OW.Shift;
      }
    if(!OW.Rx_Bit_Cnt)
      {
      OW.Index = 0;
      }
    }
  else if(!OW.Timer) switch(OW.Sequence)
    { // If nothing is to be transmitted or recieved AND the timer is zero
    case 0: // Reset Communication is done, now address all 1-Wire devices and order a convert
      if(OW_Reset())
        {
        LED_Blue = true;
        OW.Sequence++;
        }
      else
        {
        OW.Timer    = 250; // 2,5 sek min. before next attempt
        OW.Presence = false;
        }
      break;
    case 1:
      OW.Tx_Buffer[0] = OW_SKIP_ROM; // The convert command needs to gp to all
      OW.Tx_Buffer[1] = OW_CONVERT;  // Start the convert
      OW.Timer        = 120;         // Give it 1,2 sec
      OW.Tx_Bit_Cnt   = 16;
      OW.Sequence++; // Take me to the next step
      break;
    case 2:
      if(OW_Reset())
        {
        OW.Tx_Buffer[0] = OW_SKIP_ROM; // The convert command needs to go to all
        OW.Tx_Buffer[1] = OW_READ_SCRATCHPAD; // Start the convert
        OW.Tx_Bit_Cnt   = 16; // Send these datas
        OW.Rx_Bit_Cnt   = sizeof(T_OW_SCRATCHPAD) * 8; // Then recieve scratchpad
        OW.Sequence++; // Next step
        }
      else
        { // Sorry, no presence, start all over again
        OW.Timer    = 250;
        OW.Sequence = 0;
        OW.Presence = false;
        }
      break;
    case 3:
      X = 0;
      for(Y = 0; Y < sizeof(T_OW_SCRATCHPAD);Y++) X = DsCRC[X ^ OW.Rx.Buffer[Y]];
      if(!X)
        {
        // CRC is OK
        OW.Temp = (OW.Rx.Pad.Temp_LSB | (OW.Rx.Pad.Temp_MSB << 8)) * 5; // Pack it to a short
        OW.Sequence = 0;
        OW.Presence = true;
        OW.Done     = true;
        }
      else
        {
        OW.Presence = false;
        OW.Timer    = 250;
        }
      OW.Sequence = 0;
      break;
    }
  }


BYTE OW_Reset(void)
  {
  WORD Ctr;
  BYTE Result;
  OW.Timer = 1;
  while(OW.Timer);
  OW_nTx   = true;
  OW.Timer = 1;
  while(OW.Timer);
  OW_nTx   = false;
  OW.Timer = 1;
  for(Ctr = 0; Ctr < 20; Ctr++);
  Result = !OW_Rx;
  while(OW.Timer);
  return(Result);
  }


void OW_Write_Bit(BYTE Data)
  {
  BYTE Ctr, Later;
  if(Data) Ctr =  1; // If a '1', short time
  else     Ctr = 50; // If a '0', long time
  Later = 120 - Ctr;
  OW_nTx = true; // Lower the DQ on 1-wire
  while(Ctr) Ctr--;
  OW_nTx = false; // Release DQ
  while(Later) Later--;
  }


BYTE OW_Read_Bit(void)
  {
  BYTE Ctr, Result;
  Ctr    = 1;
  OW_nTx = true;
  while(Ctr) Ctr--;
  OW_nTx = false;
  Ctr    = 3;
  while(Ctr) Ctr--;
  if(OW_Rx) Ctr = Result;
  else Result = false;
  Ctr    = 110; // Secure that the sequence time is long enough
  while(Ctr) Ctr--; // Secure that the sequence time is long enough
  return(Result);
  }
Förklaring:
OW.Timer räknas ner med en timerinterrupt, i detta fall på 100Hz. I den ISR finns: if(OW.Timer) OW.Timer--;
Jag använder en transistor som pull-down samt en transistor som ingångssteg, detta för att ha en viss isolering mellan µC och ledningarna. Använder inte parasitmatning.

Sedan är jag allergisk mot att en väntan behöver låsa allt upp, alltså lägger jag en rad i main-loop:
if(OW.Sequence) OW_Sequence(); // If running, repeat this

Och för att starta en omvandling och avläsning:
OW_Sequence(); // Run it now!
Denna startas när en timer har gått ut, i detta fall en timer som bestämmer hur ofta PI-regleringen ska reglera.

På detta vis kommer det att vara en bit per kall som exekveras, behövs det väntas en viss tid ställer man den tid i OW.Timer.
Vill man ha en kontinuerlig omvandling kan man lägga in OW_Sequence() i en ISR som inte går för snabb, t.ex. samma som räknar ner OW.Timer.

Rutinen medger enbart att man har en sensor på bussen, den kan utökas med en identifiering, sekvensen blir då en bit mer avancerat men till detta är den enkel och smidig.