Browse Source

working with i2c ioctl

master
Olaf Rempel 10 years ago
commit
ccf0fbd21a
10 changed files with 755 additions and 0 deletions
  1. 7
    0
      .gitignore
  2. 9
    0
      Host/Makefile
  3. 175
    0
      Host/i2c-test.c
  4. 48
    0
      Makefile
  5. 249
    0
      alix-usv.c
  6. 32
    0
      alix-usv.h
  7. 61
    0
      eeprom.c
  8. 27
    0
      eeprom.h
  9. 141
    0
      usi-i2c-slave.c
  10. 6
    0
      usi-i2c-slave.h

+ 7
- 0
.gitignore View File

@@ -0,0 +1,7 @@
1
+*.o
2
+*.elf
3
+*.bin
4
+*.hex
5
+*.lst
6
+*.map
7
+Host/i2c-test

+ 9
- 0
Host/Makefile View File

@@ -0,0 +1,9 @@
1
+all:
2
+	gcc -O2 -Wall i2c-test.c -o i2c-test
3
+
4
+driver:
5
+	sudo modprobe i2c-dev
6
+
7
+perm:
8
+	sudo chgrp dialout /dev/i2c-0
9
+

+ 175
- 0
Host/i2c-test.c View File

@@ -0,0 +1,175 @@
1
+/*
2
+ * config file:
3
+ * - i2c device
4
+ * - i2c address
5
+ * - email-address
6
+ * - reporting config (when logging / mailing)
7
+ * - thresholds for do shutdown
8
+ *
9
+ * parameters:
10
+ * -d run as daemon
11
+ * -f foreground
12
+ * -v debug
13
+ * -c [idle|charge|test|poweroff] command (not with -d)
14
+ * --config
15
+ * --help
16
+ *
17
+ * in command mode try to use daemon first (via unix-socket)
18
+ * then fallback to i2c
19
+ */
20
+#include <stdint.h>
21
+#include <stdio.h>
22
+#include <stdlib.h>
23
+#include <unistd.h>
24
+#include <string.h>
25
+
26
+#include <sys/ioctl.h>
27
+#include <sys/types.h>
28
+#include <sys/stat.h>
29
+#include <fcntl.h>
30
+
31
+#include <linux/i2c.h>
32
+#include <linux/i2c-dev.h>
33
+
34
+struct read_data {
35
+	uint8_t sys_state;
36
+
37
+	int16_t adc_current;
38
+	int16_t adc_ubat;
39
+	int16_t adc_uin;
40
+
41
+	int16_t uin_loss;
42
+	int16_t uin_restore;
43
+	int16_t ubat_full;
44
+	int16_t ubat_low;
45
+	int16_t ubat_critical;
46
+	int16_t ibat_full;
47
+	uint16_t crc16;
48
+} __attribute__((__packed__));
49
+
50
+struct write_data {
51
+	uint8_t sys_state;
52
+
53
+	int16_t uin_loss;
54
+	int16_t uin_restore;
55
+	int16_t ubat_full;
56
+	int16_t ubat_low;
57
+	int16_t ubat_critical;
58
+	int16_t ibat_full;
59
+	uint16_t crc16;
60
+} __attribute__((__packed__));
61
+
62
+enum {
63
+	STATE_IDLE	= 0x01,
64
+	STATE_TEST	= 0x02,
65
+	STATE_CHARGE	= 0x04,
66
+	STATE_DISCHARGE	= 0x08,
67
+	STATE_POWEROFF	= 0x10,
68
+};
69
+
70
+static char * state2str(uint8_t state)
71
+{
72
+	switch (state) {
73
+	case STATE_IDLE:
74
+		return "STATE_IDLE";
75
+
76
+	case STATE_TEST:
77
+		return "STATE_TEST";
78
+
79
+	case STATE_CHARGE:
80
+		return "STATE_CHARGE";
81
+
82
+	case STATE_DISCHARGE:
83
+		return "STATE_DISCHARGE";
84
+
85
+	case STATE_POWEROFF:
86
+		return "STATE_POWEROFF";
87
+
88
+	default:
89
+		return "<UNKNOWN>";
90
+	}
91
+}
92
+
93
+static int i2c_open(const char *path)
94
+{
95
+	int fd = open(path, O_RDWR);
96
+	if (fd < 0) {
97
+		perror("open()");
98
+		return -1;
99
+	}
100
+
101
+	unsigned long funcs;
102
+	if (ioctl(fd, I2C_FUNCS, &funcs)) {
103
+		perror("ioctl(I2C_FUNCS)");
104
+		close(fd);
105
+		return -1;
106
+	}
107
+
108
+	if (!(funcs & I2C_FUNC_I2C)) {
109
+		fprintf(stderr, "I2C_FUNC_I2C not supported!\n");
110
+		close(fd);
111
+		return -1;
112
+	}
113
+
114
+	return fd;
115
+}
116
+
117
+static int i2c_setaddress(int fd, int address)
118
+{
119
+	if (ioctl(fd, I2C_SLAVE, address) < 0) {
120
+		perror("ioctl(I2C_SLAVE)");
121
+		close(fd);
122
+		return -1;
123
+	}
124
+	return 0;
125
+}
126
+
127
+int main(int argc, char *argv[])
128
+{
129
+	int fd = i2c_open("/dev/i2c-0");
130
+	if (fd < 0)
131
+		exit(-1);
132
+
133
+	if (i2c_setaddress(fd, 0x10) < 0)
134
+		exit(-1);
135
+
136
+	int cnt = 0;
137
+
138
+	while (1) {
139
+		struct read_data rdbuf;
140
+		memset(&rdbuf, 0, sizeof(rdbuf));
141
+
142
+		int ret = read(fd, &rdbuf, sizeof(rdbuf));
143
+		if (ret <= 0)
144
+			perror("read()");
145
+
146
+		printf("state:0x%02x I:%5dmA Ubat:%5dmV Usup:%5dmV (%s) [%5d,%5d,%5d,%5d,%5d,%5d,0x%04x]\n",
147
+			rdbuf.sys_state, rdbuf.adc_current, rdbuf.adc_ubat,
148
+			rdbuf.adc_uin, state2str(rdbuf.sys_state),
149
+			rdbuf.uin_loss, rdbuf.uin_restore, rdbuf.ubat_full,
150
+			rdbuf.ubat_low, rdbuf.ubat_critical, rdbuf.ibat_full,
151
+			rdbuf.crc16);
152
+
153
+		sleep(1);
154
+		cnt++;
155
+
156
+		if (cnt == 5) {
157
+			struct write_data wrbuf = {
158
+				.sys_state = STATE_CHARGE,
159
+				.uin_loss = 12000,
160
+				.uin_restore = 14000,
161
+				.ubat_full = 13300,
162
+				.ubat_low = 12000,
163
+				.ubat_critical = 10800,
164
+				.ibat_full = 150,
165
+				.crc16 = 0x0000,
166
+			};
167
+
168
+//			write (fd, &wrbuf, sizeof(wrbuf));
169
+			write (fd, &wrbuf, 1);
170
+		}
171
+	}
172
+	close(fd);
173
+
174
+	return 0;
175
+}

