aboutsummaryrefslogtreecommitdiffstats
path: root/os/hal/src/hal_onewire.c
diff options
context:
space:
mode:
authorbarthess <barthess@yandex.ru>2016-04-07 16:39:12 +0300
committerbarthess <barthess@yandex.ru>2016-04-07 16:39:12 +0300
commit9d74dd2661b80a5ae8598591f0251b197cc51756 (patch)
tree0c833cbd2f4cf5845358ee0f37fbb3f95b7a447b /os/hal/src/hal_onewire.c
parentb86af2c09feea9b50cb241c147a3881e55763a55 (diff)
downloadChibiOS-Contrib-9d74dd2661b80a5ae8598591f0251b197cc51756.tar.gz
ChibiOS-Contrib-9d74dd2661b80a5ae8598591f0251b197cc51756.tar.bz2
ChibiOS-Contrib-9d74dd2661b80a5ae8598591f0251b197cc51756.zip
STM32 mass update to current naming convention in ChibiOS
Diffstat (limited to 'os/hal/src/hal_onewire.c')
-rw-r--r--os/hal/src/hal_onewire.c890
1 files changed, 890 insertions, 0 deletions
diff --git a/os/hal/src/hal_onewire.c b/os/hal/src/hal_onewire.c
new file mode 100644
index 0000000..85f0fdc
--- /dev/null
+++ b/os/hal/src/hal_onewire.c
@@ -0,0 +1,890 @@
+/*
+ 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 to open drain mode.
+2) start 2 channels _simultaneously_. First (master channel) generates
+ pulses (read time slots) second (sample channel) generates interrupts
+ from where read pin function will be called.
+
+- --------------------------------------- master channel generates pulses
+ | / .
+ --............................. <---------- slave (not)pulls down bus here
+- -------------------------------- sample channel reads pad state
+ | |
+ -------------
+ ^
+ | read interrupt fires 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 sensible to interrupt jitter.
+ * 2) Use specialized 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 == TRUE) || defined(__DOXYGEN__)
+
+#include <string.h>
+
+/*===========================================================================*/
+/* Driver local definitions. */
+/*===========================================================================*/
+/**
+ * @brief 1MHz clock for PWM driver.
+ */
+#define ONEWIRE_PWM_FREQUENCY 1000000
+
+/**
+ * @brief Pulse width constants in microseconds.
+ * @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 Local function 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);
+#if ONEWIRE_USE_SEARCH_ROM
+static void ow_search_rom_cb(PWMDriver *pwmp, onewireDriver *owp);
+static void pwm_search_rom_cb(PWMDriver *pwmp);
+#endif
+
+/*===========================================================================*/
+/* Driver exported variables. */
+/*===========================================================================*/
+/**
+ * @brief 1-wire driver identifier.
+ */
+onewireDriver OWD1;
+
+/*===========================================================================*/
+/* Driver local variables and types. */
+/*===========================================================================*/
+/**
+ * @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
+};
+
+/*===========================================================================*/
+/* Driver local functions. */
+/*===========================================================================*/
+/**
+ * @brief Put bus in idle mode.
+ */
+static void ow_bus_idle(onewireDriver *owp) {
+#if defined(STM32F1XX)
+ palSetPadMode(owp->config->port, owp->config->pad,
+ owp->config->pad_mode_idle);
+#endif
+ pwmStop(owp->config->pwmd);
+}
+
+/**
+ * @brief Put bus in active mode.
+ */
+static void ow_bus_active(onewireDriver *owp) {
+ pwmStart(owp->config->pwmd, owp->config->pwmcfg);
+#if defined(STM32F1XX)
+ palSetPadMode(owp->config->port, owp->config->pad,
+ owp->config->pad_mode_active);
+#endif
+}
+
+/**
+ * @brief Function performing read of single bit.
+ * @note It must be callable from any context.
+ */
+static uint_fast8_t ow_read_bit(onewireDriver *owp) {
+#if ONEWIRE_SYNTH_SEARCH_TEST
+ (void)owp;
+ return _synth_ow_read_bit();
+#else
+ return palReadPad(owp->config->port, owp->config->pad);
+#endif
+}
+
+/**
+ * @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);
+}
+
+#if ONEWIRE_USE_SEARCH_ROM
+/**
+ * @brief PWM adapter
+ */
+static void pwm_search_rom_cb(PWMDriver *pwmp) {
+ ow_search_rom_cb(pwmp, &OWD1);
+}
+#endif /* ONEWIRE_USE_SEARCH_ROM */
+
+/**
+ * @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 == ow_read_bit(owp));
+
+ 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 |= ow_read_bit(owp) << 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++;
+}
+
+#if ONEWIRE_USE_SEARCH_ROM
+/**
+ * @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) {
+
+ onewire_search_rom_t *sr = &owp->search_rom;
+
+ if (0 == sr->reg.bit_step) { /* read direct bit */
+ sr->reg.bit_buf |= ow_read_bit(owp);
+ sr->reg.bit_step++;
+ }
+ else if (1 == sr->reg.bit_step) { /* read complement bit */
+ sr->reg.bit_buf |= ow_read_bit(owp) << 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;
+ }
+ return; /* next search bit iteration */
+
+THE_END:
+#if ONEWIRE_SYNTH_SEARCH_TEST
+ (void)pwmp;
+ return;
+#else
+ 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;
+}
+#endif /* ONEWIRE_USE_SEARCH_ROM */
+
+/*===========================================================================*/
+/* 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;
+
+#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));
+ 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_release));
+#endif
+
+ owp->config = config;
+ owp->config->pwmcfg->frequency = ONEWIRE_PWM_FREQUENCY;
+ owp->config->pwmcfg->period = ONEWIRE_RESET_TOTAL_WIDTH;
+
+#if !defined(STM32F1XX)
+ palSetPadMode(owp->config->port, owp->config->pad,
+ owp->config->pad_mode_active);
+#endif
+ ow_bus_idle(owp);
+ owp->reg.state = ONEWIRE_READY;
+}
+
+/**
+ * @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
+ ow_bus_idle(owp);
+ 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;
+ PWMConfig *pwmcfg;
+ size_t mch, sch;
+
+ osalDbgCheck(NULL != owp);
+ osalDbgAssert(owp->reg.state == ONEWIRE_READY, "Invalid state");
+
+ /* short circuit on bus or any other device transmit data */
+ if (PAL_LOW == ow_read_bit(owp))
+ return false;
+
+ pwmd = owp->config->pwmd;
+ pwmcfg = owp->config->pwmcfg;
+ mch = owp->config->master_channel;
+ sch = owp->config->sample_channel;
+
+
+ pwmcfg->period = ONEWIRE_RESET_LOW_WIDTH + ONEWIRE_RESET_SAMPLE_WIDTH;
+ pwmcfg->callback = NULL;
+ pwmcfg->channels[mch].callback = NULL;
+ pwmcfg->channels[mch].mode = owp->config->pwmmode;
+ pwmcfg->channels[sch].callback = pwm_reset_cb;
+ pwmcfg->channels[sch].mode = PWM_OUTPUT_ACTIVE_LOW;
+
+ ow_bus_active(owp);
+
+ osalSysLock();
+ pwmEnableChannelI(pwmd, mch, ONEWIRE_RESET_LOW_WIDTH);
+ pwmEnableChannelI(pwmd, sch, ONEWIRE_RESET_SAMPLE_WIDTH);
+ pwmEnableChannelNotificationI(pwmd, sch);
+ osalThreadSuspendS(&owp->thread);
+ osalSysUnlock();
+
+ ow_bus_idle(owp);
+
+ /* wait until slave release bus to discriminate short circuit condition */
+ osalThreadSleepMicroseconds(500);
+ return (PAL_HIGH == ow_read_bit(owp)) && (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;
+ PWMConfig *pwmcfg;
+ size_t mch, sch;
+
+ osalDbgCheck((NULL != owp) && (NULL != rxbuf));
+ osalDbgCheck((rxbytes > 0) && (rxbytes <= ONEWIRE_MAX_TRANSACTION_LEN));
+ 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;
+ pwmcfg = owp->config->pwmcfg;
+ mch = owp->config->master_channel;
+ sch = owp->config->sample_channel;
+
+ owp->reg.bit = 0;
+ owp->reg.final_timeslot = false;
+ owp->buf = rxbuf;
+ owp->reg.bytes = rxbytes;
+
+ pwmcfg->period = ONEWIRE_ZERO_WIDTH + ONEWIRE_RECOVERY_WIDTH;
+ pwmcfg->callback = NULL;
+ pwmcfg->channels[mch].callback = NULL;
+ pwmcfg->channels[mch].mode = owp->config->pwmmode;
+ pwmcfg->channels[sch].callback = pwm_read_bit_cb;
+ pwmcfg->channels[sch].mode = PWM_OUTPUT_ACTIVE_LOW;
+
+ ow_bus_active(owp);
+ osalSysLock();
+ pwmEnableChannelI(pwmd, mch, ONEWIRE_ONE_WIDTH);
+ pwmEnableChannelI(pwmd, sch, ONEWIRE_SAMPLE_WIDTH);
+ pwmEnableChannelNotificationI(pwmd, sch);
+ osalThreadSuspendS(&owp->thread);
+ osalSysUnlock();
+
+ ow_bus_idle(owp);
+}
+
+/**
+ * @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;
+ PWMConfig *pwmcfg;
+ size_t mch, sch;
+
+ osalDbgCheck((NULL != owp) && (NULL != txbuf));
+ osalDbgCheck((txbytes > 0) && (txbytes <= ONEWIRE_MAX_TRANSACTION_LEN));
+ 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;
+ pwmcfg = owp->config->pwmcfg;
+ mch = owp->config->master_channel;
+ sch = owp->config->sample_channel;
+
+ owp->buf = txbuf;
+ owp->reg.bit = 0;
+ owp->reg.final_timeslot = false;
+ owp->reg.bytes = txbytes;
+
+ pwmcfg->period = ONEWIRE_ZERO_WIDTH + ONEWIRE_RECOVERY_WIDTH;
+ pwmcfg->callback = pwm_write_bit_cb;
+ pwmcfg->channels[mch].callback = NULL;
+ pwmcfg->channels[mch].mode = owp->config->pwmmode;
+ pwmcfg->channels[sch].callback = NULL;
+ pwmcfg->channels[sch].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
+
+ ow_bus_active(owp);
+ osalSysLock();
+ pwmEnablePeriodicNotificationI(pwmd);
+ osalThreadSuspendS(&owp->thread);
+ osalSysUnlock();
+
+ pwmDisablePeriodicNotification(pwmd);
+ ow_bus_idle(owp);
+
+#if ONEWIRE_USE_STRONG_PULLUP
+ if (pullup_time > 0) {
+ osalThreadSleep(pullup_time);
+ owp->config->pullup_release();
+ owp->reg.state = ONEWIRE_READY;
+ }
+#endif
+}
+
+#if ONEWIRE_USE_SEARCH_ROM
+/**
+ * @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;
+ PWMConfig *pwmcfg;
+ uint8_t cmd;
+ size_t mch, sch;
+
+ osalDbgCheck(NULL != owp);
+ osalDbgAssert(ONEWIRE_READY == owp->reg.state, "Invalid state");
+ osalDbgCheck((max_rom_cnt <= 256) && (max_rom_cnt > 0));
+
+ pwmd = owp->config->pwmd;
+ pwmcfg = owp->config->pwmcfg;
+ cmd = ONEWIRE_CMD_SEARCH_ROM;
+ mch = owp->config->master_channel;
+ sch = owp->config->sample_channel;
+
+ 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.*/
+ pwmcfg->period = ONEWIRE_ZERO_WIDTH + ONEWIRE_RECOVERY_WIDTH;
+ pwmcfg->callback = NULL;
+ pwmcfg->channels[mch].callback = NULL;
+ pwmcfg->channels[mch].mode = owp->config->pwmmode;
+ pwmcfg->channels[sch].callback = pwm_search_rom_cb;
+ pwmcfg->channels[sch].mode = PWM_OUTPUT_ACTIVE_LOW;
+
+ ow_bus_active(owp);
+ osalSysLock();
+ pwmEnableChannelI(pwmd, mch, ONEWIRE_ONE_WIDTH);
+ pwmEnableChannelI(pwmd, sch, ONEWIRE_SAMPLE_WIDTH);
+ pwmEnableChannelNotificationI(pwmd, sch);
+ osalThreadSuspendS(&owp->thread);
+ osalSysUnlock();
+
+ ow_bus_idle(owp);
+
+ if (ONEWIRE_SEARCH_ROM_ERROR != owp->search_rom.reg.result) {
+ /* check CRC and return 0 (0 == error) 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;
+}
+#endif /* ONEWIRE_USE_SEARCH_ROM */
+
+/*
+ * Include test code (if enabled).
+ */
+#if ONEWIRE_SYNTH_SEARCH_TEST
+#include "search_rom_synth.c"
+#endif
+
+#endif /* HAL_USE_ONEWIRE */
+
+/** @} */