altos: Add attiny architecture files
[fw/altos] / src / attiny / ao_i2c_attiny.c
1 /*
2  * Copyright © 2011 Keith Packard <keithp@keithp.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; version 2 of the License.
7  *
8  * This program is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License along
14  * with this program; if not, write to the Free Software Foundation, Inc.,
15  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
16  */
17
18 #include <ao.h>
19
20 /*
21  * ATtiny USI as an I2C interface
22  */
23
24 #define I2C_USICR ((0 << USISIE) |      /* No start condition interrupt */ \
25                    (0 << USIOIE) |      /* No counter overflow interrupt */ \
26                    (1 << USIWM1) |      /* Two-wire mode */             \
27                    (0 << USIWM0) |      /*   ... */                     \
28                    (1 << USICS1) |      /* Software clock strobe */     \
29                    (0 << USICS0) |      /*   ... */                     \
30                    (1 << USICLK))       /*   ... */                     \
31
32 #define I2C_USICR_TICK (I2C_USICR | (1 << USITC))       /* Toggle the clock on every write */
33
34 #define I2C_USISR_1BIT  ((1<<USISIF)|   /* Clear start condition flag */ \
35                          (1<<USIOIF)|   /* Clear overflow flag */       \
36                          (1<<USIPF)|    /* Clear stop condition flag */ \
37                          (1<<USIDC)|    /* Clear data collision flag */ \
38                          (0xE<<USICNT0)) /* Set counter value to 0xe */
39
40 #define I2C_USISR_8BIT  ((1<<USISIF)|   /* Clear start condition flag */ \
41                          (1<<USIOIF)|   /* Clear overflow flag */       \
42                          (1<<USIPF)|    /* Clear stop condition flag */ \
43                          (1<<USIDC)|    /* Clear data collision flag */ \
44                          (0x0<<USICNT0)) /* Set counter value to 0 */
45
46 #define T2_TWI    5             /* >4.7μs */
47 #define T4_TWI    4             /* >4.0μs */
48
49 static inline void ao_i2c_transfer(uint8_t sr)
50 {
51         USISR = sr;
52         for (;;) {
53                 ao_delay_us(T2_TWI);
54
55                 /* Clock high */
56                 USICR = I2C_USICR_TICK;
57
58                 /* Wait for clock high (clock stretching) */
59                 ao_delay_us(T4_TWI);
60                 while(!(I2C_PIN & (1<<I2C_PIN_SCL)))
61                         ;
62
63                 /* Clock low */
64                 USICR = I2C_USICR_TICK;
65
66                 /* Check for transfer complete */
67                 if (USISR & (1 << USIOIF))
68                         break;
69         }
70         ao_delay_us(T2_TWI);
71 }
72
73 static inline uint8_t ao_i2c_get_byte(uint8_t sr)
74 {
75         uint8_t ret;
76
77         /* Set SDA to input */
78         I2C_DIR &= ~(1<<I2C_PIN_SDA);
79
80         ao_i2c_transfer(sr);
81
82         ret = USIDR;
83         USIDR = 0xff;
84
85         /* Set SDA to output */
86         I2C_DIR |= (1<<I2C_PIN_SDA);
87
88         return ret;
89 }
90
91 static uint8_t
92 ao_i2c_write_byte(uint8_t byte)
93 {
94         /* Pull SCL low */
95         I2C_PORT &= ~(1<<I2C_PIN_SCL);
96
97         /* Write the byte */
98         USIDR = byte;
99       
100         /* Clock and verify (N)ACK from slave */
101
102         ao_i2c_transfer(I2C_USISR_8BIT);
103
104         if (ao_i2c_get_byte(I2C_USISR_1BIT) & 0x80)
105                 return 0;
106
107         return 1;
108 }
109
110 static uint8_t
111 ao_i2c_read_byte(uint8_t ack)
112 {
113         uint8_t ret;
114
115         /* Read the data */
116         ret = ao_i2c_get_byte(I2C_USISR_8BIT);
117
118         /* Ack it */
119         USIDR = ack;
120         ao_i2c_transfer(I2C_USISR_8BIT);
121
122         return ret;
123 }
124
125 uint8_t
126 ao_i2c_start_bus(uint8_t address)
127 {
128         /* Release SCL to ensure that (repeated) Start can be performed */
129
130         I2C_PORT |= (1<<I2C_PIN_SCL);
131
132         while( !(I2C_PORT & (1<<I2C_PIN_SCL)) )
133                 ;
134         ao_delay_us(T2_TWI);
135
136         /* Generate Start Condition */
137
138         /* Pull SDA low */
139         I2C_PORT &= ~(1<<I2C_PIN_SDA);
140         ao_delay_us(T4_TWI);                         
141
142         /* Pull SCL low */
143         I2C_PORT &= ~(1<<I2C_PIN_SCL);
144
145         /* Raise SDA */
146         I2C_PORT |= (1<<I2C_PIN_SDA);
147
148         return ao_i2c_write_byte(address);
149 }
150
151 static void
152 ao_i2c_stop_bus(void)
153 {
154         /* Pull SDA low. */
155         I2C_PORT &= ~(1<<I2C_PIN_SDA);
156
157         /* Release SCL. */
158         I2C_PORT |= (1<<I2C_PIN_SCL);
159
160         /* Wait for SCL to go high */
161         while( !(I2C_PIN & (1<<I2C_PIN_SCL)) );
162         ao_delay_us(T4_TWI);
163
164         /* Raise SDA */
165         I2C_PORT |= (1<<I2C_PIN_SDA);
166         ao_delay_us(T2_TWI);
167 }
168
169 /* Send bytes over SPI.
170  *
171  * This just polls; the SPI is set to go as fast as possible,
172  * so using interrupts would take way too long
173  */
174 uint8_t
175 ao_i2c_send_bus(void __xdata *block, uint16_t len, uint8_t stop)
176 {
177         uint8_t *d = block;
178
179         while (len--)
180                 if (!ao_i2c_write_byte (*d++))
181                         return 0;
182         if (stop)
183                 ao_i2c_stop_bus();
184         return 1;
185 }
186
187 /* Send bytes over SPI.
188  *
189  * This just polls; the SPI is set to go as fast as possible,
190  * so using interrupts would take way too long
191  */
192 uint8_t
193 ao_i2c_send_fixed_bus(uint8_t d, uint16_t len, uint8_t stop)
194 {
195         while (len--)
196                 if (!ao_i2c_write_byte (d))
197                         return 0;
198         if (stop)
199                 ao_i2c_stop_bus();
200         return 1;
201 }
202
203 /* Receive bytes over SPI.
204  *
205  * Poll, sending zeros and reading data back
206  */
207 uint8_t
208 ao_i2c_recv_bus(void __xdata *block, uint16_t len, uint8_t stop)
209 {
210         uint8_t *d = block;
211
212         while (len--)
213                 *d++ = ao_i2c_read_byte (len ? 0x00 : 0xff);
214         if (stop)
215                 ao_i2c_stop_bus();
216         return 1;
217 }
218
219 /*
220  * Initialize USI
221  *
222  * Chip select is the responsibility of the caller
223  */
224
225 void
226 ao_i2c_init(void)
227 {
228         /* Pull-ups on SDA and SCL */
229         I2C_PORT |= (1<<I2C_PIN_SDA);
230         I2C_PORT |= (1<<I2C_PIN_SCL);
231   
232         /* SCL and SDA are outputs */
233         I2C_DIR  |= (1<<I2C_PIN_SCL);
234         I2C_DIR  |= (1<<I2C_PIN_SDA);
235   
236         USIDR =  0xFF;
237         USICR =  I2C_USICR;
238 }