/* This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ /** library for 1-wire protocol as master * @file * @author King Kévin * @date 2017-2018 * @note peripherals used: timer @ref onewire_master_timer, GPIO @ref onewire_master_gpio * @note overdrive mode is not provided * @implements 1-Wire protocol description from Book of iButton Standards */ /* standard libraries */ #include // standard integer types #include // boolean type #include // NULL definition /* STM32 (including CM3) libraries */ #include // Cortex M3 utilities #include // interrupt handler #include // real-time control clock library #include // general purpose input output library #include // timer library /* own libraries */ #include "global.h" // help macros #include "interrupt.h" // runtime interrupt table #include "onewire_master.h" // own definitions /** @defgroup onewire_master_timer timer used to measure 1-wire signal timing * @{ */ #define ONEWIRE_MASTER_TIMER 5 /**< timer ID */ /** @} */ /** set if the timer ISR should be set in the interrupt table instead of the vector table * @note the vector table is faster, but doesn't allow to change the ISR */ #define ONEWIRE_MASTER_TIMER_USE_INTERRUPT_TABLE false /** state of 1-Wire communication */ volatile enum { ONEWIRE_STATE_IDLE, /**< no current communication */ ONEWIRE_STATE_DONE, /**< communication complete */ ONEWIRE_STATE_ERROR, /**< communication error */ ONEWIRE_STATE_MASTER_RESET, /**< reset pulse started */ ONEWIRE_STATE_SLAVE_PRESENCE, /**< waiting for slave response to reset pulse */ ONEWIRE_STATE_MASTER_WRITE, /**< master is writing bits */ ONEWIRE_STATE_MASTER_READ, /**< master is reading bits */ ONEWIRE_MAX /** to count the number of possible states */ } onewire_master_state = ONEWIRE_STATE_IDLE; 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 */ #if defined(ONEWIRE_MASTER_TIMER_USE_INTERRUPT_TABLE) && ONEWIRE_MASTER_TIMER_USE_INTERRUPT_TABLE static void (*isr_backup)(void) = NULL; /**< backup for the existing timer ISR */ static bool irq_backup = false; /**< backup for the existing timer IRQ */ #endif /** interrupt service routine called for timer */ #if defined(ONEWIRE_MASTER_TIMER_USE_INTERRUPT_TABLE) && ONEWIRE_MASTER_TIMER_USE_INTERRUPT_TABLE static void onewire_master_timer_isr(void) #else void TIM_ISR(ONEWIRE_MASTER_TIMER)(void) #endif { if (timer_get_flag(TIM(ONEWIRE_MASTER_TIMER), TIM_SR_UIF)) { // overflow update event happened timer_clear_flag(TIM(ONEWIRE_MASTER_TIMER), TIM_SR_UIF); // clear flag switch (onewire_master_state) { case ONEWIRE_STATE_MASTER_RESET: // reset pulse has been started timer_clear_flag(TIM(ONEWIRE_MASTER_TIMER), TIM_SR_CC4IF); // clear output compare flag timer_enable_irq(TIM(ONEWIRE_MASTER_TIMER), TIM_DIER_CC4IE); // enable compare interrupt for presence detection gpio_set(GPIO(ONEWIRE_MASTER_PORT),GPIO(ONEWIRE_MASTER_PIN)); // set signal high again for slaves to respond onewire_master_state = ONEWIRE_STATE_SLAVE_PRESENCE; // set new state break; case ONEWIRE_STATE_SLAVE_PRESENCE: // waiting for slave presence but none received timer_disable_counter(TIM(ONEWIRE_MASTER_TIMER)); // disable timer timer_disable_irq(TIM(ONEWIRE_MASTER_TIMER), TIM_DIER_CC4IE); // disable compare interrupt for presence detection onewire_master_state = ONEWIRE_STATE_DONE; // go to next state break; case ONEWIRE_STATE_MASTER_READ: // end of time slot and recovery time for reading bit case ONEWIRE_STATE_MASTER_WRITE: // end of time slot and recovery time for writing bit if (buffer_bit 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_clear_flag(TIM(ONEWIRE_MASTER_TIMER), TIM_SR_CC1IF); // clear flag 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_clear_flag(TIM(ONEWIRE_MASTER_TIMER), TIM_SR_CC2IF); // clear flag 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_clear_flag(TIM(ONEWIRE_MASTER_TIMER), TIM_SR_CC3IF); // clear flag 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_clear_flag(TIM(ONEWIRE_MASTER_TIMER), TIM_SR_CC4IF); // clear flag 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 tuning 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) timer_enable_irq(TIM(ONEWIRE_MASTER_TIMER), TIM_DIER_UIE); // enable update interrupt for overflow #if defined(ONEWIRE_MASTER_TIMER_USE_INTERRUPT_TABLE) && ONEWIRE_MASTER_TIMER_USE_INTERRUPT_TABLE isr_backup = interrupt_table[NVIC_TIM_IRQ(ONEWIRE_MASTER_TIMER)]; // backup timer ISR interrupt_table[NVIC_TIM_IRQ(ONEWIRE_MASTER_TIMER)] = &onewire_master_timer_isr; // set the 1-wire timer ISR irq_backup = nvic_get_irq_enabled(NVIC_TIM_IRQ(ONEWIRE_MASTER_TIMER)); // backup timer IRQ setting #endif nvic_enable_irq(NVIC_TIM_IRQ(ONEWIRE_MASTER_TIMER)); // catch interrupt in service routine slave_presence = false; // reset state onewire_master_state = ONEWIRE_STATE_IDLE; // reset state } void onewire_master_release(void) { // release timer timer_disable_counter(TIM(ONEWIRE_MASTER_TIMER)); // disable timer timer_reset(TIM(ONEWIRE_MASTER_TIMER)); // reset timer state rcc_periph_clock_disable(RCC_TIM(ONEWIRE_MASTER_TIMER)); // disable clock for timer peripheral // release GPIO gpio_set_mode(GPIO(ONEWIRE_MASTER_PORT), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO(ONEWIRE_MASTER_PIN)); // put back to input floating // disable timer ISR #if defined(ONEWIRE_MASTER_TIMER_USE_INTERRUPT_TABLE) && ONEWIRE_MASTER_TIMER_USE_INTERRUPT_TABLE if (!irq_backup) { // don't disable the IRQ if there was already enabled nvic_disable_irq(NVIC_TIM_IRQ(ONEWIRE_MASTER_TIMER)); // stop timer IRQ } interrupt_table[NVIC_TIM_IRQ(ONEWIRE_MASTER_TIMER)] = isr_backup; // set back original timer ISR #else nvic_disable_irq(NVIC_TIM_IRQ(ONEWIRE_MASTER_TIMER)); // stop timer IRQ #endif } 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 (480 < Trst) slave_presence = false; // reset state onewire_master_state = ONEWIRE_STATE_MASTER_RESET; // set new state 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 __WFI(); // go to sleep } if (ONEWIRE_STATE_ERROR==onewire_master_state) { // an error occurred return false; } return slave_presence; } /** write bits on 1-Wire bus * @warning buffer_size must be set to the number of bits to writen and buffer must contain the data to write * @return if write succeeded */ static bool onewire_master_write(void) { buffer_bit = 0; // reset bit index onewire_master_state = ONEWIRE_STATE_MASTER_WRITE; // 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 timer_set_period(TIM(ONEWIRE_MASTER_TIMER), TIM_CCR3(TIM(ONEWIRE_MASTER_TIMER))+2*(rcc_ahb_frequency/1000000)); // set time for new time slot (recovery timer Trec>1, after time slot end ) 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 __WFI(); // go to sleep } if (ONEWIRE_STATE_ERROR==onewire_master_state) { // an error occurred return false; } return true; } /** read bits on 1-Wire bus * @warning buffer_size must be set to the number of bits to read * @return if read succeeded */ static bool onewire_master_read(void) { if (0==buffer_size) { // check input return false; } buffer_bit = 0; // reset bit index 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 timer_set_period(TIM(ONEWIRE_MASTER_TIMER), TIM_CCR3(TIM(ONEWIRE_MASTER_TIMER))+2*(rcc_ahb_frequency/1000000)); // set time for new time slot (recovery timer Trec>1, after time slot end ) 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 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 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 __WFI(); // go to sleep } if (ONEWIRE_STATE_ERROR==onewire_master_state) { // an error occurred return false; } return true; } uint8_t onewire_master_crc(uint8_t* data, uint32_t length) { if (NULL==data || 0==length) { // check input return 0; // wrong input } uint8_t crc = 0x00; // initial value for (uint8_t i=0; i>1)^0x8C; // // shift to the right (for the next bit) and XOR with (reverse) polynomial } else { crc >>= 1; // just shift right (for the next bit) } } } return crc; } bool onewire_master_read_byte(uint8_t* data) { if (NULL==data) { // check input return false; // wrong input } // read data buffer_size = 8; // save number of bits to read (1 byte) buffer = data; // set the buffer to the data to write if (!onewire_master_read()) { // read bits from slave return false; // an error occurred } return true; } bool onewire_master_write_byte(uint8_t data) { // send data byte buffer_size = 8; // function command is only one byte buffer = &data; // set the buffer to the function code if (!onewire_master_write()) { // send command return false; // an error occurred } return true; } bool onewire_master_function_read(uint8_t function, uint8_t* data, uint32_t bits) { // send function command if (!onewire_master_write_byte(function)) { return false; // an error occurred } if (NULL==data || 0==bits) { // there is no data to read return true; // operation completed } // read data buffer_size = bits; // save number of bits to read buffer = data; // set the buffer to the data to write if (!onewire_master_read()) { // read bits from slave return false; // an error occurred } return true; } bool onewire_master_function_write(uint8_t function, uint8_t* data, uint32_t bits) { // send function command if (!onewire_master_write_byte(function)) { return false; // an error occurred } if (NULL==data || 0==bits) { // there is no data to read return true; // operation completed } // copy data from user buffer buffer_size = bits; // save number of bits to write buffer = data; // set the buffer to the data to write // write data if (!onewire_master_write()) { // read bits from slave return false; // an error occurred } return true; } uint64_t onewire_master_rom_read(void) { uint8_t rom_code[8] = {0}; // to store 64 bits ROM code if (!onewire_master_function_read(0x33, rom_code, 64)) { // read ROM code (I'm cheating because the ROM command isn't a function command, but it works the same way in the end) return 0; // an error occurred } if (onewire_master_crc(rom_code, LENGTH(rom_code))) { // verify checksum return 0; // checksum is wrong (not 0) } // return ROM code uint64_t code = 0; for (uint32_t i=0; i<8; i++) { code += (uint64_t)rom_code[i]<<(8*i); // add byte } return code; } bool onewire_master_rom_search(uint64_t* code, bool alarm) { static uint8_t conflict_last = 64; // on which bit has the conflict been detected (64 means there hasn't been) uint8_t conflict_current = 64; // to remember on which bit the last unknown conflict has been detected uint8_t bits[1] = {0}; // small buffer to store the bits used to search the ROM codes // send SEARCH ROM command uint8_t command = 0xf0; // SEARCH ROM command if (alarm) { // looking only for ROM codes for slaves in alarm state command = 0xec; // use ALARM SEARCH ROM command instead } if (!onewire_master_function_read(command, NULL, 0)) { // send SEARCH ROM command goto end; // an error occurred } if (conflict_last>=64) { // no previous conflict has been detected *code = 0; // restart search codes } buffer = bits; // buffer to read up to two bits for (uint8_t bit=0; bit<64; bit++) { // go through all 64 bits ROM code buffer_size = 2; // read two first bits to detect conflict if (!onewire_master_read()) { // read ROM ID from slave goto end; // an error occurred } switch (buffer[0]&0x03) { // check 2 bits received case 0: // collision detected if (bit==conflict_last) { // this conflict is known *code |= (((uint64_t)1)<>bit); // set bit to send if (!onewire_master_write()) { // send bit goto end; // an error has occurred } } // verify ROM code uint8_t rom_code[8] = {0}; // to store ROM code for (uint8_t i=0; i>(8*i); // split and save last code in ROM code } if (onewire_master_crc(rom_code, LENGTH(rom_code))) { // verify checksum *code = 0; // return the last code found since it's valid } end: conflict_last = conflict_current; // update the last seen and unknown conflict if (conflict_current<64) { // we have seen an unknown conflict return true; // tell there are more slaves } else { // no conflict seen return false; // no more slaves } } bool onewire_master_rom_skip(void) { if (!onewire_master_function_write(0xcc, NULL, 0)) { // send SKIP ROM command return false; // an error occurred } return true; } bool onewire_master_rom_match(uint64_t code) { uint8_t rom_code[8] = {0}; // to store ROM code for (uint8_t i=0; i>(8*i); // split and save code in ROM code } if (!onewire_master_function_write(0x55, rom_code, 64)) { // send MATCH ROM command with ROM code return false; // an error occurred } return true; }