aboutsummaryrefslogtreecommitdiff
path: root/lib/onewire_master.c
blob: 730eb6fd29755ed6d03dd76bbd6b8aa2d2822a00 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
/* 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
 *  @file
 *  @author King Kévin <kingkevin@cuvoodoo.info>
 *  @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 <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 "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<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
		}
	} 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
	} 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
	}
}

void onewire_master_setup(void)
{
	// 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)
	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_disable_counter(TIM(ONEWIRE_MASTER_TIMER)); // disable timer to configure it
	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_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<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_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); // 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
				conflict_current = 64; // remember there was no conflict because 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;
}