/** library for 1-wire protocol as master * @file * @author King Kévin * @copyright SPDX-License-Identifier: GPL-3.0-or-later * @date 2017-2020 * @note peripherals used: GPIO and timer @ref onewire_slave_timer, GPIO @ref onewire_slave_gpio * @note overdrive mode is not supported * @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 #include // external interrupt library /* own libraries */ #include "global.h" // help macros #include "onewire_slave.h" // own definitions /** @defgroup onewire_slave_timer timer used to measure 1-wire signal timing * @{ */ #define ONEWIRE_SLAVE_TIMER 2 /**< timer ID */ /** @} */ /** @defgroup onewire_slave_gpio GPIO used for 1-wire signal * @warning ensure no same pin number on other parts are used for external interrupts * @note external pull-up resistor on pin is required (< 5 kOhm), generally provided by the master * @{ */ #define ONEWIRE_SLAVE_PIN PA4 /**< GPIO pin */ /** @} */ /** state of 1-Wire communication */ static volatile enum { ONEWIRE_STATE_IDLE, /**< no current communication */ ONEWIRE_STATE_RESET, /**< reset pulse has been detected */ ONEWIRE_STATE_WAIT_PRESENCE, /**< waiting before sending the presence pulse */ ONEWIRE_STATE_PULSE_PRESENCE, /**< sending the presence pulse */ ONEWIRE_STATE_ROM_COMMAND, /**< slave is reading ROM command bits */ ONEWIRE_STATE_ROM_READ, /**< slave is sending ROM code in response to ROM command READ ROM */ ONEWIRE_STATE_ROM_MATCH, /**< master is sending ROM code to select slave */ ONEWIRE_STATE_ROM_SEARCH_TRUE, /**< master is searching ROM code, slave will send first bit (not negated) */ ONEWIRE_STATE_ROM_SEARCH_FALSE, /**< master is searching ROM code, slave will send first bit (not negated) */ ONEWIRE_STATE_ROM_SEARCH_SELECT, /**< master is searching ROM code, slave will read selected bit */ ONEWIRE_STATE_FUNCTION_COMMAND, /**< slave is reading function command bits */ ONEWIRE_STATE_FUNCTION_DATA, /**< waiting for user to provide data to transfer */ ONEWIRE_STATE_FUNCTION_READ, /**< slave is reading bits */ ONEWIRE_STATE_FUNCTION_WRITE, /**< slave is writing bits */ ONEWIRE_MAX /** to count the number of possible states */ } onewire_slave_state = ONEWIRE_STATE_IDLE; static uint8_t onewire_slave_rom_code[8] = {0}; /**< slave ROM code */ volatile bool onewire_slave_function_code_received = false; volatile uint8_t onewire_slave_function_code = 0; volatile bool onewire_slave_transfer_complete = false; static volatile uint8_t bits_buffer = 0; /**< buffer for the incoming bits (up to one byte) */ static volatile uint32_t bits_bit = 0; /**< number of incoming bits */ static volatile uint8_t* onewire_slave_transfer_data = NULL; /**< data to transfer (read or write) */ static volatile uint32_t onewire_slave_transfer_bits = 0; /**< number of bits to transfer */ /** compute CRC for 1-Wire * @note this CRC-8 uses normal polynomial 0x31, reverse polynomial 0x8C, start value 0x00 * @param[in] data bytes on which to calculate CRC checksum on * @param[in] length number of bytes in data * @return computed CRC checksum */ static uint8_t onewire_slave_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; } void onewire_slave_setup(uint8_t family, uint64_t serial) { // save ROM code (LSB first) onewire_slave_rom_code[0] = family; onewire_slave_rom_code[1] = serial >> 40; onewire_slave_rom_code[2] = serial >> 32; onewire_slave_rom_code[3] = serial >> 24; onewire_slave_rom_code[4] = serial >> 16; onewire_slave_rom_code[5] = serial >> 8; onewire_slave_rom_code[6] = serial >> 0; onewire_slave_rom_code[7] = onewire_slave_crc(onewire_slave_rom_code, 7); // calculate CRC // setup timer to generate/measure signal timing rcc_periph_clock_enable(RCC_TIM(ONEWIRE_SLAVE_TIMER)); // enable clock for timer peripheral rcc_periph_reset_pulse(RST_TIM(ONEWIRE_SLAVE_TIMER)); // reset timer state timer_set_mode(TIM(ONEWIRE_SLAVE_TIMER), TIM_CR1_CKD_CK_INT, TIM_CR1_CMS_EDGE, TIM_CR1_DIR_UP); // set timer mode, use undivided timer clock, edge alignment (simple count), and count up timer_set_prescaler(TIM(ONEWIRE_SLAVE_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_period(TIM(ONEWIRE_SLAVE_TIMER), 480 * (rcc_ahb_frequency / 1000000) - 1 - 1300); // minimum time needed for a reset pulse (480 < Trst), plus hand tuning timer_set_oc_mode(TIM(ONEWIRE_SLAVE_TIMER), TIM_OC1, TIM_OCM_FROZEN); timer_set_oc_value(TIM(ONEWIRE_SLAVE_TIMER), TIM_OC1, 16 * (rcc_ahb_frequency / 1000000) - 1); // time to wait before sending the presence pulse, after the rising edge of the reset pulse (15 < Tpdh < 60) timer_set_oc_mode(TIM(ONEWIRE_SLAVE_TIMER), TIM_OC2, TIM_OCM_FROZEN); timer_set_oc_value(TIM(ONEWIRE_SLAVE_TIMER), TIM_OC2, 45 * (rcc_ahb_frequency / 1000000) - 1 - 350); // time to sample the bit after being set (1 < Tlow1 < 15, 60 < Tslot < 120), or stop sending the bit use compare function to detect slave presence (15 = Trdv + 0 < Trelease < 45), plus hand tuning timer_set_oc_value(TIM(ONEWIRE_SLAVE_TIMER), TIM_OC3, 90 * (rcc_ahb_frequency / 1000000) - 1); // time to stop the presence pulse (60 < Tpdl < 120) timer_clear_flag(TIM(ONEWIRE_SLAVE_TIMER), TIM_SR_UIF | TIM_SR_CC1IF | TIM_SR_CC2IF | TIM_SR_CC3IF); // clear all interrupt flags timer_update_on_overflow(TIM(ONEWIRE_SLAVE_TIMER)); // only use counter overflow as UEV source (use overflow as start time or timeout) timer_enable_irq(TIM(ONEWIRE_SLAVE_TIMER), TIM_DIER_UIE); // enable update interrupt for overflow nvic_enable_irq(NVIC_TIM_IRQ(ONEWIRE_SLAVE_TIMER)); // catch interrupt in service routine onewire_slave_function_code_received = false; // reset state onewire_slave_state = ONEWIRE_STATE_IDLE; // reset state onewire_slave_transfer_complete = false; // reset state // setup GPIO with external interrupt rcc_periph_clock_enable(GPIO_RCC(ONEWIRE_SLAVE_PIN)); // enable clock for GPIO peripheral gpio_set(GPIO_PORT(ONEWIRE_SLAVE_PIN), GPIO_PIN(ONEWIRE_SLAVE_PIN)); // idle is high (using pull-up resistor) gpio_set_mode(GPIO_PORT(ONEWIRE_SLAVE_PIN), GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_OPENDRAIN, GPIO_PIN(ONEWIRE_SLAVE_PIN)); // control output using open drain (this mode also allows to read the input signal) rcc_periph_clock_enable(RCC_AFIO); // enable alternate function clock for external interrupt exti_select_source(GPIO_EXTI(ONEWIRE_SLAVE_PIN), GPIO_PORT(ONEWIRE_SLAVE_PIN)); // mask external interrupt of this pin only for this port exti_set_trigger(GPIO_EXTI(ONEWIRE_SLAVE_PIN), EXTI_TRIGGER_BOTH); // trigger on signal change exti_enable_request(GPIO_EXTI(ONEWIRE_SLAVE_PIN)); // enable external interrupt nvic_enable_irq(GPIO_NVIC_EXTI_IRQ(ONEWIRE_SLAVE_PIN)); // enable interrupt } bool onewire_slave_function_read(uint8_t* data, size_t size) { if (NULL == data || 0 == size) { // verify input return false; } if (UINT32_MAX / 8 < size) { // too many bits to transfer return false; } if (onewire_slave_state != ONEWIRE_STATE_FUNCTION_DATA) { // not in the right state to transfer data return false; } onewire_slave_transfer_data = data; // save buffer to write to onewire_slave_transfer_bits = size*8; // number of bits to read onewire_slave_transfer_complete = false; // reset state bits_bit = 0; // reset number of bits read onewire_slave_state = ONEWIRE_STATE_FUNCTION_READ; // read data return true; } bool onewire_slave_function_write(const uint8_t* data, size_t size) { if (NULL == data || 0 == size) { // verify input return false; } if (onewire_slave_state != ONEWIRE_STATE_FUNCTION_DATA) { // not in the right state to transfer data return false; } if (UINT32_MAX / 8 < size) { // too many bits to transfer return false; } onewire_slave_transfer_data = (uint8_t*)data; // save buffer to read from onewire_slave_transfer_bits = size*8; // number of bits to write onewire_slave_transfer_complete = false; // reset state bits_bit = 0; // reset number of bits written bits_buffer = onewire_slave_transfer_data[0]; // prepare byte to write onewire_slave_state = ONEWIRE_STATE_FUNCTION_WRITE; // write data return true; } /** interrupt service routine called when 1-Wire signal changes */ void GPIO_EXTI_ISR(ONEWIRE_SLAVE_PIN)(void) { exti_reset_request(GPIO_EXTI(ONEWIRE_SLAVE_PIN)); // reset interrupt if (gpio_get(GPIO_PORT(ONEWIRE_SLAVE_PIN), GPIO_PIN(ONEWIRE_SLAVE_PIN))) { // it's a rising edge switch (onewire_slave_state) { case ONEWIRE_STATE_RESET: // reset pulse has ended timer_disable_counter(TIM(ONEWIRE_SLAVE_TIMER)); // stop timer for reconfiguration timer_set_counter(TIM(ONEWIRE_SLAVE_TIMER), 0); // reset timer counter timer_clear_flag(TIM(ONEWIRE_SLAVE_TIMER), TIM_SR_CC1IF); // clear flag timer_enable_irq(TIM(ONEWIRE_SLAVE_TIMER), TIM_DIER_CC1IE); // enable compare interrupt for presence pulse timer_enable_counter(TIM(ONEWIRE_SLAVE_TIMER)); // start timer to generate timing onewire_slave_state = ONEWIRE_STATE_WAIT_PRESENCE; // set new stated break; case ONEWIRE_STATE_PULSE_PRESENCE: // we stopped sending the presence pulse onewire_slave_state = ONEWIRE_STATE_ROM_COMMAND; // we now expect a ROM command bits_bit = 0; // reset buffer bit count break; // no need to stop the time, the reset will be checked correctly default: // rising edge is not important is the other cases break; // nothing to do } } else { // it's a falling edge, the beginning of a new signal timer_disable_counter(TIM(ONEWIRE_SLAVE_TIMER)); // stop timer for reconfiguration timer_set_counter(TIM(ONEWIRE_SLAVE_TIMER), 0); // reset timer counter timer_disable_irq(TIM(ONEWIRE_SLAVE_TIMER), TIM_DIER_CC1IE | TIM_DIER_CC2IE | TIM_DIER_CC3IE); // disable all timers switch (onewire_slave_state) { case ONEWIRE_STATE_PULSE_PRESENCE: // we started sending the presence pulse timer_enable_irq(TIM(ONEWIRE_SLAVE_TIMER), TIM_DIER_CC3IE); // enable timer for end of pulse break; case ONEWIRE_STATE_ROM_COMMAND: // read ROM command bits case ONEWIRE_STATE_ROM_MATCH: // read ROM code bits case ONEWIRE_STATE_FUNCTION_COMMAND: // read function command bits case ONEWIRE_STATE_ROM_SEARCH_SELECT: // read selected ROM code bit case ONEWIRE_STATE_FUNCTION_READ: // read function data bit timer_enable_irq(TIM(ONEWIRE_SLAVE_TIMER), TIM_DIER_CC2IE); // enable timer for reading bit break; case ONEWIRE_STATE_ROM_READ: // send ROM code bit case ONEWIRE_STATE_ROM_SEARCH_TRUE: // send ROM code bit while searching ROM, not negated case ONEWIRE_STATE_ROM_SEARCH_FALSE: // send ROM code bit while searching ROM, already negated case ONEWIRE_STATE_FUNCTION_WRITE: // write function data bit timer_enable_irq(TIM(ONEWIRE_SLAVE_TIMER), TIM_DIER_CC2IE); // enable timer for reading bit if (0 == (bits_buffer & (1 << (bits_bit % 8)))) { // need to send a 0 bit gpio_clear(GPIO_PORT(ONEWIRE_SLAVE_PIN), GPIO_PIN(ONEWIRE_SLAVE_PIN)); // hold low to send 0 bit } break; case ONEWIRE_STATE_IDLE: // we only expect a reset default: // we don't expect any falling edge in other states onewire_slave_state = ONEWIRE_STATE_IDLE; // unexpected signal, reset to idle state break; // the timer overflow will confirm detect reset pulses } timer_clear_flag(TIM(ONEWIRE_SLAVE_TIMER), TIM_SR_UIF | TIM_SR_CC1IF | TIM_SR_CC2IF | TIM_SR_CC3IF); // clear all flags timer_enable_counter(TIM(ONEWIRE_SLAVE_TIMER)); // start timer to measure the configured timeouts } } /** interrupt service routine called for timer */ void TIM_ISR(ONEWIRE_SLAVE_TIMER)(void) { if (timer_interrupt_source(TIM(ONEWIRE_SLAVE_TIMER), TIM_SR_UIF)) { // reset timer triggered, verify if it's a reset if (0 == gpio_get(GPIO_PORT(ONEWIRE_SLAVE_PIN), GPIO_PIN(ONEWIRE_SLAVE_PIN))) { // signal it still low, thus it must be a reset onewire_slave_state = ONEWIRE_STATE_RESET; // update state } timer_disable_counter(TIM(ONEWIRE_SLAVE_TIMER)); // stop timer since there is nothing more to measure timer_disable_irq(TIM(ONEWIRE_SLAVE_TIMER), TIM_DIER_CC1IE | TIM_DIER_CC2IE | TIM_DIER_CC3IE); // disable all timers timer_clear_flag(TIM(ONEWIRE_SLAVE_TIMER), TIM_SR_UIF | TIM_SR_CC1IF | TIM_SR_CC2IF | TIM_SR_CC3IF); // clear all flag (I have no idea why the others are get too, even when the interrupt is not enabled) } if (timer_interrupt_source(TIM(ONEWIRE_SLAVE_TIMER), TIM_SR_CC1IF)) { // wait for presence pulse timer triggered timer_clear_flag(TIM(ONEWIRE_SLAVE_TIMER), TIM_SR_CC1IF); // clear flag if (ONEWIRE_STATE_WAIT_PRESENCE == onewire_slave_state) { // we can now send the pulse onewire_slave_state = ONEWIRE_STATE_PULSE_PRESENCE; // save new state gpio_clear(GPIO_PORT(ONEWIRE_SLAVE_PIN), GPIO_PIN(ONEWIRE_SLAVE_PIN)); // send presence pulse (will also trigger the timer start) } } if (timer_interrupt_source(TIM(ONEWIRE_SLAVE_TIMER), TIM_SR_CC2IF)) { // time to read the bit, or stop writing it // read/write bit depending on bit switch (onewire_slave_state) { case ONEWIRE_STATE_ROM_COMMAND: // read ROM command code bit case ONEWIRE_STATE_ROM_MATCH: // read ROM code case ONEWIRE_STATE_FUNCTION_COMMAND: // read function command code bit case ONEWIRE_STATE_ROM_SEARCH_SELECT: // read selected ROM code bit case ONEWIRE_STATE_FUNCTION_READ: // read function data bit if (gpio_get(GPIO_PORT(ONEWIRE_SLAVE_PIN), GPIO_PIN(ONEWIRE_SLAVE_PIN))) { // bit is set to 1 bits_buffer |= (1 << (bits_bit % 8)); // set bit } else { // bit is set to 0 bits_buffer &= ~(1 << (bits_bit % 8)); // clear bit } bits_bit++; // go to next bit break; case ONEWIRE_STATE_ROM_READ: // write ROM code case ONEWIRE_STATE_FUNCTION_WRITE: // write function data bit gpio_set(GPIO_PORT(ONEWIRE_SLAVE_PIN), GPIO_PIN(ONEWIRE_SLAVE_PIN)); // stop sending bit bits_bit++; // go to next bit break; case ONEWIRE_STATE_ROM_SEARCH_TRUE: // ROM code bit is sent case ONEWIRE_STATE_ROM_SEARCH_FALSE: // ROM code bit is sent gpio_set(GPIO_PORT(ONEWIRE_SLAVE_PIN), GPIO_PIN(ONEWIRE_SLAVE_PIN)); // stop sending bit break; default: // these states don't need read/write break; } static uint8_t rom_code_byte; // which byte of the ROM code is processed // act on bit count switch (onewire_slave_state) { case ONEWIRE_STATE_ROM_COMMAND: // read ROM command if (bits_bit > 7) { // complete ROM command code received bits_bit = 0; // reset buffer rom_code_byte = 0; // reset ROM code byte index switch (bits_buffer) { // act depending on ROM command code case 0x33: // READ ROM bits_buffer = onewire_slave_rom_code[rom_code_byte]; // prepare to send the first byte onewire_slave_state = ONEWIRE_STATE_ROM_READ; // write ROM code break; case 0xcc: // SKIP ROM onewire_slave_state = ONEWIRE_STATE_FUNCTION_COMMAND; // now read function command code break; case 0x55: // MATCH ROM onewire_slave_state = ONEWIRE_STATE_ROM_MATCH; // read ROM code break; case 0xf0: // SEARCH ROM bits_buffer = onewire_slave_rom_code[rom_code_byte]; // prepare to search code onewire_slave_state = ONEWIRE_STATE_ROM_SEARCH_TRUE; // prepare to start sending first new bit break; default: // unknown ROM code onewire_slave_state = ONEWIRE_STATE_IDLE; // go back to default idle state break; } } break; case ONEWIRE_STATE_ROM_READ: // send ROM code if (bits_bit > 7) { // complete byte transmitted rom_code_byte++; // go to next ROM code byte if (rom_code_byte>LENGTH(onewire_slave_rom_code)) { // complete ROM code send onewire_slave_state = ONEWIRE_STATE_IDLE; // go back to default idle state } else { bits_bit = 0; // reset buffer bits_buffer = onewire_slave_rom_code[rom_code_byte]; // send next ROM code byte } } break; case ONEWIRE_STATE_ROM_MATCH: // compare ROM code if (bits_bit > 7) { // complete byte received if (bits_buffer == onewire_slave_rom_code[rom_code_byte]) { // ROM code byte matches bits_bit = 0; // reset buffer rom_code_byte++; // go to next ROM code byte if (rom_code_byte >= LENGTH(onewire_slave_rom_code)) { // complete ROM code matches onewire_slave_state = ONEWIRE_STATE_FUNCTION_COMMAND; // now read function command code } } else { // ROM code does not match onewire_slave_state = ONEWIRE_STATE_IDLE; // stop comparing and go back to idle } } break; case ONEWIRE_STATE_ROM_SEARCH_TRUE: // ROM code bit is send, prepare to send negated version bits_buffer ^= (1 << bits_bit); // negate bit onewire_slave_state = ONEWIRE_STATE_ROM_SEARCH_FALSE; // send negated version break; case ONEWIRE_STATE_ROM_SEARCH_FALSE: // negated ROM code bit is send, prepare to read selected bit onewire_slave_state = ONEWIRE_STATE_ROM_SEARCH_SELECT; // read selected break; case ONEWIRE_STATE_ROM_SEARCH_SELECT: // check if we are selected if ((bits_buffer&(1 << (bits_bit - 1))) == (onewire_slave_rom_code[rom_code_byte] & (1 << (bits_bit - 1)))) { // we have been selected onewire_slave_state = ONEWIRE_STATE_ROM_SEARCH_TRUE; // prepare to compare next bit } else { // we are no selected onewire_slave_state = ONEWIRE_STATE_IDLE; // go back to idle } if (bits_bit > 7) { // complete byte searched bits_bit = 0; // reset buffer rom_code_byte++; // go to next ROM code byte if (rom_code_byte >= LENGTH(onewire_slave_rom_code)) { // complete ROM code search onewire_slave_state = ONEWIRE_STATE_FUNCTION_COMMAND; // now read function command code } else { bits_buffer = onewire_slave_rom_code[rom_code_byte]; // prepare next ROM code byte } } break; case ONEWIRE_STATE_FUNCTION_COMMAND: // read function command if (bits_bit > 7) { // complete function command code received onewire_slave_function_code = bits_buffer; // save function command code to user buffer onewire_slave_state = ONEWIRE_STATE_FUNCTION_DATA; // let the user transfer data onewire_slave_function_code_received = true; // notify user } break; case ONEWIRE_STATE_FUNCTION_READ: // save function data bit if (0 == bits_bit % 8) { // complete byte received onewire_slave_transfer_data[(bits_bit - 1) / 8] = bits_buffer; // save received bytes } if (bits_bit >= onewire_slave_transfer_bits) { // read transfer complete onewire_slave_transfer_data[(bits_bit - 1) / 8] = bits_buffer; // save last bits onewire_slave_state = ONEWIRE_STATE_FUNCTION_DATA; // let the user transfer more data onewire_slave_transfer_complete = true; // notify user } break; case ONEWIRE_STATE_FUNCTION_WRITE: // update function data bit to write if (0 == bits_bit % 8) { // complete byte transfer bits_buffer = onewire_slave_transfer_data[bits_bit / 8]; // prepare next byte to write } if (bits_bit >= onewire_slave_transfer_bits) { // write transfer complete onewire_slave_state = ONEWIRE_STATE_FUNCTION_DATA; // let the user transfer more data onewire_slave_transfer_complete = true; // notify user } break; default: // no action needed break; } timer_clear_flag(TIM(ONEWIRE_SLAVE_TIMER), TIM_SR_CC2IF); // clear flag } if (timer_interrupt_source(TIM(ONEWIRE_SLAVE_TIMER), TIM_SR_CC3IF)) { // end of presence pulse timer triggered timer_clear_flag(TIM(ONEWIRE_SLAVE_TIMER), TIM_SR_CC3IF); // clear flag if (ONEWIRE_STATE_PULSE_PRESENCE == onewire_slave_state) { gpio_set(GPIO_PORT(ONEWIRE_SLAVE_PIN), GPIO_PIN(ONEWIRE_SLAVE_PIN)); // stop sending presence pulse // if the pin stays low the reset timer will catch it } } }