diff options
author | barthess <barthess@yandex.ru> | 2014-12-06 20:16:37 +0300 |
---|---|---|
committer | barthess <barthess@yandex.ru> | 2014-12-06 20:16:37 +0300 |
commit | 12da9781a05599868ace5b6311bb17b9b919b5b6 (patch) | |
tree | 008dc7ac9902cee1a0267dbda09b4946212e6eb4 /os | |
parent | 4ab64b4e4ef95d3e41c0bf74ab282aa5160e4f43 (diff) | |
download | ChibiOS-Contrib-12da9781a05599868ace5b6311bb17b9b919b5b6.tar.gz ChibiOS-Contrib-12da9781a05599868ace5b6311bb17b9b919b5b6.tar.bz2 ChibiOS-Contrib-12da9781a05599868ace5b6311bb17b9b919b5b6.zip |
Added onewire driver
Diffstat (limited to 'os')
-rw-r--r-- | os/hal/hal.mk | 3 | ||||
-rw-r--r-- | os/hal/include/hal_community.h | 1 | ||||
-rw-r--r-- | os/hal/include/onewire.h | 234 | ||||
-rw-r--r-- | os/hal/src/onewire.c | 848 |
4 files changed, 1085 insertions, 1 deletions
diff --git a/os/hal/hal.mk b/os/hal/hal.mk index 47f24b0..38868b6 100644 --- a/os/hal/hal.mk +++ b/os/hal/hal.mk @@ -1,6 +1,7 @@ include ${CHIBIOS}/os/hal/hal.mk
HALSRC += ${CHIBIOS}/community/os/hal/src/hal_community.c \
- ${CHIBIOS}/community/os/hal/src/nand.c
+ ${CHIBIOS}/community/os/hal/src/nand.c \
+ ${CHIBIOS}/community/os/hal/src/onewire.c
HALINC += ${CHIBIOS}/community/os/hal/include
diff --git a/os/hal/include/hal_community.h b/os/hal/include/hal_community.h index 502e960..bf645ae 100644 --- a/os/hal/include/hal_community.h +++ b/os/hal/include/hal_community.h @@ -36,6 +36,7 @@ #include "nand.h"
/* Complex drivers.*/
+#include "onewire.h"
/*===========================================================================*/
/* Driver constants. */
diff --git a/os/hal/include/onewire.h b/os/hal/include/onewire.h new file mode 100644 index 0000000..a5ebaa4 --- /dev/null +++ b/os/hal/include/onewire.h @@ -0,0 +1,234 @@ +/* + ChibiOS/RT - Copyright (C) 2014 Uladzimir Pylinsky aka barthess + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + + + +/** + * @file onewire.h + * @brief 1-wire Driver macros and structures. + * + * @addtogroup onewire + * @{ + */ + +#ifndef _ONEWIRE_H_ +#define _ONEWIRE_H_ + +#include "hal.h" //FIXME: delete this line when integration done + +#if HAL_USE_ONEWIRE || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver constants. */ +/*===========================================================================*/ +/** + * @brief Enable synthetic test for 'search ROM' procedure. + * @note Only for debugging/testing. + */ +#define ONEWIRE_SYNTH_SEARCH_TEST FALSE + +/** + * @brief Aliases for 1-wire protocol. + */ +#define ONEWIRE_CMD_READ_ROM 0x33 +#define ONEWIRE_CMD_SEARCH_ROM 0xF0 +#define ONEWIRE_CMD_MATCH_ROM 0x55 +#define ONEWIRE_CMD_SKIP_ROM 0xCC +#define ONEWIRE_CMD_CONVERT_TEMP 0x44 +#define ONEWIRE_CMD_READ_SCRATCHPAD 0xBE + +/*===========================================================================*/ +/* Driver pre-compile time settings. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Derived constants and error checks. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver data structures and types. */ +/*===========================================================================*/ + +/** + * @brief 1-wire strong pull up assert callback type. + */ +typedef void (*onewire_pullup_assert_t)(void); + +/** + * @brief 1-wire strong pull up release callback type. + */ +typedef void (*onewire_pullup_release_t)(void); + +/** + * @brief 1-wire read bit callback type. + * + * @return Bit acquired directly from pin (0 or 1) + */ +typedef uint_fast8_t (*onewire_read_bit_t)(void); + +/** + * @brief Driver state machine possible states. + */ +typedef enum { + ONEWIRE_UNINIT = 0, + ONEWIRE_STOP = 1, + ONEWIRE_READY = 2, +#if ONEWIRE_USE_STRONG_PULLUP + ONEWIRE_PULL_UP +#endif +} onewire_state_t; + +/** + * @brief Search ROM procedure possible state. + */ +typedef enum { + ONEWIRE_SEARCH_ROM_SUCCESS = 0, + ONEWIRE_SEARCH_ROM_LAST, + ONEWIRE_SEARCH_ROM_ERROR +} search_rom_result_t; + +/** + * @brief Search ROM procedure iteration enum. + */ +typedef enum { + ONEWIRE_SEARCH_ROM_FIRST = 0, + ONEWIRE_SEARCH_ROM_NEXT +} search_iteration_t; + +/** + * @brief Driver configuration structure. + */ +typedef struct { + PWMDriver *pwmd; + size_t master_channel; + size_t sample_channel; + onewire_read_bit_t readBitX; +#if ONEWIRE_USE_STRONG_PULLUP + onewire_pullup_assert_t pullup_assert; + onewire_pullup_release_t pullup_release; +#endif +} onewireConfig; + +/** + * @brief Some small variable used in 'search ROM' procedure combined + * in single machine word to save RAM. + */ +typedef struct { + uint32_t single_device: 1; /**< @brief Bool flag */ + uint32_t search_iter: 1; /**< @brief 0 - first, 1 - next */ + uint32_t result: 2; /**< @brief 0 - success, 1 - last, 2 - error.*/ + uint32_t bit_step: 2; /**< @brief 0 - direct, 1 - complemented, 2 - generated by master. */ + uint32_t bit_buf: 2; /**< @brief Acquired bits. 0s - direct, 1st - complement */ + uint32_t rombit: 7; /**< @brief Currently processing ROM bit. Must be big enough to store number 64.*/ + uint32_t devices_found: 8; /**< @brief Devices count discovered on bus .*/ +} search_rom_reg_t; + +/** + * @brief Helper structure for 'search ROM' procedure + */ +typedef struct { + search_rom_reg_t reg; + uint8_t *retbuf; /* buffer for currently discovering ROM */ + uint8_t prev_path[8]; + int8_t last_zero_branch; /* negative values uses to point out of tree root */ + int8_t prev_zero_branch; /* negative values uses to point out of tree root */ +} onewire_search_rom_t; + +/** + * @brief Some small variable used in driver combined + * in single machine word to save RAM. + */ +typedef struct { +#if ONEWIRE_USE_STRONG_PULLUP + /** + * @brief This flag will be asserted by driver to signalize + * ISR part when strong pullup needed. + */ + uint32_t need_pullup: 1; +#endif + uint32_t slave_present: 1; /**< @brief Bool flag */ + uint32_t state: 2; /**< @brief Driver state */ + uint32_t bit: 4; /**< @brief Bit number in currently receiving byte. Must be big enough to store 8 */ + uint32_t final_timeslot: 1; /**< @brief bool flag for premature timer stop prevention */ + uint32_t bytes: 16; /**< @brief Bytes number to be processing in current transaction. */ +} onewire_reg_t; + +/** + * @brief Structure representing an 1-wire driver. + */ +typedef struct { + onewire_reg_t reg; + const onewireConfig *config; + PWMConfig pwmcfg; + uint8_t *buf; + onewire_search_rom_t search_rom; + + /** + * @brief Thread waiting for I/O completion. + */ + thread_reference_t thread; +} onewireDriver; + +/*===========================================================================*/ +/* Driver macros. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +extern onewireDriver OWD1; + +#ifdef __cplusplus +extern "C" { +#endif + void onewireInit(void); + void onewireObjectInit(onewireDriver *owp); + void onewireStart(onewireDriver *owp, const onewireConfig *config); + void onewireStop(onewireDriver *owp); + bool onewireReset(onewireDriver *owp); + void onewireRead(onewireDriver *owp, uint8_t *rxbuf, size_t rxbytes); + void onewireWrite(onewireDriver *owp, + uint8_t *txbuf, + size_t txbytes, + systime_t pullup_time); + size_t onewireSearchRom(onewireDriver *owp, + uint8_t *result, + size_t max_rom_cnt); + uint8_t onewireCRC(const uint8_t *buf, size_t len); +#if ONEWIRE_SYNTH_SEARCH_TEST + void _synth_ow_write_bit(onewireDriver *owp, uint8_t bit); + uint_fast8_t _synth_ow_read_bit(void); + void synthSearchRomTest(onewireDriver *owp); +#endif /* ONEWIRE_SYNTH_SEARCH_TEST */ +#ifdef __cplusplus +} +#endif + +#endif /* HAL_USE_ONEWIRE */ + +#endif /* _ONEWIRE_H_ */ + +/** @} */ + + + + + + + + + diff --git a/os/hal/src/onewire.c b/os/hal/src/onewire.c new file mode 100644 index 0000000..0717893 --- /dev/null +++ b/os/hal/src/onewire.c @@ -0,0 +1,848 @@ +/* + ChibiOS/RT - Copyright (C) 2014 Uladzimir Pylinsky aka barthess + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/*===========================================================================*/ +/* Main ideas: */ +/*=========================================================================== + +1) switch PWM output pin it opendrain mode. +2) start 2 channels simultaneously. First (master channel) generates + pulses (read time slots) second (sample channel) generates interrupts + from where read pin function calls. + +- ---------------------------------------------- master channel + | / + ---- +- ------------------------------------ sample channel + | | + ------------------ + ^ + read interrupt here + +For data write it is only master channel needed. Data bit width updates +on every timer overflow event. +*/ + +/*===========================================================================*/ +/* General recommendations for strong pull usage */ +/*=========================================================================== + * + * + * 1) Use separate power rail instead of strong pull up whenever possible. + * Driver's strong pull up feature is very interrupt jitter sensible. + * 2) Use special 1-wire bus master (DS2484 for example) if you are + * forced to handle bus requiring strong pull up feature. + */ + +/** + * @file onewire.c + * @brief 1-wire Driver code. + * + * @addtogroup onewire + * @{ + */ + +#include "hal.h" + +#if HAL_USE_ONEWIRE || defined(__DOXYGEN__) + +#include <string.h> + +/*===========================================================================*/ +/* Driver local definitions. */ +/*===========================================================================*/ +/** + * @brief Pulse width constants. + * @details Inspired by Microchip's AN1199 + * "1-Wire® Communication with PIC® Microcontroller" + */ +#define ONEWIRE_ZERO_WIDTH 60 +#define ONEWIRE_ONE_WIDTH 6 +#define ONEWIRE_SAMPLE_WIDTH 15 +#define ONEWIRE_RECOVERY_WIDTH 10 +#define ONEWIRE_RESET_LOW_WIDTH 480 +#define ONEWIRE_RESET_SAMPLE_WIDTH 550 +#define ONEWIRE_RESET_TOTAL_WIDTH 960 + +/** + * @brief Forward declarations. + */ +static void ow_reset_cb(PWMDriver *pwmp, onewireDriver *owp); +static void pwm_reset_cb(PWMDriver *pwmp); +static void ow_read_bit_cb(PWMDriver *pwmp, onewireDriver *owp); +static void pwm_read_bit_cb(PWMDriver *pwmp); +static void ow_write_bit_cb(PWMDriver *pwmp, onewireDriver *owp); +static void pwm_write_bit_cb(PWMDriver *pwmp); +static void ow_search_rom_cb(PWMDriver *pwmp, onewireDriver *owp); +static void pwm_search_rom_cb(PWMDriver *pwmp); + +/*===========================================================================*/ +/* Driver exported variables. */ +/*===========================================================================*/ +/** + * @brief 1-wire driver identifier. + */ +onewireDriver OWD1; + +/*===========================================================================*/ +/* Driver local variables and types. */ +/*===========================================================================*/ +/** + * @brief Config for fast intialization of all fields + */ +static const PWMConfig pwm_default_cfg = { + 1000000, + ONEWIRE_RESET_TOTAL_WIDTH, + NULL, + { + {PWM_OUTPUT_DISABLED, NULL}, + {PWM_OUTPUT_DISABLED, NULL}, + {PWM_OUTPUT_DISABLED, NULL}, + {PWM_OUTPUT_DISABLED, NULL} + }, + 0, + 0 +}; + +/** + * @brief Look up table for fast 1-wire CRC calculation + */ +static const uint8_t onewire_crc_table[256] = { + 0x0, 0x5e, 0xbc, 0xe2, 0x61, 0x3f, 0xdd, 0x83, + 0xc2, 0x9c, 0x7e, 0x20, 0xa3, 0xfd, 0x1f, 0x41, + 0x9d, 0xc3, 0x21, 0x7f, 0xfc, 0xa2, 0x40, 0x1e, + 0x5f, 0x1, 0xe3, 0xbd, 0x3e, 0x60, 0x82, 0xdc, + 0x23, 0x7d, 0x9f, 0xc1, 0x42, 0x1c, 0xfe, 0xa0, + 0xe1, 0xbf, 0x5d, 0x3, 0x80, 0xde, 0x3c, 0x62, + 0xbe, 0xe0, 0x2, 0x5c, 0xdf, 0x81, 0x63, 0x3d, + 0x7c, 0x22, 0xc0, 0x9e, 0x1d, 0x43, 0xa1, 0xff, + 0x46, 0x18, 0xfa, 0xa4, 0x27, 0x79, 0x9b, 0xc5, + 0x84, 0xda, 0x38, 0x66, 0xe5, 0xbb, 0x59, 0x7, + 0xdb, 0x85, 0x67, 0x39, 0xba, 0xe4, 0x6, 0x58, + 0x19, 0x47, 0xa5, 0xfb, 0x78, 0x26, 0xc4, 0x9a, + 0x65, 0x3b, 0xd9, 0x87, 0x4, 0x5a, 0xb8, 0xe6, + 0xa7, 0xf9, 0x1b, 0x45, 0xc6, 0x98, 0x7a, 0x24, + 0xf8, 0xa6, 0x44, 0x1a, 0x99, 0xc7, 0x25, 0x7b, + 0x3a, 0x64, 0x86, 0xd8, 0x5b, 0x5, 0xe7, 0xb9, + 0x8c, 0xd2, 0x30, 0x6e, 0xed, 0xb3, 0x51, 0xf, + 0x4e, 0x10, 0xf2, 0xac, 0x2f, 0x71, 0x93, 0xcd, + 0x11, 0x4f, 0xad, 0xf3, 0x70, 0x2e, 0xcc, 0x92, + 0xd3, 0x8d, 0x6f, 0x31, 0xb2, 0xec, 0xe, 0x50, + 0xaf, 0xf1, 0x13, 0x4d, 0xce, 0x90, 0x72, 0x2c, + 0x6d, 0x33, 0xd1, 0x8f, 0xc, 0x52, 0xb0, 0xee, + 0x32, 0x6c, 0x8e, 0xd0, 0x53, 0xd, 0xef, 0xb1, + 0xf0, 0xae, 0x4c, 0x12, 0x91, 0xcf, 0x2d, 0x73, + 0xca, 0x94, 0x76, 0x28, 0xab, 0xf5, 0x17, 0x49, + 0x8, 0x56, 0xb4, 0xea, 0x69, 0x37, 0xd5, 0x8b, + 0x57, 0x9, 0xeb, 0xb5, 0x36, 0x68, 0x8a, 0xd4, + 0x95, 0xcb, 0x29, 0x77, 0xf4, 0xaa, 0x48, 0x16, + 0xe9, 0xb7, 0x55, 0xb, 0x88, 0xd6, 0x34, 0x6a, + 0x2b, 0x75, 0x97, 0xc9, 0x4a, 0x14, 0xf6, 0xa8, + 0x74, 0x2a, 0xc8, 0x96, 0x15, 0x4b, 0xa9, 0xf7, + 0xb6, 0xe8, 0xa, 0x54, 0xd7, 0x89, 0x6b, 0x35 +}; + +/** + * @brief Measurement unit for driver profiling. + */ +static time_measurement_t search_rom_tm; + +/*===========================================================================*/ +/* Driver local functions. */ +/*===========================================================================*/ +/** + * @brief PWM adapter + */ +static void pwm_reset_cb(PWMDriver *pwmp) { + ow_reset_cb(pwmp, &OWD1); +} + +/** + * @brief PWM adapter + */ +static void pwm_read_bit_cb(PWMDriver *pwmp) { + ow_read_bit_cb(pwmp, &OWD1); +} + +/** + * @brief PWM adapter + */ +static void pwm_write_bit_cb(PWMDriver *pwmp) { + ow_write_bit_cb(pwmp, &OWD1); +} + +/** + * @brief PWM adapter + */ +static void pwm_search_rom_cb(PWMDriver *pwmp) { + ow_search_rom_cb(pwmp, &OWD1); +} + +/** + * @brief Write bit routine. + * @details Switch PWM channel to 'width' or 'narrow' pulse depending + * on value of bit need to be transmitted. + * + * @param[in] owp pointer to the @p onewireDriver object + * @param[in] bit value to be written + * + * @notapi + */ +static void ow_write_bit_I(onewireDriver *owp, uint_fast8_t bit) { +#if ONEWIRE_SYNTH_SEARCH_TEST + _synth_ow_write_bit(owp, bit); +#else + osalSysLockFromISR(); + if (0 == bit) { + pwmEnableChannelI(owp->config->pwmd, owp->config->master_channel, + ONEWIRE_ZERO_WIDTH); + } + else { + pwmEnableChannelI(owp->config->pwmd, owp->config->master_channel, + ONEWIRE_ONE_WIDTH); + } + osalSysUnlockFromISR(); +#endif +} + +/** + * @brief 1-wire reset pulse callback. + * @note Must be called from PWM's ISR. + * + * @param[in] pwmp pointer to the @p PWMDriver object + * @param[in] owp pointer to the @p onewireDriver object + * + * @notapi + */ +static void ow_reset_cb(PWMDriver *pwmp, onewireDriver *owp) { + + owp->reg.slave_present = (PAL_LOW == owp->config->readBitX()); + + osalSysLockFromISR(); + pwmDisableChannelI(pwmp, owp->config->sample_channel); + osalThreadResumeI(&owp->thread, MSG_OK); + osalSysUnlockFromISR(); +} + +/** + * @brief 1-wire read bit callback. + * @note Must be called from PWM's ISR. + * + * @param[in] pwmp pointer to the @p PWMDriver object + * @param[in] owp pointer to the @p onewireDriver object + * + * @notapi + */ +static void ow_read_bit_cb(PWMDriver *pwmp, onewireDriver *owp) { + + if (true == owp->reg.final_timeslot) { + osalSysLockFromISR(); + pwmDisableChannelI(pwmp, owp->config->sample_channel); + osalThreadResumeI(&owp->thread, MSG_OK); + osalSysUnlockFromISR(); + return; + } + else { + *owp->buf |= owp->config->readBitX() << owp->reg.bit; + owp->reg.bit++; + if (8 == owp->reg.bit) { + owp->reg.bit = 0; + owp->buf++; + owp->reg.bytes--; + if (0 == owp->reg.bytes) { + owp->reg.final_timeslot = true; + osalSysLockFromISR(); + /* Only master channel must be stopped here. + Sample channel will be stopped in next ISR call. + It is still needed to generate final interrupt. */ + pwmDisableChannelI(pwmp, owp->config->master_channel); + osalSysUnlockFromISR(); + } + } + } +} + +/** + * @brief 1-wire bit transmission callback. + * @note Must be called from PWM's ISR. + * + * @param[in] pwmp pointer to the @p PWMDriver object + * @param[in] owp pointer to the @p onewireDriver object + * + * @notapi + */ +static void ow_write_bit_cb(PWMDriver *pwmp, onewireDriver *owp) { + + if (8 == owp->reg.bit) { + owp->buf++; + owp->reg.bit = 0; + owp->reg.bytes--; + + if (0 == owp->reg.bytes) { + osalSysLockFromISR(); + pwmDisableChannelI(pwmp, owp->config->master_channel); + osalSysUnlockFromISR(); + /* used to prevent premature timer stop from userspace */ + owp->reg.final_timeslot = true; + return; + } + } + + /* wait until timer generate last pulse */ + if (true == owp->reg.final_timeslot) { + #if ONEWIRE_USE_STRONG_PULLUP + if (owp->reg.need_pullup) { + owp->reg.state = ONEWIRE_PULL_UP; + owp->config->pullup_assert(); + owp->reg.need_pullup = false; + } + #endif + + osalSysLockFromISR(); + osalThreadResumeI(&owp->thread, MSG_OK); + osalSysUnlockFromISR(); + return; + } + + ow_write_bit_I(owp, (*owp->buf >> owp->reg.bit) & 1); + owp->reg.bit++; +} + +/** + * @brief Helper function for collision handler + * + * @param[in] sr pointer to the @p onewire_search_rom_t helper structure + * @param[in] bit discovered bit to be stored in helper structure + */ +static void store_bit(onewire_search_rom_t *sr, uint_fast8_t bit) { + + size_t rb = sr->reg.rombit; + + /* / 8 % 8 */ + sr->retbuf[rb >> 3] |= bit << (rb & 7); + sr->reg.rombit++; +} + +/** + * @brief Helper function for collision handler + * @details Extract bit from previous search path. + * + * @param[in] path pointer to the array with previous path stored in + * 'search ROM' helper structure + * @param[in] bit number of bit [0..63] + */ +static uint_fast8_t extract_path_bit(const uint8_t *path, uint_fast8_t bit) { + /* / 8 % 8 */ + return (path[bit >> 3] >> (bit & 7)) & 1; +} + +/** + * @brief Collision handler for 'search ROM' procedure. + * @details You can find algorithm details in APPNOTE 187 + * "1-Wire Search Algorithm" from Maxim + * + * @param[in,out] sr pointer to the @p onewire_search_rom_t helper structure + */ +static uint_fast8_t collision_handler(onewire_search_rom_t *sr) { + + uint_fast8_t bit; + + switch(sr->reg.search_iter) { + case ONEWIRE_SEARCH_ROM_NEXT: + if ((int)sr->reg.rombit < sr->last_zero_branch) { + bit = extract_path_bit(sr->prev_path, sr->reg.rombit); + if (0 == bit) { + sr->prev_zero_branch = sr->reg.rombit; + sr->reg.result = ONEWIRE_SEARCH_ROM_SUCCESS; + } + store_bit(sr, bit); + return bit; + } + else if ((int)sr->reg.rombit == sr->last_zero_branch) { + sr->last_zero_branch = sr->prev_zero_branch; + store_bit(sr, 1); + return 1; + } + else { + /* found next branch some levels deeper */ + sr->prev_zero_branch = sr->last_zero_branch; + sr->last_zero_branch = sr->reg.rombit; + store_bit(sr, 0); + sr->reg.result = ONEWIRE_SEARCH_ROM_SUCCESS; + return 0; + } + break; + + case ONEWIRE_SEARCH_ROM_FIRST: + /* always take 0-branch */ + sr->prev_zero_branch = sr->last_zero_branch; + sr->last_zero_branch = sr->reg.rombit; + store_bit(sr, 0); + sr->reg.result = ONEWIRE_SEARCH_ROM_SUCCESS; + return 0; + break; + + default: + osalSysHalt("Unhandled case"); + return 0; /* warning supressor */ + break; + } +} + +/** + * @brief 1-wire search ROM callback. + * @note Must be called from PWM's ISR. + * + * @param[in] pwmp pointer to the @p PWMDriver object + * @param[in] owp pointer to the @p onewireDriver object + * + * @notapi + */ +static void ow_search_rom_cb(PWMDriver *pwmp, onewireDriver *owp) { + + chTMStartMeasurementX(&search_rom_tm); + + onewire_search_rom_t *sr = &owp->search_rom; + + if (0 == sr->reg.bit_step) { /* read direct bit */ + sr->reg.bit_buf |= owp->config->readBitX(); + sr->reg.bit_step++; + } + else if (1 == sr->reg.bit_step) { /* read complement bit */ + sr->reg.bit_buf |= owp->config->readBitX() << 1; + sr->reg.bit_step++; + switch(sr->reg.bit_buf){ + case 0b11: + /* no one device on bus or any other fail happened */ + sr->reg.result = ONEWIRE_SEARCH_ROM_ERROR; + goto THE_END; + break; + case 0b01: + /* all slaves have 1 in this position */ + store_bit(sr, 1); + ow_write_bit_I(owp, 1); + break; + case 0b10: + /* all slaves have 0 in this position */ + store_bit(sr, 0); + ow_write_bit_I(owp, 0); + break; + case 0b00: + /* collision */ + sr->reg.single_device = false; + ow_write_bit_I(owp, collision_handler(sr)); + break; + } + } + else { /* start next step */ + #if !ONEWIRE_SYNTH_SEARCH_TEST + ow_write_bit_I(owp, 1); + #endif + sr->reg.bit_step = 0; + sr->reg.bit_buf = 0; + } + + /* one ROM successfully discovered */ + if (64 == sr->reg.rombit) { + sr->reg.devices_found++; + sr->reg.search_iter = ONEWIRE_SEARCH_ROM_NEXT; + if (true == sr->reg.single_device) + sr->reg.result = ONEWIRE_SEARCH_ROM_LAST; + goto THE_END; + } + + /* next search bit iteration */ + chTMStopMeasurementX(&search_rom_tm); + return; + +THE_END: +#if ONEWIRE_SYNTH_SEARCH_TEST + (void)pwmp; + return; +#else + chTMStopMeasurementX(&search_rom_tm); + osalSysLockFromISR(); + pwmDisableChannelI(pwmp, owp->config->master_channel); + pwmDisableChannelI(pwmp, owp->config->sample_channel); + osalThreadResumeI(&(owp)->thread, MSG_OK); + osalSysUnlockFromISR(); +#endif +} + +/** + * @brief Helper function. Initialize structures required by 'search ROM'. + * @details Early reset. Call it once before 'search ROM' routine. + * + * @param[in] sr pointer to the @p onewire_search_rom_t helper structure + */ +static void search_clean_start(onewire_search_rom_t *sr) { + + sr->reg.single_device = true; /* presume simplest way at beginning */ + sr->reg.result = ONEWIRE_SEARCH_ROM_LAST; + sr->reg.search_iter = ONEWIRE_SEARCH_ROM_FIRST; + sr->retbuf = NULL; + sr->reg.devices_found = 0; + memset(sr->prev_path, 0, 8); + + sr->reg.rombit = 0; + sr->reg.bit_step = 0; + sr->reg.bit_buf = 0; + sr->last_zero_branch = -1; + sr->prev_zero_branch = -1; +} + +/** + * @brief Helper function. Prepare structures required by 'search ROM'. + * + * @param[in] sr pointer to the @p onewire_search_rom_t helper structure + */ +static void search_clean_iteration(onewire_search_rom_t *sr) { + + sr->reg.rombit = 0; + sr->reg.bit_step = 0; + sr->reg.bit_buf = 0; + sr->reg.result = ONEWIRE_SEARCH_ROM_LAST; +} + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +/** + * @brief Calculates 1-wire CRC. + * + * @param[in] buf pointer to the data buffer + * @param[in] len lenght of data buffer + * + * @init + */ +uint8_t onewireCRC(const uint8_t *buf, size_t len) { + uint8_t ret = 0; + size_t i; + + for (i=0; i<len; i++) + ret = onewire_crc_table[ret ^ buf[i]]; + + return ret; +} + +/** + * @brief Initializes @p onewireDriver structure. + * + * @param[out] owp pointer to the @p onewireDriver object + * + * @init + */ +void onewireObjectInit(onewireDriver *owp) { + + osalDbgCheck(NULL != owp); + + owp->config = NULL; + owp->reg.slave_present = false; + owp->reg.state = ONEWIRE_STOP; + owp->thread = NULL; + + owp->reg.bytes = 0; + owp->reg.bit = 0; + owp->reg.final_timeslot = false; + owp->buf = NULL; + + owp->pwmcfg = pwm_default_cfg; + +#if ONEWIRE_USE_STRONG_PULLUP + owp->reg.need_pullup = false; +#endif +} + +/** + * @brief Configures and activates the 1-wire driver. + * + * @param[in] owp pointer to the @p onewireDriver object + * @param[in] config pointer to the @p onewireConfig object + * + * @api + */ +void onewireStart(onewireDriver *owp, const onewireConfig *config) { + + osalDbgCheck((NULL != owp) && (NULL != config)); + osalDbgCheck(NULL != config->readBitX); + osalDbgAssert(PWM_STOP == config->pwmd->state, + "PWM will be started by onewire driver internally"); + osalDbgAssert(ONEWIRE_STOP == owp->reg.state, "Invalid state"); +#if ONEWIRE_USE_STRONG_PULLUP + osalDbgCheck((NULL != config->pullup_assert) && + (NULL != config->pullup_assert)); +#endif + + owp->config = config; + owp->reg.state = ONEWIRE_READY; + chTMObjectInit(&search_rom_tm); +} + +/** + * @brief Deactivates the UART peripheral. + * + * @param[in] owp pointer to the @p onewireDriver object + * + * @api + */ +void onewireStop(onewireDriver *owp) { + osalDbgCheck(NULL != owp); +#if ONEWIRE_USE_STRONG_PULLUP + owp->config->pullup_release(); +#endif + pwmStop(owp->config->pwmd); + owp->config = NULL; + owp->reg.state = ONEWIRE_STOP; +} + +/** + * @brief Generate reset pulse on bus. + * + * @param[in] owp pointer to the @p onewireDriver object + * + * @return Bool flag denoting device presence. + * @retval true There is at least one device on bus. + */ +bool onewireReset(onewireDriver *owp) { + PWMDriver *pwmd; + + osalDbgCheck(NULL != owp); + osalDbgAssert(owp->reg.state == ONEWIRE_READY, "Invalid state"); + + /* short circuit on bus or any other device transmit data */ + if (0 == owp->config->readBitX()) + return false; + + pwmd = owp->config->pwmd; + + owp->pwmcfg.period = ONEWIRE_RESET_LOW_WIDTH + ONEWIRE_RESET_SAMPLE_WIDTH; + owp->pwmcfg.callback = NULL; + owp->pwmcfg.channels[owp->config->master_channel].callback = NULL; + owp->pwmcfg.channels[owp->config->master_channel].mode = PWM_OUTPUT_ACTIVE_LOW; + owp->pwmcfg.channels[owp->config->sample_channel].callback = pwm_reset_cb; + owp->pwmcfg.channels[owp->config->sample_channel].mode = PWM_OUTPUT_ACTIVE_LOW; + + pwmStart(pwmd, &owp->pwmcfg); + pwmEnableChannel(pwmd, owp->config->master_channel, ONEWIRE_RESET_LOW_WIDTH); + pwmEnableChannel(pwmd, owp->config->sample_channel, ONEWIRE_RESET_SAMPLE_WIDTH); + pwmEnableChannelNotification(pwmd, owp->config->sample_channel); + + osalSysLock(); + osalThreadSuspendS(&owp->thread); + osalSysUnlock(); + + pwmStop(pwmd); + + /* wait until slave release bus to discriminate short circuit condition */ + osalThreadSleepMicroseconds(500); + return (1 == owp->config->readBitX()) && (true == owp->reg.slave_present); +} + +/** + * @brief Read some bites from slave device. + * + * @param[in] owp pointer to the @p onewireDriver object + * @param[out] rxbuf pointer to the buffer for read data + * @param[in] rxbytes amount of data to be received + */ +void onewireRead(onewireDriver *owp, uint8_t *rxbuf, size_t rxbytes) { + PWMDriver *pwmd; + + osalDbgCheck((NULL != owp) && (NULL != rxbuf)); + osalDbgCheck((rxbytes > 0) && (rxbytes < 65536)); + osalDbgAssert(owp->reg.state == ONEWIRE_READY, "Invalid state"); + + /* Buffer zeroing. This is important because of driver collects + bits using |= operation.*/ + memset(rxbuf, 0, rxbytes); + + pwmd = owp->config->pwmd; + + owp->reg.bit = 0; + owp->reg.final_timeslot = false; + owp->buf = rxbuf; + owp->reg.bytes = rxbytes; + + owp->pwmcfg.period = ONEWIRE_ZERO_WIDTH + ONEWIRE_RECOVERY_WIDTH; + owp->pwmcfg.callback = NULL; + owp->pwmcfg.channels[owp->config->master_channel].callback = NULL; + owp->pwmcfg.channels[owp->config->master_channel].mode = PWM_OUTPUT_ACTIVE_LOW; + owp->pwmcfg.channels[owp->config->sample_channel].callback = pwm_read_bit_cb; + owp->pwmcfg.channels[owp->config->sample_channel].mode = PWM_OUTPUT_ACTIVE_LOW; + + pwmStart(pwmd, &owp->pwmcfg); + pwmEnableChannel(pwmd, owp->config->master_channel, ONEWIRE_ONE_WIDTH); + pwmEnableChannel(pwmd, owp->config->sample_channel, ONEWIRE_SAMPLE_WIDTH); + pwmEnableChannelNotification(pwmd, owp->config->sample_channel); + + osalSysLock(); + osalThreadSuspendS(&owp->thread); + osalSysUnlock(); + + pwmStop(pwmd); +} + +/** + * @brief Read some bites from slave device. + * + * @param[in] owp pointer to the @p onewireDriver object + * @param[in] txbuf pointer to the buffer with data to be written + * @param[in] txbytes amount of data to be written + * @param[in] pullup_time how long strong pull up must be activated. Set + * it to 0 if not needed. + */ +void onewireWrite(onewireDriver *owp, uint8_t *txbuf, + size_t txbytes, systime_t pullup_time) { + PWMDriver *pwmd; + + osalDbgCheck((NULL != owp) && (NULL != txbuf)); + osalDbgCheck((txbytes > 0) && (txbytes < 65536)); + osalDbgAssert(owp->reg.state == ONEWIRE_READY, "Invalid state"); +#if !ONEWIRE_USE_STRONG_PULLUP + osalDbgAssert(0 == pullup_time, + "Non zero time is valid only when strong pull enabled"); +#endif + + pwmd = owp->config->pwmd; + + owp->buf = txbuf; + owp->reg.bit = 0; + owp->reg.final_timeslot = false; + owp->reg.bytes = txbytes; + + owp->pwmcfg.period = ONEWIRE_ZERO_WIDTH + ONEWIRE_RECOVERY_WIDTH; + owp->pwmcfg.callback = pwm_write_bit_cb; + owp->pwmcfg.channels[owp->config->master_channel].callback = NULL; + owp->pwmcfg.channels[owp->config->master_channel].mode = PWM_OUTPUT_ACTIVE_LOW; + owp->pwmcfg.channels[owp->config->sample_channel].callback = NULL; + owp->pwmcfg.channels[owp->config->sample_channel].mode = PWM_OUTPUT_DISABLED; + +#if ONEWIRE_USE_STRONG_PULLUP + if (pullup_time > 0) { + owp->reg.state = ONEWIRE_PULL_UP; + owp->reg.need_pullup = true; + } +#endif + + pwmStart(pwmd, &owp->pwmcfg); + pwmEnablePeriodicNotification(pwmd); + + osalSysLock(); + osalThreadSuspendS(&owp->thread); + osalSysUnlock(); + + pwmDisablePeriodicNotification(pwmd); + pwmStop(pwmd); + +#if ONEWIRE_USE_STRONG_PULLUP + if (pullup_time > 0) { + osalThreadSleep(pullup_time); + owp->config->pullup_release(); + owp->reg.state = ONEWIRE_READY; + } +#endif +} + +/** + * @brief Performs tree search on bus. + * @note This function does internal 1-wire reset calls every search + * iteration. + * + * @param[in] owp pointer to a @p OWDriver object + * @param[out] result pointer to buffer for discovered ROMs + * @param[in] max_rom_cnt buffer size in ROMs count for overflow prevention + * + * @return Count of discovered ROMs. May be more than max_rom_cnt. + * @retval 0 no ROMs found or communication error occurred. + */ +size_t onewireSearchRom(onewireDriver *owp, uint8_t *result, + size_t max_rom_cnt) { + PWMDriver *pwmd; + uint8_t cmd; + + osalDbgCheck(NULL != owp); + osalDbgAssert(ONEWIRE_READY == owp->reg.state, "Invalid state"); + osalDbgCheck((max_rom_cnt <= 256) && (max_rom_cnt > 0)); + + pwmd = owp->config->pwmd; + cmd = ONEWIRE_CMD_SEARCH_ROM; + + search_clean_start(&owp->search_rom); + + do { + /* every search must be started from reset pulse */ + if (false == onewireReset(owp)) + return 0; + + /* initialize buffer to store result */ + if (owp->search_rom.reg.devices_found >= max_rom_cnt) + owp->search_rom.retbuf = result + 8*(max_rom_cnt-1); + else + owp->search_rom.retbuf = result + 8*owp->search_rom.reg.devices_found; + memset(owp->search_rom.retbuf, 0, 8); + + /* clean iteration state */ + search_clean_iteration(&owp->search_rom); + + /**/ + onewireWrite(&OWD1, &cmd, 1, 0); + + /* Reconfiguration always needed because of previous call onewireWrite.*/ + owp->pwmcfg.period = ONEWIRE_ZERO_WIDTH + ONEWIRE_RECOVERY_WIDTH; + owp->pwmcfg.callback = NULL; + owp->pwmcfg.channels[owp->config->master_channel].callback = NULL; + owp->pwmcfg.channels[owp->config->master_channel].mode = PWM_OUTPUT_ACTIVE_LOW; + owp->pwmcfg.channels[owp->config->sample_channel].callback = pwm_search_rom_cb; + owp->pwmcfg.channels[owp->config->sample_channel].mode = PWM_OUTPUT_ACTIVE_LOW; + pwmStart(pwmd, &owp->pwmcfg); + pwmEnableChannel(pwmd, owp->config->master_channel, ONEWIRE_ONE_WIDTH); + pwmEnableChannel(pwmd, owp->config->sample_channel, ONEWIRE_SAMPLE_WIDTH); + pwmEnableChannelNotification(pwmd, owp->config->sample_channel); + + osalSysLock(); + osalThreadSuspendS(&owp->thread); + osalSysUnlock(); + + pwmStop(pwmd); + + if (ONEWIRE_SEARCH_ROM_ERROR != owp->search_rom.reg.result) { + /* check CRC and return 0 (error status) if mismatch */ + if (owp->search_rom.retbuf[7] != onewireCRC(owp->search_rom.retbuf, 7)) + return 0; + /* store cached result for usage in next iteration */ + memcpy(owp->search_rom.prev_path, owp->search_rom.retbuf, 8); + } + } + while (ONEWIRE_SEARCH_ROM_SUCCESS == owp->search_rom.reg.result); + + /**/ + if (ONEWIRE_SEARCH_ROM_ERROR == owp->search_rom.reg.result) + return 0; + else + return owp->search_rom.reg.devices_found; +} + +/* + * Included (if enabled) test code + */ +#if ONEWIRE_SYNTH_SEARCH_TEST +#include "search_rom_synth.c" +#endif + +#endif /* HAL_USE_ONEWIRE */ + +/** @} */ |