+ 48
- 0
Makefile View File

@@ -0,0 +1,48 @@
1
+PRG            = alix-usv
2
+OBJ            = alix-usv.o eeprom.o usi-i2c-slave.o
3
+MCU_TARGET     = attiny84
4
+OPTIMIZE       = -Os
5
+
6
+DEFS           =
7
+LIBS           =
8
+
9
+# Override is only needed by avr-lib build system.
10
+override CFLAGS        = -g -Wall $(OPTIMIZE) -mmcu=$(MCU_TARGET) $(DEFS)
11
+override LDFLAGS       = -Wl,-Map,$(PRG).map
12
+
13
+CC             = avr-gcc
14
+OBJCOPY        = avr-objcopy
15
+OBJDUMP        = avr-objdump
16
+SIZE           = avr-size
17
+
18
+all: $(PRG).elf lst text
19
+	$(SIZE) -x -A $(PRG).elf
20
+
21
+$(PRG).elf: $(OBJ)
22
+	$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ $(LIBS)
23
+
24
+clean:
25
+	rm -rf *.o *.lst *.map $(PRG).elf *.hex *.bin
26
+
27
+lst:  $(PRG).lst
28
+
29
+%.lst: %.elf
30
+	$(OBJDUMP) -h -S $< > $@
31
+
32
+text: hex bin
33
+
34
+hex:  $(PRG).hex
35
+bin:  $(PRG).bin
36
+
37
+%.hex: %.elf
38
+	$(OBJCOPY) -j .text -j .data -O ihex $< $@
39
+
40
+%.bin: %.elf
41
+	$(OBJCOPY) -j .text -j .data -O binary $< $@
42
+
43
+install: text
44
+	avrdude -c dragon_isp -P usb -p t84 -U flash:w:$(PRG).hex
45
+
46
+# no self programming, 2.7V BOD, 8MHz internal RC Osz.
47
+fuses:
48
+	avrdude -c dragon_isp -P usb -p t84 -U lfuse:w:0xc2:m -U hfuse:w:0xdd:m -U efuse:w:0xff:m

