From fcded8a6273bb0c147f27631b975cd2612c4a05b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?King=20K=C3=A9vin?= Date: Wed, 2 Aug 2017 13:44:49 +0200 Subject: [PATCH] onewire_master: add parasite power support --- lib/onewire_master.c | 77 ++++++++++++++++++++++++++++++++++---------- lib/onewire_master.h | 11 ++++--- 2 files changed, 67 insertions(+), 21 deletions(-) diff --git a/lib/onewire_master.c b/lib/onewire_master.c index 56ad126..88951d5 100644 --- a/lib/onewire_master.c +++ b/lib/onewire_master.c @@ -17,8 +17,8 @@ * @author King Kévin * @date 2017 * @note peripherals used: timer @ref onewire_master_timer, GPIO @ref onewire_master_gpio - * @note overdrive mode is not supported - * @warning this library does not support active parasite power mode (more than the pull-up resistor itself) + * @note overdrive mode is not provided + * @implements 1-Wire protocol description from Book of iButton Standards */ /* standard libraries */ @@ -44,7 +44,7 @@ /** @} */ /** @defgroup onewire_master_gpio GPIO used for 1-wire signal - * @note use external pull-up resistor on pin + * @note external pull-up resistor on pin is required (< 5 kOhm) * @{ */ #define ONEWIRE_MASTER_PORT A /**< GPIO port */ @@ -63,17 +63,25 @@ volatile enum { ONEWIRE_MAX /** to count the number of possible states */ } onewire_master_state = ONEWIRE_STATE_IDLE; -volatile bool slave_presence = false; /**< if slaves have been detected */ -uint8_t* buffer = NULL; /**< input/output buffer for read/write commands/functions */ -uint32_t buffer_size = 0; /**< size of buffer in bits */ -volatile uint32_t buffer_bit = 0; /**< number of bits read/written */ +static volatile bool slave_presence = false; /**< if slaves have been detected */ +static uint8_t* buffer = NULL; /**< input/output buffer for read/write commands/functions */ +static uint32_t buffer_size = 0; /**< size of buffer in bits */ +static volatile uint32_t buffer_bit = 0; /**< number of bits read/written */ +static bool onewire_master_parasite = false; /**< if parasite power should be provided whenever the is no communication */ +static uint16_t onewire_master_recovery = 0; /**< the recovery time in us (1 < Trec) */ -void onewire_master_setup(void) +void onewire_master_setup(bool parasite, uint16_t recovery) { // setup GPIO with external interrupt rcc_periph_clock_enable(RCC_GPIO(ONEWIRE_MASTER_PORT)); // enable clock for GPIO peripheral - gpio_set_mode(GPIO(ONEWIRE_MASTER_PORT), GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_OPENDRAIN, GPIO(ONEWIRE_MASTER_PIN)); // setup GPIO pin as output (master starts communication before slave replies) gpio_set(GPIO(ONEWIRE_MASTER_PORT),GPIO(ONEWIRE_MASTER_PIN)); // idle is high (using pull-up resistor) + onewire_master_parasite = parasite; // save if we should provide parasite power + // setup GPIO pin as output (master starts communication before slave replies) + if (onewire_master_parasite) { + gpio_set_mode(GPIO(ONEWIRE_MASTER_PORT), GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO(ONEWIRE_MASTER_PIN)); // provide parasite power (external pull-up resistor is still require for communication) + } else { + gpio_set_mode(GPIO(ONEWIRE_MASTER_PORT), GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_OPENDRAIN, GPIO(ONEWIRE_MASTER_PIN)); // normal 1-Wire communication (only using external pull-up resistor) + } // setup timer to generate/measure signal timing rcc_periph_clock_enable(RCC_TIM(ONEWIRE_MASTER_TIMER)); // enable clock for timer peripheral @@ -82,10 +90,19 @@ void onewire_master_setup(void) timer_set_prescaler(TIM(ONEWIRE_MASTER_TIMER), 1-1); // don't use prescale since this 16 bits timer allows to wait > 480 us used for the reset pulse ( 1/(72E6/1/(2**16))=910us ) // use comparator to time signal (without using the output), starting at slot start - timer_set_oc_value(TIM(ONEWIRE_MASTER_TIMER), TIM_OC1, 1*(rcc_ahb_frequency/1000000)-1); // use compare function to time master pulling low (1 < Tlowr < 15) - timer_set_oc_value(TIM(ONEWIRE_MASTER_TIMER), TIM_OC2, 7*(rcc_ahb_frequency/1000000)-1); // use compare function to read of write (1 < Trw < 15) + timer_set_oc_value(TIM(ONEWIRE_MASTER_TIMER), TIM_OC1, 1*(rcc_ahb_frequency/1000000)-1); // use compare function to time master pulling low when reading (1 < Tlowr < 15) + timer_set_oc_value(TIM(ONEWIRE_MASTER_TIMER), TIM_OC2, 7*(rcc_ahb_frequency/1000000)-1); // use compare function to read or write 0 or 1 (1 < Trw < 15) timer_set_oc_value(TIM(ONEWIRE_MASTER_TIMER), TIM_OC3, 62*(rcc_ahb_frequency/1000000)-1); // use compare function to end time slot (60 < Tslot < 120), this will be followed by a recovery time (end of timer) - timer_set_oc_value(TIM(ONEWIRE_MASTER_TIMER), TIM_OC4, 120*(rcc_ahb_frequency/1000000)-1); // use compare function to detect slave presence (60 < Tpdl < 240) + timer_set_oc_value(TIM(ONEWIRE_MASTER_TIMER), TIM_OC4, (70-10)*(rcc_ahb_frequency/1000000)-1); // use compare function to detect slave presence (15 < Tpdh < 60 + 60 < Tpdl < 240), with hand tunig + onewire_master_recovery = 5; // set minimum recovery time + if (recovery>onewire_master_recovery) { + onewire_master_recovery = recovery; // save desired recovery time + } + if (UINT16_MAX/onewire_master_recovery<(rcc_ahb_frequency/1000000)) { // catch integer overflow + onewire_master_recovery = UINT16_MAX; // save maximum value + } else { + onewire_master_recovery *= (rcc_ahb_frequency/1000000); // save actual recovery time value + } timer_clear_flag(TIM(ONEWIRE_MASTER_TIMER), TIM_SR_UIF); // clear update (overflow) flag timer_update_on_overflow(TIM(ONEWIRE_MASTER_TIMER)); // only use counter overflow as UEV source (use overflow as start time or timeout) @@ -101,12 +118,13 @@ bool onewire_master_reset(void) // prepare timer timer_disable_counter(TIM(ONEWIRE_MASTER_TIMER)); // disable timer to reconfigure it timer_set_counter(TIM(ONEWIRE_MASTER_TIMER),0); // reset counter - timer_set_period(TIM(ONEWIRE_MASTER_TIMER), 490*(rcc_ahb_frequency/1000000)-1); // set timeout to > 480 us + timer_set_period(TIM(ONEWIRE_MASTER_TIMER), 490*(rcc_ahb_frequency/1000000)-1); // set timeout to > 480 us (480 < Trst) slave_presence = false; // reset state onewire_master_state = ONEWIRE_STATE_MASTER_RESET; // set new state - gpio_clear(GPIO(ONEWIRE_MASTER_PORT),GPIO(ONEWIRE_MASTER_PIN)); // pull signal low to start reset (it's not important if it was low in the first place since the reset pulse has no maximum time) + gpio_set_mode(GPIO(ONEWIRE_MASTER_PORT), GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_OPENDRAIN, GPIO(ONEWIRE_MASTER_PIN)); // normal 1-Wire communication (only using external pull-up resistor) + gpio_clear(GPIO(ONEWIRE_MASTER_PORT),GPIO(ONEWIRE_MASTER_PIN)); // pull signal low to start reset (it's not important if it was low in the first place since the reset pulse has no maximum time) timer_enable_counter(TIM(ONEWIRE_MASTER_TIMER)); // start timer while (onewire_master_state!=ONEWIRE_STATE_DONE && onewire_master_state!=ONEWIRE_STATE_ERROR) { // wait until reset procedure completed @@ -131,13 +149,20 @@ static bool onewire_master_write(void) // prepare timer timer_disable_counter(TIM(ONEWIRE_MASTER_TIMER)); // disable timer to reconfigure it timer_set_counter(TIM(ONEWIRE_MASTER_TIMER),0); // reset counter - timer_set_period(TIM(ONEWIRE_MASTER_TIMER), TIM_CCR3(TIM(ONEWIRE_MASTER_TIMER))+5*(rcc_ahb_frequency/1000000)); // set time for new time slot (Trec>1, after time slot end) + uint16_t timeout = TIM_CCR3(TIM(ONEWIRE_MASTER_TIMER)); // time until new slot (= end of slot+recovery) + if (UINT16_MAX-timeout1, after time slot end and recovery time) timer_clear_flag(TIM(ONEWIRE_MASTER_TIMER), TIM_SR_CC2IF); // clear output compare flag timer_enable_irq(TIM(ONEWIRE_MASTER_TIMER), TIM_DIER_CC2IE); // enable compare interrupt for bit setting timer_clear_flag(TIM(ONEWIRE_MASTER_TIMER), TIM_SR_CC3IF); // clear output compare flag timer_enable_irq(TIM(ONEWIRE_MASTER_TIMER), TIM_DIER_CC3IE); // enable compare interrupt for end of time slow // start writing + gpio_set_mode(GPIO(ONEWIRE_MASTER_PORT), GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_OPENDRAIN, GPIO(ONEWIRE_MASTER_PIN)); // normal 1-Wire communication (only using external pull-up resistor) gpio_clear(GPIO(ONEWIRE_MASTER_PORT),GPIO(ONEWIRE_MASTER_PIN)); // pull signal low to start slot timer_enable_counter(TIM(ONEWIRE_MASTER_TIMER)); // start timer while (onewire_master_state!=ONEWIRE_STATE_DONE && onewire_master_state!=ONEWIRE_STATE_ERROR) { // wait until write procedure completed @@ -162,7 +187,15 @@ static bool onewire_master_read(void) onewire_master_state = ONEWIRE_STATE_MASTER_READ; // set new state // prepare timer + timer_disable_counter(TIM(ONEWIRE_MASTER_TIMER)); // disable timer to reconfigure it timer_set_counter(TIM(ONEWIRE_MASTER_TIMER),0); // reset counter + uint16_t timeout = TIM_CCR3(TIM(ONEWIRE_MASTER_TIMER)); // time until new slot (= end of slot+recovery) + if (UINT16_MAX-timeout1, after time slot end and recovery time) timer_clear_flag(TIM(ONEWIRE_MASTER_TIMER), TIM_SR_CC1IF); // clear output compare flag timer_enable_irq(TIM(ONEWIRE_MASTER_TIMER), TIM_DIER_CC1IE); // enable compare interrupt for stop pulling low timer_clear_flag(TIM(ONEWIRE_MASTER_TIMER), TIM_SR_CC2IF); // clear output compare flag @@ -171,6 +204,7 @@ static bool onewire_master_read(void) timer_enable_irq(TIM(ONEWIRE_MASTER_TIMER), TIM_DIER_CC3IE); // enable compare interrupt for end of time slow // start reading + gpio_set_mode(GPIO(ONEWIRE_MASTER_PORT), GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_OPENDRAIN, GPIO(ONEWIRE_MASTER_PIN)); // normal 1-Wire communication (only using external pull-up resistor) gpio_clear(GPIO(ONEWIRE_MASTER_PORT),GPIO(ONEWIRE_MASTER_PIN)); // pull signal low to start slot timer_enable_counter(TIM(ONEWIRE_MASTER_TIMER)); // start timer while (onewire_master_state!=ONEWIRE_STATE_DONE && onewire_master_state!=ONEWIRE_STATE_ERROR) { // wait until read procedure completed @@ -386,13 +420,19 @@ void TIM_ISR(ONEWIRE_MASTER_TIMER)(void) break; default: // unknown state for this stage timer_disable_counter(TIM(ONEWIRE_MASTER_TIMER)); // disable timer + timer_disable_irq(TIM(ONEWIRE_MASTER_TIMER), TIM_DIER_CC1IE); // disable all compare interrupt timer_disable_irq(TIM(ONEWIRE_MASTER_TIMER), TIM_DIER_CC2IE); // disable all compare interrupt timer_disable_irq(TIM(ONEWIRE_MASTER_TIMER), TIM_DIER_CC3IE); // disable all compare interrupt timer_disable_irq(TIM(ONEWIRE_MASTER_TIMER), TIM_DIER_CC4IE); // disable all compare interrupt gpio_set(GPIO(ONEWIRE_MASTER_PORT),GPIO(ONEWIRE_MASTER_PIN)); // pull signal high (idle state) onewire_master_state = ONEWIRE_STATE_ERROR; // indicate error } - } else if (timer_get_flag(TIM(ONEWIRE_MASTER_TIMER), TIM_SR_CC1IF)) { // compare event happened for master pull low end + if (onewire_master_parasite && (ONEWIRE_STATE_ERROR==onewire_master_state || ONEWIRE_STATE_DONE==onewire_master_state)) { + gpio_set_mode(GPIO(ONEWIRE_MASTER_PORT), GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO(ONEWIRE_MASTER_PIN)); // provide parasite power + } else { + gpio_set_mode(GPIO(ONEWIRE_MASTER_PORT), GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_OPENDRAIN, GPIO(ONEWIRE_MASTER_PIN)); // normal 1-Wire communication (only using external pull-up resistor) + } + } else if (timer_get_flag(TIM(ONEWIRE_MASTER_TIMER), TIM_SR_CC1IF)) { // compare event happened for master pull low end for read timer_clear_flag(TIM(ONEWIRE_MASTER_TIMER), TIM_SR_CC1IF); // clear flag switch (onewire_master_state) { case ONEWIRE_STATE_MASTER_READ: // master has to read a bit @@ -433,7 +473,10 @@ void TIM_ISR(ONEWIRE_MASTER_TIMER)(void) } } else if (timer_get_flag(TIM(ONEWIRE_MASTER_TIMER), TIM_SR_CC3IF)) { // compare event happened for end to time slot timer_clear_flag(TIM(ONEWIRE_MASTER_TIMER), TIM_SR_CC3IF); // clear flag - gpio_set(GPIO(ONEWIRE_MASTER_PORT),GPIO(ONEWIRE_MASTER_PIN)); // pull signal high to end time slot + gpio_set(GPIO(ONEWIRE_MASTER_PORT),GPIO(ONEWIRE_MASTER_PIN)); // pull signal high to end time slot + if (onewire_master_parasite) { // provide power during recovery time + gpio_set_mode(GPIO(ONEWIRE_MASTER_PORT), GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO(ONEWIRE_MASTER_PIN)); // provide parasite power + } } else if (timer_get_flag(TIM(ONEWIRE_MASTER_TIMER), TIM_SR_CC4IF)) { // compare event happened for slave presence detection timer_clear_flag(TIM(ONEWIRE_MASTER_TIMER), TIM_SR_CC4IF); // clear flag if (gpio_get(GPIO(ONEWIRE_MASTER_PORT),GPIO(ONEWIRE_MASTER_PIN))) { // check is a slave let its presence know by pulling low diff --git a/lib/onewire_master.h b/lib/onewire_master.h index 6b60b6c..aace27d 100644 --- a/lib/onewire_master.h +++ b/lib/onewire_master.h @@ -17,13 +17,16 @@ * @author King Kévin * @date 2017 * @note peripherals used: timer @ref onewire_master_timer, GPIO @ref onewire_master_gpio - * @note overdrive mode is not supported - * @warning this library does not support active parasite power mode (more than the pull-up resistor itself) + * @note overdrive mode is not provided */ #pragma once -/** setup 1-wire peripheral */ -void onewire_master_setup(void); +/** setup 1-wire peripheral + * @param[in] parasite enable parasite power (provide power over 1-Wire line when not communicating) + * @warning multiple masters and interrupts are prevented when parasite power is used + * @param[in] recovery recovery time in us between timeslot, e.g. to ensure enough parasite power is provided (0 if not required) + */ +void onewire_master_setup(bool parasite, uint16_t recovery); /** send reset pulse * @return if slaves have indicated their presence */