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