Mercurial > ~darius > hgwebdir.cgi > stm32test
diff 1wire.c @ 13:96c345d304af default tip
Add 1wire code.
1wire.c, 1wire.h and 1wire-config.h are copied avr-lib.
author | Daniel O'Connor <darius@dons.net.au> |
---|---|
date | Wed, 08 Feb 2012 10:37:22 +1030 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/1wire.c Wed Feb 08 10:37:22 2012 +1030 @@ -0,0 +1,658 @@ +/* + * Various 1 wire routines + * Search routine is copied from the Dallas owpd library with mods + * available from here http://www.ibutton.com/software/1wire/wirekit.html + * + * $Id$ + * + * 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. + */ + +/* + * No user servicable parts inside + * + * Modify 1wire-config.h + */ + +#include <stdio.h> +#include <stdint.h> +#include "1wire-config.h" +#include "1wire.h" + +static uint8_t OW_LastDevice = 0; +static uint8_t OW_LastDiscrepancy = 0; +static uint8_t OW_LastFamilyDiscrepancy = 0; + +const PROGMEM char *OWProgROM_Status[] = { + "OK", + "no HW support", + "Invalid params", + "module missing/broken" +}; + +/*----------------------------------------------------------------------------- + * Configure the IO port as we need + */ +void +OWInit(void) { + OWBUSINIT(); + OWSETBUSHIGH(); +} + +/*----------------------------------------------------------------------------- + * Generate a 1-Wire reset, return 0 if presence pulse was found, 1 if it + * wasn't, or 2 if the line appears to be being held low. + * + * (NOTE: Does not handle alarm presence from DS2404/DS1994) + */ +uint8_t +OWTouchReset(void) { + uint8_t i; + + OWDELAY_G; + + /* Check the bus isn't being held low (ie it's broken) Do it after + * the delay so we guarantee we don't see a slave from a previous + * comms attempt + */ +#if 1 + OWSETREAD(); + if(OWREADBUS() == 0) + return 2; +#endif + + OWSETBUSLOW(); + OWDELAY_H; + OWSETBUSHIGH(); + OWDELAY_I; + + OWSETREAD(); + i = OWREADBUS(); + + OWDELAY_J; + return(i); +} + +/*----------------------------------------------------------------------------- + * Send a 1-wire write bit. + */ +void +OWWriteBit(uint8_t bit) { + OWDELAY_I; + + if (bit) { + OWSETBUSLOW(); + OWDELAY_A; + OWSETBUSHIGH(); + OWDELAY_B; + } else { + OWSETBUSLOW(); + OWDELAY_C; + OWSETBUSHIGH(); + OWDELAY_D; + } +} + +/*----------------------------------------------------------------------------- + * Read a bit from the 1-wire bus and return it. + */ +uint8_t +OWReadBit(void) { + uint8_t i; + + OWDELAY_I; + + OWSETBUSLOW(); + OWDELAY_A; + OWSETBUSHIGH(); + OWDELAY_E; + OWSETREAD(); + i = OWREADBUS(); + OWDELAY_F; + return(i); +} + +/*----------------------------------------------------------------------------- + * Write a byte to the 1-wire bus + */ +void +OWWriteByte(uint8_t data) { + uint8_t i; + + /* Send LSB first */ + for (i = 0; i < 8; i++) { + OWWriteBit(data & 0x01); + data >>= 1; + } +} + +/*----------------------------------------------------------------------------- + * Read a byte from the 1-wire bus + */ +uint8_t +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. + */ +uint8_t +OWTouchByte(uint8_t data) { + uint8_t 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) { + uint8_t 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) + * + * OWFirst/OWNext return.. + * 1 when something is found, + * 0 no more modules + * -1 if no presence pulse, + * -2 if bad CRC, + * -3 if bad wiring. + */ +uint8_t +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 */ +uint8_t +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, rom_byte_mask; + uint8_t lastcrc8, crcaccum; + int8_t next_result; + + /* Init for search */ + bit_number = 1; + last_zero = 0; + rom_byte_number = 0; + rom_byte_mask = 1; + next_result = OW_NOMODULES; + 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 */ + OWPUTSP(PSTR("Resetting\r\n")); + switch (OWTouchReset()) { + case 0: + OWPUTSP(PSTR("Found device(s)\r\n")); + break; + + case 1: + /* reset the search */ + OW_LastDiscrepancy = 0; + OW_LastFamilyDiscrepancy = 0; + OWPUTSP(PSTR("No devices on bus\r\n")); + return OW_NOPRESENCE; + break; + + case 2: + /* reset the search */ + OW_LastDiscrepancy = 0; + OW_LastFamilyDiscrepancy = 0; + OWPUTSP(PSTR("Bus appears to be being held low\r\n")); + return OW_BADWIRE; + break; + + } + } + + /* 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_I; + OWDELAY_I; + OWDELAY_I; + + /* loop to do the search */ + do { + /* read a bit and its compliment */ + bit_test = OWReadBit() << 1; + bit_test |= OWReadBit(); + + OWPRINTFP(PSTR("bit_test = %d\r\n"), bit_test); + + /* check for no devices on 1-wire */ + if (bit_test == 3) { + OWPRINTFP(PSTR("bit_test = %d\r\n"), bit_test); + return(OW_BADWIRE); + } + 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) { + OWPRINTFP(PSTR("Bad CRC (%d)\r\n"), lastcrc8); + next_result = OW_BADCRC; + } else { + /* search successful so set LastDiscrepancy,LastDevice,next_result */ + OW_LastDiscrepancy = last_zero; + OW_LastDevice = (OW_LastDiscrepancy == 0); + OWPRINTFP(PSTR("Last device = %d\r\n"), OW_LastDevice); + next_result = OW_FOUND; + } + } + } + + /* if no device found then reset counters so next 'next' will be + * like a first */ + if (next_result != OW_FOUND || ROM[0] == 0) { + OW_LastDiscrepancy = 0; + OW_LastDevice = 0; + OW_LastFamilyDiscrepancy = 0; + } + + if (next_result == OW_FOUND && ROM[0] == 0x00) + next_result = OW_BADWIRE; + + 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 +}; + +/*----------------------------------------------------------------------------- + * Update *crc based on the value of x + */ +void +OWCRC(uint8_t x, uint8_t *crc) { + *crc = pgm_read_byte(&dscrc_table[(*crc) ^ x]); +} + +/*----------------------------------------------------------------------------- + * Program a DS2502's memory + * + * Arguments + * ROM - ROM ID (or NULL to send SKIP_ROM) + * start - Start address (bytes) + * len - Length of data to write + * data - Data to write + * exact - If true, only accept exact matches for programming, + * otherwise only ensure the bits we requested were + * programmed [to 0] + * status - If true program status rather than memory + * + * Returns.. + * 0 if all is OK + * 1 if the programming is not possible + * 2 if the parameters were invalid + * 3 if the DS2502 didn't respond appropriately (also happens if the + * module doesn't exist) + */ +#if defined(OWSETVPPON) && defined(OWSETVPPOFF) +uint8_t +OWProgROM(uint8_t *ROM, uint8_t start, uint8_t len, uint8_t *data, uint8_t exact, uint8_t status) { + uint8_t crc, i, tmp; + + /* Stupid programmer detection */ + if (status) { + if (start + len > 3) + return(2); + } else { + if (start + len > 127) + return(2); + } + + if (len < 1) + return(2); + + OWDELAY_I; + if (OWTouchReset() != 0) { + cons_putsP(PSTR("No presence pulse\r\n")); + return(3); + } + + crc = 0; + + /* Send the command */ + if (status) { + OWSendCmd(ROM, OW_WRITE_STATUS); + OWCRC(OW_WRITE_STATUS, &crc); + } else { + OWSendCmd(ROM, OW_WRITE_MEMORY); + OWCRC(OW_WRITE_MEMORY, &crc); + } + + /* And the start address + * (2 bytes even though one would do) + */ + OWWriteByte(start); + OWCRC(start, &crc); + + OWWriteByte(0x00); + OWCRC(0x00, &crc); + + for (i = 0; i < len; i++) { + cons_putsP(PSTR("Programming ")); + cons_puts_hex(data[i]); + cons_putsP(PSTR(" to ")); + cons_puts_hex(start + i); + cons_putsP(PSTR("\r\n")); + + OWWriteByte(data[i]); + OWCRC(data[i], &crc); + + tmp = OWReadByte(); + + if (crc != tmp) { + cons_putsP(PSTR("CRC mismatch ")); + cons_puts_hex(crc); + cons_putsP(PSTR(" vs ")); + cons_puts_hex(tmp); + cons_putsP(PSTR("\r\n")); + + OWTouchReset(); + return(3); + } + + OWSETVPPON(); + OWDELAY_H; + OWSETVPPOFF(); + + tmp = OWReadByte(); + + /* Check the bits we turned off are off */ +/* + for (i = 0; i < 8; i++) + if (!(data[i] & 1 << i) && (tmp & 1 << i)) + return(-3); +*/ + if ((!data[i] & tmp) != 0) { + cons_putsP(PSTR("Readback mismatch ")); + cons_puts_hex(data[i]); + cons_putsP(PSTR(" vs ")); + cons_puts_hex(data[i]); + cons_putsP(PSTR("\r\n")); + + OWTouchReset(); + return(3); + } + + /* The DS2502 loads it's CRC register with the address of the + * next byte */ + crc = 0; + OWCRC(start + i + 1, &crc); + } + + return(0); +} +#else +uint8_t +OWProgROM(uint8_t *ROM __attribute((unused)), uint8_t start __attribute((unused)), uint8_t len __attribute((unused)), uint8_t *data __attribute((unused)), uint8_t exact __attribute((unused)), uint8_t status __attribute((unused))) { + return(1); +} +#endif + +/* + * OWGetTemp + * + * Get the temperature from a 1wire bus module + * + * Returns temperature in hundredths of a degree or OW_TEMP_xxx on + * error. + */ +int16_t +OWGetTemp(uint8_t *ROM) { + int8_t i; + uint8_t crc, buf[9]; + int16_t temp; + int16_t tfrac; + + if (ROM[0] != OW_FAMILY_TEMP) + return OW_TEMP_WRONG_FAM; + + OWSendCmd(ROM, OW_CONVERTT_CMD); + + i = 0; + + /* Wait for the conversion */ + while (OWReadBit() == 0) + i = 1; + + /* Check that we talked to a module and it did something */ + if (i == 0) { + return OW_TEMP_NO_ROM; + } + + OWSendCmd(ROM, OW_RD_SCR_CMD); + crc = 0; + for (i = 0; i < 8; i++) { + buf[i] = OWReadByte(); + OWCRC(buf[i], &crc); + } + buf[i] = OWReadByte(); + if (crc != buf[8]) + return OW_TEMP_CRC_ERR; + temp = buf[0]; + if (buf[1] & 0x80) + temp -= 256; + + /* Chop off 0.5 degree bit */ + temp >>= 1; + + /* Calulate the fractional remainder */ + tfrac = buf[7] - buf[6]; + + /* Work in 100'th of degreess to save on floats */ + tfrac *= (int16_t)100; + + /* Divide by count */ + tfrac /= buf[7]; + + /* Subtract 0.25 deg from temp */ + tfrac += 75; + if (tfrac < 100) + temp--; + else + tfrac -= 100; + + i = temp; + temp *= 100; + temp += tfrac; + + return(temp); +} + +/* + * OWTempStatusStr + * + * Return a string for each OW_TEMP_xxx error code + * + * shrt = 1 returns short strings + * + */ +const char * +OWTempStatusStr(int16_t val, uint8_t shrt) { + if (val > OW_TEMP_BADVAL) { + if (shrt) + return PSTR("OK"); + else + return PSTR("OK"); + } + + switch (val) { + case OW_TEMP_WRONG_FAM: + if (shrt) + return PSTR("WrFam"); + else + return PSTR("Wrong family"); + break; + case OW_TEMP_CRC_ERR: + if (shrt) + return PSTR("CRCErr"); + else + return PSTR("CRC Error"); + break; + case OW_TEMP_NO_ROM: + if (shrt) + return PSTR("NoROM"); + else + return PSTR("ROM did not reply"); + break; + default: + if (shrt) + return PSTR("???"); + else + return PSTR("Unknown error code"); + break; + } +}