+ 249
- 0
alix-usv.c View File

@@ -0,0 +1,249 @@
1
+/***************************************************************************
2
+ *   Copyright (C) 01/2009 by Olaf Rempel                                  *
3
+ *   razzor@kopf-tisch.de                                                  *
4
+ *                                                                         *
5
+ *   This program is free software; you can redistribute it and/or modify  *
6
+ *   it under the terms of the GNU General Public License as published by  *
7
+ *   the Free Software Foundation; version 2 of the License,               *
8
+ *                                                                         *
9
+ *   This program is distributed in the hope that it will be useful,       *
10
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
11
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
12
+ *   GNU General Public License for more details.                          *
13
+ *                                                                         *
14
+ *   You should have received a copy of the GNU General Public License     *
15
+ *   along with this program; if not, write to the                         *
16
+ *   Free Software Foundation, Inc.,                                       *
17
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
18
+ ***************************************************************************/
19
+#include <avr/io.h>
20
+#include <avr/interrupt.h>
21
+
22
+#include "alix-usv.h"
23
+#include "eeprom.h"
24
+#include "usi-i2c-slave.h"
25
+
26
+extern volatile struct ee_param params;
27
+
28
+static const uint8_t adc_mux[] = {
29
+	CH_CURRENT_P, CH_CURRENT_N,
30
+	CH_VOLTAGE_BAT, CH_VOLTAGE_SUP,
31
+};
32
+
33
+static volatile uint8_t adc_update;
34
+static volatile int16_t adc_value[3];
35
+
36
+enum {
37
+	ADC_CURRENT = 0,
38
+	ADC_UBAT = 1,
39
+	ADC_UIN = 2,
40
+};
41
+
42
+static volatile uint8_t sys_state;
43
+enum {
44
+	STATE_IDLE	= 0x01,
45
+	STATE_TEST	= 0x02,
46
+	STATE_CHARGE	= 0x04,
47
+	STATE_DISCHARGE	= 0x08,
48
+	STATE_POWEROFF	= 0x10,
49
+};
50
+
51
+static uint32_t adc_buf[4];
52
+ISR(ADC_vect)
53
+{
54
+	static uint8_t cnt;
55
+
56
+	/* use result of second conversion (switching ADC-gain needs time..) */
57
+	if (cnt & 0x01) {
58
+		/* moving average filter */
59
+		uint8_t ch = (cnt >> 1);
60
+		adc_buf[ch] = ((adc_buf[ch] * 7) + ((uint32_t)ADC << 8)) /8;
61
+		uint16_t value = (adc_buf[ch] >> 8) + ((adc_buf[ch] & 0x80) ? 1 : 0);
62
+
63
+		if (ch == 0 && (adc_buf[0] >= adc_buf[1])) {
64
+			/* charging: positive current = ADC * 1.25 */
65
+			adc_value[ADC_CURRENT] = value + (value >> 2);
66
+
67
+		} else if (ch == 1 && (adc_buf[0] < adc_buf[1])) {
68
+			/* discharging: negative current = ADC * -1.25 */
69
+			adc_value[ADC_CURRENT] = -(value + (value >> 2));
70
+
71
+		} else if (ch == 2) {
72
+			/* Ubat = (ADC * 21.28) - (Ishunt * 0.1) */
73
+			adc_value[ADC_UBAT] = (value * 21) + ((value * 9) >> 5) - (adc_value[ADC_CURRENT] / 10);
74
+
75
+		} else if (ch == 3) {
76
+			/* Uin = ADC * 21.28 */
77
+			adc_value[ADC_UIN] = (value * 21) + ((value * 9) >> 5);
78
+
79
+			/* all values updated */
80
+			adc_update = 1;
81
+		}
82
+	}
83
+
84
+	/* trigger next conversion */
85
+	cnt = (cnt +1) & 0x07;
86
+	ADMUX = adc_mux[cnt >> 1];
87
+	ADCSRA |= (1<<ADSC);
88
+}
89
+
90
+void usi_write(uint8_t data, uint8_t bcnt)
91
+{
92
+	if (bcnt == 0) {
93
+		/* poweroff is always allowed */
94
+		if (data & STATE_POWEROFF)
95
+			sys_state = data;
96
+
97
+		/* discharge cmd is never allowed */
98
+		else if (data & STATE_DISCHARGE)
99
+			return;
100
+
101
+		/* during discharge nothing is allowed */
102
+		else if (sys_state != STATE_DISCHARGE)
103
+			sys_state = data;
104
+
105
+	} else {
106
+		uint8_t index = (bcnt -1);
107
+		uint8_t *ptr = (uint8_t *)&params;
108
+		if (index < sizeof(params))
109
+			ptr[index] = data;
110
+
111
+		if (index == (sizeof(params) -1))
112
+			write_parameters();
113
+	}
114
+}
115
+
116
+uint8_t usi_read(uint8_t bcnt)
117
+{
118
+	static int16_t adc_copy[3];
119
+
120
+	/* first byte read is sys_state */
121
+	if (bcnt == 0) {
122
+		/* take a snapshot in interrupt-mode */
123
+		adc_copy[ADC_CURRENT] = adc_value[ADC_CURRENT];
124
+		adc_copy[ADC_UBAT] = adc_value[ADC_UBAT];
125
+		adc_copy[ADC_UIN] = adc_value[ADC_UIN];
126
+		return sys_state;
127
+	}
128
+
129
+	uint8_t index = (bcnt -1);
130
+	uint8_t *ptr = (uint8_t *)adc_copy;
131
+
132
+	/* Current and Voltages (in mA/mV) */
133
+	if (index < sizeof(adc_copy))
134
+		return ptr[index];
135
+
136
+	index -= sizeof(adc_copy);
137
+	ptr = (uint8_t *)&params;
138
+
139
+	/* eeprom parameters (no snapshot needed, changed only in usi_write) */
140
+	if (index < sizeof(params))
141
+		return ptr[index];
142
+
143
+	return 0xFF;
144
+}
145
+
146
+/*
147
+ * the watchdog timer remains active even after a
148
+ * system reset. So disable it as soon as possible.
149
+ * automagically called on startup
150
+ */
151
+void disable_wdt_timer(void) __attribute__((naked, section(".init3")));
152
+void disable_wdt_timer(void)
153
+{
154
+	MCUSR = 0;
155
+	WDTCSR = (1<<WDCE) | (1<<WDE);
156
+	WDTCSR = (0<<WDE);
157
+}
158
+
159
+int main(void)
160
+{
161
+	PORTA = I2C_SCL | I2C_SDA;
162
+	DDRA = I2C_SCL | LED_GN;
163
+
164
+	DDRB = EN_POWER | EN_CHARGER | EN_TEST;
165
+
166
+	/* Disable Digital Inputs */
167
+	DIDR0 = AIN_REF | AIN_CURRENT_N | AIN_CURRENT_P | AIN_VOLTAGE_BAT | AIN_VOLTAGE_SUP;
168
+
169
+	/* ADC: freerunning with F_CPU/128 */
170
+	ADMUX = adc_mux[0];
171
+	ADCSRA = (1<<ADEN) | (1<<ADSC) | (1<<ADIF) | (1<<ADIE) | (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0);
172
+
173
+	/* init TWI statemachine */
174
+	usi_statemachine();
175
+
176
+	/* read statemachine parameters from eeprom */
177
+	read_parameters();
178
+
179
+	sei();
180
+
181
+	/* enable power */
182
+	sys_state = STATE_IDLE;
183
+	PORTB = EN_POWER;
184
+
185
+	/* wait 1s for moving average filters */
186
+	uint8_t i;
187
+	for (i = 0; i < 50; i++)
188
+		_delay_ms(20);
189
+
190
+	while (1) {
191
+		while (!adc_update);
192
+		adc_update = 0;
193
+
194
+		/* disable interrupts while using adc_value and params */
195
+		cli();
196
+
197
+		/* loss of external power => discharge */
198
+		if (sys_state & (STATE_IDLE | STATE_TEST | STATE_CHARGE))
199
+			if (adc_value[ADC_UIN] < params.uin_loss)
200
+				sys_state = STATE_DISCHARGE;
201
+
202
+		/* external power restored => charge */
203
+		if (sys_state & (STATE_DISCHARGE | STATE_POWEROFF))
204
+			if (adc_value[ADC_UIN] > params.uin_restore)
205
+				sys_state = STATE_CHARGE;
206
+
207
+		/* battery is low => charge */
208
+		if (sys_state & (STATE_IDLE | STATE_TEST))
209
+			if (adc_value[ADC_UBAT] < params.ubat_low)
210
+				sys_state = STATE_CHARGE;
211
+
212
+		switch (sys_state) {
213
+		case STATE_IDLE:
214
+			PORTB = EN_POWER;
215
+			break;
216
+
217
+		case STATE_TEST:
218
+			PORTB = EN_POWER | EN_TEST;
219
+			break;
220
+
221
+		case STATE_CHARGE:
222
+			PORTB = EN_POWER | EN_CHARGER;
223
+
224
+			/* battery is full => idle */
225
+			if (adc_value[ADC_UBAT] > params.ubat_full && adc_value[ADC_CURRENT] < params.ibat_full)
226
+				sys_state = STATE_IDLE;
227
+			break;
228
+
229
+		case STATE_DISCHARGE:
230
+			PORTB = EN_POWER;
231
+
232
+			/* battery is critical low => poweroff */
233
+			if (adc_value[ADC_UBAT] < params.ubat_critical)
234
+				sys_state = STATE_POWEROFF;
235
+			break;
236
+
237
+		case STATE_POWEROFF:
238
+			PORTB = 0x00;
239
+			break;
240
+
241
+		default:
242
+			sys_state = STATE_IDLE;
243
+			break;
244
+		}
245
+
246
+		/* re-enable interrupts */
247
+		sei();
248
+	}
249
+}

