summaryrefslogtreecommitdiffstatshomepage
path: root/src/emu/machine/ds2401.c
blob: baa987a7e1962da0f0aac011710de0cac6bb0f6a (plain) (blame)
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
/*
 * DS2401
 *
 * Dallas Semiconductor
 * Silicon Serial Number
 *
 */

#include <stdarg.h>
#include "driver.h"
#include "state.h"
#include "machine/ds2401.h"

#define VERBOSE_LEVEL ( 0 )

INLINE void verboselog( int n_level, const char *s_fmt, ... )
{
	if( VERBOSE_LEVEL >= n_level )
	{
		va_list v;
		char buf[ 32768 ];
		va_start( v, s_fmt );
		vsprintf( buf, s_fmt, v );
		va_end( v );
		if( cpu_getactivecpu() != -1 )
		{
			logerror( "%08x: %s", activecpu_get_pc(), buf );
		}
		else
		{
			logerror( "(timer) : %s", buf );
		}
	}
}

struct ds2401_chip
{
	int state;
	int bit;
	int byte;
	int shift;
	int rx;
	int tx;
	UINT8 *data;
	emu_timer *timer;
	emu_timer *reset_timer;
	attotime t_samp;
	attotime t_rdv;
	attotime t_rstl;
	attotime t_pdh;
	attotime t_pdl;
};

#define SIZE_DATA ( 8 )

#define STATE_IDLE ( 0 )
#define STATE_RESET ( 1 )
#define STATE_RESET1 ( 2 )
#define STATE_RESET2 ( 3 )
#define STATE_COMMAND ( 4 )
#define STATE_READROM ( 5 )

#define COMMAND_READROM ( 0x33 )

static struct ds2401_chip ds2401[ DS2401_MAXCHIP ];

static TIMER_CALLBACK( ds2401_reset )
{
	int which = param;
	struct ds2401_chip *c = &ds2401[ which ];

	verboselog( 1, "ds2401_reset(%d)\n", which );

	c->state = STATE_RESET;
	timer_adjust( c->timer, attotime_never, which, attotime_never );
}

static TIMER_CALLBACK( ds2401_tick )
{
	int which = param;
	struct ds2401_chip *c = &ds2401[ which ];

	switch( c->state )
	{
	case STATE_RESET1:
		verboselog( 2, "ds2401_tick(%d) state_reset1 %d\n", which, c->rx );
		c->tx = 0;
		c->state = STATE_RESET2;
		timer_adjust( c->timer, c->t_pdl, which, attotime_never );
		break;
	case STATE_RESET2:
		verboselog( 2, "ds2401_tick(%d) state_reset2 %d\n", which, c->rx );
		c->tx = 1;
		c->bit = 0;
		c->shift = 0;
		c->state = STATE_COMMAND;
		break;
	case STATE_COMMAND:
		verboselog( 2, "ds2401_tick(%d) state_command %d\n", which, c->rx );
		c->shift >>= 1;
		if( c->rx != 0 )
		{
			c->shift |= 0x80;
		}
		c->bit++;
		if( c->bit == 8 )
		{
			switch( c->shift )
			{
			case COMMAND_READROM:
				verboselog( 1, "ds2401_tick(%d) readrom\n", which );
				c->bit = 0;
				c->byte = 0;
				c->state = STATE_READROM;
				break;
			default:
				verboselog( 0, "ds2401_tick(%d) command not handled %02x\n", which, c->shift );
				c->state = STATE_IDLE;
				break;
			}
		}
		break;
	case STATE_READROM:
		c->tx = 1;
		if( c->byte == 8 )
		{
			verboselog( 1, "ds2401_tick(%d) readrom finished\n", which );
			c->state = STATE_IDLE;
		}
		else
		{
			verboselog( 2, "ds2401_tick(%d) readrom window closed\n", which );
		}
		break;
	default:
		verboselog( 0, "ds2401_tick(%d) state not handled: %d\n", which, c->state );
		break;
	}
}

void ds2401_init( int which, UINT8 *data )
{
	struct ds2401_chip *c = &ds2401[ which ];

	if( data == NULL )
	{
		data = auto_malloc( SIZE_DATA );
	}

	c->state = STATE_IDLE;
	c->bit = 0;
	c->byte = 0;
	c->shift = 0;
	c->rx = 1;
	c->tx = 1;
	c->data = data;
	c->t_samp = ATTOTIME_IN_USEC( 15 );
	c->t_rdv = ATTOTIME_IN_USEC( 15 );
	c->t_rstl = ATTOTIME_IN_USEC( 480 );
	c->t_pdh = ATTOTIME_IN_USEC( 15 );
	c->t_pdl = ATTOTIME_IN_USEC( 60 );

	state_save_register_item( "ds2401", which, c->state );
	state_save_register_item( "ds2401", which, c->bit );
	state_save_register_item( "ds2401", which, c->byte );
	state_save_register_item( "ds2401", which, c->shift );
	state_save_register_item( "ds2401", which, c->rx );
	state_save_register_item( "ds2401", which, c->tx );
	state_save_register_item_pointer( "ds2401", which, data, SIZE_DATA );

	c->timer = timer_alloc( ds2401_tick );
	c->reset_timer = timer_alloc( ds2401_reset );
}

