/* 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 <http://www.gnu.org/licenses/>.
 *
 */
/** library for 1-wire protocol as master (code)
 *  @file onewire_master.c
 *  @author King Kévin <kingkevin@cuvoodoo.info>
 *  @date 2017
 *  @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 <stdint.h> // standard integer types
#include <stdbool.h> // boolean type
#include <stddef.h> // NULL definition

/* STM32 (including CM3) libraries */
#include <libopencmsis/core_cm3.h> // Cortex M3 utilities
#include <libopencm3/cm3/nvic.h> // interrupt handler
#include <libopencm3/stm32/rcc.h> // real-time control clock library
#include <libopencm3/stm32/gpio.h> // general purpose input output library
#include <libopencm3/stm32/timer.h> // timer library

/* own libraries */
#include "global.h" // help macros
#include "onewire_master.h" // own definitions

/** @defgroup onewire_master_timer timer used to measure 1-wire signal timing
 *  @{
 */
#define ONEWIRE_MASTER_TIMER 2 /**< timer ID */
/** @} */

/** @defgroup onewire_master_gpio GPIO used for 1-wire signal
 *  @note external pull-up resistor on pin is required (< 5 kOhm)
 *  @{
 */
#define ONEWIRE_MASTER_PORT A /**< GPIO port */
#define ONEWIRE_MASTER_PIN 4 /**< GPIO pin */
/** @} */