+ 32
- 0
alix-usv.h View File

@@ -0,0 +1,32 @@
1
+#ifndef _MAIN_H_
2
+#define _MAIN_H_
3
+
4
+#include <avr/io.h>
5
+
6
+#define F_CPU 8000000
7
+#include <util/delay.h>
8
+
9
+#define EN_POWER	(1<<PORTB0)
10
+#define EN_CHARGER	(1<<PORTB1)
11
+#define EN_TEST		(1<<PORTB2)
12
+
13
+#define AIN_REF		(1<<PINA0)
14
+#define AIN_CURRENT_N	(1<<PINA1)
15
+#define AIN_CURRENT_P	(1<<PINA2)
16
+#define AIN_VOLTAGE_BAT	(1<<PINA3)
17
+#define I2C_SCL		(1<<PORTA4)
18
+#define LED_GN		(1<<PORTA5)
19
+#define I2C_SDA		(1<<PORTA6)
20
+#define AIN_VOLTAGE_SUP	(1<<PINA7)
21
+
22
+#define I2C_ADDRESS	(0x10 << 1)
23
+
24
+#define CH_CURRENT_P	((1<<REFS0) | 0x0d)	/* current A1->A2, 20x gain */
25
+#define CH_CURRENT_N	((1<<REFS0) | 0x2d)	/* current A2->A1, 20x gain */
26
+#define CH_VOLTAGE_BAT	((1<<REFS0) | 0x03)	/* voltage A3->gnd, 1x gain */
27
+#define CH_VOLTAGE_SUP	((1<<REFS0) | 0x07)	/* voltage A7->gnd, 1x gain */
28
+
29
+void usi_write(uint8_t data, uint8_t bcnt);
30
+uint8_t usi_read(uint8_t bcnt);
31
+
32
+#endif // _USI_I2C_SLAVE_H_

