/* 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 (code) * @file onewire_slave.c * @author King Kévin * @date 2017 * @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_PORT A /**< GPIO port */ #define ONEWIRE_SLAVE_PIN 4 /**< 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 timer_reset(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(RCC_GPIO(ONEWIRE_SLAVE_PORT)); // enable clock for GPIO peripheral gpio_set(GPIO(ONEWIRE_SLAVE_PORT), GPIO(ONEWIRE_SLAVE_PIN)); // idle is high (using pull-up resistor) gpio_set_mode(GPIO(ONEWIRE_SLAVE_PORT), GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_OPENDRAIN, GPIO(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(EXTI(ONEWIRE_SLAVE_PIN), GPIO(ONEWIRE_SLAVE_PORT)); // mask external interrupt of this pin only for this port exti_set_trigger(EXTI(ONEWIRE_SLAVE_PIN), EXTI_TRIGGER_BOTH); // trigger on signal change exti_enable_request(EXTI(ONEWIRE_SLAVE_PIN)); // enable external interrupt nvic_enable_irq(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/87) { // 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<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(ONEWIRE_SLAVE_PORT), GPIO(ONEWIRE_SLAVE_PIN)); // stop sending presence pulse // if the pin stays low the reset timer will catch it } } }