/** 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 */
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(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(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
	timer_reset(TIM(ONEWIRE_MASTER_TIMER)); // reset timer state
	timer_set_mode(TIM(ONEWIRE_MASTER_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_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 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, (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)
	timer_enable_irq(TIM(ONEWIRE_MASTER_TIMER), TIM_DIER_UIE); // enable update interrupt for overflow
	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
}

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
	uint16_t timeout = TIM_CCR3(TIM(ONEWIRE_MASTER_TIMER)); // time until new slot (= end of slot+recovery)
	if (UINT16_MAX-timeout<onewire_master_recovery) { // catch integer overflow
		timeout = UINT16_MAX; // set maximum value
	} else {
		timeout += onewire_master_recovery; // add recovery time to end of slot
	}
	timer_set_period(TIM(ONEWIRE_MASTER_TIMER), timeout-1); // set time for new time slot (Trec>1, 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
		__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
	uint16_t timeout = TIM_CCR3(TIM(ONEWIRE_MASTER_TIMER)); // time until new slot (= end of slot+recovery)
	if (UINT16_MAX-timeout<onewire_master_recovery) { // catch integer overflow
		timeout = UINT16_MAX; // set maximum value
	} else {
		timeout += onewire_master_recovery; // add recovery time to end of slot
	}
	timer_set_period(TIM(ONEWIRE_MASTER_TIMER), timeout-1); // set time for new time slot (Trec>1, 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
	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<length; i++) { // go through every byte
		crc ^= data[i]; // XOR byte
		for (uint8_t b=0; b<8; b++) { // go through every bit
			if (crc&0x01) { // least significant bit is set (we are using the reverse way)
				crc = (crc>>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_function_read(uint8_t function, uint8_t* data, uint32_t bits)
{
	// send function command
	buffer_size = 8; // function command is only one byte
	buffer = &function; // set the buffer to the function code
	if (!onewire_master_write()) { // send command
		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
	buffer_size = 8; // function command is only one byte
	buffer = &function; // set the buffer to the function code
	if (!onewire_master_write()) { // send command
		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); // use 0 as next bit
				} else { // unknown conflict
					conflict_current = bit; // remember conflict
					*code &= ~(((uint64_t)1)<<bit); // use 1 as next bit
				}
				break;
			case 1: // no conflict, valid bit is 1
				*code |= (((uint64_t)1)<<bit); // remember valid bit 1
				break;
			case 2: // no conflict, valid bit is 0
				*code &= ~(((uint64_t)1)<<bit); // remember valid bit 0
				break;
			default: // two 1's indicate there is no slave
				goto end; // an error has occurred
		}
		buffer_size = 1; // to send next bit
		buffer[0] = (*code>>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<LENGTH(rom_code); i++) {
		rom_code[i] = *code>>(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<LENGTH(rom_code); i++) {
		rom_code[i] = code>>(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;
}


/** interrupt service routine called for timer */
void TIM_ISR(ONEWIRE_MASTER_TIMER)(void)
{
	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<buffer_size) { // check if byte to read/write are remaining
					gpio_clear(GPIO(ONEWIRE_MASTER_PORT),GPIO(ONEWIRE_MASTER_PIN)); // pull signal low to start next slot
				} else { // all bytes read/written
					timer_disable_counter(TIM(ONEWIRE_MASTER_TIMER)); // disable timer
 					timer_disable_irq(TIM(ONEWIRE_MASTER_TIMER), TIM_DIER_CC1IE); // disable compare interrupt for master pull low
					timer_disable_irq(TIM(ONEWIRE_MASTER_TIMER), TIM_DIER_CC2IE); // disable compare interrupt for read/write bit
					timer_disable_irq(TIM(ONEWIRE_MASTER_TIMER), TIM_DIER_CC3IE); // disable compare interrupt for end of slot
					onewire_master_state = ONEWIRE_STATE_DONE; // set end state
				}
				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
		}
		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
				gpio_set(GPIO(ONEWIRE_MASTER_PORT),GPIO(ONEWIRE_MASTER_PIN)); // pull signal high to end time slot 
				break;
			default: // unknown state for this stage
				break; // let the overflow handle the error if any
		}
	} else if (timer_get_flag(TIM(ONEWIRE_MASTER_TIMER), TIM_SR_CC2IF)) { // compare event happened for bit sampling/setting
		timer_clear_flag(TIM(ONEWIRE_MASTER_TIMER), TIM_SR_CC2IF); // clear flag
		switch (onewire_master_state) {
			case ONEWIRE_STATE_MASTER_WRITE: // master has to write a bit
				if (buffer_bit<buffer_size) { // check if byte to send are remaining
					if (buffer[buffer_bit/8]&(1<<(buffer_bit%8))) { // check bit (LSb first)
						gpio_set(GPIO(ONEWIRE_MASTER_PORT),GPIO(ONEWIRE_MASTER_PIN)); // set signal high again to write "1"
					}
					buffer_bit++; // got to next bit
				} else {
					timer_disable_irq(TIM(ONEWIRE_MASTER_TIMER), TIM_DIER_CC2IE); // disable compare interrupt
					onewire_master_state = ONEWIRE_STATE_ERROR; // indicate error
				}
				break;
			case ONEWIRE_STATE_MASTER_READ: // master has to read a bit set by slave 
				if (buffer_bit<buffer_size) { // check if byte to send are remaining
					if (gpio_get(GPIO(ONEWIRE_MASTER_PORT),GPIO(ONEWIRE_MASTER_PIN))) { // check if the slave kept it low
						buffer[buffer_bit/8] |= (1<<(buffer_bit%8)); // save bit "1"
					} else {
						buffer[buffer_bit/8] &= ~(1<<(buffer_bit%8)); // save bit "0"
					}
					buffer_bit++; // got to next bit
				} else {
					timer_disable_irq(TIM(ONEWIRE_MASTER_TIMER), TIM_DIER_CC2IE); // disable compare interrupt
					onewire_master_state = ONEWIRE_STATE_ERROR; // indicate error
				}
				break;
			default: // unknown state for this stage
				break; // let the overflow handle the error if any
		}
	} 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
		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
			slave_presence = false; // remember no slave(s) responded
		} else {
			slave_presence = true; // remember slave(s) responded
		}
	} else { // no other interrupt should occur
		while (true); // unhandled exception: wait for the watchdog to bite
	}
}