+ 61
- 0
eeprom.c View File

@@ -0,0 +1,61 @@
1
+/***************************************************************************
2
+ *   Copyright (C) 02/2008 by Olaf Rempel                                  *
3
+ *   razzor@kopf-tisch.de                                                  *
4
+ *                                                                         *
5
+ *   This program is free software; you can redistribute it and/or modify  *
6
+ *   it under the terms of the GNU General Public License as published by  *
7
+ *   the Free Software Foundation; version 2 of the License,               *
8
+ *                                                                         *
9
+ *   This program is distributed in the hope that it will be useful,       *
10
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
11
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
12
+ *   GNU General Public License for more details.                          *
13
+ *                                                                         *
14
+ *   You should have received a copy of the GNU General Public License     *
15
+ *   along with this program; if not, write to the                         *
16
+ *   Free Software Foundation, Inc.,                                       *
17
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
18
+ ***************************************************************************/
19
+#include <avr/eeprom.h>
20
+#include <util/crc16.h>
21
+#include "eeprom.h"
22
+
23
+static const struct ee_param defaults = DEFAULT_PARAMETERS;
24
+
25
+struct ee_param params;
26
+struct ee_param params_in_eeprom EEMEM = DEFAULT_PARAMETERS;
27
+
28
+void write_parameters(void)
29
+{
30
+	uint8_t i;
31
+	uint16_t crc = 0x0000;
32
+	uint8_t *tmp = (uint8_t *)&params;
33
+	for (i = 0; i < sizeof(struct ee_param) -2; i++)
34
+		crc = _crc_ccitt_update(crc, *tmp++);
35
+
36
+	params.crc16 = crc;
37
+	eeprom_write_block(&params, &params_in_eeprom, sizeof(struct ee_param));
38
+}
39
+
40
+uint8_t read_parameters(void)
41
+{
42
+	eeprom_read_block(&params, &params_in_eeprom, sizeof(struct ee_param));
43
+
44
+	uint8_t i;
45
+	uint16_t crc = 0x0000;
46
+	uint8_t *tmp = (uint8_t *)&params;
47
+	for (i = 0; i < sizeof(struct ee_param); i++)
48
+		crc = _crc_ccitt_update(crc, *tmp++);
49
+
50
+	if (crc != 0x0000) {
51
+		i = sizeof(struct ee_param);
52
+		uint8_t *src = (uint8_t *)&defaults;
53
+		uint8_t *dst = (uint8_t *)&params;
54
+		while (i--)
55
+			*dst++ = *src++;
56
+
57
+		write_parameters();
58
+		return 1;
59
+	}
60
+	return 0;
61
+}