void ds2401_write( int which, int data )
{
	struct ds2401_chip *c = &ds2401[ which ];

	verboselog( 1, "ds2401_write( %d, %d )\n", which, data );

	if( data == 0 && c->rx != 0 )
	{
		switch( c->state )
		{
		case STATE_IDLE:
			break;
		case STATE_COMMAND:
			verboselog( 2, "ds2401_write(%d) state_command\n", which );
			timer_adjust( c->timer, c->t_samp, which, attotime_never );
			break;
		case STATE_READROM:
			if( c->bit == 0 )
			{
				c->shift = c->data[ 7 - c->byte ];
				verboselog( 1, "ds2401_write(%d) <- data %02x\n", which, c->shift );
			}
			c->tx = c->shift & 1;
			c->shift >>= 1;
			c->bit++;
			if( c->bit == 8 )
			{
				c->bit = 0;
				c->byte++;
			}
			verboselog( 2, "ds2401_write(%d) state_readrom %d\n", which, c->tx );
			timer_adjust( c->timer, c->t_rdv, which, attotime_never );
			break;
		default:
			verboselog( 0, "ds2401_write(%d) state not handled: %d\n", which, c->state );
			break;
		}
		timer_adjust( c->reset_timer, c->t_rstl, which, attotime_never );
	}
	else if( data == 1 && c->rx == 0 )
	{
		switch( c->state )
		{
		case STATE_RESET:
			c->state = STATE_RESET1;
			timer_adjust( c->timer, c->t_pdh, which, attotime_never );
			break;
		}
		timer_adjust( c->reset_timer, attotime_never, which, attotime_never );
	}
	c->rx = data;
}

int ds2401_read( int which )
{
	struct ds2401_chip *c = &ds2401[ which ];

	verboselog( 2, "ds2401_read( %d ) %d\n", which, c->tx & c->rx );
	return c->tx & c->rx;
}

/*

app74.pdf

Under normal circumstances an ibutton will sample the line 30us after the falling edge of the start condition.
The internal time base of ibutton may deviate from its nominal value. The allowed tollerance band ranges from 15us to 60us.
This means that the actual slave sampling may occur anywhere from 15 and 60us after the start condition, which is a ratio of 1 to 4.
During this time frame the voltage on the data line must stay below Vilmax or above Vihmin.

In the 1-Wire system, the logical values 1 and 0 are represented by certain voltages in special waveforms.
The waveforms needed to write commands or data to ibuttons are called write-1 and write-0 time slots.
The duration of a low pulse to write a 1 must be shorter than 15us.
To write a 0, the duration of the low pulse must be at least 60us to cope with worst-case conditions.

The duration of the active part of a time slot can be extended beyond 60us.
The maximum extension is limited by the fact that a low pulse of a duration of at least eight active time slots ( 480us ) is defined as a Reset Pulse.
Allowing the same worst-case tolerance ratio, a low pulse of 120us might be sufficient for a reset.
This limits the extension of the active part of a time slot to a maximum of 120us to prevent misinterpretation with reset.

Commands and data are sent to ibuttons by combining write-0 and write-1 time slots.
To read data, the master has to generate read-data time slots to define the start condition of each bit.
The read-data time slots looks essentially the same as a write-1 time slot from the masters point of view.
Starting at the high-to-low transition, the ibuttons sends 1 bit of its addressed contents.
If the data bit is a 1, the ibutton leaves the pulse unchanged.
If the data bit is a 0, the ibutton will pull the data line low for 15us.
In this time frame data is valid for reading by the master.
The duration of the low pulse sent by the master should be a minimum of 1us with a maximum value as short as possible to maximize the master sampling window.

The Reset Pulse provides a clear starting condition that supersedes any time slot synchronisation.
It is defined as single low pulse of minimum duration of eight time slots or 480us followed by a Reset-high time tRSTH of another 480us.
After a Reset Pulse has been sent, the ibutton will wait for the time tPDH and then generate a Pulse-Presence Pulse of duration tPDL.
No other communication on the 1-Wire bus is allowed during tRSTH.

There are 1,000 microseconds in a millisecond, and 1,000 milliseconds in a second.
Thus, there are 1,000,000 microseconds in a second. Why is it "usec"?
The "u" is supposed to look like the Greek letter Mu that we use for "micro". .
*/