TINI-specific: internal 1-wire access to read unit MAC address
[fw/sdcc] / device / examples / ds390 / readmac / ow.c
1 //---------------------------------------------------------------------------
2 // Copyright (C) 2000 Dallas Semiconductor Corporation, All Rights Reserved.
3 // 
4 // Permission is hereby granted, free of charge, to any person obtaining a 
5 // copy of this software and associated documentation files (the "Software"), 
6 // to deal in the Software without restriction, including without limitation 
7 // the rights to use, copy, modify, merge, publish, distribute, sublicense, 
8 // and/or sell copies of the Software, and to permit persons to whom the 
9 // Software is furnished to do so, subject to the following conditions:
10 // 
11 // The above copyright notice and this permission notice shall be included 
12 // in all copies or substantial portions of the Software.
13 // 
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 
15 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 
16 // MERCHANTABILITY,  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 
17 // IN NO EVENT SHALL DALLAS SEMICONDUCTOR BE LIABLE FOR ANY CLAIM, DAMAGES 
18 // OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 
19 // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 
20 // OTHER DEALINGS IN THE SOFTWARE.
21 // 
22 // Except as contained in this notice, the name of Dallas Semiconductor 
23 // shall not be used except as stated in the Dallas Semiconductor 
24 // Branding Policy. 
25 //---------------------------------------------------------------------------
26 //
27 // ow.c
28 //
29 // Minimal access routines for TINI internal one-wire bus patched together
30 // from Dallas example code (hence the copyright notice above).
31 //
32 // Kevin Vigor, 11/20/2000
33  
34 #include <ds80c390.h>
35 #include <stdio.h>
36 #include <serial390.h>
37
38 #include "ow.h"
39 #include "crcutil.h"
40
41 /* The internal 1-wire bus is hooked to P3.5, a.k.a T1 */
42 /* The "activity" LED is also hooked to this line.     */
43 #define INT_OW_PORT T1
44
45 // global variables for this module to hold search state information
46 static int LastDiscrepancy;
47 static int LastFamilyDiscrepancy;
48 static int LastDevice;
49 static unsigned char SerialNum[8];
50
51 //--------------------------------------------------------------------------
52 // Reset all of the devices on the 1-Wire Net and return the result.
53 //
54 // Returns: TRUE(1):  presense pulse(s) detected, device(s) reset
55 //          FALSE(0): no presense pulses detected
56 //
57 unsigned char owTouchReset(void)
58 {
59    unsigned char result;
60    
61    /* Code stolen straight from appnote 126. */
62    INT_OW_PORT = 0;     /* drive bus low. */
63    msDelay(480);        /* sleep 480 ms   */
64    INT_OW_PORT = 1;     /* bus high. */
65    msDelay(120);
66    result = INT_OW_PORT; /* get presence detect pulse. */
67    msDelay(360);
68
69    return result;
70 }
71
72 //--------------------------------------------------------------------------
73 // Send 1 bit of communication to the 1-Wire Net and return the
74 // result 1 bit read from the 1-Wire Net.  The parameter 'sendbit'
75 // least significant bit is used and the least significant bit
76 // of the result is the return bit.
77 //
78 // Returns: 0:   0 bit read from sendbit
79 //          1:   1 bit read from sendbit
80 //
81 unsigned char owTouchBit(unsigned char sendbit)
82 {
83    unsigned char result;
84    
85    INT_OW_PORT = 0;             /* start timeslot. */
86    msDelay(1);          
87
88    INT_OW_PORT = sendbit;       /* send bit out. */ 
89    msDelay(9);
90    result = INT_OW_PORT;                /* sample result @ 10 ms. */
91    msDelay(50);         /* Kill rest of slot. */
92    INT_OW_PORT = 1;             /* timeslot done. */
93    msDelay(5);          /* Just to make sure... */
94
95    return result;
96 }
97
98 //--------------------------------------------------------------------------
99 // Send 8 bits of communication to the 1-Wire Net and return the
100 // result 8 bits read from the 1-Wire Net.  The parameter 'sendbyte'
101 // least significant 8 bits are used and the least significant 8 bits
102 // of the result is the return byte.
103 //
104 // 'sendbyte'   - 8 bits to send (least significant byte)
105 //
106 // Returns:  8 bytes read from sendbyte
107 //
108 unsigned char owTouchByte(unsigned char sendbyte)
109 {
110    unsigned char i;
111    unsigned char result = 0;
112    
113    for (i = 0; i < 8; i++)
114    {
115        result |= (owTouchBit(sendbyte & 1) << i);
116        sendbyte >>= 1;
117    }
118     
119    return result;
120 }
121
122 //--------------------------------------------------------------------------
123 // Send 8 bits of communication to the 1-Wire Net and verify that the
124 // 8 bits read from the 1-Wire Net is the same (write operation).  
125 // The parameter 'sendbyte' least significant 8 bits are used.
126 //
127 // 'sendbyte'   - 8 bits to send (least significant byte)
128 //
129 // Returns:  TRUE: bytes written and echo was the same
130 //           FALSE: echo was not the same 
131 //
132 unsigned char owWriteByte(unsigned char sendbyte)
133 {
134    return (owTouchByte(sendbyte) == sendbyte) ? TRUE : FALSE;
135 }
136
137 //--------------------------------------------------------------------------
138 // Send 8 bits of read communication to the 1-Wire Net and and return the
139 // result 8 bits read from the 1-Wire Net.   
140 //
141 // Returns:  8 bytes read from 1-Wire Net
142 //
143 unsigned char owReadByte(void)
144 {
145    return owTouchByte(0xFF);
146 }
147
148 //--------------------------------------------------------------------------
149 //  Description:
150 //     Delay for at least 'len' ms
151 // 
152 void msDelay(unsigned int len)
153 {
154    len; /* shut compiler up. */
155
156    _asm
157    
158    ; len is in dpl/dph
159    mov  r0, dpl
160    mov  r1, dph
161    
162    mov  a, r0
163    orl  a, r1                   ; quick out for zero case.
164    jz   _sleepDone
165    
166    inc  r1
167    cjne r0, #0, _sleep1msLoop
168    dec  r1
169    
170    _sleep1msLoop:
171    nop
172    nop
173    nop
174    nop
175    nop
176    nop
177    nop                          ; 7 nops
178    djnz r0, _sleep1msLoop       ; 3 cycles x 1 = 3 cycles
179                                 ; 10 cycles per iter; we want 9.216, but more is better
180                                 ; than less.
181    djnz r1, _sleep1msLoop       
182 _sleepDone:
183    
184    _endasm;
185 }
186
187 //--------------------------------------------------------------------------
188 // The 'owBlock' transfers a block of data to and from the 
189 // 1-Wire Net with an optional reset at the begining of communication.
190 // The result is returned in the same buffer.
191 //
192 // 'do_reset' - cause a owTouchReset to occure at the begining of 
193 //              communication TRUE(1) or not FALSE(0)
194 // 'tran_buf' - pointer to a block of unsigned
195 //              chars of length 'TranferLength' that will be sent 
196 //              to the 1-Wire Net
197 // 'tran_len' - length in bytes to transfer
198
199 // Supported devices: all 
200 //
201 // Returns:   TRUE (1) : The optional reset returned a valid 
202 //                       presence (do_reset == TRUE) or there
203 //                       was no reset required.
204 //            FALSE (0): The reset did not return a valid prsence
205 //                       (do_reset == TRUE).
206 //
207 //  The maximum tran_len is 64
208 //
209 unsigned char owBlock(unsigned char do_reset, 
210                       unsigned char *tran_buf,
211                       unsigned char tran_len)
212 {
213    int i;
214
215    // check for a block too big
216    if (tran_len > 64)
217       return FALSE;
218
219    // check if need to do a owTouchReset first
220    if (do_reset)
221    {
222       if (!owTouchReset())
223          return FALSE;
224    }  
225
226    // send and receive the buffer
227    for (i = 0; i < tran_len; i++)
228       tran_buf[i] = owTouchByte(tran_buf[i]);
229       
230    return TRUE;
231 }
232
233 //--------------------------------------------------------------------------
234 // The 'owFirst' finds the first device on the 1-Wire Net  This function 
235 // contains one parameter 'alarm_only'.  When 
236 // 'alarm_only' is TRUE (1) the find alarm command 0xEC is 
237 // sent instead of the normal search command 0xF0.
238 // Using the find alarm command 0xEC will limit the search to only
239 // 1-Wire devices that are in an 'alarm' state. 
240 //
241 // 'do_reset' - TRUE (1) 
242 //                perform reset before search. 
243 // 'alarm_only' - TRUE (1) the find alarm command 0xEC is 
244 //                sent instead of the normal search command 0xF0
245 //
246 // Returns:   TRUE (1) : when a 1-Wire device was found and it's 
247 //                        Serial Number placed in the global SerialNum[portnum]
248 //            FALSE (0): There are no devices on the 1-Wire Net.
249 // 
250 unsigned char owFirst(unsigned char do_reset, unsigned char alarm_only)
251 {
252    // reset the search state
253    LastDiscrepancy = 0;
254    LastDevice = FALSE;
255    LastFamilyDiscrepancy = 0; 
256
257    return owNext(do_reset,alarm_only);
258 }
259
260 //--------------------------------------------------------------------------
261 // The 'owNext' function does a general search.  This function
262 // continues from the previos search state. The search state
263 // can be reset by using the 'owFirst' function.
264 // This function contains one parameter 'alarm_only'.  
265 // When 'alarm_only' is TRUE (1) the find alarm command 
266 // 0xEC is sent instead of the normal search command 0xF0.
267 // Using the find alarm command 0xEC will limit the search to only
268 // 1-Wire devices that are in an 'alarm' state. 
269 //
270 // 'do_reset'   - TRUE (1) perform reset before search, FALSE (0) do not
271 //                perform reset before search. 
272 // 'alarm_only' - TRUE (1) the find alarm command 0xEC is 
273 //                sent instead of the normal search command 0xF0
274 //
275 // Returns:   TRUE (1) : when a 1-Wire device was found and it's 
276 //                       Serial Number placed in the global SerialNum[portnum]
277 //            FALSE (0): when no new device was found.  Either the
278 //                       last search was the last device or there
279 //                       are no devices on the 1-Wire Net.
280 // 
281 unsigned char owNext(unsigned char do_reset, unsigned char alarm_only)
282 {
283    int bit_test, search_direction, bit_number;
284    int last_zero, serial_byte_number, next_result;
285    unsigned char serial_byte_mask;
286    unsigned char lastcrc8;
287
288    // initialize for search 
289    bit_number = 1;
290    last_zero = 0;
291    serial_byte_number = 0;
292    serial_byte_mask = 1;
293    next_result = 0;     
294    lastcrc8 = 0;
295    
296    // if the last call was not the last one 
297    if (!LastDevice)
298    {
299       // check if reset first is requested
300       if (do_reset)
301       {
302          // reset the 1-wire 
303          // if there are no parts on 1-wire, return FALSE
304          if (!owTouchReset())
305          {
306             // reset the search
307             LastDiscrepancy = 0;        
308             LastFamilyDiscrepancy = 0; 
309             return FALSE;
310          }
311       }
312
313       // If finding alarming devices issue a different command
314       if (alarm_only)
315          owWriteByte(0xEC);  // issue the alarming search command 
316       else
317          owWriteByte(0xF0);  // issue the search command 
318
319       // loop to do the search  
320       do
321       {
322          // read a bit and its compliment 
323          bit_test = owTouchBit(1) << 1;
324          bit_test |= owTouchBit(1);
325
326          // check for no devices on 1-wire
327          if (bit_test == 3)
328          {
329             break;
330          }
331          else
332          {
333             // all devices coupled have 0 or 1
334             if (bit_test > 0)
335             {
336               search_direction = !(bit_test & 0x01);  // bit write value for search 
337             }
338             else
339             {
340                // if this discrepancy if before the Last Discrepancy
341                // on a previous next then pick the same as last time 
342                if (bit_number < LastDiscrepancy) 
343                   search_direction = ((SerialNum[serial_byte_number] & serial_byte_mask) > 0);
344                else
345                   // if equal to last pick 1, if not then pick 0              
346                   search_direction = (bit_number == LastDiscrepancy);       
347
348                // if 0 was picked then record its position in LastZero 
349                if (search_direction == 0) 
350                   last_zero = bit_number;  
351
352                // check for Last discrepancy in family 
353                if (last_zero < 9) 
354                   LastFamilyDiscrepancy = last_zero;
355             }
356
357             // set or clear the bit in the SerialNum byte serial_byte_number 
358             // with mask serial_byte_mask 
359             if (search_direction == 1)
360               SerialNum[serial_byte_number] |= serial_byte_mask;
361             else
362               SerialNum[serial_byte_number] &= ~serial_byte_mask;
363
364             // serial number search direction write bit 
365             owTouchBit(search_direction);
366
367             // increment the byte counter bit_number 
368             // and shift the mask serial_byte_mask 
369             bit_number++;
370             serial_byte_mask <<= 1;
371
372             // if the mask is 0 then go to new SerialNum byte serial_byte_number
373             // and reset mask 
374             if (serial_byte_mask == 0)
375             {
376                 lastcrc8 = docrc8(lastcrc8,SerialNum[serial_byte_number]);  // accumulate the CRC 
377                 serial_byte_number++; 
378                 serial_byte_mask = 1;
379             }
380          }
381       } 
382       while(serial_byte_number < 8);  // loop until through all SerialNum bytes 0-7 
383
384       // if the search was successful then 
385       if (!((bit_number < 65) || lastcrc8))  
386       {
387          // search successful so set LastDiscrepancy,LastDevice,next_result 
388          LastDiscrepancy = last_zero;
389          LastDevice = (LastDiscrepancy == 0);
390          next_result = TRUE;
391       }
392    }
393    
394    // if no device found then reset counters so next 'next' will be
395    // like a first 
396    if (!next_result || !SerialNum[0])
397    {
398       LastDiscrepancy = 0;
399       LastDevice = FALSE;
400       LastFamilyDiscrepancy = 0; 
401       next_result = FALSE;
402    }
403
404    return next_result;
405 }
406
407 //--------------------------------------------------------------------------
408 // The 'owSerialNum' function either reads or sets the SerialNum buffer 
409 // that is used in the search functions 'owFirst' and 'owNext'.  
410 // This function contains two parameters, 'serialnum_buf' is a pointer
411 // to a buffer provided by the caller.  'serialnum_buf' should point to 
412 // an array of 8 unsigned chars.  The second parameter is a flag called
413 // 'do_read' that is TRUE (1) if the operation is to read and FALSE
414 // (0) if the operation is to set the internal SerialNum buffer from 
415 // the data in the provided buffer.
416 //
417 // 'serialnum_buf' - buffer to that contains the serial number to set
418 //                   when do_read = FALSE (0) and buffer to get the serial
419 //                   number when do_read = TRUE (1).
420 // 'do_read'       - flag to indicate reading (1) or setting (0) the current
421 //                   serial number.
422 //
423 void owSerialNum(unsigned char *serialnum_buf, unsigned char do_read)
424 {
425    int i;
426
427    // read the internal buffer and place in 'serialnum_buf'
428    if (do_read)
429    {
430       for (i = 0; i < 8; i++)
431       {
432          serialnum_buf[i] = SerialNum[i];
433       }
434    }
435    // set the internal buffer from the data in 'serialnum_buf'
436    else
437    {
438       for (i = 0; i < 8; i++)
439       {
440          SerialNum[i] = serialnum_buf[i];
441       }
442    }
443 }
444