Mercurial > ~darius > hgwebdir.cgi > avr
diff 1wire.c @ 0:ffeab3c04e83
Initial revision
author | darius |
---|---|
date | Sun, 11 Jul 2004 00:45:50 +0930 |
parents | |
children | f9a085a0ba93 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/1wire.c Sun Jul 11 00:45:50 2004 +0930 @@ -0,0 +1,530 @@ +/* + * Various 1 wire routines + * Search routine is copied from the Dallas owpd library with mods. + * + * Copyright (c) 2004 + * Daniel O'Connor <darius@dons.net.au>. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <stdio.h> +#include <avr/io.h> +#include <avr/pgmspace.h> + +#include "1wire.h" + +void uart_putsP(const char *addr); +void uart_puts(const char *addr); +void uart_getc(); +int uart_putc(char c); + +static uint8_t OW_LastDevice = 0; +static uint8_t OW_LastDiscrepancy = 0; +static uint8_t OW_LastFamilyDiscrepancy = 0; + +static void +OWdelay(void) { + asm volatile ( + "ldi r21, 50\n\t" + "_OWdelay:\n\t" + "nop\n\t" + "nop\n\t" + "nop\n\t" + "nop\n\t" + "nop\n\t" + "nop\n\t" + "nop\n\t" + "nop\n\t" + "nop\n\t" + "nop\n\t" + "nop\n\t" + "nop\n\t" + "nop\n\t" + "dec r21\n\t" + "brne _OWdelay\n\t" + ::: "r21"); +} + +/*----------------------------------------------------------------------------- + * Generate a 1-Wire reset, return 1 if no presence detect was found, + * return 0 otherwise. + * (NOTE: Does not handle alarm presence from DS2404/DS1994) + */ +int +OWTouchReset(void) { + uint8_t result; + + asm volatile ( + /* Delay G (0 usec) */ + "\n\t" + /* Drive bus low */ + "cbi %[out], %[opin]\n\t" + "sbi %[ddr], %[opin]\n\t" + /* Delay H (480 usec) */ + "ldi r21, 120\n\t" + "loopH:\n\t" + "nop\n\t" + "nop\n\t" + "nop\n\t" + "nop\n\t" + "nop\n\t" + "nop\n\t" + "nop\n\t" + "nop\n\t" + "nop\n\t" + "nop\n\t" + "nop\n\t" + "nop\n\t" + "nop\n\t" + "dec r21\n\t" + "brne loopH\n\t" + /* Release bus */ + "cbi %[ddr], %[opin]\n\t" + /* Delay I (70 usec) */ + "ldi r21, 35\n\t" + "loopI:\n\t" + "nop\n\t" + "nop\n\t" + "nop\n\t" + "nop\n\t" + "nop\n\t" + "dec r21\n\t" + "brne loopI\n\t" + /* Sample for presense */ + "ldi %[result], 0\n\t" + "sbi %[ddr], 1\n\t" + "sbi %[out], 1\n\t" + /* 1 == no presence */ + "sbic %[in], %[ipin]\n\t" + "ldi %[result], 1\n\t" + "cbi %[out], 1\n\t" + + : [result] "=r" (result) /* Outputs */ + : [out] "I" (_SFR_IO_ADDR(OWIREOUTPORT)), /* Inputs */ + [ddr] "I" (_SFR_IO_ADDR(OWIREDDR)), + [opin] "I" (OWIREOUTPIN), + [in] "I" (_SFR_IO_ADDR(OWIREINPORT)), + [ipin] "I" (OWIREINPIN) + : "r21"); /* Clobbers */ + + return(result); +} + +/*----------------------------------------------------------------------------- + * Send a 1-wire write bit. + */ +void +OWWriteBit(int bit) { + OWdelay(); + if (bit) { + asm volatile ( + /* Drive bus low */ + "cbi %[out], %[pin]\n\t" + "sbi %[ddr], %[pin]\n\t" + /* Delay A (6 usec) */ + "ldi r21, 1\n\t" + "loopA:\n\t" + "nop\n\t" + "dec r21\n\t" + "brne loopA\n\t" + /* Release bus */ + "cbi %[ddr], %[pin]\n\t" + /* Delay B (64 usec) */ + "ldi r21, 32\n\t" + "loopB:\n\t" + "nop\n\t" + "nop\n\t" + "nop\n\t" + "nop\n\t" + "nop\n\t" + "dec r21\n\t" + "brne loopB\n\t" + : /* Outputs */ + : [out] "I" (_SFR_IO_ADDR(OWIREOUTPORT)), /* Inputs */ + [ddr] "I" (_SFR_IO_ADDR(OWIREDDR)), + [pin] "I" (OWIREOUTPIN) + : "r21"); /* Clobbers */ + } else { + asm volatile ( + /* Drive bus low */ + "cbi %[out], %[pin]\n\t" + "sbi %[ddr], %[pin]\n\t" + /* Delay C (60 usec) */ + "ldi r21, 30\n\t" + "loopC:\n\t" + "nop\n\t" + "nop\n\t" + "nop\n\t" + "nop\n\t" + "nop\n\t" + "dec r21\n\t" + "brne loopC\n\t" + /* Release bus */ + "cbi %[ddr], %[pin]\n\t" + /* Delay D (10 usec) */ + "ldi r21, 9\n\t" + "loopD:\n\t" + "nop\n\t" + "dec r21\n\t" + "brne loopD\n\t" + : /* Outputs */ + : [out] "I" (_SFR_IO_ADDR(OWIREOUTPORT)), /* Inputs */ + [ddr] "I" (_SFR_IO_ADDR(OWIREDDR)), + [pin] "I" (OWIREOUTPIN) + : "r21"); /* Clobbers */ + } +} + +/*----------------------------------------------------------------------------- + * Read a bit from the 1-wire bus and return it. + */ +int +OWReadBit(void) { + uint8_t result; + + OWdelay(); + + asm volatile ( + /* Drive bus low */ + "cbi %[out], %[opin]\n\t" + "sbi %[ddr], %[opin]\n\t" + /* Delay A (6 usec) */ + "ldi r21, 1\n\t" + "loopA1:\n\t" + "dec r21\n\t" + "brne loopA1\n\t" + /* Release bus */ + "cbi %[ddr], %[opin]\n\t" + /* Delay E (9 usec) */ + "ldi r21, 8\n\t" + "loopE:\n\t" + "nop\n\t" + "dec r21\n\t" + "brne loopE\n\t" + /* Sample */ + "ldi %[res], 0\n\t" + "sbic %[in], %[ipin]\n\t" + "ldi %[res], 1\n\t" + + /* Delay F (55 usec) */ + "ldi r21, 27\n\t" + "loopF:\n\t" + "nop\n\t" + "nop\n\t" + "nop\n\t" + "nop\n\t" + "nop\n\t" + "nop\n\t" + "nop\n\t" + "nop\n\t" + "nop\n\t" + "nop\n\t" + "nop\n\t" + "nop\n\t" + "nop\n\t" + "dec r21\n\t" + "brne loopF\n\t" + + : [res] "=r" (result) /* Outputs */ + : [out] "I" (_SFR_IO_ADDR(OWIREOUTPORT)), /* Inputs */ + [ddr] "I" (_SFR_IO_ADDR(OWIREDDR)), + [opin] "I" (OWIREOUTPIN), + [in] "I" (_SFR_IO_ADDR(OWIREINPORT)), + [ipin] "I" (OWIREINPIN) + : "r21"); /* Clobbers */ + + return(result); +} + +/*----------------------------------------------------------------------------- + * Write a byte to the 1-wire bus + */ +void +OWWriteByte(uint8_t data) { + int i; + + /* Send LSB first */ + for (i = 0; i < 8; i++) { + OWWriteBit(data & 0x01); + data >>= 1; + } +} + +/*----------------------------------------------------------------------------- + * Read a byte from the 1-wire bus + */ +int +OWReadByte(void) { + int i, result = 0; + + for (i = 0; i < 8; i++) { + result >>= 1; + if (OWReadBit()) + result |= 0x80; + } + return(result); +} + +/*----------------------------------------------------------------------------- + * Write a 1-wire data byte and return the sampled result. + */ +int +OWTouchByte(uint8_t data) { + int i, result = 0; + + for (i = 0; i < 8; i++) { + result >>= 1; + + /* If sending a 1 then read a bit, otherwise write a 0 */ + if (data & 0x01) { + if (OWReadBit()) + result |= 0x80; + } else + OWWriteBit(0); + + data >>= 1; + } + + return(result); +} + +/*----------------------------------------------------------------------------- + * Write a block of bytes to the 1-wire bus and return the sampled result in + * the same buffer + */ +void +OWBlock(uint8_t *data, int len) { + int i; + + for (i = 0; i < len; i++) + data[i] = OWTouchByte(data[i]); +} + + +/*----------------------------------------------------------------------------- + * Send a 1 wire command to a device, or all if no ROM ID provided + */ +void +OWSendCmd(uint8_t *ROM, uint8_t cmd) { + int i; + + OWTouchReset(); + + if (ROM == NULL) + OWWriteByte(OW_SKIP_ROM_CMD); + else { + OWWriteByte(OW_MATCH_ROM_CMD); + for (i = 0; i < 8; i++) + OWWriteByte(ROM[i]); + } + + OWWriteByte(cmd); +} + +/*----------------------------------------------------------------------------- + * Search algorithm from App note 187 (and 162) + * + * Returns 1 when something is found, 0 if nothing present + */ +int +OWFirst(uint8_t *ROM, uint8_t do_reset, uint8_t alarm_only) { + /* Reset state */ + OW_LastDiscrepancy = 0; + OW_LastDevice = 0; + OW_LastFamilyDiscrepancy = 0; + + /* Go looking */ + return (OWNext(ROM, do_reset, alarm_only)); +} + +/* Returns 1 when something is found, 0 if nothing left */ +int +OWNext(uint8_t *ROM, uint8_t do_reset, uint8_t alarm_only) { + uint8_t bit_test, search_direction, bit_number; + uint8_t last_zero, rom_byte_number, next_result; + uint8_t rom_byte_mask; + uint8_t lastcrc8, crcaccum; + char errstr[30]; + + /* Init for search */ + bit_number = 1; + last_zero = 0; + rom_byte_number = 0; + rom_byte_mask = 1; + next_result = 0; + lastcrc8 = 0; + crcaccum = 0; + + /* if the last call was not the last one */ + if (!OW_LastDevice) { + /* check if reset first is requested */ + if (do_reset) { + /* reset the 1-wire + * if there are no parts on 1-wire, return 0 */ +#if OW_DEBUG + uart_putsP(PSTR("Resetting\n\r")); +#endif + if (OWTouchReset()) { + /* reset the search */ + OW_LastDiscrepancy = 0; + OW_LastFamilyDiscrepancy = 0; +#if OW_DEBUG + uart_putsP(PSTR("No devices on bus\n\r")); +#endif + return 0; + } + } + + /* If finding alarming devices issue a different command */ + if (alarm_only) + OWWriteByte(OW_SEARCH_ALRM_CMD); /* issue the alarming search command */ + else + OWWriteByte(OW_SEARCH_ROM_CMD); /* issue the search command */ + + /* pause before beginning the search */ + OWdelay(); + OWdelay(); + OWdelay(); + + /* loop to do the search */ + do { + /* read a bit and its compliment */ + bit_test = OWReadBit() << 1; + bit_test |= OWReadBit(); + +#if OW_DEBUG + sprintf_P(errstr, PSTR("bit_test = %d\n\r"), bit_test); + uart_puts(errstr); +#endif + + /* check for no devices on 1-wire */ + if (bit_test == 3) { + sprintf_P(errstr, PSTR("bit_test = %d\n\r"), bit_test); + uart_puts(errstr); + break; + } + else { + /* all devices coupled have 0 or 1 */ + if (bit_test > 0) + search_direction = !(bit_test & 0x01); /* bit write value for search */ + else { + /* if this discrepancy is before the Last Discrepancy + * on a previous OWNext then pick the same as last time */ + if (bit_number < OW_LastDiscrepancy) + search_direction = ((ROM[rom_byte_number] & rom_byte_mask) > 0); + else + /* if equal to last pick 1, if not then pick 0 */ + search_direction = (bit_number == OW_LastDiscrepancy); + + /* if 0 was picked then record its position in LastZero */ + if (search_direction == 0) { + last_zero = bit_number; + + /* check for Last discrepancy in family */ + if (last_zero < 9) + OW_LastFamilyDiscrepancy = last_zero; + } + } + + /* set or clear the bit in the ROM byte rom_byte_number + * with mask rom_byte_mask */ + if (search_direction == 1) + ROM[rom_byte_number] |= rom_byte_mask; + else + ROM[rom_byte_number] &= ~rom_byte_mask; + + /* serial number search direction write bit */ + OWWriteBit(search_direction); + + /* increment the byte counter bit_number + * and shift the mask rom_byte_mask */ + bit_number++; + rom_byte_mask <<= 1; + + /* if the mask is 0 then go to new ROM byte rom_byte_number + * and reset mask */ + if (rom_byte_mask == 0) { + OWCRC(ROM[rom_byte_number], &crcaccum); /* accumulate the CRC */ + lastcrc8 = crcaccum; + + rom_byte_number++; + rom_byte_mask = 1; + } + } + } while (rom_byte_number < 8); /* loop until through all ROM bytes 0-7 */ + + /* if the search was successful then */ + if (!(bit_number < 65) || lastcrc8) { + if (lastcrc8) { + sprintf_P(errstr, PSTR("Bad CRC (%d)\n\r"), lastcrc8); + uart_puts(errstr); + next_result = 0; + } else { + /* search successful so set LastDiscrepancy,LastDevice,next_result */ + OW_LastDiscrepancy = last_zero; + OW_LastDevice = (OW_LastDiscrepancy == 0); +#if OW_DEBUG + sprintf_P(errstr, PSTR("Last device = %d\n\r"), OW_LastDevice); + uart_puts(errstr); +#endif + next_result = 1; + } + } + } + + /* if no device found then reset counters so next 'next' will be + * like a first */ + if (!next_result || !ROM[0]) { + OW_LastDiscrepancy = 0; + OW_LastDevice = 0; + OW_LastFamilyDiscrepancy = 0; + next_result = 0; + } + + return next_result; + +} + +uint8_t PROGMEM dscrc_table[] = { + 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 +OWCRC(uint8_t x, uint8_t *crc) { + *crc = pgm_read_byte(&dscrc_table[(*crc) ^ x]); +}