mingw: make simavr compilable with MinGW
[simavr] / examples / parts / i2c_eeprom.c
1 /*
2         i2c_eeprom.c
3
4         Copyright 2008, 2009 Michel Pollet <buserror@gmail.com>
5
6         This file is part of simavr.
7
8         simavr is free software: you can redistribute it and/or modify
9         it under the terms of the GNU General Public License as published by
10         the Free Software Foundation, either version 3 of the License, or
11         (at your option) any later version.
12
13         simavr is distributed in the hope that it will be useful,
14         but WITHOUT ANY WARRANTY; without even the implied warranty of
15         MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16         GNU General Public License for more details.
17
18         You should have received a copy of the GNU General Public License
19         along with simavr.  If not, see <http://www.gnu.org/licenses/>.
20  */
21
22
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <string.h>
26 #include "sim_avr.h"
27 #include "avr_twi.h"
28 #include "i2c_eeprom.h"
29
30 /*
31  * called when a RESET signal is sent
32  */
33 static void
34 i2c_eeprom_in_hook(
35                 struct avr_irq_t * irq,
36                 uint32_t value,
37                 void * param)
38 {
39         i2c_eeprom_t * p = (i2c_eeprom_t*)param;
40         avr_twi_msg_irq_t v;
41         v.u.v = value;
42
43         /*
44          * If we receive a STOP, check it was meant to us, and reset the transaction
45          */
46         if (v.u.twi.msg & TWI_COND_STOP) {
47                 if (p->selected) {
48                         // it was us !
49                         if (p->verbose)
50                                 printf("eeprom received stop\n");
51                 }
52                 p->selected = 0;
53                 p->index = 0;
54                 p->reg_addr = 0;
55         }
56         /*
57          * if we receive a start, reset status, check if the slave address is
58          * meant to be us, and if so reply with an ACK bit
59          */
60         if (v.u.twi.msg & TWI_COND_START) {
61                 p->selected = 0;
62                 p->index = 0;
63                 if ((p->addr_base & p->addr_mask) == (v.u.twi.addr & p->addr_mask)) {
64                         // it's us !
65                         p->selected = v.u.twi.addr;
66                         avr_raise_irq(p->irq + TWI_IRQ_MISO,
67                                         avr_twi_irq_msg(TWI_COND_ACK, p->selected, 1));
68                 }
69         }
70         /*
71          * If it's a data transaction, first check it is meant to be us (we
72          * received the correct address and are selected)
73          */
74         if (p->selected) {
75                 /*
76                  * This is a write transaction, first receive as many address bytes
77                  * as we need, then set the address register, then start
78                  * writing data,
79                  */
80                 if (v.u.twi.msg & TWI_COND_WRITE) {
81                         // address size is how many bytes we use for address register
82                         avr_raise_irq(p->irq + TWI_IRQ_MISO,
83                                         avr_twi_irq_msg(TWI_COND_ACK, p->selected, 1));
84                         int addr_size = p->size > 256 ? 2 : 1;
85                         if (p->index < addr_size) {
86                                 p->reg_addr |= (v.u.twi.data << (p->index * 8));
87                                 if (p->index == addr_size-1) {
88                                         // add the slave address, if relevant
89                                         p->reg_addr += ((p->selected & 1) - p->addr_base) << 7;
90                                         if (p->verbose)
91                                                 printf("eeprom set address to 0x%04x\n", p->reg_addr);
92                                 }
93                         } else {
94                                 if (p->verbose)
95                                         printf("eeprom WRITE data 0x%04x: %02x\n", p->reg_addr, v.u.twi.data);
96                                 p->ee[p->reg_addr++] = v.u.twi.data;
97                         }
98                         p->reg_addr &= (p->size -1);
99                         p->index++;
100                 }
101                 /*
102                  * It's a read transaction, just send the next byte back to the master
103                  */
104                 if (v.u.twi.msg & TWI_COND_READ) {
105                         if (p->verbose)
106                                 printf("eeprom READ data 0x%04x: %02x\n", p->reg_addr, p->ee[p->reg_addr]);
107                         uint8_t data = p->ee[p->reg_addr++];
108                         avr_raise_irq(p->irq + TWI_IRQ_MISO,
109                                         avr_twi_irq_msg(TWI_COND_READ, p->selected, data));
110                         p->reg_addr &= (p->size -1);
111                         p->index++;
112                 }
113         }
114 }
115
116 static const char * _ee_irq_names[2] = {
117                 [TWI_IRQ_MISO] = "8>eeprom.out",
118                 [TWI_IRQ_MOSI] = "32<eeprom.in",
119 };
120
121 void
122 i2c_eeprom_init(
123                 struct avr_t * avr,
124                 i2c_eeprom_t * p,
125                 uint8_t addr,
126                 uint8_t mask,
127                 uint8_t * data,
128                 size_t size)
129 {
130         memset(p, 0, sizeof(*p));
131         memset(p->ee, 0xff, sizeof(p->ee));
132         p->irq = avr_alloc_irq(&avr->irq_pool, 0, 2, _ee_irq_names);
133         avr_irq_register_notify(p->irq + TWI_IRQ_MOSI, i2c_eeprom_in_hook, p);
134
135         p->size = size > sizeof(p->ee) ? sizeof(p->ee) : size;
136         if (data)
137                 memcpy(p->ee, data, p->size);
138 }
139
140 void
141 i2c_eeprom_attach(
142                 struct avr_t * avr,
143                 i2c_eeprom_t * p,
144                 uint32_t i2c_irq_base )
145 {
146         // "connect" the IRQs of the eeprom to the TWI/i2c master of the AVR
147         avr_connect_irq(
148                 p->irq + TWI_IRQ_MISO,
149                 avr_io_getirq(avr, i2c_irq_base, TWI_IRQ_MISO));
150         avr_connect_irq(
151                 avr_io_getirq(avr, i2c_irq_base, TWI_IRQ_MOSI),
152                 p->irq + TWI_IRQ_MOSI );
153 }