+ 27
- 0
eeprom.h View File

@@ -0,0 +1,27 @@
1
+#ifndef _EEPROM_PARAMETERS_H_
2
+#define _EEPROM_PARAMETERS_H_
3
+
4
+struct ee_param {
5
+	int16_t uin_loss;
6
+	int16_t uin_restore;
7
+	int16_t ubat_full;
8
+	int16_t ubat_low;
9
+	int16_t ubat_critical;
10
+	int16_t ibat_full;
11
+
12
+	uint16_t crc16;
13
+};
14
+
15
+#define DEFAULT_PARAMETERS { \
16
+	.uin_loss = 12000, \
17
+	.uin_restore = 14000, \
18
+	.ubat_full = 13300, \
19
+	.ubat_low = 12000, \
20
+	.ubat_critical = 11000, \
21
+	.ibat_full = 150, \
22
+};
23
+
24
+uint8_t read_parameters(void);
25
+void write_parameters(void);
26
+
27
+#endif

+ 141
- 0
usi-i2c-slave.c View File

@@ -0,0 +1,141 @@
1
+/***************************************************************************
2
+ *   Copyright (C) 01/2009 by Olaf Rempel                                  *
3
+ *   razzor@kopf-tisch.de                                                  *
4
+ *                                                                         *
5
+ *   This program is free software; you can redistribute it and/or modify  *
6
+ *   it under the terms of the GNU General Public License as published by  *
7
+ *   the Free Software Foundation; version 2 of the License,               *
8
+ *                                                                         *
9
+ *   This program is distributed in the hope that it will be useful,       *
10
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
11
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
12
+ *   GNU General Public License for more details.                          *
13
+ *                                                                         *
14
+ *   You should have received a copy of the GNU General Public License     *
15
+ *   along with this program; if not, write to the                         *
16
+ *   Free Software Foundation, Inc.,                                       *
17
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
18
+ ***************************************************************************/
19
+#include <avr/io.h>
20
+#include <avr/interrupt.h>
21
+
22
+#include "alix-usv.h"
23
+#include "usi-i2c-slave.h"
24
+
25
+#define USI_MASK	0x0F
26
+#define USI_MASK_ACKBIT	0x10	/* one (ack)bit / 8bits */
27
+#define USI_MASK_OUTPUT	0x20	/* SDA is output / input */
28
+
29
+#define USI_RESET	0x00	/* wait for Start Condition */
30
+#define USI_START	0x01	/* Start detected */
31
+#define USI_SLA		0x02	/* wait for Slave Address */
32
+#define USI_SLAW_ACK	0x03	/* ACK Slave Address + Write (Master writes) */
33
+#define USI_SLAR_ACK	0x04	/* ACK Slave Address + Read (Master reads) */
34
+#define USI_DATW	0x05	/* receive Data */
35
+#define USI_DATW_ACK	0x06	/* transmit ACK for received Data */
36
+#define USI_DATR	0x07	/* transmit Data */
37
+#define USI_DATR_ACK	0x08	/* receive ACK for transmitted Data */
38
+
39
+static uint8_t usi_state;
40
+
41
+void usi_statemachine(void)
42
+{
43
+	static uint8_t bcnt;
44
+
45
+	uint8_t tmp = USIDR;
46
+	uint8_t state = usi_state & USI_MASK;
47
+
48
+	/* Start Condition detected */
49
+	if (state == USI_START) {
50
+		usi_state = USI_SLA;
51
+
52
+	/* Slave Address received => prepare ACK/NACK */
53
+	} else if (state == USI_SLA) {
54
+		bcnt = 0;
55
+		if (tmp == (I2C_ADDRESS | 0x00))
56
+			usi_state = USI_SLAW_ACK | USI_MASK_ACKBIT | USI_MASK_OUTPUT;
57
+
58
+		else if (tmp == (I2C_ADDRESS | 0x01))
59
+			usi_state = USI_SLAR_ACK | USI_MASK_ACKBIT | USI_MASK_OUTPUT;
60
+
61
+		else
62
+			usi_state = USI_RESET;
63
+
64
+	/* ACK after SLA+W / Data received => prepare data receive */
65
+	} else if (state == USI_SLAW_ACK || state == USI_DATW_ACK) {
66
+		usi_state = USI_DATW;
67
+
68
+	/* Data received => prepare ACK transmit */
69
+	} else if (state == USI_DATW) {
70
+		usi_write(tmp, bcnt++);
71
+		usi_state = USI_DATW_ACK | USI_MASK_ACKBIT | USI_MASK_OUTPUT;
72
+
73
+	/* ACK after SLA+R transmitted / ACK after Data received => prepare data transmit */
74
+	} else if (state == USI_SLAR_ACK || (state == USI_DATR_ACK && tmp == 0x00)) {
75
+		USIDR = usi_read(bcnt++);
76
+		usi_state = USI_DATR | USI_MASK_OUTPUT;
77
+
78
+	/* Data transmitted => prepare ACK receive */
79
+	} else if (state == USI_DATR) {
80
+		usi_state = USI_DATR_ACK | USI_MASK_ACKBIT;
81
+
82
+	/* NACK after Data received / default => go idle */
83
+	} else {
84
+		usi_state = USI_RESET;
85
+	}
86
+
87
+	state = usi_state & USI_MASK;
88
+	if (state == USI_RESET) {
89
+		/* input */
90
+		DDRA &= ~I2C_SDA;
91
+
92
+		/* Enable StartCondition Interrupt, TWI Mode, count both edges */
93
+		USICR = (1<<USISIE) | (1<<USIWM1) | (1<<USICS1);
94
+
95
+		/* Clear Interrupt Flags, Clear Stop Condition Flag, wait for 2 edges */
96
+		USISR = (1<<USISIF) | (1<<USIOIF) | (1<<USIPF) | ((16 -2)<<USICNT0);
97
+
98
+	} else if (state == USI_SLA) {
99
+		/* Enable Overflow Interrupt, TWI Mode, extend SCL on Overflow */
100
+		USICR = (1<<USISIE) | (1<<USIOIE) | (1<<USIWM1) | (1<<USIWM0) | (1<<USICS1);
101
+
102
+		/* Clear Interrupt Flags, Clear Stop Condition Flag, wait for 16 edges */
103
+		USISR = (1<<USISIF) | (1<<USIOIF) | (1<<USIPF) | ((16 -16)<<USICNT0);
104
+
105
+	} else {
106
+		if (usi_state & USI_MASK_OUTPUT) {
107
+			DDRA |= I2C_SDA;
108
+
109
+		} else {
110
+			DDRA &= ~I2C_SDA;
111
+		}
112
+
113
+		if (usi_state & USI_MASK_ACKBIT) {
114
+			USIDR = 0x00;
115
+			USISR = (1<<USIOIF) | ((16 -2)<<USICNT0);
116
+
117
+		} else {
118
+			USISR = (1<<USIOIF) | ((16 -16)<<USICNT0);
119
+		}
120
+	}
121
+}
122
+
123
+ISR(USI_START_vect)
124
+{
125
+	usi_state = USI_START;
126
+
127
+	/* check for STOP Condition */
128
+	while (PINA & I2C_SCL) {
129
+		if (PINA & I2C_SDA) {
130
+			usi_state = USI_RESET;
131
+			break;
132
+		}
133
+	}
134
+
135
+	usi_statemachine();
136
+}
137
+
138
+ISR(USI_OVF_vect)
139
+{
140
+	usi_statemachine();
141
+}

+ 6
- 0
usi-i2c-slave.h View File

@@ -0,0 +1,6 @@
1
+#ifndef _USI_I2C_SLAVE_H_
2
+#define _USI_I2C_SLAVE_H_
3
+
4
+void usi_statemachine(void);
5
+
6
+#endif // _USI_I2C_SLAVE_H_

Loading…
Cancel
Save